<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>KINTO Tech Blog | キントテックブログ</title>
        <link>https://blog.kinto-technologies.com/</link>
        <description>年齢・性別・国籍問わず多様なメンバーが、トヨタグループのモビリティサービスの世界展開を実現する技術集団として様々な情報を発信します !</description>
        <lastBuildDate>Sun, 14 Jun 2026 03:23:25 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>KINTO Tech Blog | キントテックブログ</title>
            <url>https://blog.kinto-technologies.com/assets/common/thumbnail_default.png</url>
            <link>https://blog.kinto-technologies.com/</link>
        </image>
        <copyright>©KINTO Technologies Corporation. All rights reserved.</copyright>
        <item>
            <title><![CDATA[Microsoft MVP（Microsoft Foundry カテゴリ）を受賞しました]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-06-11-microsoft-mvp-foundry/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-06-11-microsoft-mvp-foundry/</guid>
            <pubDate>Thu, 11 Jun 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[KTCのAIファーストGに所属する野村が、2026年6月にMicrosoft MVP（Microsoft Foundryカテゴリ）を受賞しました。受賞のご報告と、その背景にあった活動についてご紹介します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは。KINTOテクノロジーズ（以下、KTC）の AIファーストG に所属している、野村宏樹です。</p>
<p>このたび、2026年6月1日付で <strong>Microsoft MVP（Most Valuable Professional）を Microsoft Foundry カテゴリ</strong> で受賞しました。Azure 上での生成AI／AIエージェント開発を中心に発信してきた活動を評価いただいたもので、とても光栄に思っています。本記事では、受賞のご報告と、その背景にあったKTCでの活動についてご紹介します。</p>
<p>![Microsoft MVP として届いたトロフィー](/assets/blog/authors/nomura/2026-06-11-microsoft-mvp-foundry/MVP-trophy.jpg =400x)
<em>Microsoft MVP として届いたトロフィー</em></p>
<p>受賞の証として、MVPプロフィールも公開されています。</p>
<p><a href="https://mvp.microsoft.com/ja-JP/mvp/profile/93ebd9f1-a8c0-492c-8cc2-adc7f5f980e9">https://mvp.microsoft.com/ja-JP/mvp/profile/93ebd9f1-a8c0-492c-8cc2-adc7f5f980e9</a></p>
<p><img src="/assets/blog/authors/nomura/2026-06-11-microsoft-mvp-foundry/mvp-profile.png" alt="Microsoft MVP プロフィールページのスクリーンショット">
<em>Microsoft MVP プロフィールページ</em></p>
<h2>Microsoft MVP とは</h2>
<p>Microsoft MVP は、Microsoft 製品・技術に関する深い知見と、技術コミュニティへの継続的な貢献を称えて Microsoft 社が「個人」に授与するアワードです。直近およそ1年間の活動が審査対象で、毎年の更新審査があります。技術領域ごとにカテゴリが分かれており、世界で約 3,000 名が受賞しています。受賞すると、製品の早期アクセスや製品チームと直接つながれるチャネル、年次の Global MVP Summit への招待などの機会があります。</p>
<p>私が受賞した <strong>Microsoft Foundry カテゴリ</strong> は、比較的新しいカテゴリ名です。</p>
<p>:::message
Microsoft Foundry のカテゴリーは、Azure 上で生成AIアプリやAIエージェントを開発するための基盤に関する領域です。Azure AI Foundry がリブランディングされたもので、モデルやエージェントの開発・運用をまとめて扱うエコシステムを指します。
:::</p>
<h2>受賞につながった活動：KTCのカルチャーに支えられて</h2>
<p>今回の受賞は、KTC の <strong>Output文化・登壇文化</strong>、そして全社的に生成AIの業務活用を進めている環境に、大きく後押ししていただいたものでした。2025年8月にKTCへ入社して以来、「やってみたことを外に出す」「登壇して共有する」が当たり前にある雰囲気のなかで、自然と活動を続けることができました。</p>
<p>発信は、大きく2つの軸で行ってきました。</p>
<h3>① KTC事例の共有</h3>
<p>KTCでは2023年から社内向けの生成AIチャットツールを導入し、全社で生成AIの活用を進めています。その現場で得た学びを、たとえば「社内で使うチャットアプリを自分たちで内製する意義」といったテーマで共有してきました。このテーマは、NoMaps 2025（札幌）で当時のチームリーダーである和田颯馬さんと共同で登壇しています。
<a href="https://no-maps.jp/program/tech/121500/">https://no-maps.jp/program/tech/121500/</a></p>
<p>社内で使うものを自分たちの手で作るからこそ、現場のフィードバックを素早く反映でき、業務に本当に必要な形へ磨き込んでいけます。そうした内製ならではの価値を、実体験ベースでお話ししました。</p>
<h3>② ユースケースを軸とした技術活用の共有</h3>
<p>個人的に「面白い」「使えそう」と感じた技術を PoC で試し、ブログにまとめ、少し抽象化したものを登壇してOutputする、というサイクルで発信してきました。テーマは MCP（Model Context Protocol）、マルチエージェント（AutoGen / Microsoft Agent Framework）、Azure AI Foundry エコシステム、ローカルLLM／SLM（Foundry Local・phi-4）など。単に「何ができるか」だけでなく、<strong>どんなユースケースで、どう使うか</strong>まで踏み込むことを意識しています。</p>
<p>コミュニティ活動としては、KTCが主催する <strong>名古屋LLM MeetUp</strong> をはじめ、なごあず（JAZUG 名古屋支部）、JAZUG、すきやねん Azure など各地のコミュニティで登壇させていただきました。2025年度は個人として、ブログ31本・登壇11回ほどの活動になりました。</p>
<p>2025年度の登壇は以下のとおりです。</p>
<table>
<thead>
<tr>
<th>日付</th>
<th>イベント</th>
<th>タイトル</th>
</tr>
</thead>
<tbody><tr>
<td>2025/6/21</td>
<td><a href="https://75az.connpass.com/">なごあず（JAZUG 名古屋支部）</a></td>
<td>AzureでMCPサーバ！！どう活用する？</td>
</tr>
<tr>
<td>2025/7/23</td>
<td><a href="https://kinto-technologies.connpass.com/event/354960/">名古屋LLM MeetUp（KTC主催）</a></td>
<td>チャットアプリ失敗談！製造業業務への生成AI導入</td>
</tr>
<tr>
<td>2025/8/16</td>
<td><a href="https://jazug.connpass.com/event/361970/">JAZUG×なんでもCopilot #jaznancopa</a></td>
<td>Azure AI Foundry Portal デモ</td>
</tr>
<tr>
<td>2025/9/15</td>
<td><a href="https://no-maps.jp/program/tech/121500/">NoMaps 2025（札幌）</a></td>
<td>生成AI最前線：最新トレンドと活用事例（和田颯馬さんと共同）</td>
</tr>
<tr>
<td>2025/11/21</td>
<td><a href="https://kinto-technologies.connpass.com/event/370357/">名古屋LLM MeetUp（KTC主催）</a></td>
<td>AzureでのAIエージェントはここから！Azure Functions × AI</td>
</tr>
<tr>
<td>2025/11/27</td>
<td><a href="https://yonayona.connpass.com/event/374591/">YonaAz</a></td>
<td>AzureでAIエージェント、さて何から始める？</td>
</tr>
<tr>
<td>2025/11/29</td>
<td><a href="https://jazug.connpass.com/event/368296/">JAZUG Shizuoka</a></td>
<td>リアルタイム音声モデル gpt-realtime を使った音声対話ツール</td>
</tr>
<tr>
<td>2025/12/6</td>
<td><a href="https://75az.connpass.com/event/373754/">なごあず（JAZUG 名古屋支部）</a></td>
<td>ローカルとクラウドLLMのハイブリッドAI活用</td>
</tr>
<tr>
<td>2025/12/26</td>
<td><a href="https://sukiyanenazure.connpass.com/event/375556/">すきやねんAzure</a></td>
<td>ハイブリッド構成 Queue Polling</td>
</tr>
<tr>
<td>2026/2/28</td>
<td><a href="https://globalai.community/chapters/tokyo/events/agentcon-tokyo/">AgentCon Tokyo</a></td>
<td>エージェント開発とライフサイクル管理 ～構築から AgentStore 基盤まで～</td>
</tr>
<tr>
<td>2026/3/14</td>
<td><a href="https://75az.connpass.com/event/383389/">なごあず（JAZUG 名古屋支部）</a></td>
<td>gpt-realtime-1.5 モデルでスタックチャン</td>
</tr>
</tbody></table>
<h2>これから</h2>
<p>これからも、<strong>「どう使うか（ユースケース）」を大事にしながら、技術のOutputを続けていきたい</strong>と思っています。AIにより技術のキャッチアップや開発は非常にしやすくなっています。大事なのはその手段をどこにどう使うと価値がでるのか？だと思っています。引き続きAzure・生成AI・AIエージェント領域の技術を突き詰めながら、ユースケースを軸にした発信を続けていきます。</p>
<p>改めて、日々の活動を支えてくれているKTCの環境と、関わってくださったみなさまに感謝します。</p>
<p>発信内容は、個人のZennにもまとめています。よろしければこちらもご覧ください。</p>
<p><a href="https://zenn.dev/nomhiro">https://zenn.dev/nomhiro</a></p>
<h2>最後に</h2>
<p>KINTOテクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
<p>最後までお読みいただき、ありがとうございました。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/nomura/2026-06-11-microsoft-mvp-foundry/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[analyzer のパッチ番号 1 違いで止まった Flutter ビルド：retrofit と custom_lint の依存デッドロック解決記]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-06-02-flutter-build-deadlock-retrofit-custom-lint/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-06-02-flutter-build-deadlock-retrofit-custom-lint/</guid>
            <pubDate>Tue, 02 Jun 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[Flutter SDK のメジャーアップグレード中に遭遇した retrofit と custom_lint の analyzer 依存デッドロックを、pubspec のピン 2 行で解いた記録です。]]></description>
            <content:encoded><![CDATA[<blockquote>
<p>Flutter SDK 3.29 → 3.38 へのアップグレード中に遭遇した <code>retrofit</code> / <code>analyzer</code> / <code>custom_lint</code> の依存衝突を解いた記録です。
「なぜ <code>pub solver</code> が答えを見つけられないのか」から順を追って説明します。</p>
</blockquote>
<hr>
<h2>はじめに</h2>
<p>はじめまして、KINTOテクノロジーズ（KTC）でモバイルアプリ（Flutter）の開発を担当しているHand-Tomiです。</p>
<p>Flutter SDK のメジャーアップグレードを進めていたある日、<code>dart pub get</code> が突然失敗するようになりました。エラーメッセージを読み解くと、<code>retrofit_generator</code> と <code>custom_lint</code> がそれぞれ別々の <code>analyzer</code> バージョンを要求していて、両者が要求する <code>analyzer</code> のバージョン差はわずか 1 パッチ。けれど <code>pub solver</code> ではどうやっても解けない <strong>デッドロック</strong> でした。</p>
<p>本記事では、その原因と解決方法、そしてなぜ <code>dependency_overrides</code> が罠になるのかを順を追って解説します。同じ Flutter プロジェクトで似た衝突に遭遇した方の参考になれば幸いです。</p>
<p>:::message
<code>pub solver</code> は <code>dart pub get</code> の内部で動く依存解決エンジンです。すべての制約を同時に満たすバージョンの組み合わせを探すのが役割で、本記事ではこの後も繰り返し登場します。
:::</p>
<p>:::message
バージョン管理でよく耳にする <strong>SemVer (Semantic Versioning)</strong> は、バージョン番号を <code>MAJOR.MINOR.PATCH</code> の 3 桁で表す規約です。本記事では以降、それぞれ <strong>メジャー</strong>／<strong>マイナー</strong>／<strong>パッチ</strong> と表記します。</p>
<ul>
<li><code>MAJOR</code>（メジャー）：互換性のない変更（壊れる）</li>
<li><code>MINOR</code>（マイナー）：後方互換のある機能追加</li>
<li><code>PATCH</code>（パッチ）：後方互換のあるバグ修正</li>
</ul>
<p>たとえば <code>analyzer 8.4.0</code> → <code>8.4.1</code> は <strong>パッチ</strong> リリースなので、本来なら「コードを変えずに上げても安全」なはずです。本記事の 3 節で、この「はず」が崩れる仕組みを掘り下げます。
:::</p>
<hr>
<h2>TL;DR</h2>
<ul>
<li>Flutter のメジャーアップグレード中に <code>dart pub get</code> が失敗。原因は <code>retrofit_generator</code> と <code>custom_lint_visitor</code> が同じ <code>analyzer</code> に対して別々のバージョンを要求していたこと。</li>
<li>最終的な解：<strong><code>retrofit: ^4.9.2</code> + <code>retrofit_generator: ^10.2.1</code></strong>。<code>pubspec</code> のピン 2 行ですっきり解決します。</li>
<li><code>dependency_overrides</code> には罠があり、推奨しません。<code>pub get</code> は通っても <code>dart_style</code> が知らぬ間に昇格してビルドが壊れます。</li>
<li>この衝突は <strong>構造的な問題</strong> です。<code>analyzer</code> のメジャーが上がるたびに再発します。</li>
</ul>
<hr>
<h2>1. 始まり — 止まってしまったビルド</h2>
<p>Flutter SDK 3.29.2 から 3.38.10 へのメジャーアップグレードを進めていました。<code>flutter_riverpod</code> 2 → 3、<code>freezed</code> 2 → 3、<code>analyzer</code> 6 → 8 といった大きな変更が立て続けに来ていて、いつもなら <code>flutter upgrade</code> のあと <code>dart pub get</code> で済む作業のはずでした。</p>
<p>ところがビルドが止まりました。要点だけ抜き出すと、こういうメッセージです。</p>
<pre><code class="language-text">And because retrofit_generator &gt;=10.2.4 depends on analyzer &gt;=8.4.1 &lt;13.0.0
and custom_lint_core &gt;=0.7.0 depends on custom_lint_visitor ^1.0.0,
if retrofit_generator &gt;=10.2.4 and custom_lint_core &gt;=0.7.0 then analyzer 9.0.0.

And because custom_lint &gt;=0.8.1 depends on both analyzer ^8.0.0 and custom_lint_core 0.8.1,
custom_lint &gt;=0.8.1 is incompatible with retrofit_generator &gt;=10.2.4.

So, because app depends on both retrofit_generator ^10.2.5 and custom_lint ^0.8.1,
version solving failed.
</code></pre>
<p><code>pub solver</code> が答えを見つけられなかったのです。片方を上げればもう片方が壊れ、下げればまた別のところが壊れる。普通のバージョン衝突ではなく、<strong>デッドロック</strong> でした。</p>
<hr>
<h2>2. 誰と誰が戦っているのか</h2>
<p>主な登場人物は次のとおりです。</p>
<ul>
<li><code>analyzer</code> — Dart コードの静的解析エンジン（共有資源）</li>
<li><code>retrofit_generator</code> — <code>.g.dart</code> を生成するコードジェネレータ</li>
<li><code>custom_lint</code> / <code>custom_lint_core</code> / <code>custom_lint_builder</code> — lint プラグインのランナーと、その builder</li>
<li><code>custom_lint_visitor</code> — analyzer の AST を訪問する visitor 実装</li>
</ul>
<p><img src="/assets/blog/authors/semyeong/2026-06-02-flutter-build-deadlock-retrofit-custom-lint/02-dependency-diagram-ja.png" alt="依存関係図 — analyzer を真ん中に置き、retrofit_generator 陣営と custom_lint_visitor 陣営が両側から引っ張り合う構造"></p>
<p>問題の核心は、<strong><code>analyzer</code> という共有資源</strong> です。両陣営が同じ <code>analyzer</code> に対して別々のバージョンを要求しています。</p>
<ul>
<li><code>retrofit_generator 10.2.3+</code> → 「<code>analyzer</code> 8.4.1 以上が必要」</li>
<li><code>custom_lint_visitor 1.0.0+8.4.0</code> → 「<code>analyzer</code> 8.4.0 ちょうど」</li>
</ul>
<p>差は 8.4.0 と 8.4.1、<strong>わずか 1 パッチ</strong>。これだけでビルドが止まるのです。</p>
<p>なお、1 節のエラーメッセージ末尾には <code>analyzer 9.0.0</code> も登場しますが、これは <code>custom_lint_visitor</code> に <code>1.0.0+8.4.0</code> のほかに <code>1.0.0+9.0.0</code> ビルドも存在し、<code>custom_lint_visitor: ^1.0.0</code> を介した solver が両方を順に試した結果です。どちらも <code>analyzer</code> をバージョン固定で要求する点は同じなので、本質的な対立点は変わりません。</p>
<hr>
<h2>3. なぜ 1 パッチ差で壊れるのか</h2>
<p>ここで 2 つの事実が噛み合います。</p>
<h3>事実 1. <code>analyzer</code> の <strong>内部 API</strong> はパッチリリースでも変わる</h3>
<p>SemVer の約束は「パッチリリースでは <strong>公開 API は後方互換</strong> を保つ」です。ところが <code>custom_lint_visitor</code> が使っているのは <code>analyzer</code> の公開 API ではなく、<strong>内部 API</strong>（AST ノードの型など、パッケージの内部実装に属するもの）です。SemVer の保護範囲外なので、メジャー・パッチを問わず、型が消えたり、シグネチャが変わったりするのは珍しくありません。</p>
<p>後ほど引用するメンテナ自身の言葉を借りれば <em>&quot;some more unique APIs&quot;</em> — SemVer の通常のセーフティネットの外側で扱う必要のある API です。本記事の 5 節で扱う <code>dart_style 3.1.9</code> の <code>LabelReference</code> / <code>NamedArgument</code> 欠落も、「内部 API は SemVer 保護外」という同じ構造から生じる事例の 1 つです（こちらは analyzer のメジャー間で起きたケースで、3 節でいうパッチ単位の例ではありません）。</p>
<h3>事実 2. <code>custom_lint_visitor</code> はそれゆえ <strong>バージョンを完全に固定</strong> する</h3>
<p>これを知っているからこそ、<code>custom_lint_visitor</code> のメンテナは意図的に <code>analyzer</code> を完全に固定しています。パッケージ名そのものがその証拠です。</p>
<pre><code class="language-text">custom_lint_visitor 1.0.0+8.4.0
                          ^^^^^
                          analyzer のバージョン
</code></pre>
<p><code>pubspec.yaml</code> の中でも <code>analyzer: 8.4.0</code>（<code>^</code>(caret) なしのバージョン固定）になっています。</p>
<p>これがミスならば PR 一本で解決する話ですが、これは<a href="https://github.com/invertase/dart_custom_lint/issues/345">関連する GitHub issue</a> でメンテナ自身が明言した <strong>意図的な方針</strong> です。</p>
<blockquote>
<p>&quot;Custom_lint depends on some more unique APIs. I&#39;ll probably stick to requiring 8.0 for it.&quot;
— <a href="https://github.com/invertase/dart_custom_lint/issues/345">invertase/dart_custom_lint#345</a></p>
</blockquote>
<p>発言の直接の意図は「メジャー（<code>8.0</code>）単位で範囲を狭めて require する」ですが、その方針が実際のリリースにも反映されており、リリースされる <code>custom_lint_visitor</code> の各バージョンでは <code>analyzer: 8.4.0</code> のように <strong>バージョンが完全に固定</strong> されています（<code>1.0.0+8.4.0</code> → <code>analyzer: 8.4.0</code>、<code>^</code>(caret) なし）。つまり <strong>意図された決定</strong> の結果としてバージョン固定が生まれており、両者が同じ <code>analyzer</code> バージョンを要求するビルドが揃うまでは <code>pub solver</code> だけでは解けません。</p>
<hr>
<h2>4. 効果のなかった試みリスト</h2>
<p>問題が難しく見えると、人は迂回路を探したくなります。しかし直感的に思いつく次の試みはどれも徒労でした。</p>
<table>
<thead>
<tr>
<th>試み</th>
<th>なぜ失敗するのか</th>
</tr>
</thead>
<tbody><tr>
<td><code>retrofit_generator</code> を最新（10.2.5）に上げる</td>
<td><code>analyzer</code> 8.4.1 を要求するため <code>custom_lint_visitor</code> と衝突</td>
</tr>
<tr>
<td><code>retrofit</code> の上限を狭めてみる（<code>&lt;4.9.1</code>）</td>
<td>generator 10.2.1 を選びたい動機は 6 節で詳述しますが、retrofit を 4.9.0 系に下げると今度は generator 10.2.1 のソースが <code>retrofit</code> 4.9.2 の新 enum 値（<a href="https://github.com/trevorwang/retrofit.dart/blob/retrofit-v4.9.2/retrofit/lib/http.dart#L31"><code>Parser.DartMappable</code></a>）を参照しているため、generator 自体の AOT コンパイルが <code>Member not found</code> で失敗します</td>
</tr>
<tr>
<td><code>custom_lint_builder</code> のダウングレード</td>
<td>analyzer のメジャーが 7.x まで引きずり下ろされ、今度は <code>retrofit_generator</code>（analyzer 8.x 依存）と別の衝突を起こす — freezed / riverpod など analyzer 8 に依存するパッケージがある環境でも同様</td>
</tr>
<tr>
<td><code>analysis_options.yaml</code> の lint を切る</td>
<td>（<code>dependency_overrides</code> で solver を通した後でも）<code>lint</code> を切って <code>exclude: &#39;**/*.g.dart&#39;</code> を両方適用しても、generator AOT 段階で発生する <code>Member not found</code> 系のコンパイルエラーはそのまま発生する</td>
</tr>
<tr>
<td><code>dependency_overrides</code> で強制固定</td>
<td><code>pub get</code> は通るがビルド段階で <code>dart_style</code> が壊れる（5 節を参照）</td>
</tr>
</tbody></table>
<p>特に最後の項目、<code>dependency_overrides</code> は罠が深いので、別途取り上げる価値があります。</p>
<blockquote>
<p>上の表の各行は、本記事と同じリポジトリの検証成果物（<code>reports/01-reproduction.md</code>、<code>reports/03-overrides-fallback.md</code>）で実際のコマンド出力として再現されています。</p>
</blockquote>
<hr>
<h2>5. <code>dependency_overrides</code> という罠</h2>
<p>最初の発想は単純です。「2 つのパッケージが争うなら、こちらで強制的に片方のバージョンを打ち込もう」。</p>
<pre><code class="language-yaml">dependency_overrides:
  retrofit: ^4.9.2
  retrofit_generator: ^10.2.5
  analyzer: ^8.4.1
</code></pre>
<p>驚くことに <code>dart pub get</code> は通ります。なぜなら <code>dependency_overrides</code> は <strong>オーバーライドした依存に対する他パッケージからの制約を黙らせ</strong>、solver の選択肢を広げるからです。</p>
<p>ところが <code>dart run build_runner build</code> の段階で、突然ビルドが壊れます。</p>
<pre><code class="language-text">Failed to build build_runner:build_runner:
  .../dart_style-3.1.9/lib/src/front_end/ast_node_visitor.dart:1279:28:
    Error: Type &#39;LabelReference&#39; not found.
</code></pre>
<p><code>dart_style</code> です。私たちが明示的に依存もしていないパッケージです。</p>
<p>理由を辿ってみると、次のようになっています。</p>
<p><img src="/assets/blog/authors/semyeong/2026-06-02-flutter-build-deadlock-retrofit-custom-lint/03-silent-promotion-chain-ja.png" alt="サイレント昇格の因果連鎖 — override が制約を黙らせる → solver が自由 → 最新 dart_style 3.1.9 を自動選択 → analyzer 13 の AST 型を参照 → 強制された `8.x` 系にないのでコンパイル失敗"></p>
<ol>
<li><code>dependency_overrides</code> がオーバーライドした依存（<code>retrofit</code>、<code>retrofit_generator</code>、<code>analyzer</code>）に対する他パッケージからの制約を黙らせ、solver の選択肢が広がる</li>
<li>その結果、solver は transitive で <code>dart_style</code> の最新版（<code>3.1.9</code>）を自動的に選ぶ</li>
<li><code>dart_style 3.1.9</code> は <code>analyzer</code> の最新メジャーで導入された AST 型（<code>LabelReference</code>、<code>NamedArgument</code>、<code>BlockEnumBody</code> など）を参照している</li>
<li>しかし私たちは override で <code>analyzer ^8.4.1</code>（解決範囲は <code>&gt;=8.4.1 &lt;9.0.0</code>）を強制している</li>
<li>→ その範囲には存在しない型を参照しようとしてコンパイル失敗</li>
</ol>
<p>要するに <strong><code>dependency_overrides</code> は制約を黙らせるだけで、互換性を保証しません。</strong> 一箇所を押さえるとまた別の場所から噴き出します。これを抑え込もうとすると <code>dart_style</code> もピン、<code>custom_lint_visitor</code> も確認…… と際限なく増えていきます。</p>
<hr>
<h2>6. 結局解けた方法 — シンプルなピン調整 2 行</h2>
<p>問題を逆から見ると答えが見えます。</p>
<ul>
<li>私たちが変えられないもの：<code>custom_lint_visitor 1.0.0+8.4.0</code> → <code>analyzer 8.4.0</code>（正確には <code>custom_lint_visitor</code> 自体は <code>1.0.0+9.0.0</code> ビルドも存在しますが、それを選ぶと <code>custom_lint</code> 本体が要求する <code>analyzer ^8.0.0</code> と衝突するため、<code>custom_lint</code> を使う限り 8.4.0 ピン側に寄せるしかありません。1 節のエラーメッセージにも <code>custom_lint &gt;=0.8.1 depends on ... analyzer ^8.0.0</code> として現れています）</li>
<li>私たちが変えられるもの：<code>retrofit_generator</code> のバージョン</li>
</ul>
<p>であれば「<code>analyzer 8.4.0</code> でも動く最新の <code>retrofit_generator</code>」を探せばよいわけです。</p>
<p><code>retrofit_generator</code> のバージョン別要求を表にまとめると：</p>
<p><img src="/assets/blog/authors/semyeong/2026-06-02-flutter-build-deadlock-retrofit-custom-lint/04-version-matrix-ja.png" alt="retrofit_generator バージョン × analyzer 要求範囲のマトリクス — retrofit 4.9.2 互換 ∩ analyzer 8.4.0 互換の交点は 10.2.1 のみ"></p>
<table>
<thead>
<tr>
<th><code>retrofit_generator</code></th>
<th><code>analyzer</code> 要求</th>
<th><code>logError</code> の呼び出し形式</th>
</tr>
</thead>
<tbody><tr>
<td>10.2.0</td>
<td><code>&gt;=7.7.1 &lt;10.0.0</code></td>
<td>positional 4 個</td>
</tr>
<tr>
<td><strong>10.2.1</strong></td>
<td><strong><code>&gt;=8.0.0 &lt;10.0.0</code></strong></td>
<td><strong>named (<code>response: _result</code>)</strong></td>
</tr>
<tr>
<td>10.2.3</td>
<td><code>&gt;=8.4.1 &lt;11.0.0</code></td>
<td>named</td>
</tr>
<tr>
<td>10.2.4 / 10.2.5</td>
<td><code>&gt;=8.4.1 &lt;13.0.0</code></td>
<td>named</td>
</tr>
</tbody></table>
<blockquote>
<p>補足: <code>retrofit.dart</code> は monorepo で、<code>retrofit_generator</code>（タグ <code>v10.x.x</code>）と <code>retrofit</code>（タグ <code>retrofit-vX.Y.Z</code>）を別系統で管理しています。本記事のリンクで prefix が混在するのはそのためです。なお <code>10.2.2</code> はリリースが存在しますが、本記事の議論には影響しないため上の表では省略しています。</p>
</blockquote>
<p>答えが見えます。<strong>10.2.1</strong> です。</p>
<ul>
<li><code>analyzer 8.4.0</code> と互換 ✓（<code>&gt;=8.0.0</code> なので）</li>
<li><code>retrofit 4.9.2</code> の <code>{Response? response}</code> named optional シグネチャと互換 ✓</li>
<li><code>dependency_overrides</code> 不要 ✓</li>
</ul>
<pre><code class="language-yaml:pubspec.yaml">dependencies:
  retrofit: ^4.9.2

dev_dependencies:
  retrofit_generator: ^10.2.1
</code></pre>
<p>これだけです。<code>^10.2.1</code> というキャレット範囲を書いても、10.2.3+ は <code>analyzer 8.4.1</code> を要求してくるので自動的に候補から外れ、実効的に 10.2.1 が選ばれます。</p>
<blockquote>
<p>ちなみに <code>retrofit_generator 10.2.1</code> と <code>10.2.5</code> の <strong><code>logError</code> の呼び出しシグネチャは同一</strong> です。generator のソース（<code>lib/src/generator.dart</code>）を直接比較しても、両バージョンとも <code>&#39;$_errorLoggerVar?.logError(e, s, $_optionsVar, response: $_resultVar);&#39;</code> という同一の出力テンプレートを使っています（<a href="https://github.com/trevorwang/retrofit.dart/blob/v10.2.1/generator/lib/src/generator.dart#L3777">v10.2.1#L3777</a> / <a href="https://github.com/trevorwang/retrofit.dart/blob/v10.2.5/generator/lib/src/generator.dart#L3849">v10.2.5#L3849</a>）。10.2.5 には <code>Stream&lt;Uint8List&gt;</code> / <code>Stream&lt;String&gt;</code> 処理の検証など別の機能が追加されていますが、本記事が扱う retrofit ↔ analyzer インターフェイスそのものは変更されていません。つまり 10.2.1 に留まることは、コア機能面で損ではありません。</p>
</blockquote>
<hr>
<h2>7. それで私たちが学んだこと</h2>
<p>この件が片付いたとき、最初に浮かんだ考えは <strong>「次のメジャーアップグレードでまた出くわすだろうな」</strong> でした。</p>
<p>理由は 2 つです。</p>
<ol>
<li><strong><code>analyzer</code> の内部 API は今後もパッチで変わり続ける。</strong> それが AST を扱う解析器パッケージの本質です。</li>
<li><strong><code>custom_lint_visitor</code> は今後もバージョンを完全に固定し続ける。</strong> メンテナが意図的に取っている方針だからです。</li>
</ol>
<p>つまりこの衝突は <strong>構造的</strong> です。本記事を書いている 2026 年春の時点で、<code>analyzer</code> はすでに 13.0.0 までリリースされており、<code>custom_lint_visitor</code> のピンラインは <code>1.0.0+9.0.0</code> までしか追いついていません。</p>
<p><img src="/assets/blog/authors/semyeong/2026-06-02-flutter-build-deadlock-retrofit-custom-lint/05-analyzer-release-timeline-ja.png" alt="analyzer のメジャーリリースのタイムライン（8 → 9 → 10 → 11 → 12 → 13）の上に custom_lint_visitor のピン（1.0.0+8.4.0、1.0.0+9.0.0）を重ねた図 — custom_lint_visitor がまばらに追従するパターン"></p>
<p><code>custom_lint_visitor</code> がメジャーごとに 1 〜 2 個のビルドだけ追いつくこのまばらなパターンが続く限り、<code>analyzer</code> がさらに一段上がるたびに同じ形で再発します。実際、<a href="https://github.com/trevorwang/retrofit.dart/issues/911"><code>retrofit.dart</code> の issue tracker</a> を見ると <code>analyzer 10.0</code> の段階でも同じシグネチャミスマッチが報告されています。</p>
<p>であれば、私たちにできることは：</p>
<ul>
<li><strong>自然な解決を先に試す。</strong> ピン 1 つの調整で解けるかをまず確認する。シンプルな答えがあるのに <code>dependency_overrides</code> を最初に持ち出さない。</li>
<li><strong><code>dependency_overrides</code> は最後の手段。</strong> 黙らせるだけでは解決にならない。一箇所を押さえると別の場所から噴き出す。</li>
<li><strong>プレイブックを残す。</strong> 次の人（あるいは 6 か月後の自分）が同じ罠にはまらないように。メカニズムと意思決定ツリーを一緒に書き残しておく（本記事自体がそのプレイブックの 1 つです）。</li>
</ul>
<hr>
<h2>最後に</h2>
<p>ここまで読んでいただき、ありがとうございます。</p>
<p><code>analyzer</code> のような共有依存をめぐる衝突は、一見すると「2 つのパッケージのバグ」に見えますが、実際にはエコシステム側の構造的な制約が背景にあります。同じ罠に出会ったときに「最初に何を疑い、何を試し、どこで止まるか」を整理できれば、次は数時間で解けるはずです。</p>
<p>皆さんの参考になれば幸いです。</p>
<hr>
<h2>参考</h2>
<ul>
<li><a href="https://github.com/invertase/dart_custom_lint/issues/345"><code>dart_custom_lint</code> #345 — Support analyzer 8</a></li>
<li><a href="https://github.com/trevorwang/retrofit.dart/issues/911"><code>retrofit.dart</code> #911 — analyzer 10.0.0+ compatibility</a></li>
<li><a href="https://pub.dev/packages/retrofit_generator/versions">pub.dev — <code>retrofit_generator</code> バージョン別依存関係</a></li>
<li><a href="https://github.com/trevorwang/retrofit.dart/blob/retrofit-v4.9.2/retrofit/lib/http.dart#L17-L34"><code>retrofit</code> 4.9.2 — <code>Parser.DartMappable</code> enum 追加箇所</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/semyeong/2026-06-02-flutter-build-deadlock-retrofit-custom-lint/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[テックブログの「関連記事レコメンド」をローカル Embedding で再構築した話]]></title>
            <link>https://blog.kinto-technologies.com/posts/torii-techblog-related-content-gen/</link>
            <guid>https://blog.kinto-technologies.com/posts/torii-techblog-related-content-gen/</guid>
            <pubDate>Mon, 01 Jun 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[テックブログの「関連する記事」を Go + Ollama + Qwen3-Embedding-0.6B で再構築しました。Python + Azure OpenAI から、ローカルで動く Embedding ベースの類似度計算に移行した過程を書いています。PoC でのモデル選定、テキスト切り詰めの品質検証、num_ctx のサイレントトランケーション問題、SHA-256 差分キャッシュの設計など。]]></description>
            <content:encoded><![CDATA[<p>※本記事は Claude Code との協働で執筆し、人間がレビューの上投稿しています。</p>
<h2>1. はじめに</h2>
<p>こんにちは、共通サービス開発グループの鳥居(<a href="https://x.com/yu_torii">@yu_torii</a>)です。</p>
<p>前回の記事では、Slack 上で LLM を活用する社内チャットボットの実装事例を紹介しました。</p>
<p>@<a href="https://blog.kinto-technologies.com/posts/torii-ai_tool_slack/">card</a></p>
<p>今回は、このテックブログの「関連する記事」と「関連する求人」機能をゼロから再構築した話をします。</p>
<h3>「関連する記事」「関連する求人」とは</h3>
<p>各記事ページの下部に、2つのレコメンドセクションがあります。</p>
<ul>
<li>関連する記事: 現在読んでいる記事と内容が近い記事を最大12件表示</li>
<li>関連する求人: 記事の技術領域に関連する KINTO Technologies の求人情報を最大8件表示</li>
</ul>
<p>読者が興味のある技術領域を深掘りする導線であり、過去の記事の発見にもつながります。採用への接点でもあります。</p>
<h3>仕組みの基本：Embedding とコサイン類似度</h3>
<p>この機能の核は <strong>Embedding</strong>（埋め込みベクトル）です。Embedding モデルにテキストを入力すると、その意味を表す数百〜数千次元の数値ベクトルが返ってきます。意味的に近いテキスト同士は、ベクトル空間上で近い位置に配置されます。</p>
<p>2 つのベクトルの「近さ」を測る指標が<strong>コサイン類似度</strong>です。値が 1 に近いほど意味が近く、0 に近いほど無関係（直交）です。すべての記事を Embedding し、ペアごとにコサイン類似度を計算してスコアの高い順に並べれば、「関連する記事」のランキングが得られます。</p>
<h3>旧システムの課題</h3>
<p>この機能は以前、Python + Azure OpenAI の Embedding API で実装されていました。運用を続ける中で 3 つの問題が出てきました。</p>
<ol>
<li>差分更新が無い。毎回全記事を再 Embed</li>
</ol>
<p>CI が走るたびに全記事（当時 900 件超）を Azure OpenAI に送って Embedding していました。1 記事の追加でも全件再処理が走り、ビルド時間の大半を占めていました。</p>
<ol start="2">
<li>Azure OpenAI の 429 (Rate Limit) エラーが頻発</li>
</ol>
<p>900 件超の記事を一気に送ると、Azure OpenAI のレート制限に頻繁にヒットしていました。リトライロジックを入れてもタイミング次第で CI が失敗し、再実行が必要になることも珍しくありませんでした。</p>
<ol start="3">
<li>外部 API 依存 = コスト増加</li>
</ol>
<p>Embedding API の呼び出し回数がビルドのたびに積み上がり、コストが増え続けていました。記事数が増えるほど状況は悪化する構造です。</p>
<h3>今回やったこと</h3>
<p>これらの問題を解決するため、Go + Ollama（ローカル Embedding）でシステムを一から再構築しました。</p>
<p>SHA-256 ハッシュで変更記事だけ再 Embed する差分更新と、Ollama による CI ランナー上でのローカル実行（外部 API 呼び出しゼロ）で、旧システムの 3 つの課題を解消しました。</p>
<p>PoC でのモデル選定からパフォーマンス最適化、CI/CD パイプラインの構築まで、実装の全体像を書きます。開発には Claude Code を使いました（おまけで触れます）。</p>
<hr>
<h2>この記事で得られること</h2>
<ul>
<li>Go + Ollama + Qwen3-Embedding でローカル Embedding による類似度計算を組む方法</li>
<li>Ollama <code>num_ctx</code> のサイレントトランケーション（無警告の文字切り詰め）問題</li>
<li>事前正規化と min-heap Top-K によるコサイン類似度ランキングの効率化</li>
<li>SHA-256 差分キャッシュで変更記事だけ再 Embed する仕組み</li>
</ul>
<p>:::message
この記事の内容は執筆時点（2026年4月）の実装に基づいています。Ollama や Qwen3-Embedding のバージョンアップにより、API の仕様やパフォーマンス特性が変わる可能性があります。また、記事中のベンチマーク値は GitHub Actions ランナーでの計測結果であり、環境によって異なります。
:::</p>
<hr>
<h2>2. PoC 検証とモデル選定</h2>
<p>旧システムの課題（セクション 1 で述べた 429 エラー・全量実行・コスト増加）を解決するため、ローカル Embedding への移行を決めました。Go で使える Embedding ライブラリを 3 つの方式で PoC 検証しました。</p>
<h3>3 つの PoC アプローチ</h3>
<p>方式 1: hugot（Pure Go ONNX ランタイム）</p>
<p><a href="https://github.com/knights-analytics/hugot">knights-analytics/hugot</a> は Go ネイティブの ONNX ランタイムで、bge-m3 や Qwen3 の ONNX モデルを直接実行できます。cgo 不要ですが、ONNX モデルファイルのサイズが巨大（bge-m3 で約 2.2GB）で、CI 環境でのダウンロードとメモリ管理に課題がありました。</p>
<p>方式 2: kelindar/search（llama.cpp via purego）</p>
<p><a href="https://github.com/kelindar/search">kelindar/search</a> は一見 Pure Go に見えますが、内部では <code>purego</code> 経由で llama.cpp のバイナリを呼び出しています。cgo は使っていませんが、実質的に llama.cpp バイナリへの外部依存がありました。「cgo 不要」の表面的な特徴に惑わされかけた案件です。</p>
<p>方式 3: Ollama API（HTTP クライアント）</p>
<p>選んだのは Ollama の HTTP API を Go クライアントから呼ぶ方式です。</p>
<pre><code class="language-go:cmd/related-content-gen-poc-ollama/main.go">client, err := api.ClientFromEnvironment()
if err != nil {
    slog.Error(&quot;Ollama クライアント作成失敗&quot;, &quot;error&quot;, err)
    os.Exit(1)
}

resp, err := client.Embed(ctx, &amp;api.EmbedRequest{
    Model: model,
    Input: testTexts,
})
</code></pre>
<h3>比較表</h3>
<table>
<thead>
<tr>
<th>方式</th>
<th>cgo</th>
<th>モデル管理</th>
<th>バッチ対応</th>
<th>コンテキスト制御</th>
<th>判定</th>
</tr>
</thead>
<tbody><tr>
<td>hugot (ONNX)</td>
<td>不要</td>
<td>手動</td>
<td>○</td>
<td>×</td>
<td>△ モデルサイズ問題</td>
</tr>
<tr>
<td>kelindar (llama.cpp)</td>
<td>purego 経由で不要に見えるが llama.cpp バイナリ依存</td>
<td>手動</td>
<td>×</td>
<td>×</td>
<td>× 実質外部依存</td>
</tr>
<tr>
<td>Ollama API</td>
<td>不要</td>
<td>自動</td>
<td>○</td>
<td>○ (<code>num_ctx</code>)</td>
<td>◎</td>
</tr>
</tbody></table>
<h3>選定の決め手</h3>
<p>cgo 不要で <code>GOOS=linux GOARCH=arm64 go build</code> 一発のクロスコンパイルが壊れない。Ollama がモデルのダウンロードからライフサイクル管理まで担う。バッチ Embed API で複数テキストを一度に送信できる。<code>num_ctx</code> でコンテキストウィンドウを明示制御できる。</p>
<h3>なぜ Qwen3-Embedding-0.6B か</h3>
<p>Qwen3-Embedding-0.6B を選んだ理由は、2025 年リリースの最新モデルで、量子化後 639MB と CI ランナーのメモリに収まるサイズだったこと。1024 次元ベクトルで表現力と計算量のバランスが良い。日本語・英語のバイリンガルサポートは、当ブログの運用上の必須要件でした。RAG の検索精度が求められるタスクではなく関連記事の推薦用途なので、最高精度モデルは不要です。</p>
<p>:::details 量子化とは
量子化（Quantization）は、モデルの重み（パラメータ）を元の精度（通常 float16 = 16bit）からより少ないビット数（8bit、4bit など）に変換する手法です。精度はわずかに低下しますが、モデルサイズとメモリ使用量を大幅に削減できます。</p>
<p>Qwen3-Embedding-0.6B は Ollama で <a href="https://huggingface.co/Qwen/Qwen3-Embedding-0.6B-GGUF">Q8_0（8bit 量子化）</a>として配布されており、595M パラメータで 639MB。一方、bge-m3 は F16（16bit）配布のため、パラメータ数はほぼ同じ（568M）でもサイズが 1.2GB と約 2 倍になります。
:::</p>
<p>:::message
PoC の段階では bge-m3 も候補でしたが、モデルサイズだけでなくベンチマークでも Qwen3 が優位でした。<a href="https://qwenlm.github.io/blog/qwen3-embedding/">MTEB ベンチマーク</a>の英語検索（61.82 vs 57.03）、多言語検索（64.64 vs 58.36）、コード検索（75.41 vs 41.38）で Qwen3-Embedding-0.6B が上回っています。bge-m3 が優位なのは長文検索（MLDR: 59.51 vs 50.26）ですが、先頭 4000 文字に切り詰める本システムでは該当しません。Ollama でのモデルサイズも約半分（639MB vs 1.2GB）で、CI キャッシュの効率も含めて総合的に Qwen3 を選択しました。
:::</p>
<hr>
<h2>3. アーキテクチャの全体像</h2>
<h3>パイプライン</h3>
<pre><code class="language-mermaid">flowchart LR
    A[&quot;_posts/*.md&quot;] --&gt; B[&quot;Markdown&lt;br&gt;クリーニング&quot;]
    B --&gt; C[&quot;Ollama Embed API&lt;br&gt;(Qwen3-Embedding)&quot;]
    C --&gt; D[&quot;SHA-256&lt;br&gt;キャッシュ&quot;]
    D --&gt; E[&quot;コサイン類似度&lt;br&gt;ランキング&quot;]
    E --&gt; F[&quot;related_posts.json&quot;]
</code></pre>
<p>Markdown をクリーニングして Ollama で Embedding を取得し、コサイン類似度でランキングして JSON を出力します。</p>
<h3>パッケージ構成</h3>
<pre><code class="language-text">cmd/related-content-gen/
├── main.go                          # CLI エントリポイント
├── internal/
│   ├── markdown/                    # Markdown パース・クリーニング
│   │   ├── cleaner.go              #   frontmatter 除去、URL/assets 除去
│   │   └── parser.go               #   _posts/*.md の読み込み
│   ├── embedding/                   # Ollama クライアント・キャッシュ
│   │   ├── client.go               #   Embed API ラッパー（num_ctx 制御）
│   │   └── cache.go                #   SHA-256 ハッシュベースの差分更新
│   ├── similarity/                  # 類似度計算・ランキング
│   │   ├── cosine.go               #   コサイン類似度（テスト用）
│   │   └── ranking.go              #   L2正規化 + dotProduct、min-heap Top-K
│   └── output/                      # JSON 出力
│       └── json.go                 #   UTF-8、4スペースインデント、HTMLエスケープなし
└── go.mod
</code></pre>
<p><code>internal</code> パッケージに分離することで、各パッケージが単一責任を持ち、独立してテスト可能になっています。</p>
<h3><code>run()</code> 関数のパイプライン</h3>
<p>メイン処理は <code>run()</code> 関数に集約されています。</p>
<pre><code class="language-go:main.go">func run(...) error {
    // 1. 記事の読み込みとクリーニング
    posts, err := markdown.ParsePosts(postsDir)

    // 2. Ollama クライアント作成
    client, err := embedding.NewClient(ollamaURL, model, numCtx)

    // 3. キャッシュ読み込み → 不要エントリ削除 → 変更記事検出
    cache, err := embedding.LoadCache(cacheFile)
    cache.Prune(posts)
    dirty := cache.FindDirty(posts, model)

    // 4. 変更分のみ Embed（1件ずつ処理して都度キャッシュ保存）
    for _, p := range dirty {
        vectors, err := client.Embed(ctx, []string{text})
        cache.Entries[p.Slug] = embedding.CacheEntry{...}
        cache.Save(cacheFile) // 中断耐性のため毎回保存
    }

    // 5. コサイン類似度でランキング
    rankings := similarity.RankRelatedPosts(postVectors, 12)

    // 6. JSON 出力
    output.WriteJSON(outPath, postsOutput)
}
</code></pre>
<h3>Next.js フロントエンドとの連携</h3>
<p>出力される JSON は Next.js の <code>getStaticProps</code> でビルド時に読み込まれます。</p>
<ul>
<li><code>static/related_posts/related_posts.json</code> → <code>lib/related_posts.ts</code> が読み込み</li>
</ul>
<p>フロントエンド側では、JSON に関連記事データがあればそれを使い、無ければカテゴリベースのフォールバックに切り替わります。Go CLI とフロントエンドの間の契約は、この JSON スキーマだけです。</p>
<hr>
<h2>4. Markdown のクリーニングと前処理</h2>
<p>当ブログの記事は <a href="https://zenn.dev/zenn/articles/markdown-guide">Zenn Markdown</a>（<code>:::message</code>、<code>:::details</code>、<code>@[card]()</code> など）で書かれています。各記事ファイルの先頭には YAML frontmatter（タイトル、著者、公開日、カテゴリなどのメタ情報）があり、これらをそのまま Embed するとノイズになります。</p>
<h3>クリーニングパイプライン</h3>
<ol>
<li>frontmatter の分離: <code>---</code> で囲まれた YAML ヘッダーからタイトルだけ抽出し、残りのメタ情報（author, date, category 等）は除去</li>
<li>URL の除去: <code>http://</code> / <code>https://</code> で始まるすべての URL を除去</li>
<li>アセットリンクの除去: <code>/assets/</code> を含むリンク（画像パスなど）を除去</li>
</ol>
<p>クリーニングのエントリポイントは <code>CleanMarkdown</code> 関数で、frontmatter からタイトルを抽出しつつ、本文のノイズを除去します。frontmatter パースには <code>strings.Cut</code> を使い、<code>---</code> デリミタ間の YAML を <code>gopkg.in/yaml.v3</code> で解析しています。</p>
<p>:::details コードの詳細（cleaner.go / parser.go）</p>
<pre><code class="language-go:internal/markdown/cleaner.go">var (
    reURL   = regexp.MustCompile(`https?://[^\s)\]&gt;]+`)
    reAsset = regexp.MustCompile(`!?\[[^\]]*\]\(/assets/[^)]+\)|/assets/[^\s)]+`)
)

func CleanMarkdown(raw []byte) (title, content string) {
    s := string(raw)
    if len(s) == 0 {
        return &quot;&quot;, &quot;&quot;
    }
    title, body := splitFrontmatter(s)
    body = removeURLs(body)
    body = removeAssetLinks(body)
    return title, body
}

func splitFrontmatter(s string) (title, body string) {
    const delimiter = &quot;---&quot;
    _, after, ok := strings.Cut(s, delimiter)
    if !ok { return &quot;&quot;, s }
    before, after, ok := strings.Cut(after, delimiter)
    if !ok { return &quot;&quot;, s }
    var fm frontmatter
    if err := yaml.Unmarshal([]byte(before), &amp;fm); err == nil {
        title = fm.Title
    }
    return title, after
}
</code></pre>
<pre><code class="language-go:internal/markdown/parser.go">type Post struct {
    Slug    string // ファイル名から .md を除去
    Title   string // frontmatter の title フィールド
    Content string // クリーニング済み本文
}

func ParsePosts(dir string) ([]Post, error) {
    entries, err := os.ReadDir(dir)
    // ... *.md ファイルを読み込み、CleanMarkdown で処理
    return posts, nil
}
</code></pre>
<p>:::</p>
<p>ポイントは、Embedding 時にタイトルをテキストの先頭に結合すること（<code>title + &quot;\n&quot; + content</code>）。セクション 5.1 で述べますが、Embedding モデルはテキストの先頭部分を重視する傾向があるため、タイトルの情報がベクトルに強く反映されます。</p>
<hr>
<h2>5. Embedding の最適化</h2>
<p>Embedding 処理の高速化で 2 つの工夫をしました。</p>
<ul>
<li><strong>5.1</strong>: テキストを先頭 4,000 文字に切り詰めて処理時間を約 1/8 に短縮</li>
<li><strong>5.2</strong>: 実装中に踏んだ Ollama <code>num_ctx</code> の無警告切り詰め問題</li>
</ul>
<h3>5.1 テキスト切り詰めの最適化</h3>
<p>最初は記事の全文をそのまま Ollama に送っていました。CI で実行すると、全記事の Embedding に数十時間かかる計算です。全文が本当に必要なのか、検証しました。</p>
<p>まず、全記事のクリーニング済みテキスト長の分布を調べました。</p>
<ul>
<li>平均: 約 8,000 文字</li>
<li>中央値: 約 6,300 文字</li>
<li>上位 10%: 14,600 文字以上</li>
<li>最大: 53,000 文字超</li>
</ul>
<p>大半の記事は 10,000 文字以内に収まりますが、一部の長文記事は 40,000 文字を超えます。長い記事の後半には参考文献リストや補足情報が多く、記事のテーマを表す情報は先頭に集中する傾向がありました。</p>
<p>そこで「先頭 N 文字に切り詰めても品質を維持できるか？」を検証するため、長文の上位 5 記事で<strong>全文 Embedding（<code>num_ctx=8192</code> 明示指定）をベースライン</strong>として、切り詰め文字数を変えて類似度と速度を比較しました。</p>
<table>
<thead>
<tr>
<th>切り詰め</th>
<th>ベースラインとの類似度</th>
<th>平均速度</th>
<th>高速化</th>
</tr>
</thead>
<tbody><tr>
<td>全文</td>
<td>1.000</td>
<td>229 秒</td>
<td>1.0x</td>
</tr>
<tr>
<td>2,000 文字</td>
<td>0.868</td>
<td>13 秒</td>
<td>17.6x</td>
</tr>
<tr>
<td><strong>4,000 文字</strong></td>
<td><strong>0.887</strong></td>
<td><strong>29 秒</strong></td>
<td><strong>7.9x</strong></td>
</tr>
<tr>
<td>6,000 文字</td>
<td>0.902</td>
<td>42 秒</td>
<td>5.5x</td>
</tr>
<tr>
<td>8,000 文字</td>
<td>0.909</td>
<td>53 秒</td>
<td>4.3x</td>
</tr>
</tbody></table>
<p>4,000 → 8,000 文字に増やしても類似度の改善は <strong>+2.2 ポイント</strong>（0.887 → 0.909）に留まりますが、速度は 1.8 倍遅くなります。関連記事のランキング品質に影響が出ないことを本番データで確認した上で、<strong>先頭 4,000 文字 + <code>num_ctx=8192</code></strong> を採用しました。</p>
<p>:::message
なぜ先頭の切り詰めが有効か？</p>
<p>2 つの要因が相乗しています。</p>
<ol>
<li><strong>モデルの位置バイアス</strong>: Transformer ベースの Embedding モデルでは、テキスト先頭への撹乱がベクトルに与える影響が末尾より約 15% 大きいことが報告されています（<a href="https://arxiv.org/html/2412.15241v3">arXiv:2412.15241</a>）。Qwen3-Embedding も <a href="https://huggingface.co/Qwen/Qwen3-Embedding-0.6B/blob/main/config.json">RoPE</a> を採用した Transformer モデルであり、同様の傾向があると考えられます。</li>
<li><strong>コンテンツの構造バイアス</strong>: 技術ブログは「タイトル→導入→概要→詳細」の逆ピラミッド構造を持ち、テーマ情報が冒頭に集中します（いわゆる <a href="https://arxiv.org/abs/1912.11602">Lead Bias</a>）。
:::</li>
</ol>
<h3>5.2 Ollama の <code>num_ctx</code> に潜む落とし穴</h3>
<p>5.1 の検証に入る前に、<code>num_ctx</code> 周りで罠を踏みました。Ollama で Embedding を扱う人は全員引っかかりうる問題です。</p>
<h4>何が起きたか</h4>
<p>切り詰めを検証する前に、まず <code>num_ctx</code> の効果を確認しようと次の 2 パターンで全文 Embedding を比較しました。</p>
<ul>
<li>A: 全文 + <code>num_ctx=4096</code></li>
<li>B: 全文 + <code>num_ctx=8192</code></li>
</ul>
<p>A と B のコサイン類似度が全記事で <strong>1.000</strong> でした。完全に同一のベクトルです。処理時間も平均約 70 秒で差がない。35,000 文字超の記事でコンテキスト長を倍にしたのに、結果が変わっていません。</p>
<table>
<thead>
<tr>
<th>記事</th>
<th>文字数</th>
<th>平均処理時間(秒)</th>
<th>A-B 類似度</th>
</tr>
</thead>
<tbody><tr>
<td>torii-ai_tool_slack</td>
<td>35,417</td>
<td>68</td>
<td>1.000</td>
</tr>
<tr>
<td>Android-Compose-OO-Nav</td>
<td>37,803</td>
<td>76</td>
<td>1.000</td>
</tr>
<tr>
<td>aurora-mysql-stats</td>
<td>32,648</td>
<td>71</td>
<td>1.000</td>
</tr>
<tr>
<td>Jetpack-Compose-Anim</td>
<td>34,621</td>
<td>65</td>
<td>1.000</td>
</tr>
<tr>
<td>SecureDBPassword</td>
<td>38,978</td>
<td>69</td>
<td>1.000</td>
</tr>
<tr>
<td><strong>平均</strong></td>
<td></td>
<td><strong>約 70 秒</strong></td>
<td><strong>1.000</strong></td>
</tr>
</tbody></table>
<h4>原因: Options に入れないと num_ctx は効かない</h4>
<p><code>num_ctx</code> を <code>EmbedRequest.Options</code> で<strong>明示的に渡さない限り</strong>、Ollama は <a href="https://github.com/ollama/ollama/blob/main/docs/context-length.mdx">VRAM に応じたデフォルト値</a>（24GiB 未満で 4k、24-48GiB で 32k、48GiB 以上で 256k。<code>OLLAMA_CONTEXT_LENGTH</code> 環境変数で変更可能）を使い、超過分を無警告で切り詰めます。</p>
<p>パターン B で <code>num_ctx=8192</code> を設定したつもりが、API の Options に渡されておらず、A と同じ 4096 トークンで処理されていました。類似度 1.000 は、両方とも同じ入力を処理していた証拠です。</p>
<p>:::message alert
注意: Ollama は入力テキストがコンテキスト長を超えてもエラーを返しません。API レスポンスにも切り詰めの有無を示すフィールドがありません。意図せず不完全な Embedding が生成される可能性があります。これは Ollama の <a href="https://github.com/ollama/ollama/issues/14259">Issue #14259</a> でも報告されています。
:::</p>
<h4>修正と効果の確認</h4>
<p><code>num_ctx</code> を <code>EmbedRequest.Options</code> で明示的に渡すよう修正したのが、次の実装です。</p>
<pre><code class="language-go:internal/embedding/client.go">func (c *Client) Embed(ctx context.Context, texts []string) ([][]float32, error) {
    req := &amp;api.EmbedRequest{
        Model: c.model,
        Input: texts,
    }
    if c.numCtx &gt; 0 {
        req.Options = map[string]any{&quot;num_ctx&quot;: c.numCtx}
    }

    resp, err := c.api.Embed(ctx, req)
    if err != nil {
        return nil, fmt.Errorf(&quot;Ollama Embed API エラー: %w&quot;, err)
    }
    // レスポンスのバリデーション（件数・空ベクトルチェック）
    if len(resp.Embeddings) != len(texts) {
        return nil, fmt.Errorf(&quot;レスポンス数が不一致: %d embeddings / %d texts&quot;, len(resp.Embeddings), len(texts))
    }
    return resp.Embeddings, nil
}
</code></pre>
<p>修正後は A-B 類似度が <strong>0.947</strong> に下がり、B の処理時間は A の約 3 倍（229 秒 vs 78 秒）になりました。8192 トークン分を処理していることが時間からも裏付けられます。</p>
<table>
<thead>
<tr>
<th>記事</th>
<th>文字数</th>
<th>A(秒)</th>
<th>B(秒)</th>
<th>A-B 類似度</th>
</tr>
</thead>
<tbody><tr>
<td>torii-ai_tool_slack</td>
<td>35,417</td>
<td>77</td>
<td>224</td>
<td>0.969</td>
</tr>
<tr>
<td>Android-Compose-OO-Nav</td>
<td>37,803</td>
<td>81</td>
<td>218</td>
<td>0.920</td>
</tr>
<tr>
<td>aurora-mysql-stats</td>
<td>32,648</td>
<td>84</td>
<td>224</td>
<td>0.919</td>
</tr>
<tr>
<td>Jetpack-Compose-Anim</td>
<td>34,621</td>
<td>74</td>
<td>243</td>
<td>0.947</td>
</tr>
<tr>
<td>SecureDBPassword</td>
<td>38,978</td>
<td>73</td>
<td>238</td>
<td>0.977</td>
</tr>
<tr>
<td><strong>平均</strong></td>
<td></td>
<td><strong>78</strong></td>
<td><strong>229</strong></td>
<td><strong>0.947</strong></td>
</tr>
</tbody></table>
<p>CLI のデフォルト値は <code>--num-ctx=8192</code> に設定し、4000 文字切り詰めと組み合わせることで無警告の文字切り詰めが発生しないことを保証しています。</p>
<h4>Ollama 利用者への教訓</h4>
<p>Ollama で Embedding や LLM を扱うなら：</p>
<ul>
<li><code>num_ctx</code> は Modelfile の <code>PARAMETER</code> か、API の <code>Options.num_ctx</code> で<strong>明示的に設定する</strong></li>
<li>入力のトークン数を事前に把握し、コンテキスト長に収まるか確認する</li>
<li>類似度や品質が「なぜか変わらない」ときは、無警告切り詰めを疑う</li>
</ul>
<h3>5.3 コードブロックは残すべきか？</h3>
<p>先頭 4000 文字のうち、コードブロックが大量に含まれる記事があります。Android Compose のナビゲーション記事では 2,213 文字（55%超）がコードでした。コードを除去して本文を増やす方が良さそうに思えます。</p>
<p>日英翻訳ペア（同じ <code>postId</code> で <code>locale</code> が異なる記事）のコサイン類似度で検証しました。</p>
<ul>
<li>コードブロックあり: 0.893</li>
<li>コードブロック除去: 0.868</li>
</ul>
<p>コードブロックを除去すると類似度が下がりました。</p>
<p>クラス名、関数名、ライブラリ名（<code>NavHost</code>、<code>Composable</code>、<code>goroutine</code> など）は言語に依存しません。日本語の記事でも英語の記事でも、同じ技術ならコード中に同じキーワードが出現します。コードブロックはクリーニング対象から除外（残す）としました。</p>
<h3>切り詰めの実装</h3>
<pre><code class="language-go:main.go">const maxEmbedRunes = 4000

for _, p := range dirty {
    text := p.Title + &quot;\n&quot; + p.Content
    if p.Content == &quot;&quot; {
        text = p.Title
    }
    if runes := []rune(text); len(runes) &gt; maxEmbedRunes {
        text = string(runes[:maxEmbedRunes])
    }
    vectors, err := client.Embed(ctx, []string{text})
    // ...
}
</code></pre>
<p><code>[]rune</code> に変換してからスライスすることで、マルチバイト文字（日本語）の途中で切れることを防いでいます。</p>
<hr>
<h2>6. SHA-256 差分キャッシュによる効率化</h2>
<p>セクション 1 で述べた「毎回全量実行」の問題を解決するため、差分キャッシュを導入しました。「前回から何が変わったか」を高速に判定する必要がありますが、ファイルの更新日時（mtime）は Git のチェックアウトでリセットされるため CI 環境では使えません。そこで、コンテンツ自体の SHA-256 ハッシュで変更を検知する方式を採用しました。</p>
<h3>キャッシュの設計</h3>
<pre><code class="language-go:internal/embedding/cache.go">type Cache struct {
    Version   int                   `json:&quot;version&quot;`
    ModelName string                `json:&quot;model_name&quot;`
    Entries   map[string]CacheEntry `json:&quot;entries&quot;`
}

type CacheEntry struct {
    ContentHash string    `json:&quot;content_hash&quot;`
    Vector      []float32 `json:&quot;vector&quot;`
}
</code></pre>
<h3>SHA-256 による変更検知</h3>
<p>記事のタイトルと本文を結合して SHA-256 ハッシュを計算し、前回のキャッシュと比較します。</p>
<pre><code class="language-go:internal/embedding/cache.go">func ContentHash(title, content string) string {
    h := sha256.New()
    h.Write([]byte(title + &quot;\n&quot; + content))
    return hex.EncodeToString(h.Sum(nil))
}

func (c *Cache) FindDirty(posts []markdown.Post, modelName string) []markdown.Post {
    if c.ModelName != modelName {
        return posts // モデル変更 → 全記事を再Embed
    }
    var dirty []markdown.Post
    for _, p := range posts {
        entry, ok := c.Entries[p.Slug]
        if !ok || entry.ContentHash != ContentHash(p.Title, p.Content) {
            dirty = append(dirty, p)
        }
    }
    return dirty
}
</code></pre>
<p>モデル名が変わると全記事が dirty になります。Embedding モデルが変われば次元数やベクトル空間が異なるため、古いキャッシュは無効です。</p>
<h3>キャッシュフロー</h3>
<pre><code class="language-mermaid">flowchart TB
    A[&quot;記事読み込み&lt;br&gt;(956件)&quot;] --&gt; B[&quot;キャッシュ読み込み&quot;]
    B --&gt; C{&quot;モデル変更?&quot;}
    C --&gt;|Yes| D[&quot;全記事をEmbed&quot;]
    C --&gt;|No| E[&quot;SHA-256比較&quot;]
    E --&gt; F{&quot;変更あり?&quot;}
    F --&gt;|Yes| G[&quot;変更分のみEmbed&quot;]
    F --&gt;|No| H[&quot;スキップ&quot;]
    D --&gt; I[&quot;1件ずつ保存&lt;br&gt;(中断耐性)&quot;]
    G --&gt; I
</code></pre>
<p>Embed のたびにキャッシュファイルを保存します。CI のタイムアウトや中断が起きても、それまで処理した分はキャッシュに残ります。次回実行時は中断箇所から再開できるため、初回の全量 Embedding を複数回に分けて進められます。</p>
<h3>初回構築で効いた「中断耐性」</h3>
<p>この「1 記事ごとに cache ファイルへ保存」という設計が、初回構築で実際に役に立ちました。</p>
<p>当時 956 件あった全記事の初回全量ビルドでは、Ollama での Embedding 処理が GitHub Actions の job timeout（<code>timeout-minutes: 60</code>）に収まらず、5 回連続で 60 分 timeout に到達しました。それでも 6 回目の run で完走できたのは、各 cancelled run で完了していた分の Embedding が次の run に引き継がれたからです。</p>
<table>
<thead>
<tr>
<th>run</th>
<th>結果</th>
<th>Generate related content</th>
</tr>
</thead>
<tbody><tr>
<td>1 〜 5 回目</td>
<td>timeout</td>
<td>各 60 分</td>
</tr>
<tr>
<td><strong>6 回目</strong></td>
<td><strong>success</strong></td>
<td><strong>55 分</strong></td>
</tr>
<tr>
<td><strong>累計</strong></td>
<td></td>
<td><strong>約 6 時間</strong></td>
</tr>
</tbody></table>
<p>これを成立させたのは 2 つの噛み合わせです。</p>
<ol>
<li><strong>アプリ側</strong>: 1 記事 Embed するごとに <code>output/embeddings_cache.json</code> へ保存</li>
<li><strong>CI 側</strong>: <code>actions/cache/save@v5</code> を <strong><code>if: always()</code></strong> で走らせる</li>
</ol>
<pre><code class="language-yaml:.github/workflows/auto-create-related-data-on-pushd-to-main.yml">- name: Save embeddings cache
  if: always()   # timeout/cancel 時も cache save を走らせる
  uses: actions/cache/save@v5
  with:
    path: output/embeddings_cache.json
    key: embeddings-cache-${{ hashFiles(&#39;_posts/**&#39;) }}-${{ github.run_id }}
</code></pre>
<p><code>if: always()</code> を付けておくと、job が timeout/cancel で終わるときにも cache save ステップが走ります。結果、途中まで処理した Embedding は cache に残り、次 run は <code>restore-keys</code> のフォールバックで前 run の cache を拾って残り分から続行できる。</p>
<p>この仕組みがなければ、60 分 timeout で毎回 Embedding が巻き戻り、6 時間で完走することはなかったはずです。</p>
<hr>
<h2>7. コサイン類似度ランキングの最適化</h2>
<p>Embedding ベクトルが得られたら、記事間の類似度を計算してランキングを生成します。956 記事の各記事が他の 955 件と比較するため、約 91 万回の内積計算が走ります。この規模なら FAISS 等の ANN（近似最近傍探索）ライブラリを導入するよりも、brute-force の方がシンプルで依存も増えません。</p>
<p>最初の実装（毎回ノルム計算 + 全件ソート）ではテストで約 1.6 秒かかっていました。事前正規化 + min-heap への変更と、ループアンローリングの 2 段階で 730ms まで改善しました。</p>
<p>:::::details 最適化の詳細</p>
<p><strong>1. 事前正規化 (Pre-normalization)</strong></p>
<p>コサイン類似度の式は以下です。</p>
<p>$$
\cos(a, b) = \frac{a \cdot b}{|a| \times |b|}
$$</p>
<p>毎回 2 つのベクトルの長さ（ノルム $|a|$）を計算するのは無駄なので、全ベクトルの長さを事前に 1 に揃えておきます（正規化）。すると分母が $1 \times 1 = 1$ になり、コサイン類似度は内積 $a \cdot b$（各要素を掛けて足すだけ）と等しくなります。正規化は記事数分（956回）だけ。その後の 91 万回のペア比較では掛け算と足し算だけで済みます。</p>
<p>:::details コサイン類似度の補足</p>
<p>内積 $a \cdot b$ は 2 つのベクトルの各要素を掛けて足した値です。意味が近い記事同士は内積が大きくなりますが、長い記事のベクトルは値が大きくなりがちで、内積だけだと「ベクトルの長さ」に引っ張られます。ノルム $|a|$ で割ることで長さの影響を消し、純粋に「向き」（意味の近さ）だけを比較するのがコサイン類似度です。結果は $-1$ 〜 $1$ の範囲で、1 に近いほど意味が近い。</p>
<p>正規化とは、各要素をノルムで割ってベクトルの長さを 1 にする処理です。向きはそのまま、長さだけ揃えます。</p>
<pre><code>元: a = [3, 4]       → 長さ = √(9+16) = 5
正規化: a&#39; = [0.6, 0.8] → 長さ = √(0.36+0.64) = 1
</code></pre>
<p>:::</p>
<p><strong>2. min-heap Top-K</strong></p>
<p>全 955 件のスコアを <code>sort.Slice</code> でソートしていましたが、実際に必要なのは上位 12 件だけ。サイズ 12 の min-heap（Go 標準ライブラリの <code>container/heap</code>）を使い、スコアが最小値より大きければ入れ替える方式に変更。計算量は $O(N \log N)$ から $O(N \log K)$ に改善します。</p>
<p><strong>3. ループアンローリング</strong></p>
<p>内積計算のホットパス（約 91 万回 × 1024 次元）に 4-way ループアンローリングを適用。4 つの独立したアキュムレータ変数を使うことで、前のループ結果への依存を断ち切り、CPU が乗算と加算を並列実行できるようになります。</p>
<p>:::details ループアンローリングの補足</p>
<p>通常のループでは 1 つの変数 <code>sum</code> に順番に足していきます。<code>sum += a[0]*b[0]</code> の結果が出るまで次の <code>sum += a[1]*b[1]</code> が始められません（データ依存）。</p>
<p>4-way では 4 つの変数 <code>s0, s1, s2, s3</code> に分けて、それぞれ独立に計算します。CPU は依存関係のない命令を同時に実行できるため（命令レベル並列性）、4 つの乗算・加算が並列に走ります。最後に <code>s0 + s1 + s2 + s3</code> で合計するだけです。</p>
<pre><code>通常:     sum += a[0]*b[0] → sum += a[1]*b[1] → sum += a[2]*b[2] → sum += a[3]*b[3]
          （前の結果を待ってから次へ）

4-way:    s0 += a[0]*b[0]    s1 += a[1]*b[1]    s2 += a[2]*b[2]    s3 += a[3]*b[3]
          （4つ同時に実行）
          → s0 + s1 + s2 + s3
</code></pre>
<p>:::</p>
<pre><code class="language-go:internal/similarity/ranking.go">// 事前正規化: 全ベクトルのノルムを 1 にする
normalized := normalizeAll(slugs, vectors)

// min-heap Top-K: 上位 maxResults 件だけを効率的に抽出
h := &amp;minHeap{}
for j, other := range slugs {
    if i == j { continue }
    score := dotProduct(vi, normalized[j])
    if h.Len() &lt; maxResults {
        heap.Push(h, ScoredItem{Key: other, Score: score})
    } else if score &gt; (*h)[0].Score {
        (*h)[0] = ScoredItem{Key: other, Score: score}
        heap.Fix(h, 0)
    }
}

// 4-way ループアンローリング
func dotProduct(a, b []float32) float32 {
    var s0, s1, s2, s3 float32
    n := len(a)
    i := 0
    for ; i &lt;= n-4; i += 4 {
        s0 += a[i]*b[i]; s1 += a[i+1]*b[i+1]
        s2 += a[i+2]*b[i+2]; s3 += a[i+3]*b[i+3]
    }
    for ; i &lt; n; i++ { s0 += a[i] * b[i] }
    return s0 + s1 + s2 + s3
}
</code></pre>
<p>:::::</p>
<h3>パフォーマンス推移</h3>
<table>
<thead>
<tr>
<th>段階</th>
<th>手法</th>
<th>ランキング処理時間（956記事）</th>
</tr>
</thead>
<tbody><tr>
<td>初期</td>
<td>毎回ノルム計算 + sort.Slice</td>
<td>~1.58s</td>
</tr>
<tr>
<td>1</td>
<td>事前正規化 + min-heap Top-K</td>
<td>~1.18s</td>
</tr>
<tr>
<td>2</td>
<td>+ ループアンローリング（4-way）</td>
<td><strong>730ms</strong></td>
</tr>
</tbody></table>
<p>最終的なスペック：</p>
<table>
<thead>
<tr>
<th>指標</th>
<th>値</th>
</tr>
</thead>
<tbody><tr>
<td>記事数</td>
<td>956 件</td>
</tr>
<tr>
<td>ベクトル次元数</td>
<td>1024</td>
</tr>
<tr>
<td>類似度計算回数</td>
<td>約 912,980 回（956 × 955）</td>
</tr>
<tr>
<td>ランキング処理時間</td>
<td><strong>730ms</strong></td>
</tr>
</tbody></table>
<p>なお、Go の map はイテレーション順序が非決定的です。同じ入力に対して常に同じ JSON 出力を得るため、<code>slices.Sort</code> でスラッグをソートしてから処理しています。これを忘れると CI のたびに diff が発生し、不要なコミットが生まれてしまいます。</p>
<hr>
<h2>8. GitHub Actions での CI/CD</h2>
<h3>ワークフロー全体像</h3>
<pre><code class="language-mermaid">flowchart LR
    A[&quot;create-branch&quot;] --&gt; B[&quot;generate-related-content&lt;br&gt;(ARM runner + Ollama)&quot;]
    A --&gt; C[&quot;generate-metadata&quot;]
    A --&gt; D[&quot;generate-search-index&quot;]
    B --&gt; E[&quot;create-pull-request&quot;]
    C --&gt; E
    D --&gt; E
    E --&gt; F[&quot;auto-merge&quot;]
</code></pre>
<p><code>create-branch</code> でブランチを作成した後、3 つのジョブが並列実行されます。</p>
<h3>ARM ランナーの選択</h3>
<p>Embedding 処理には <code>arm-ubuntu-latest-4</code> ランナーを使用しています。GitHub の ARM ランナーは x86 の約半額（1分あたり $0.004 vs $0.008）で、初回の全量 Embedding のように数時間かかるジョブではコスト差が大きくなります。</p>
<h3>Ollama モデルキャッシュ</h3>
<p>639MB のモデルファイルを毎回ダウンロードしないため、<code>actions/cache</code> でキャッシュします。</p>
<h3>cache/restore + cache/save パターン</h3>
<p>:::message
<code>actions/cache@v5</code> の <code>save-always</code> オプションは非推奨になりました。代わりに <code>cache/restore</code> と <code>cache/save</code> を分離し、<code>cache/save</code> に <code>if: always()</code> を付けるパターンを使います。
:::</p>
<pre><code class="language-yaml:auto-create-related-data-on-push-to-main.yml">- name: Restore embeddings cache
  uses: actions/cache/restore@v5
  with:
    path: output/embeddings_cache.json
    key: embeddings-cache-${{ hashFiles(&#39;_posts/**&#39;) }}-${{ github.run_id }}
    restore-keys: embeddings-cache-

# ... Embedding 実行 ...

- name: Save embeddings cache
  if: always()
  uses: actions/cache/save@v5
  with:
    path: output/embeddings_cache.json
    key: embeddings-cache-${{ hashFiles(&#39;_posts/**&#39;) }}-${{ github.run_id }}
</code></pre>
<p><code>if: always()</code> により、タイムアウト時でもキャッシュを保存します。セクション 6 の「1 件ずつ保存」と組み合わせて、中断と再実行を繰り返してもキャッシュが蓄積されます。</p>
<h3>キャッシュキーに <code>run_id</code> を付ける理由</h3>
<p>GitHub Actions のキャッシュは同じキーで上書きできません（イミュータブル）。これはタイムアウト→再実行のパターンで問題になります。</p>
<p><code>run_id</code> なしの場合:</p>
<pre><code class="language-text">key: embeddings-cache-abc123

1回目: save &quot;abc123&quot; → ✅ 200記事分保存
2回目: restore &quot;abc123&quot; → 200記事復元 → 追加200記事 → save &quot;abc123&quot; → ❌ キーが既に存在
3回目: restore &quot;abc123&quot; → 1回目の200記事分しかない（2回目の成果が消えた）
</code></pre>
<p><code>run_id</code> ありの場合:</p>
<pre><code class="language-text">save key: embeddings-cache-abc123-{run_id}     ← 毎回ユニーク
restore-keys: embeddings-cache-abc123-          ← プレフィックス一致で最新を取得

1回目: save &quot;abc123-100&quot; → ✅ 200記事分
2回目: restore &quot;abc123-&quot; → run100から200記事復元 → 追加200記事 → save &quot;abc123-200&quot; → ✅ 400記事分
3回目: restore &quot;abc123-&quot; → run200から400記事復元 → 続きから
</code></pre>
<h3>push のリトライロジック</h3>
<p>3 つのジョブが並列でブランチに push するため、競合が発生します。指数バックオフ付きのリトライで対処します。</p>
<pre><code class="language-yaml">pushed=false
for i in 1 2 3 4 5; do
  git pull --rebase origin &quot;$BRANCH_NAME&quot; &amp;&amp; git push origin &quot;$BRANCH_NAME&quot; &amp;&amp; pushed=true &amp;&amp; break
  echo &quot;Push failed (attempt $i), retrying...&quot;
  sleep $((i * 2))
done
[ &quot;$pushed&quot; = &quot;true&quot; ] || { echo &quot;ERROR: All push attempts failed&quot;; exit 1; }
</code></pre>
<h3>古いブランチの問題</h3>
<p>自動生成用ブランチが前回の実行から残っている場合、古いコードがベースになります。<code>git reset --hard ${{ github.sha }}</code> で毎回トリガー元の最新コミットにリセットします。</p>
<h3>workflow_dispatch でのテスト実行</h3>
<p><code>main</code> にマージ前の動作確認では <code>workflow_dispatch</code> トリガーを一時的に追加しました。ただし、GUI の Actions タブにはデフォルトブランチのワークフローしか表示されないため、feature ブランチの <code>workflow_dispatch</code> は GUI から実行できません。</p>
<p>CLI 経由であれば <code>--ref</code> でブランチを指定して実行可能です。</p>
<pre><code class="language-bash">gh workflow run &quot;Auto Create Related Data&quot; --ref feat/related-content-gen-go-rewrite
</code></pre>
<hr>
<h2>9. 実運用で見えた効果</h2>
<p>旧システム（Python + Azure OpenAI）から新システム（Go + Ollama）への移行で、セクション 1 で挙げた 3 つの課題はそれぞれ次のように変わりました。</p>
<table>
<thead>
<tr>
<th>課題</th>
<th>旧（Python + Azure OpenAI）</th>
<th>新（Go + Ollama）</th>
</tr>
</thead>
<tbody><tr>
<td>実行戦略</td>
<td>毎回全量 Embed（900+ 件）</td>
<td>差分のみ Embed（SHA-256 ハッシュ比較）</td>
</tr>
<tr>
<td>Rate Limit (429)</td>
<td>頻発・リトライで不安定</td>
<td>構造的に発生しない（外部 API なし）</td>
</tr>
<tr>
<td>推論コスト</td>
<td>従量課金（Azure OpenAI）</td>
<td>ゼロ（CI ランナー内完結）</td>
</tr>
</tbody></table>
<p>比較すべきは単発の処理秒数ではなく、「記事追加のたびに全量再計算が必要か」「外部 API 制約に運用が振り回されるか」という運用特性です。旧は Azure のマネージド並列推論、新は self-hosted CI ランナー 1 台のシーケンシャル処理で、そもそも尺度が違います。</p>
<h3>差分更新時の実測例</h3>
<p>959 記事中 49 件（5%）が dirty だった run では、<strong>19 分 21 秒で完走</strong>しました（self-hosted runner 1 台・逐次処理で 1 記事あたり約 22〜24 秒）。差分ゼロなら Embed はスキップされ、ランキング計算と出力だけで 1〜2 分で完了します。</p>
<h3>残課題</h3>
<ul>
<li>dirty が 150 件を超える状況（cache eviction 直後や cron が長期間失敗していたあとなど）では <code>timeout-minutes: 60</code> に収まらないことがあります。現状は複数 run に分けて進捗を積み上げる設計でカバーしていますが、次の打ち手として timeout 延長と <code>output/embeddings_cache.json</code> の git 管理化が候補です</li>
<li>GitHub Actions cache は 7 日アクセスなしで自動 eviction されるため、週次 cron（月曜 9 時）で Restore を触って keep-warm しています。より確実にするなら git 管理化か、S3 などの外部 storage に寄せる手もあります</li>
</ul>
<hr>
<h2>10. まとめ</h2>
<p>本記事では、関連記事のレコメンドシステムを Go + Ollama（ローカル Embedding）で再構築した過程を紹介しました。なお、関連求人についても同様の Embedding + コサイン類似度の仕組みで生成しています。</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>結果</th>
</tr>
</thead>
<tbody><tr>
<td>対象記事数</td>
<td>960 件前後（執筆時点）</td>
</tr>
<tr>
<td>ランキング計算</td>
<td>730ms（Embedding 生成は含まず、測定時点 956 件）</td>
</tr>
<tr>
<td>テキスト切り詰め</td>
<td>先頭 4000 文字で全文比 88.7% の類似度を維持</td>
</tr>
<tr>
<td>差分キャッシュ</td>
<td>差分ゼロなら 1〜2 分、少数差分なら数分〜十数分</td>
</tr>
<tr>
<td>外部依存</td>
<td>Ollama + Qwen3-Embedding（API キー不要）</td>
</tr>
</tbody></table>
<p>SHA-256 差分キャッシュで変更記事だけを再 Embed し、ランキングは事前正規化と min-heap Top-K で 730ms（956記事のペアワイズ計算）。外部 API 依存を排除して、429 エラーとコストの問題を解消しました。</p>
<p>初回の全量 Embedding は CPU ランナーで数時間かかり、モデル変更や初期導入時にも同じコストを払うことになります。扱い方はセクション 6 と 9 に書いた通りで、GPU ランナーが使えれば改善しますが、現時点では CI の制約です。</p>
<p>もう 1 つ、推薦品質の定量評価がまだありません。「Embedding の類似度が 88.7% 保たれている」ことと「関連記事の推薦が妥当である」ことは別の問題です。旧システムとの Top-K 一致率や、クリックスルー率の計測が残っています。</p>
<p>テキスト切り詰めも改善の余地があります。現在は先頭 4000 文字をルーン単位でカットしていますが、文の途中で切れる可能性があります。句点（<code>。</code>）や改行の位置で切る方が、Embedding の入力としてはクリーンです。今回のユースケースでは影響は軽微ですが、精度を追求する場合は検討に値します。</p>
<hr>
<h2>11. この仕組みの応用可能性</h2>
<p>「ローカル Embedding + コサイン類似度 + 差分キャッシュ」の仕組みは、ブログの関連記事に限りません。Confluence や Notion の社内ドキュメントを同じパイプラインで Embedding すれば、「この仕様書に関連するドキュメント」を自動提示できます。Ollama はローカル実行なので、社外に送信できない社内文書でも扱えます。</p>
<p>SHA-256 差分キャッシュと 1 件ずつ保存の中断耐性パターンはそのまま流用できます。Ollama + 軽量モデルなら API キー不要で CI でもローカルでも動きます。</p>
<hr>
<h2>おまけ: Claude Code との開発プロセス</h2>
<p>今回の開発は Claude Code とのペアプログラミングで進めました。</p>
<h3>kairo による開発ワークフロー</h3>
<p>開発ワークフローにはクラスメソッド社の <a href="https://github.com/classmethod/tsumiki">tsumiki</a> の kairo を使いました。kairo は Claude Code 向けのスキルで、4 つのコマンドでソフトウェア開発を進めます。</p>
<ol>
<li><code>kairo-requirements</code>: EARS 記法で機能・非機能要件を定義。今回は 3 方式の PoC 比較（ONNX / llama.cpp / Ollama）もこのフェーズで実行しました</li>
<li><code>kairo-design</code>: 要件からアーキテクチャ図、データフロー、型定義を生成</li>
<li><code>kairo-tasks</code>: 設計を実装タスクに分割。依存関係とテストケースも定義。今回は 10 タスク・3 フェーズに分解</li>
<li><code>kairo-loop</code>: タスクを 1 つずつ Red → Green → Refactor の TDD サイクルで実装。7 タスクをこのコマンドで回しました</li>
</ol>
<h3>PR レビュー</h3>
<p>実装後の PR レビューでは、Claude Code に以下のように指示しました。</p>
<pre><code>/pr-review-toolkit:review-pr all
</code></pre>
<p><a href="https://github.com/anthropics/claude-plugins-public/tree/main/plugins/pr-review-toolkit">pr-review-toolkit</a> は Anthropic 公式の Claude Code プラグインで、6 種のレビューエージェント（コード品質、エラーハンドリング、テストカバレッジ、コメント整合性、型設計、コード簡素化）が並列にレビューします。セクション 5.2 のレスポンスバリデーション（件数・空ベクトルチェック）は、このレビューで指摘された問題への対応です。</p>
<h3>Go 1.26 での最適化</h3>
<p>Claude Code に「Go 1.26 で最適化して」と指示しました。<code>go fix</code> による自動変換（<code>strings.Index</code> → <code>strings.Cut</code>、<code>sort.Strings</code> → <code>slices.Sort</code>、<code>context.Background()</code> → <code>t.Context()</code> など）に加え、新しい言語機能やライブラリ API を活用したリファクタリングも実施されました。</p>
<h3>記事の執筆・校正</h3>
<p>この記事自体も Claude Code で執筆しています。校正には 3 つのツールを使いました。</p>
<ul>
<li><a href="https://github.com/textlint/textlint">textlint</a> + <a href="https://github.com/textlint-ja/textlint-rule-preset-ja-technical-writing">ja-technical-writing</a>: 冗長表現や接続詞の重複など、日本語の技術文書向け校正</li>
<li><a href="https://github.com/stephenturner/skill-deslop">skill-deslop</a>: AI 生成文章に特有の冗長パターン（回りくどい前置き、受動態の多用など）の検出・除去</li>
<li><a href="https://github.com/openai/codex-plugin-cc">Codex plugin for Claude Code</a>: OpenAI 公式の Claude Code プラグインで、Codex CLI をサブエージェントとして呼び出します。記事全体の論理破綻や数値矛盾のチェックに使いました。実験データ更新に伴う数値の不整合やコードスニペットの変数名不一致など、人間のレビューでは見落としやすい問題を検出できました</li>
</ul>
<hr>
<p>ここまで読んでいただきありがとうございました。何かの参考になれば幸いです。なお、この記事の下部に表示されている「関連する記事」と「関連する求人」が、本記事で紹介した仕組みで生成された実物です。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Amplify Hosting + CDK 環境で WAF が 2 系統に分かれていた話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-05-29-aws-waf-ip-allowlist-troubleshoot/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-05-29-aws-waf-ip-allowlist-troubleshoot/</guid>
            <pubDate>Fri, 29 May 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[AWS Amplify Hosting + CDK 構成で WAF が 2 系統に分かれており、拠点 IP 変更時に片方しか更新できておらず 403 が残った調査記録です。さらに Amplify Console の UI と API の実態が乖離しているという罠にも直面しました。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは、 Cloud Infrastructure G の山中です！</p>
<p>「拠点のグローバル IP が変わったので、AWS WAF の allowlist を更新してください」というよくある作業を引き受けたところ、半日以上溶かしました。</p>
<p>原因は <strong>同じシステムの中に WAF が 2 系統</strong> 存在しており、片方を更新しても、もう片方が古い IP リストを握っていたためです。さらに <strong>Amplify Console の Firewall UI が「Firewall: 無効」と表示しているのに、API で確認すると WebACL がしっかり attach されている</strong> という UI と実態の乖離まで重なり、見えている情報をそのまま信じてよいか判断がつかない状況でした。</p>
<p>この記事では、その 2 系統 WAF の全体像と、UI を信用できないときに API で実態を確認する手順を共有します。同じような構成（Amplify Hosting + CDK 管理の WAF）を運用している方の参考になれば幸いです。</p>
<h2>TL;DR</h2>
<ul>
<li>拠点 IP 変更で全環境 403 が発生</li>
<li>Backend API 側の WAF（CDK 管理）は Amplify の環境変数 + rebuild で解消できた</li>
<li>しかしフロントエンド側は <strong>Amplify Hosting が自動管理する別 WAF</strong>（<code>AmplifyIPSet-*</code>）が原因で 403 が残った</li>
<li>さらに Amplify Console の Firewall UI は「Firewall: 無効」表示なのに、API では <strong>WebACL が attach 済み・ default action が Block</strong> だった</li>
<li><code>aws wafv2 list-resources-for-web-acl</code> の <code>--resource-type</code> を <code>AMPLIFY</code> にしないと、attach 状態が見えない罠も踏んだ</li>
<li>最終的に <code>aws wafv2 update-ip-set</code> で直接 IPSet を書き換えて解決</li>
<li>教訓：<strong>Amplify Hosting の WAF は UI を信用せず、API で実態を確認すべし</strong></li>
</ul>
<h2>背景</h2>
<h3>サービス構成</h3>
<p>ユーザー向けに提供予定のフロントエンド + バックエンド API のシステムで、構成は以下の通りです。</p>
<ul>
<li>バックエンド: AWS Amplify Gen 2（CDK で WAF や CloudFront を含むインフラを定義）</li>
<li>フロントエンド: AWS Amplify Hosting Gen 1（platform=WEB）</li>
<li>CDN: CloudFront</li>
<li>WAF: AWS WAFv2</li>
<li>環境: dev / stage / prod（それぞれ別 AWS アカウント）</li>
</ul>
<h3>アクセス制御の方針</h3>
<p>まだリリース前のシステムのため、複数拠点からのオフィス IP のみを許可しています。
拠点が増減したり、回線変更で IP が変わったりすると、各環境の WAF の IPSet を更新する必要があります。</p>
<h3>出来事</h3>
<p>ある日「拠点 A が新しい IP に切り替わるので、X 日までに各環境の allowlist を入れ替えてほしい」という依頼が来ました。
作業手順は社内に整備されていたので、淡々と進めていたつもりだったのですが、ここから泥沼に入っていきます。</p>
<h2>WAF が 2 系統に分かれている全体像</h2>
<p>最初に結論を絵にしておきます。後の章で何度もこの絵に戻ってきます。</p>
<p><img src="/assets/blog/authors/d.yamanaka/20260529/waf-two-systems-overview.svg" alt="Backend API 系（CDK 管理の WebACL/IPSet）と Frontend 系（Amplify Hosting が自動管理する WebACL/IPSet）が並列に存在する WAF 2 系統構成図"></p>
<p>ポイントは、<strong>同じ「拠点 IP 許可」という概念を、別々のリソースとして 2 箇所で独立に管理している</strong> ことです。</p>
<ul>
<li>Backend API 側: CDK / CloudFormation で IPSet を定義 → Amplify の環境変数 <code>WAF_ALLOWED_IP_LIST</code> を更新して rebuild すれば IPSet が書き換わる、という仕組みを自前で組んでいる</li>
<li>Frontend 側: Amplify Hosting の「Firewall（AWS WAF 統合）」機能を有効にすると、Amplify サービス側で勝手に IPSet と WebACL を作成し、Amplify app にくっつける</li>
</ul>
<p>片方しか更新しないと、当然、もう片方の経路で 403 が出ます。今回まさにそこにハマりました。</p>
<h2>タイムライン</h2>
<p>実際に起きた流れを表にすると、こうなります。</p>
<table>
<thead>
<tr>
<th>タイミング</th>
<th>出来事</th>
</tr>
</thead>
<tbody><tr>
<td>Day 0</td>
<td>社内ドキュメントで「新しい拠点 IP 一覧」が共有される</td>
</tr>
<tr>
<td>Day 1</td>
<td>dev の Backend API WAF を Amplify の環境変数経由で更新 → IPSet 反映確認</td>
</tr>
<tr>
<td>Day 2 朝</td>
<td>stage の Backend API WAF を更新 → IPSet 反映確認</td>
</tr>
<tr>
<td>Day 2 昼</td>
<td>prod の Backend API WAF を更新 → IPSet 反映確認</td>
</tr>
<tr>
<td>Day 2 昼</td>
<td>動作確認のためフロントエンド URL にアクセス → <strong>まだ 403</strong></td>
</tr>
<tr>
<td>Day 2 昼</td>
<td>「Backend は更新したのに何で？」と調査開始</td>
</tr>
<tr>
<td>Day 2 午後</td>
<td>フロントエンドは別 WAF （<code>AmplifyIPSet-*</code>） で守られていることに気付く</td>
</tr>
<tr>
<td>Day 2 午後</td>
<td>Amplify Console の Firewall UI を見る → <strong>「Firewall: 無効」と表示</strong></td>
</tr>
<tr>
<td>Day 2 午後</td>
<td>API で確認 → <strong>WebACL は attach 済み、IPSet は旧 IP のままで Block</strong></td>
</tr>
<tr>
<td>Day 2 夕方</td>
<td><code>aws wafv2 update-ip-set</code> で 3 環境の <code>AmplifyIPSet-*</code> を直接更新 → 全環境 200 OK</td>
</tr>
</tbody></table>
<p>「Backend は更新したのにフロントだけ 403」となった時点で、Amplify Hosting 側に別 WAF があると気付くまでが一番遠回りでした。</p>
<h2>最初の対応 — Backend API WAF はあっさり直る</h2>
<p>Backend API 側の更新手順は、すでに社内で整備されていました。</p>
<ol>
<li>Amplify Console で対象 app の環境変数 <code>WAF_ALLOWED_IP_LIST</code> を新しい IP の CSV に書き換える</li>
<li>Amplify の build を回す</li>
<li>CDK で定義された <code>CfnIPSet</code> のリソースが新しい IP で更新される</li>
</ol>
<p>この仕組みのおかげで、dev / stage / prod の Backend API は Day 1 から Day 2 にかけて順次切り替え、いずれも更新自体は数分で完了しました。
WAFv2 のコンソールで IPSet を覗いて、新しい IP が並んでいるのを確認 → よし、終わったな、と思ったのが甘かったです。</p>
<p>念のため、フロントエンドの URL にも <code>curl</code> を投げて確認しました。</p>
<pre><code class="language-bash">$ curl -i https://&lt;env&gt;.example.internal/
HTTP/1.1 403 Forbidden
content-type: text/html
...
&lt;HTML&gt;&lt;HEAD&gt;&lt;TITLE&gt;Request blocked.&lt;/TITLE&gt;&lt;/HEAD&gt;
&lt;BODY&gt;Request blocked.&lt;/BODY&gt;&lt;/HTML&gt;
</code></pre>
<p><code>Request blocked.</code> というレスポンスボディは、AWS WAF の Block ルールが返す典型的な文字列です。S3 オリジンの「Access Denied」とは別物なので、これが見えた時点でほぼ WAF を疑って間違いありません。</p>
<p>:::message
<strong>ちょっとした見分け方</strong>
CloudFront 経由の 403 で、ボディに <code>Request blocked.</code> が入っていれば、ほぼ WAF が原因です。
オリジン（S3 など）の Access Denied であれば、ボディは <code>Access Denied</code> という別の文字列になります。
:::</p>
<p>つまり Backend は直ったが、フロントエンドの経路では別の何かが Block している、という状況です。</p>
<h2>真犯人を探す — フロントエンドは別 WAF で守られていた</h2>
<p>「フロントエンドも CloudFront → S3 のはず。WAF はどこに付いているんだろう？」と探したところ、Amplify Hosting には <strong>AWS WAF 統合（Firewall）</strong> という機能があり、これを有効化していたことを思い出しました。</p>
<p>:::message
<strong>用語の整理</strong>
Amplify Hosting には紛らわしい 2 つの保護機能があります。</p>
<ul>
<li><strong>Access control</strong>: ベーシック認証（ユーザー名 + パスワード）でブランチを保護する機能。IP 制限ではありません。</li>
<li><strong>Firewall（AWS WAF 統合）</strong>: AWS WAF v2 と統合し、IP allowlist / レートリミット / マネージドルールなどを適用する機能。</li>
</ul>
<p>今回 IP allowlist として利用していたのは後者の <strong>Firewall</strong> 機能のほうです。
:::</p>
<p>Amplify Hosting の Firewall を有効化すると、Amplify サービス側で以下を自動的に作成・関連付けします。</p>
<ul>
<li><code>AmplifyIPSet-&lt;guid&gt;</code> という名前の IPSet（us-east-1, scope=CLOUDFRONT）</li>
<li><code>CreatedByAmplify-&lt;appId&gt;-&lt;guid&gt;</code> という名前の WebACL</li>
<li>上記 WebACL を Amplify app のリソース ARN に <code>AssociateWebACL</code> で紐付け</li>
</ul>
<p>そして <strong>これらのリソースは CloudFormation / CDK の管理外</strong> です。Amplify サービスが直接 WAF API を叩いて作成しています。
そのため、CDK 側でいくら WAF を更新しても、フロントエンドの WAF は変わりません。</p>
<p>WAFv2 のコンソールから IPSet の一覧を眺めると、確かに <code>AmplifyIPSet-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX</code> のような IPSet が、CDK 由来の <code>my-app-api-allowed-ips-&lt;env&gt;</code> とは別に存在していました。中身を見ると、まさに <strong>旧拠点 IP のみが入っている</strong> 状態。これが原因です。</p>
<p>:::message
<strong>Tips: CloudFront scope の WAF は us-east-1 にしかない</strong>
CloudFront は Global サービスですが、CloudFront に付けるための WAFv2 リソース（scope=CLOUDFRONT）は <strong>us-east-1 のエンドポイントからしか触れません</strong>。
東京リージョン （<code>ap-northeast-1</code>） でいくら <code>aws wafv2 list-ip-sets</code> を叩いても、CloudFront scope の IPSet は出てきません。「IPSet が見当たらない！」と焦ったときの 9 割はこれが原因です。
:::</p>
<pre><code class="language-bash"># 正しい（CloudFront scope は us-east-1 で見る）
aws wafv2 list-ip-sets --scope CLOUDFRONT --region us-east-1

# 間違い（REGIONAL scope のものしか返ってこない）
aws wafv2 list-ip-sets --scope REGIONAL --region ap-northeast-1
</code></pre>
<h2>UI と API が一致しない問題</h2>
<p>「じゃあ Amplify Console の Firewall 画面で IP を入れ替えればいいか」と思って画面を開いたところ、目を疑う表示が出ていました。</p>
<blockquote>
<p><strong>Firewall: 無効（このアプリは Web Application Firewall で保護されていません）</strong></p>
</blockquote>
<p>つまり「WAF はかかっていません」という意味の表示です。
しかし <code>curl</code> を打つと、明らかに 403 （<code>Request blocked.</code>） が返ってくる。どちらを信じればよいのか分からない状況です。</p>
<p>ここで AWS CLI を使って、API レベルで実態を確認します。</p>
<h3>1. Amplify app に WebACL が attach されているかを確認</h3>
<p><code>list-resources-for-web-acl</code> を使うと、ある WebACL がどのリソースに attach されているかが分かります。</p>
<pre><code class="language-bash">aws wafv2 list-resources-for-web-acl \
  --region us-east-1 \
  --web-acl-arn arn:aws:wafv2:us-east-1:XXXXXXXXXXXX:global/webacl/CreatedByAmplify-XXXXXXXXXX-XXXXXXXX/XXXXXXXX
</code></pre>
<p>これだけだと <strong>空の配列が返ってきます</strong>。</p>
<p>「あれ、attach されていない？じゃあ何が Block しているの？」と一瞬混乱しました。
ですがこれは罠で、<code>--resource-type</code> を指定していないとデフォルト値 <code>APPLICATION_LOAD_BALANCER</code> で検索されます。Amplify Hosting の場合、resource-type は <code>AMPLIFY</code> なので、デフォルトでは見えません。
さらに <code>list-resources-for-web-acl</code> は <strong>そもそも CloudFront Distribution には使えません</strong>（CloudFront の関連付けを調べたいときは <code>aws cloudfront list-distributions-by-web-acl-id</code> を使うのが正解です）。</p>
<pre><code class="language-bash"># 正しい呼び方
aws wafv2 list-resources-for-web-acl \
  --region us-east-1 \
  --web-acl-arn arn:aws:wafv2:us-east-1:XXXXXXXXXXXX:global/webacl/CreatedByAmplify-XXXXXXXXXX-XXXXXXXX/XXXXXXXX \
  --resource-type AMPLIFY
</code></pre>
<p>これでようやく、Amplify app の ARN が返ってきました。<strong>UI は「Firewall: 無効」と言っていたが、実態としては WebACL がしっかり attach されていた</strong> わけです。</p>
<p>:::message
<strong>ハマりポイント: <code>--resource-type</code> のデフォルト</strong>
<code>aws wafv2 list-resources-for-web-acl</code> は <code>--resource-type</code> を省略すると <strong><code>APPLICATION_LOAD_BALANCER</code> で検索されます</strong>（CloudFront Distribution はこの API では扱えず、<code>aws cloudfront list-distributions-by-web-acl-id</code> を使う必要があります）。
Amplify Hosting を疑うときは、必ず <code>--resource-type AMPLIFY</code> を明示しましょう。これに気付かないと、「attach されていない」と誤認して別の方向の調査に走ってしまいます。
:::</p>
<h3>2. WebACL のデフォルトアクションを確認</h3>
<p><code>get-web-acl</code> でデフォルトアクションを覗くと、default は <code>Block</code>、その上で IPSet ベースの allow ルールが乗っかっている構成でした。</p>
<pre><code class="language-bash">aws wafv2 get-web-acl \
  --scope CLOUDFRONT --region us-east-1 \
  --name CreatedByAmplify-XXXXXXXXXX-XXXXXXXX --id XXXXXXXX \
  --query &#39;WebACL.{DefaultAction:DefaultAction, Rules:Rules[].Name}&#39;
</code></pre>
<p>つまり、<strong>IPSet に載っていない IP からのアクセスは全部 Block</strong>。
UI 上は「Firewall: 無効」と表示していても、API レベルでは「Block ベース + 古い IPSet で allow」になっており、見事に食い違っていました。</p>
<h3>3. なぜ乖離したのか — CloudTrail で犯人探し</h3>
<p>UI と API がここまで一致しないのは流石におかしいので、CloudTrail で履歴を漁ってみました。</p>
<pre><code class="language-bash">aws cloudtrail lookup-events \
  --max-items 50 \
  --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateIPSet \
  --output json
</code></pre>
<p><code>AssociateWebACL</code> や <code>DisassociateWebACL</code> も同じように引いて、時系列で並べると、過去にある担当者が <strong>何度も Associate / Disassociate を繰り返しており、最終的に Associate で終わっていた</strong> ことが分かりました。
時系列を追っても、UI が「Firewall: 無効」を表示し続けるに至った直接的な原因までは特定できませんでした。<strong>ただし「Amplify Console の Firewall UI と、WAFv2 API が示す実態とが食い違うケースが起こりうる」という事実だけは、今回の調査で確認できた</strong>ことになります。</p>
<p>ともあれ、API の実態を信じるしかないことが確定したので、次は IPSet を直接書き換えに行きます。</p>
<h2>解決手順 — IPSet を直接更新</h2>
<p>すでに <code>AmplifyIPSet-*</code> がどこにあり、どの WebACL に紐付いているかは分かっているので、あとは <strong>WAFv2 の IPSet を直接更新</strong> すれば終わりです。
ただし WAFv2 には楽観的排他制御の仕組みがあって、<code>LockToken</code> を毎回取り直す必要があります。</p>
<p>:::message
<strong>WAFv2 の LockToken（楽観的排他制御）</strong>
<code>Get*</code> 系 API のレスポンスに <code>LockToken</code> が含まれており、<code>Update*</code> 系の API ではこの <code>LockToken</code> を渡す必要があります。
他のプロセスが先に更新していると <code>WAFOptimisticLockException</code> で失敗します。
<strong>毎回 get → update をセットで実行する</strong>のが安全です。
:::</p>
<p>実際に流したコマンドの雛形がこれです。</p>
<pre><code class="language-bash"># 環境変数で接続先を切り替え（dev/stage/prod ごとに AWS_PROFILE を変える）
export AWS_PROFILE=my-app-prod
IPSET_NAME=AmplifyIPSet-XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
IPSET_ID=XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

# 1) 現在の LockToken を取得
LOCK=$(aws wafv2 get-ip-set \
  --scope CLOUDFRONT --region us-east-1 \
  --name &quot;$IPSET_NAME&quot; --id &quot;$IPSET_ID&quot; \
  --query &quot;LockToken&quot; --output text)

# 2) IPSet の中身を新しい IP リストで上書き
aws wafv2 update-ip-set \
  --scope CLOUDFRONT --region us-east-1 \
  --name &quot;$IPSET_NAME&quot; --id &quot;$IPSET_ID&quot; \
  --lock-token &quot;$LOCK&quot; \
  --addresses 192.0.2.10/32 192.0.2.11/32 198.51.100.0/24 203.0.113.0/24
</code></pre>
<p>これを dev / stage / prod の 3 環境で順に実行し、それぞれの環境で <code>curl</code> を打って 200 OK を確認しました。</p>
<p>:::message
<strong>ポイント</strong>
<code>update-ip-set</code> の <code>--addresses</code> は <strong>上書き</strong> です（既存に追加ではなく差し替え）。誤って一部の IP を漏らすと、その IP からのアクセスが全部止まるので、現在の中身を一度ファイルに保存してから差分を取り、新しいリストとして渡すのが安全です。
:::</p>
<h2>学んだこと</h2>
<h3>1. 「同じ目的のリソースが複数経路で管理されている」状態を疑う</h3>
<p>今回は、<code>my-app-api-allowed-ips-&lt;env&gt;</code> と <code>AmplifyIPSet-*</code> という、<strong>同じ「拠点 IP 許可リスト」を別々の場所で独立に管理している</strong> 構造が根本原因でした。
インフラを段階的に作っていくと、こうした「二重管理状態」がいつのまにか出来上がっていることがあります。今回のような全社的な IP 変更のタイミングは、その整理の絶好の機会でもあるな、と感じました。</p>
<h3>2. UI を信用せず、API で実態を確認する癖を付ける</h3>
<p>Amplify Console のような上位のマネジメントコンソールは、内部で何かをキャッシュしていたり、過去の状態を表示し続けていたりすることがあります。
今回のように UI 表示 （「Firewall: 無効」） と API が示す実態 （Block ルール有り） が一致しないパターンも、十分起こり得ます。</p>
<h3>3. <code>list-resources-for-web-acl</code> の <code>--resource-type</code> を忘れない</h3>
<p>WAFv2 の <code>list-resources-for-web-acl</code> は、<code>--resource-type</code> を省略すると <strong>デフォルト値 <code>APPLICATION_LOAD_BALANCER</code> で検索されます</strong>。Amplify Hosting の場合は <code>AMPLIFY</code> を明示する必要があります。
また、この API は <strong>CloudFront Distribution を対象に取れません</strong>。CloudFront の関連付けは <code>aws cloudfront list-distributions-by-web-acl-id</code> という別の API を使うことになっており、これを知らないと「該当 WebACL がどこにも attach されていない」と誤認してしまいます。
今回も、ここに気付くまでが一番大きく時間を溶かしたポイントでした。</p>
<h3>4. CloudFront scope の WAF は us-east-1 にしかない</h3>
<p>これは基礎中の基礎なのですが、改めて。
WAFv2 で CloudFront に付けるリソースは <strong>必ず us-east-1</strong> にあります。CLI なら <code>--region us-east-1</code> 必須、コンソールなら左上のリージョンを Global （CloudFront） に切り替える必要があります。</p>
<h3>5. 「Request blocked.」というレスポンスボディは WAF Block の典型</h3>
<p>CloudFront 経由の 403 で、ボディに <code>Request blocked.</code> の文字列があれば、ほぼ WAF の block ルールが原因です。S3 の Access Denied とは見た目で区別できるので、最初の切り分けに便利です。</p>
<h2>使ったコマンドまとめ</h2>
<p>トラブルシュート中に何度も叩いたコマンドを、ここに集めておきます。</p>
<h3>IPSet 周り</h3>
<pre><code class="language-bash"># IPSet 一覧（CloudFront scope）
aws wafv2 list-ip-sets --scope CLOUDFRONT --region us-east-1

# IPSet の中身を確認
aws wafv2 get-ip-set \
  --scope CLOUDFRONT --region us-east-1 \
  --name &lt;name&gt; --id &lt;id&gt; \
  --query &quot;IPSet.Addresses&quot; --output json

# IPSet の更新（LockToken 必須）
LOCK=$(aws wafv2 get-ip-set \
  --scope CLOUDFRONT --region us-east-1 \
  --name &lt;name&gt; --id &lt;id&gt; \
  --query &quot;LockToken&quot; --output text)

aws wafv2 update-ip-set \
  --scope CLOUDFRONT --region us-east-1 \
  --name &lt;name&gt; --id &lt;id&gt; \
  --lock-token &quot;$LOCK&quot; \
  --addresses 192.0.2.0/24 198.51.100.0/24
</code></pre>
<h3>WebACL の attach 先確認</h3>
<pre><code class="language-bash"># Amplify Hosting に付いているかを確認（--resource-type を忘れない！）
aws wafv2 list-resources-for-web-acl \
  --region us-east-1 \
  --web-acl-arn &lt;arn&gt; \
  --resource-type AMPLIFY
</code></pre>
<h3>CloudTrail で履歴を追う</h3>
<pre><code class="language-bash"># 特定の API イベントを引く
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventName,AttributeValue=UpdateIPSet \
  --max-items 20 --output json

# 特定リソースに対する操作履歴
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=ResourceName,AttributeValue=&lt;arn&gt;
</code></pre>
<h2>まとめ</h2>
<p><code>Request blocked.</code> だけが手がかりの 403 から、Amplify Hosting 裏の WAF の存在に気付き、UI と API の乖離まで辿り着いた、というやや遠回りなトラブルシュート記でした。</p>
<p>整理すると、今回の学びは次の通りです。</p>
<ul>
<li>同じ目的のリソースが <strong>複数経路</strong> で管理されていないかを疑う</li>
<li>マネジメントコンソールの UI は実態と一致しないことがある。<strong>最後の真実は API レスポンス</strong></li>
<li>WAFv2 の <code>list-resources-for-web-acl</code> は <code>--resource-type</code> を忘れずに</li>
<li>CloudFront scope の WAF は <strong>必ず us-east-1</strong></li>
<li>WAFv2 の更新は <strong>LockToken をセットで</strong> 扱う</li>
</ul>
<p>Amplify Hosting の Firewall （AWS WAF 統合） は便利ですが、「いつのまにか UI と実態が乖離する」リスクがあることは覚えておいて損はないと思います。
同じような構成の運用に関わる方の参考になれば嬉しいです。最後まで読んでいただきありがとうございました！</p>
<h2>参考リンク</h2>
<ul>
<li><a href="https://docs.aws.amazon.com/waf/latest/developerguide/how-aws-waf-works.html">AWS WAF Developer Guide — How AWS WAF works</a></li>
<li><a href="https://docs.aws.amazon.com/amplify/latest/userguide/WAF-integration.html">AWS Amplify Hosting User Guide — Firewall support for Amplify hosted sites</a></li>
<li><a href="https://aws.amazon.com/blogs/aws/firewall-support-for-aws-amplify-hosted-sites/">AWS Blog — Firewall support for AWS Amplify hosted sites</a></li>
<li><a href="https://docs.aws.amazon.com/cli/latest/reference/wafv2/list-resources-for-web-acl.html"><code>list-resources-for-web-acl</code> API リファレンス（WAFv2）</a></li>
<li><a href="https://docs.aws.amazon.com/cli/latest/reference/cloudfront/list-distributions-by-web-acl-id.html"><code>list-distributions-by-web-acl-id</code> API リファレンス（CloudFront）</a></li>
<li><a href="https://docs.aws.amazon.com/cli/latest/reference/cloudtrail/lookup-events.html"><code>lookup-events</code> API リファレンス（CloudTrail）</a></li>
<li><a href="https://github.com/aws/aws-cli/issues/5417">aws/aws-cli #5417 — <code>wafv2 list-resources-for-web-acl</code> only fetches load balancers by default</a>（本記事で触れた <code>--resource-type</code> デフォルト挙動の罠について、CLI リポジトリで「ドキュメントに記載がない」と指摘された Issue）</li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/d.yamanaka/20260529/coverimage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Aurora MySQL メジャーバージョンアップで utf8mb4_general_ci を使い続けるために必要だったすべてのこと]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-05-26-aurora-mysql-collation-migration/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-05-26-aurora-mysql-collation-migration/</guid>
            <pubDate>Tue, 26 May 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[Aurora MySQL 2系から3系へのバージョンアップにおいて、既存の utf8mb4_general_ci を維持したまま移行する際に発生した問題と、Illegal mix of collations への対応・移行手順・移行後インシデント・COLLATIONチェックの仕組みについて共有します。]]></description>
            <content:encoded><![CDATA[<p>こんにちは。 KINTO テクノロジーズの DBRE チーム所属の <a href="https://x.com/RmcHoshi">@hoshino</a> です。</p>
<h1>はじめに</h1>
<p>Aurora MySQL 2系（MySQL 5.7互換）から3系（MySQL 8.0互換）へのメジャーバージョンアップを、19クラスタ・46スキーマ規模のメインシステムで実施しました。</p>
<p>このバージョンアップで最も苦労したのが COLLATION の問題です。</p>
<p>Aurora MySQL 3系ではデフォルト COLLATION が <code>utf8mb4_0900_ai_ci</code> に変わりますが、既存システムでは、検索条件、ORDER BY、ユニーク制約、JOIN、帳票、バッチ処理などが<code>utf8mb4_general_ci</code> の比較・ソート挙動を前提に動いています。</p>
<p><code>utf8mb4_0900_ai_ci</code> への変更は単なる DB 設定変更ではなく、アプリケーション仕様の変更に近いため、今回は互換性維持を優先し、<code>utf8mb4_general_ci</code> を維持したまま移行する方針を取りました。</p>
<p>しかし、Aurora MySQL 3系では <code>default_collation_for_utf8mb4</code> が <code>utf8mb4_0900_ai_ci</code> 固定で、サーバー側で変更する手段が用意されておらず、明示的に指定しないとセッションのデフォルトが <code>utf8mb4_0900_ai_ci</code> になってしまいます。そのため、<code>utf8mb4_general_ci</code> を維持するために以下の対策を実施しました。</p>
<p><strong>今回実施した対策</strong></p>
<ul>
<li><code>SCHEMA / TABLE / COLUMN / VIEW / ROUTINE / TRIGGER / EVENT</code> の COLLATION を統一</li>
<li>接続設定・SQL クエリで COLLATION を明示指定することで COLLATION を制御</li>
<li>意図しない COLLATION が設定されないように <code>information_schema</code> を使った Slack 自動通知によるチェック体制の整備</li>
</ul>
<p>本記事では、これらの対策の詳細について説明します。</p>
<h1>背景</h1>
<p>KINTO テクノロジーズの DBRE チームでは、Aurora MySQL 2系（MySQL 5.7互換）から3系（MySQL 8.0互換）へのメジャーバージョンアップを進めてきました。</p>
<p>弊社では多数のクラスタを運用していますが、今回対象となったのは複数プロダクトが共有するメインシステムの DB です。</p>
<p>このメインシステムは少し特殊な構成になっています。</p>
<p>1つの環境に対して 2つの Aurora クラスタが存在しており、複数プロダクトがこの2クラスタを共有して利用しています。</p>
<p>両クラスタは密接に連携しているため、片方だけバージョンアップするわけにはいかず、同時に移行する必要がありました。</p>
<p>対象規模は dev・stg・prod などの全環境を合計して 19クラスタ・46スキーマ・56ユーザー にのぼります。</p>
<p>構成を図にすると以下のようになります。</p>
<p><img src="/assets/blog/authors/hoshino/aurora_collation_system_overview.webp" alt="対象システムの構成図"></p>
<p>この移行で最も苦労したのが COLLATION の問題でした。</p>
<p>Aurora MySQL 3系（MySQL 8.0）のデフォルト COLLATION は <code>utf8mb4_0900_ai_ci</code> です。</p>
<p>一方、既存のデータベースは <code>utf8mb4_general_ci</code> で運用されていました。</p>
<p>システム全体を <code>utf8mb4_0900_ai_ci</code> に切り替えるという選択肢もゼロではありませんでしたが、COLLATION の変更はアプリケーションの挙動に直接影響します。</p>
<p><code>utf8mb4_general_ci</code> と <code>utf8mb4_0900_ai_ci</code> は、どちらも大文字・小文字を区別しない COLLATION ですが、内部のソートアルゴリズムが異なります。</p>
<p><code>utf8mb4_0900_ai_ci</code> は Unicode Collation Algorithm（UCA 9.0.0）に準拠しており、<code>=</code> 演算子による比較結果や <code>ORDER BY</code> のソート順が <code>utf8mb4_general_ci</code> とは異なるケースがあります。</p>
<p>既存のアプリケーションが <code>utf8mb4_general_ci</code> の挙動を前提としている場合、COLLATION を切り替えただけで検索結果やソート順が変わり、意図しない不具合につながる可能性があります。</p>
<p>そうなると各プロダクト側でも影響調査や改修が必要になります。</p>
<p>複数プロダクトが共有しているデータベースであるため、その改修範囲は広く、プロダクト側の開発コストも大きくなります。</p>
<p>プロダクト側の負担を最小限にするためにも、<code>utf8mb4_general_ci</code> を維持したままバージョンアップするという方針を選択しました。</p>
<h1>Illegal mix of collations に対する対応</h1>
<p><code>utf8mb4_general_ci</code> を維持する方針で進めるにあたって直面したのが、<code>Illegal mix of collations</code> というエラーです。</p>
<p>このエラーは、テーブル側の COLLATION とセッション側の COLLATION が混在した状態でクエリを実行したときに発生します。Aurora MySQL 3系では、サーバー側でデフォルト COLLATION を変更する手段がないため、何も対策しないとこのエラーが発生しやすい構造になっています。</p>
<p>MySQL 8.0 には <code>default_collation_for_utf8mb4</code> というシステム変数があります（<a href="https://dev.mysql.com/doc/refman/8.0/ja/server-system-variables.html">MySQL 公式: Server System Variables</a>）。</p>
<p>これは <code>CHARACTER SET utf8mb4</code> を指定して COLLATE を省略したとき、どの COLLATION がデフォルトで使われるかを決める変数で、デフォルト値は <code>utf8mb4_0900_ai_ci</code> です。</p>
<p>通常の MySQL であれば、<code>SET PERSIST default_collation_for_utf8mb4=&#39;utf8mb4_general_ci&#39;;</code> を実行することでこの値を変更できますが、Aurora MySQL ではこの変数を変更する手段がありません。</p>
<p>理由としては <code>SET PERSIST</code> は Aurora では使えず、パラメータグループにもこの設定項目が存在しないためです。</p>
<p>この制約により、<code>collation_connection</code> を指定せずに接続した場合、セッションのデフォルトが <code>utf8mb4_0900_ai_ci</code> になってしまいます。</p>
<p>影響は実行するクエリだけではありませんでした。</p>
<p>VIEW や ROUTINE（ストアドプロシージャ・ファンクション）は、作成時のセッションの <code>character_set_client</code> や <code>collation_connection</code> が定義に依存するため、<code>utf8mb4_0900_ai_ci</code> のセッションで VIEW を作成すると、その VIEW 自体が <code>utf8mb4_0900_ai_ci</code> を持ってしまいます。</p>
<p>後からセッションの COLLATION を変えても、すでに作成された VIEW の定義は変わりません。</p>
<p>さらに、クエリの中で COLLATION が動的に決まる箇所にも影響します。</p>
<p>たとえば UNION や CAST 関数を含むクエリでは、TABLE 側の COLLATION（<code>utf8mb4_general_ci</code>）とセッション側の COLLATION（<code>utf8mb4_0900_ai_ci</code>）が混在してエラーが発生します。</p>
<p><strong>例1：CAST 関数を使った JOIN</strong></p>
<pre><code class="language-sql">SELECT *
FROM table_a AS t1
JOIN table_b AS t2
  ON CAST(t1.id AS CHAR) = t2.code;
--    ^^^^^^^^^^^^^^^^^^   ^^^^^^^
--    utf8mb4_0900_ai_ci   utf8mb4_general_ci
--   （セッションのデフォルト）（テーブルの COLLATION）
</code></pre>
<p><code>CAST(t1.id AS CHAR)</code> はセッションの <code>collation_connection</code> に従うため、Aurora のデフォルトである <code>utf8mb4_0900_ai_ci</code> になります。一方、<code>t2.code</code> はテーブル定義の <code>utf8mb4_general_ci</code> のままです。この2つを <code>=</code> で比較するため、COLLATION の不一致が発生します。</p>
<p><strong>例2：UNION で異なる COLLATION が混在</strong></p>
<pre><code class="language-sql">SELECT name FROM table_a
--     ^^^^
--     utf8mb4_general_ci（テーブルの COLLATION）
UNION
SELECT CAST(id AS CHAR) FROM table_b;
--     ^^^^^^^^^^^^^^^^
--     utf8mb4_0900_ai_ci（セッションのデフォルト）
</code></pre>
<p>UNION は各 SELECT の COLLATION を統一する必要がありますが、上記のように一方が <code>utf8mb4_general_ci</code>、もう一方が <code>utf8mb4_0900_ai_ci</code> になると統一できず、エラーになります。</p>
<p>どちらのクエリも、最終的には以下のエラーになります。</p>
<pre><code class="language-text">ERROR 1267 (HY000): Illegal mix of collations (utf8mb4_general_ci,IMPLICIT) and
(utf8mb4_0900_ai_ci,IMPLICIT) for operation &#39;=&#39;
</code></pre>
<p>これを防ぐには、接続時に COLLATION を明示的に指定するか、SQL 文の中で COLLATE 句を明示する方法があります。</p>
<p><strong>方法1：接続時に COLLATION を指定する</strong></p>
<pre><code class="language-sql">-- MySQL クライアントから接続する場合
SET NAMES utf8mb4 COLLATE utf8mb4_general_ci;
</code></pre>
<pre><code class="language-text"># JDBC URL での指定例
jdbc:mysql://host:3306/mydb?connectionCollation=utf8mb4_general_ci
</code></pre>
<p><strong>方法2：SQL 文の中で COLLATE 句を明示する</strong></p>
<p>UNION や CAST など動的に COLLATION が決まる箇所に、直接 COLLATE 句を付与する方法です。</p>
<pre><code class="language-sql">-- UNION での指定例
SELECT name COLLATE utf8mb4_general_ci FROM table_a
UNION
SELECT name COLLATE utf8mb4_general_ci FROM table_b;

-- CAST での指定例
SELECT CAST(column AS CHAR CHARACTER SET utf8mb4) COLLATE utf8mb4_general_ci
FROM table_a;
</code></pre>
<p>しかし、接続時の COLLATION 指定やクエリへの COLLATE 句付与は、あくまで移行後の運用で問題を防ぐための対策です。</p>
<p>移行するにあたり、移行前に Aurora 2系側の COLLATION を統一しておく必要がありました。</p>
<p><strong>注意事項</strong></p>
<p><code>SET NAMES utf8mb4</code>（COLLATE 句を省略）を実行すると、それまでに設定していた <code>collation_connection</code> が破棄され、Aurora MySQL 3系のデフォルトである <code>utf8mb4_0900_ai_ci</code> に戻ってしまいます。</p>
<pre><code class="language-sql">SET SESSION collation_connection = &#39;utf8mb4_general_ci&#39;;
-- ↑ ここで utf8mb4_general_ci になる

SET NAMES utf8mb4;
-- ↑ COLLATE 句がないため utf8mb4_0900_ai_ci に戻ってしまう
</code></pre>
<p>ORM やアプリケーションフレームワークが内部で <code>SET NAMES utf8mb4</code> を発行する実装も存在するため、実際に発行されるクエリのログを確認し、暗黙の <code>SET NAMES</code> が含まれていないかを把握しておく必要があります。</p>
<p><code>SET NAMES</code> を使う場合は、必ず COLLATE 句までセットで指定するのが確実です。</p>
<h1>移行手順と事前準備</h1>
<h2>移行方法</h2>
<p>今回の移行は mysqldump などで論理ダンプを取得し、それをインポートする方式を採用しました。</p>
<p>Aurora MySQL 2系から3系への移行方式としては、Blue/Green デプロイやインプレースアップグレードといった選択肢もありますが、今回は以下の理由からダンプ・インポートを採用しました。</p>
<ul>
<li>COLLATION の事前調整で DDL 変更が必要だった<ul>
<li>VIEW や ROUTINE の定義を書き換えて再作成する必要がありましたが、Blue/Green デプロイでは DDL 変更が Green 環境へのレプリケーション中断を引き起こすリスクがあり、レプリケーションとの互換性検証コストが高いと判断しました</li>
</ul>
</li>
<li>ダンプ・インポート方式の社内実績が豊富だった<ul>
<li>弊社では全環境で数百の DB クラスタが存在しており、そのほとんどをダンプ・インポート方式で移行しました</li>
<li>そのため、今回のような複数プロダクトが共有する大規模システムの移行において、Blue/Green デプロイなどの実績のない手法を採用するリスクは取れませんでした</li>
</ul>
</li>
</ul>
<p>安全を最優先に考えた結果、確実にコントロールできるダンプ・インポート方式を選択しました。</p>
<h2>ダンプ・インポート時の COLLATION エラー</h2>
<p>ダンプ・インポート方式で移行を進めたところ、COLLATION の不整合によるエラーが発生しました。</p>
<p>Aurora 2系側の COLLATION が <code>utf8mb4_general_ci</code> に統一されていない状態でダンプを取ってインポートすると、VIEW の作成時に <code>Illegal mix of collations</code> エラーとなり、移行そのものが失敗します。</p>
<p>そのため、以下の手順で移行を実施しました。</p>
<ol>
<li>Aurora 2系側で COLLATION を <code>utf8mb4_general_ci</code> に統一する</li>
<li>その状態でダンプを取得する</li>
<li>Aurora 3系にインポートする</li>
</ol>
<h2>事前作業の内容</h2>
<p>事前作業では SCHEMA / TABLE / COLUMN / VIEW / ROUTINE のすべてに手を入れる必要がありました。</p>
<p>対象となるのは2クラスタ × 全環境（dev・stg・prod 等）にまたがる数十スキーマです。複数プロダクトが共有しているため、各スキーマの VIEW や ROUTINE がどのプロダクトに属するかを把握し、プロダクトチームと調整しながら進める必要がありました。</p>
<p>調整箇所は全環境合計で数千にのぼり、環境ごとにリストを作成し、プロダクトチームにレビューを依頼し、反映前に最終チェックを行うというサイクルを、すべての環境に対して繰り返し実施しました。</p>
<p>以下、具体的な調整方法をオブジェクトの種類ごとに説明します。</p>
<h3>SCHEMA / TABLE / COLUMN の調整</h3>
<p>SCHEMA・TABLE・COLUMN は ALTER 文で COLLATION を <code>utf8mb4_general_ci</code> に変更しました。</p>
<pre><code class="language-sql">-- SCHEMA
ALTER DATABASE ${schema} CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

-- TABLE
ALTER TABLE ${table} CHARACTER SET utf8mb4 COLLATE &#39;utf8mb4_general_ci&#39;;

-- COLUMN
ALTER TABLE ${table} CONVERT TO CHARACTER SET utf8mb4 COLLATE &#39;utf8mb4_general_ci&#39;;
</code></pre>
<h3>VIEW / ROUTINE / TRIGGER / EVENT の調整</h3>
<p>一方、VIEW・ROUTINE・TRIGGER・EVENT は ALTER では対応できないため、定義を書き換えて再作成する必要がありました。</p>
<p>定義内の文字コード・COLLATION を一括で置換してから <code>CREATE OR REPLACE VIEW</code> で再作成するアプローチを取りました。主な置換パターンは以下の通りです。</p>
<pre><code class="language-text">&quot;utf8 &quot;              → &quot;utf8mb4 &quot;
&quot;utf8_general_ci&quot;    → &quot;utf8mb4_general_ci&quot;
&quot;utf8mb4_0900_ai_ci&quot; → &quot;utf8mb4_general_ci&quot;
&quot;utf8mb4_unicode_ci&quot; → &quot;utf8mb4_general_ci&quot;
&quot;charset utf8mb4) AS&quot; → &quot;charset utf8mb4) COLLATE utf8mb4_general_ci AS&quot;
</code></pre>
<p>最後のパターンは CAST 関数の末尾に該当します。<code>CAST(column AS CHAR)</code> のような式では COLLATION が動的に決まるため、明示的に COLLATE を付与する必要がありました。</p>
<p>ただし、文字列置換だけでは対応しきれないケースも存在しました。</p>
<p>CAST 関数の使い方が複雑であったり、置換パターンに収まらない定義を持つ VIEW がいくつかありました。</p>
<p>こうした箇所は <code>information_schema</code> で COLLATION の状態を一つひとつ確認しながら、手動で定義を修正して再作成しました。</p>
<pre><code class="language-sql">-- 置換漏れがないか確認するクエリ
SELECT table_schema, table_name,
       character_set_client, collation_connection
FROM information_schema.views
WHERE collation_connection != &#39;utf8mb4_general_ci&#39;
  AND table_schema NOT IN (&#39;mysql&#39;, &#39;information_schema&#39;, &#39;performance_schema&#39;, &#39;sys&#39;);
</code></pre>
<p>この確認を怠ると、一見すると置換が完了しているように見えても <code>utf8mb4_0900_ai_ci</code> が残ったままの定義が存在する場合インポート時にエラーが発生するか、移行後に <code>Illegal mix of collations</code> となってしまいます。</p>
<h1>移行後に発生したインシデント</h1>
<p>事前作業で Aurora 2系の COLLATION を統一し、Aurora 3系への移行を完了しました。</p>
<p>しかし移行後、プロダクトから <code>Illegal mix of collations</code> のエラーが発生したとの報告がありました。</p>
<h2>発生したエラー</h2>
<p>エラーの内容は以下の通りです。</p>
<pre><code class="language-text">1267 (HY000): Illegal mix of collations (utf8mb4_0900_ai_ci,IMPLICIT) and
(utf8mb4_general_ci,IMPLICIT) for operation &#39;=&#39;
</code></pre>
<p>Aurora 2系では問題なく動作していた機能が、Aurora 3系への移行後にエラーとなっていました。</p>
<h2>原因の調査</h2>
<p>まず、エラーが発生しているクエリの調査を行いました。</p>
<p>問題のクエリには CAST 関数を使った JOIN が含まれていました。</p>
<p>以下は同様の構造を持つ例です。</p>
<pre><code class="language-sql">-- 例：CAST 関数を使った JOIN で COLLATION の不一致が発生するケース
SELECT *
FROM table_a AS t1
LEFT JOIN table_b AS t2
  ON CAST(t1.id AS CHAR) = t2.code;
</code></pre>
<p><code>CAST(... AS CHAR)</code> の結果にはセッションの <code>collation_connection</code> が適用されます。</p>
<p>該当のアプリケーションでは <code>collation_connection</code> が指定されておらず、Aurora のデフォルトである <code>utf8mb4_0900_ai_ci</code> が適用されていました。</p>
<p>その結果、CAST 関数の結果は <code>utf8mb4_0900_ai_ci</code> となり、テーブル側の <code>utf8mb4_general_ci</code> と混在して <code>Illegal mix of collations</code> が発生していました。</p>
<h2>対処方法</h2>
<p>対処として、アプリケーションの DB 接続設定に <code>collation_connection=utf8mb4_general_ci</code> を追加しました。</p>
<pre><code class="language-text"># 接続文字列に COLLATION 設定を追加
mysql+mysqlconnector://user:password@host/dbname
  ?init_command=SET SESSION collation_connection=utf8mb4_general_ci
</code></pre>
<p><code>init_command</code> は接続確立直後に実行されるため、以降のクエリでは <code>collation_connection</code> が <code>utf8mb4_general_ci</code> の状態で処理されます。</p>
<p><code>collation_connection</code> が <code>utf8mb4_general_ci</code> になることで、<code>CAST</code> や <code>UNION</code> のようにセッションの COLLATION 値で動的に COLLATION が決まる箇所も <code>utf8mb4_general_ci</code> に揃えられ、テーブル側との不一致を防げます。</p>
<p>この変更をリリースした後、エラーは解消し、現在は安定稼働しています。</p>
<h1>COLLATION の定期チェックと自動通知</h1>
<p>一度問題を修正しても、新しい VIEW が作成されたりアプリケーションが更新されたりすると、同様の問題が再発する可能性があります。</p>
<p>本番環境でエラーが発生してから気づくのではなく、開発段階で COLLATION の不一致を早期に検知するために、全環境の COLLATION の状態を定期的にチェックし、意図しない COLLATION が設定された場合に自動で通知する仕組みと、手動で現状のCOLLATIONの状態を確認できる仕組みを構築しました。</p>
<h2>自動化の仕組み</h2>
<p>仕組みの全体像は以下の通りです。</p>
<p><img src="/assets/blog/authors/hoshino/aurora_collation_monitoring_architecture.webp" alt="COLLATION チェック自動化の構成図"></p>
<h3>1. 日次で COLLATION 情報を自動取得</h3>
<p>COLLATION をチェックするクエリを CLI コマンドとして実装しました。</p>
<p>このコマンドを全クラスタに対して日次で自動実行し、取得結果を JSON 形式で S3 に保存しています。</p>
<h3>2. 期待する COLLATION との照合と Slack 通知</h3>
<p>EventBridge で決まった時間に、S3 上の JSON データを精査します。</p>
<p>DynamoDB にあらかじめ登録してある「期待する COLLATION」と照合し、意図しない COLLATION が検出された場合は Slack の専用チャンネルに自動通知します。</p>
<h3>3. CLI による手動チェック</h3>
<p>CLI コマンドは手動でも実行できます。</p>
<p>新規 TABLE 作成後やトラブルシューティング時など、任意のタイミングで特定のクラスタの状態を確認したい場合に使用しています。</p>
<h2>COLLATION チェックで実行しているクエリ</h2>
<p>自動化の仕組みの中で各クラスタに対して実行しているクエリは、<code>information_schema</code> を使って <code>utf8mb4_general_ci</code> 以外の COLLATION が混入していないかを検出するものです。対象が SCHEMA・TABLE・COLUMN・VIEW・ROUTINE・TRIGGER の6種類です。</p>
<pre><code class="language-sql">-- SCHEMA の COLLATION 確認
SELECT schema_name, default_character_set_name, default_collation_name
FROM information_schema.schemata
WHERE schema_name NOT IN (&#39;mysql&#39;, &#39;information_schema&#39;, &#39;performance_schema&#39;, &#39;sys&#39;);

-- TABLE の COLLATION 確認
SELECT table_schema, table_name, table_collation
FROM information_schema.tables
WHERE table_collation != &#39;utf8mb4_general_ci&#39;
  AND table_schema NOT IN (&#39;mysql&#39;, &#39;information_schema&#39;, &#39;performance_schema&#39;, &#39;sys&#39;);

-- COLUMN の COLLATION 確認
SELECT table_schema, table_name, column_name, collation_name
FROM information_schema.columns
WHERE collation_name IS NOT NULL
  AND collation_name != &#39;utf8mb4_general_ci&#39;
  AND table_schema NOT IN (&#39;mysql&#39;, &#39;information_schema&#39;, &#39;performance_schema&#39;, &#39;sys&#39;);

-- VIEW の collation_connection 確認
SELECT table_schema, table_name,
       character_set_client, collation_connection
FROM information_schema.views
WHERE collation_connection != &#39;utf8mb4_general_ci&#39;;

-- ROUTINE の COLLATION 確認
SELECT routine_schema, routine_name, routine_type,
       collation_connection, database_collation
FROM information_schema.routines
WHERE collation_connection != &#39;utf8mb4_general_ci&#39;;

-- TRIGGER の COLLATION 確認
SELECT trigger_schema, trigger_name,
       collation_connection, database_collation
FROM information_schema.triggers
WHERE collation_connection != &#39;utf8mb4_general_ci&#39;;
</code></pre>
<h1>今後のAuroraバージョンアップ（Aurora MySQL 4）に向けて</h1>
<p>MySQL 8.4 で <code>default_collation_for_utf8mb4</code> を <code>SET PERSIST</code> で変更すると、以下の deprecated 警告が表示されます。</p>
<pre><code class="language-text">mysql&gt; SET PERSIST default_collation_for_utf8mb4=&#39;utf8mb4_general_ci&#39;;
Query OK, 0 rows affected, 1 warning (0.00 sec)

mysql&gt; SHOW WARNINGS;
+---------+------+--------------------------------------------------------------------------------------------------------+
| Level   | Code | Message                                                                                                |
+---------+------+--------------------------------------------------------------------------------------------------------+
| Warning | 1681 | Updating &#39;default_collation_for_utf8mb4&#39; is deprecated. It will be made read-only in a future release. |
+---------+------+--------------------------------------------------------------------------------------------------------+
</code></pre>
<p>「将来のリリースで read-only にする」と警告されていることから、今後この変数による COLLATION の制御はさらに難しくなる可能性があります。COLLATION を確実に制御するためには、SCHEMA・TABLE・COLUMN・VIEW・ROUTINE のすべてで明示指定し、<code>information_schema</code> で定期的にチェックするアプローチが引き続き有効です。</p>
<p><a href="https://docs.aws.amazon.com/AmazonRDS/latest/AuroraMySQLReleaseNotes/AuroraMySQL.release-calendars.html">Aurora MySQL のリリースカレンダー</a>によると、Aurora MySQL 3 のメジャーバージョン標準サポートは 2028年4月30日 までとなっています。その後は次のメジャーバージョンへの移行が必要になるため、今回整備した定期チェックの仕組みや CLI コマンドを次のバージョンアップでもそのまま活用できるようにしておくことが重要だと考えています。</p>
<h1>まとめ</h1>
<ul>
<li>Aurora MySQL 3系では MySQL 8.0 互換となり、デフォルト COLLATION が utf8mb4_0900_ai_ci に変わったことで、既存 DB の utf8mb4_general_ci と混在しやすくなった。これが今回の苦労の根本原因</li>
<li>Aurora MySQL では <code>SET PERSIST</code> で <code>default_collation_for_utf8mb4</code> を変更できないため、サーバー側で utf8mb4 の デフォルト COLLATION を制御できない</li>
<li>接続時に <code>collation_connection</code> を明示指定しないと、セッションのデフォルトが <code>utf8mb4_0900_ai_ci</code> となり <code>Illegal mix of collations</code> が発生する可能性がある</li>
<li>ダンプ・インポートで移行する場合、移行前に Aurora 2系側の SCHEMA / TABLE / COLUMN / VIEW / ROUTINE の COLLATION を統一しておく必要がある</li>
<li><code>SET NAMES utf8mb4;</code>（COLLATE 省略）は直前の COLLATE 指定を破棄するため、接続文字列の <code>init_command</code> で指定するのが確実</li>
<li>移行後も <code>information_schema</code> を使った COLLATION の定期チェックと自動通知の仕組みが有効</li>
<li>今回整備した COLLATION チェックの仕組みや CLI コマンドは、次のバージョンアップでもそのまま活用できる</li>
</ul>
<p>本記事の内容が、同じ課題に取り組んでいる方々の参考になれば幸いです。</p>
<h1>参考文献</h1>
<ul>
<li><a href="https://dev.mysql.com/doc/refman/8.0/ja/upgrading-from-previous-series.html">Changes in MySQL 8.0</a><ul>
<li><code>collation_server</code> のデフォルトが <code>utf8mb4_0900_ai_ci</code> に変更</li>
</ul>
</li>
<li><a href="https://dev.mysql.com/doc/refman/8.0/ja/server-system-variables.html">Server System Variables</a><ul>
<li><code>default_collation_for_utf8mb4</code> パラメータの補足</li>
</ul>
</li>
<li><a href="https://dev.mysql.com/doc/refman/8.0/ja/charset-connection.html">接続に関するパラメータの理解</a><ul>
<li><code>character_set_*</code> / <code>collation_*</code> 各パラメータの関係</li>
</ul>
</li>
<li><a href="https://dev.mysql.com/doc/refman/8.0/ja/set-names.html">SET NAMES の補足</a><ul>
<li>セッションの COLLATION 指定方法</li>
</ul>
</li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2026年2月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-05-15-newcomer-202602/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-05-15-newcomer-202602/</guid>
            <pubDate>Fri, 15 May 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[2026年2月に入社した6人の皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、2026年2月入社の岩月です！</p>
<p>本記事では、2026年2月入社のみなさまに入社直後の感想をお伺いし、まとめてみました。
KINTO テクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>森田和明</h1>
<p>![森田和明さんのプロフィール画像](/assets/blog/authors/iwatsuki/2026-05-15-newcomer-202602/morita.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>コーポレートIT部AIファーストGの森田です。</li>
<li>社内の生成AI活用の推進やトヨタグループにおけるAI活用支援を担当しています。</li>
<li>奈良に住んでます。</li>
<li>最近書籍を執筆しました！<a href="https://gihyo.jp/book/2026/978-4-297-15458-5">AWSではじめるMCP実践ガイド</a></li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>AIファーストGは「AI Transformation 」「AI Engineering」「AI Development」の3チーム体制で、私はAI Engineeringに所属です。</li>
<li>「アイデア生成」→「実現可能性の検証」→「実施とデリバリー」→「ケース展開」→「アイデア生成」とループを回し、AI活用の活性化に取り組んでいます</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>入社前のカジュアル面談などを通じて思っていた通りでした。</li>
<li>エンジニアが多い会社ではありますが、技術スタックが様々で、各自がそれぞれの分野でスペシャリストという印象です。</li>
<li>AIファーストGも全員バックグラウンドが違うので、それぞれの得意分野とAIを掛け合わせて専門性を発揮しています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>私はOsaka Tech Labで勤務していまして、まず、オフィスが綺麗です。</li>
<li>所属は様々ですが「大阪を盛り上げていこう！」という雰囲気があり、技術交流イベントなど一致団結できる取り組みがあります。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>趣味として技術ブログをやっているので、すんなり書けました！</li>
</ul>
</li>
<li><strong>岩月 ⇒ 森田さんへの質問</strong><blockquote>
<p>森田さんは技術系の書籍をいくつか執筆されていますが、執筆のきっかけや苦労話があったら教えてください！</p>
</blockquote>
<ul>
<li>私が執筆した書籍は、執筆メンバーが共著者を探している中で、声をかけてもらって参加したというのが経緯です。</li>
<li>苦労はたくさんありますが（笑）、扱うテーマがAWSや生成AIなので執筆している最中にアップデートがあり、その度に原稿の更新や画面キャプチャの取り直しを行っています</li>
</ul>
</li>
</ul>
<h1>成島大介</h1>
<p>![成島大介さんのプロフィール画像](/assets/blog/authors/iwatsuki/2026-05-15-newcomer-202602/narushima.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>新サービス開発部 プロジェクト推進Ｇに所属しています。</li>
<li>名古屋オフィス勤務です。</li>
<li>トヨタグループ向け案件のプロジェクトマネジメントを担当しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>プロジェクト推進Ｇは兼務除くと5名体制で名古屋3名、東京1名、福岡1名です。</li>
<li>プロジェクトマネージャだけの組織なので、開発メンバーは状況に応じて他部署から参画してもらい開発体制を作ります。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>名古屋オフィスに開発者が少ないことが入社前の印象とのギャップです。</li>
<li>KINTOとKTCが同じフロアなので、入社前のオフィス見学では気づきませんでした。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>仕事については、問題課題がない限り任されていると感じます。</li>
<li>一緒にランチに行く機会が多くいろいろ情報収集できて助かってます。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>個人（匿名）で技術ブログは書いてましたが、最近はさっぱりです。</li>
<li>一番読んでもらえた記事が専門外のC++ネタで何を書くと良いのか分かってません。</li>
</ul>
</li>
<li><strong>森田さん ⇒ 成島さんへの質問</strong><blockquote>
<p>最近の猫ちゃんの面白エピソードを教えてください！</p>
</blockquote>
<ul>
<li>猫がドアノブに飛びついてドアを開けることをマスターしました。
娘（中３）の部屋にも問答無用で侵入します（ドア全開）。
娘も親には怒るが、猫には怒りません。</li>
</ul>
</li>
</ul>
<h1>きゅーじ</h1>
<p>![きゅーじさんのプロフィール画像](/assets/blog/authors/iwatsuki/2026-05-15-newcomer-202602/kyuji.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>my route開発部ビジネス開発支援グループに所属しています。</li>
<li>勤務場所は福岡のFukuoka Tech Labです。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>主にトヨタファイナンシャルサービス株式会社のmyroute業務支援を行っています。主に営業、マーケティング業務をサポートしており、2～5名のチームで動いています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>Fukuoka Tech Labからの景色が最高に良い！※入社後初めて入りました。</li>
<li>オンボーディングがしっかりあり、安心して業務が開始できました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>良いプレッシャーの中で、和やかな雰囲気かなと思います。</li>
<li>それぞれの個性と強みを生かしながら、どんどん仕事を作っていく感じが良いなと思っています。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>テックブログは、書いたことなかったかつ、非エンジニアの私が書けるのか不安でした。</li>
</ul>
</li>
<li><strong>成島さん ⇒ きゅーじさんへの質問</strong><blockquote>
<p>ミシンで最近作った作品教えてください！</p>
</blockquote>
<ul>
<li>2月にミシンを買っていろいろ作ろうと息巻いておりましたが、現状、カーテンの裾上げ、布団カバーの修理等々が私の作品ですかね。クッションカバーを今度作ろうと布屋さんに行こうと思います。</li>
</ul>
</li>
</ul>
<h1>かわちゃん</h1>
<p>![かわちゃんさんのプロフィール画像](/assets/blog/authors/iwatsuki/2026-05-15-newcomer-202602/kawachan.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>my route開発部のプロダクト推進グループに所属しています。</li>
<li>神保町オフィス勤務です。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>プロダクト開発チームにいますが、私を含め4名です。うち3名はプロデューサーとして、うち1名は他部署から分析部分のみお手伝いいただいています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>オンボーディングの研修が手厚くて驚きました。</li>
<li>フリーアドレスかなと思ったのですが、固定だったのが新鮮でした。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>大人数ですが、思った以上に静かな部署です。</li>
<li>外国籍の方が多いので最初ドキドキしましたが今は慣れました。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>テックブログは書いたことがないのでちょっと焦りました。</li>
</ul>
</li>
<li><strong>きゅーじさん ⇒ かわちゃんさんへの質問</strong><blockquote>
<p> 国内旅行でおすすめの場所はありますか？</p>
</blockquote>
<ul>
<li>あまり国内も海外も旅行に行っておらずおすすめできる場所がありませんが、高知は2回ほど行っていて居心地が良かったです。桂浜がとても素敵でした。</li>
</ul>
</li>
</ul>
<h1>SHN</h1>
<p>![SHNさんのプロフィール画像](/assets/blog/authors/iwatsuki/2026-05-15-newcomer-202602/shn.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>KTC 業務システム開発部に所属しつつ、現在は KINTO 業務部に出向（兼務）しています。</li>
<li>名古屋市在住で、桜通オフィスに勤務しています。</li>
<li>バックオフィス業務の改善や効率化を担当しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>出向先の KINTO 業務部では、IT 推進チームに所属しており、4 人体制で業務にあたっています。</li>
<li>バックオフィス業務を IT 目線で改善するチームとして、KTC の開発編成部や業務委託先などの関係者と連携しながら仕事を進めています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>良い意味で「トヨタっぽくない」ところがギャップでした。</li>
<li>トヨタ系列の会社ということで、縦割りな組織・慎重な意思決定・多重な申請フローなどがある程度あるだろうと想像していましたが、実際にそんなことはなく、オープンでフランク、かつスピード感のある職場だと感じています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>KINTO 業務部はサービスを円滑に運営するため、販売店様との架電対応を担うメンバーも多く、適度な緊張感があります。</li>
<li>IT スキルだけでなく、リースや保険を含む KINTO サービスへの深い理解がなければ対応しきれない場面も多く、日々多くのことを学んでいます。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>KTC への応募・入社を検討する前からこのブログを読んでいたので、「とうとう自分の番が来たか」という感慨がありました。</li>
<li>応募・入社を検討されている方にとって有益な情報を発信できる、良い機会だと思っています。</li>
</ul>
</li>
<li><strong>かわちゃんさん ⇒ SHNさんへの質問</strong><blockquote>
<p>ランニングするときのこだわりや、自分だけのルールはありますか？</p>
</blockquote>
<ul>
<li>ランニングは習慣的に続けているのですが、「今日は走りたくないな」と感じる日も正直よくあります。 </li>
<li>そんな日は、走り終わった後にコンビニへ直行してアイスやスイーツを買うことを自分へのご褒美にして、モチベーションを保つようにしています。</li>
</ul>
</li>
</ul>
<h1>岩月</h1>
<p>![岩月のプロフィール画像](/assets/blog/authors/iwatsuki/2026-05-15-newcomer-202602/iwatsuki.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>コーポレートIT部コーポレートIT Gの岩月です。</li>
<li>社内IT業務の改善や効率化のために座席表システムをはじめとするいくつかのツールを開発しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>東京と名古屋合わせて11名が在籍しています。</li>
<li>人数もそれなりに多く業務も多種多様なチームなので、これから業務範囲を広げていく中で、少しずつ全体像を理解していきたいと思っています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>生成AIを活用した、スピード感のある社内IT改善に取り組めると感じて入社しました。</li>
<li>前々職での上司や前職の同僚が在籍していて、入社前から社内の様子を伺えていたこともあり、大きなギャップは感じていません。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>打ち合わせで積極的に発言が飛び交い、現場からボトムアップに課題を挙げて改善していく雰囲気があると感じています。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>ここしばらく書く機会がなかったのですが、こうした機会をいただけるのであれば、今後は積極的に情報発信していきたいと思います。</li>
</ul>
</li>
<li><strong>SHNさん ⇒ 岩月への質問</strong><blockquote>
<p>デスクワークで手放せない or 仕事が捗るガジェットはありますか？</p>
</blockquote>
<ul>
<li>業務端末にはセキュリティを考慮して個人所有のデバイスは接続していませんが、その分ソフトウェアで工夫しています。</li>
<li>業務効率化のために自作しているmacOS用のアプリで、メニューバーに次の予定の時刻を常時表示しつつ、当日のスケジュール確認や、ワンクリックでZoom・Teams・Slackハドルへの参加、会議資料へのアクセスができるようにしています。</li>
<li>これまでも似たアプリを個人で作って使い続けていたこともあり、今やこれがないと会議の時間を忘れてしまう体になってしまいました。</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTOテクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[【Osaka Tech Lab】年度末イベント「O-KINI FY2026」開催！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-05-01-o-kini-fy2026/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-05-01-o-kini-fy2026/</guid>
            <pubDate>Fri, 01 May 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[2026年3月末に開催した年度末イベントについて本記事で紹介します]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>KINTOテクノロジーズ Osaka Tech Lab所属のひがしです。</p>
<p>2026年3月末に、年度末イベント【O-KINI FY2026】を開催しました！</p>
<p>Osaka Tech Labでは、メンバーそれぞれが異なるプロジェクトを担当することも多く、普段はなかなか横のつながりが生まれにくいこともあります。そのため、拠点全体で交流を深める文化を大切にしており、今回のイベントもその一環です。</p>
<p>2025年度の活躍をメンバー同士で労いながら、一体感をさらに高めることを目的に、有志メンバーが企画したOsaka Tech Lab初の取り組みとなりました。</p>
<p>企画チーム7名のうち5名は入社1年以内のメンバー。先輩社員2名からOsaka Tech Labの雰囲気や文化を吸収しながら、一緒にイベントを作り上げました。そんな新入り5名で、当日の様子をブログにまとめます！</p>
<h2>ノベルティ</h2>
<p><strong>執筆者：m</strong></p>
<p>イベントを行うにあたり今回様々なノベルティを用意しました！
参加してくれたみなさん全員に、Osaka Tech Labらしさをぎゅっと詰め込んだネックストラップとステッカーを用意しました。
また、受賞者の皆さんにはデスクに飾るとふと目に入るたびに「今年もがんばろう」と前向きな気持ちになれる、そんな&quot;日常の中でふりかえれる記念品&quot;になることを目指しました。</p>
<ul>
<li>全員向け<ul>
<li>ネックストラップ</li>
<li>ステッカー</li>
</ul>
</li>
<li>表彰者<ul>
<li>アクリルスタンド</li>
<li>トロフィー</li>
</ul>
</li>
</ul>
<p>これらのノベルティのデザインは、すべてOsaka Tech Lab所属のデザイナーが担当しました。
Osaka Tech Labらしさを大切にしながら、日常使いもしやすい世界観に仕上げています。</p>
<p>![](/assets/blog/authors/higashiji/20260501/image2_trophy.JPG =600x)</p>
<h2>FY2026 振り返り</h2>
<p><strong>執筆者：S.N</strong></p>
<p>イベントのトップバッターを飾ったのは、FY2026（2025年度）の拠点振り返りコンテンツです！
出来事の報告にとどまらず、メンバー個人の「色」を引き出すために事前アンケートを実施。「今年がんばった仕事」や「将来の野望」など、共に働くメンバーの意外な一面や熱い想いを共有する時間となりました。</p>
<p>もちろん拠点としてのトピックスも盛りだくさんで、オフィスの引っ越しや新たな仲間の採用、数々のイベント開催など、濃い1年を振り返りました。
笑いあり、涙あり、そして愛のある「メンバーいじり」あり。小気味良くOsaka Tech Labの1年を共有するコンテンツになりました。</p>
<p>参加者からも「プレゼンがYouTubeみたい！」「データの見せ方がおもしろい」「入社直後だが理解が深まった」などといった声をいただきました。</p>
<blockquote>
<p>スライドの一部抜粋「今年の印象に残った出来事は？」</p>
</blockquote>
<p>![](/assets/blog/authors/higashiji/20260501/image3_furikaeri1.jpeg =600x)</p>
<blockquote>
<p>スライドの一部抜粋「来年大阪でなにがしたい？」</p>
</blockquote>
<p>![](/assets/blog/authors/higashiji/20260501/image4_furikaeri2.png =600x)</p>
<h2>表彰</h2>
<p><strong>執筆者：ひがし</strong></p>
<p>続いて、表彰イベントに移りました！賞は全部で4つ設けました。</p>
<ol>
<li><strong>HONMA ARIGATO賞</strong>
Osaka Tech Labに多大な貢献をされた方へ感謝を伝える賞</li>
<li><strong>MECCHYA TECH賞</strong>
技術面で印象的な活躍や貢献をされた方へ贈る賞</li>
<li><strong>BARI NINKIMON賞</strong>
部署問わず、多くの方と積極的にコミュニケーションを取った方へ贈る賞</li>
<li><strong>O-KINI AWARD FY2026賞</strong>
&quot;めっちゃブレイクスルーするラボ&quot;・&quot;集GO!発SHIN!Co-LAB&quot;というOsaka Tech Labの共通指針・あいことばを最も体現した年間MVPへ贈る賞</li>
</ol>
<p>各受賞者は、事前に実施したアンケートでの投票数をもとに選定しました。
また、受賞者には景品として、お名前と賞名を記載したアクリルスタンドを贈呈し、そして【O-KINI AWARD FY2026賞】の受賞者にはあわせてトロフィーも贈呈しました！</p>
<p>![](/assets/blog/authors/higashiji/20260501/image5_award.jpg =600x)</p>
<p>さらに、贈呈する側のメンバーにもひと工夫を加え、&quot;その受賞者に投票したメンバーの中から1名&quot;が景品を渡す形式にすることで、「1年間の活躍をメンバー同士で労い合う」という本イベントの目的を達成することができました！</p>
<h2>ST大会</h2>
<p><strong>執筆者：さやま</strong></p>
<p>続いてはST大会を開催しました。
STは「ソニックトーク」の略で、LT（ライトニングトーク）よりもさらに短く、気軽に話してもらうことを目的とした発表形式です！</p>
<p>STには決まった運用がないため、今回は以下のルールで実施しました。</p>
<ul>
<li>発表時間は1人3分まで</li>
<li>スライド枚数は自由</li>
<li>テーマはOsaka Tech Labに関する内容なら何でもOK</li>
</ul>
<p>発表者は応募形式とし、11名の方にご応募いただきました。
最新技術の話や採用の話、個人開発の話、Osaka Tech Labにまつわる話まで、かなり幅広いテーマが集まりました。
3分という短い持ち時間での発表は今回が初めてでしたが、そのぶん一人ひとりの個性がしっかり伝わる、濃い内容になりました。
またイベント終了後のアンケートでも、「今まで知らなかった一面を知ることができてよかった」「面白かった」といった声を多数いただきました。</p>
<p>![](/assets/blog/authors/higashiji/20260501/image6_st.jpg =600x)</p>
<h2>懇親会</h2>
<p><strong>執筆者：M.K</strong></p>
<p>イベントの締めくくりは、Osaka Tech Labらしいカジュアルな懇親会でした。</p>
<p>会場は立食形式とし、「お花見」をコンセプトに飾り付けを実施。ケータリングのオードブルやアルコールを囲みながら、部署や職種を越えて交流できる時間になりました。</p>
<p>![](/assets/blog/authors/higashiji/20260501/image7_hanami.jpg =600x)</p>
<p>乾杯の挨拶は、Osaka Tech Labメンバー全員の中からルーレットでランダムに選出する方式に。結果として、最年長者が当選し、会場が笑いに包まれるスタートとなりました。</p>
<p>表彰パートとも連動し、社長の小寺が持参してくださったワインが、受賞者への特別な一杯として振る舞われました。また、ちょうど小寺のお誕生月だったこともあり、ギター演奏に合わせた「ハッピーバースデー」の合唱と、名物の豚まんをバースデーケーキに見立てたサプライズでお祝いしました。</p>
<p>![](/assets/blog/authors/higashiji/20260501/image8_cake.jpg =600x)</p>
<p>懇親会の中盤では、最近Osaka Tech Labに加入された方や、今後異動予定の方にもマイクをお渡しし、イベントや拠点に対する率直な感想を共有していただきました。新しいメンバーを自然と巻き込み、拠点全体で歓迎するOsaka Tech Labの文化が表れた時間になったと感じています。</p>
<p>最後は、小寺から本日の総括と、来年のOsaka Tech Labに期待することについて一言をいただき、一本締めならぬ「おおきに！」の掛け声でクロージングしました。</p>
<h2>最後に</h2>
<p>【O-KINI FY2026】は、年度の締めくくりとして、Osaka Tech Labのメンバーが互いの頑張りを称え合い、気持ちを1つにする大切な時間となりました。</p>
<p>また、Osaka Tech Labには「自分たちの手で楽しみを共創しよう」という文化があります。今回、企画メンバーとしてその文化を実際に経験することができました。</p>
<p>今後も、Osaka Tech Labの雰囲気や文化を大切にしながら、拠点が大きくなっても、人が増えても、その良さを保ちつつ成長していきたいと思います。</p>
<h2>📢 KINTOテクノロジーズ Osaka Tech Lab 積極採用中！</h2>
<p>最後までお読みいただき、ありがとうございました！KINTOテクノロジーズでは、Osaka Tech Labを共に創り上げ、一緒に楽しんでくれる仲間を絶賛募集しています。</p>
<p>拠点が拡大していくこのワクワクするフェーズで、あなたの力を発揮してみませんか？</p>
<p>「ちょっと面白そうかも」「まずはオフィスの雰囲気を知りたい」という方は、ぜひ一度ざっくばらんにお話ししましょう！</p>
<p>ご応募お待ちしております！</p>
<p>![](/assets/blog/authors/higashiji/20260501/image9_all.jpg =600x)</p>
<p>👇 <strong>詳細はこちらをチェック！</strong></p>
<p><a href="https://www.kinto-technologies.com/recruit/#job-list">https://www.kinto-technologies.com/recruit/#job-list</a></p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/1859151978603163665">https://hrmos.co/pages/kinto-technologies/jobs/1859151978603163665</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/higashiji/20260501/image1_cover.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[2026年1月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-04-21-newcomer-202601/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-04-21-newcomer-202601/</guid>
            <pubDate>Thu, 30 Apr 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[2026年1月に入社した皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、2026年1月入社のI.Kobayashiです！</p>
<p>本記事では、2026年1月入社のみなさまに入社直後の感想をお伺いし、まとめてみました。
KINTOテクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>YY</h1>
<p>![YYさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/yy.webp =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>デジタル戦略部 データグロースグループでプロデューサーをしています。</li>
<li>各サービスの成長に向けて、データドリブンな意思決定を支援する施策を企画・推進しています。</li>
<li>また、社内ツールのプロダクトマネージャー（PdM）も兼任しており、社内業務効率化のためのツール開発や改善にも取り組んでいます。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>ビジネス支援を行うチームに所属しており、デジタルマーケティングに強みを持つプロデューサー、定性調査に強みを持つプロデューサー、データサイエンスに強みを持つエンジニア達などに囲まれて仕事をしています。</li>
<li>それぞれの専門性を活かしながら、チーム一丸となってサービスの成長を支えています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>制作・開発に比重の強い会社だという印象を持っていましたが、実際にはビジネス側との距離も近く、連携が密である点にギャップを感じました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>私が所属するデータ活用チームは、複数のサービスチームと横断的に関わるため、日常的にコミュニケーションが活発です。データで支援する立場から、サービスの理想の姿やデータから見える実像について、普段の会話の中で自然に議論が交わされています。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>スカイツリーを眺めながらランチできる休憩スペースがお気に入りです。</li>
<li>眺望の良さはもちろん、リフレッシュしやすい雰囲気があり、午後の仕事への切り替えにも役立っています。</li>
</ul>
</li>
<li><strong>satoshiさん　⇒　YYさんへの質問</strong><blockquote>
<p>普段の業務でAIってどうやって使われていますか？</p>
</blockquote>
<ul>
<li>データ分析をAIに任せて、プロジェクトの進む方向性や現在地を一緒に考えることを行っています。
壁打ち相手としても、分析担当としても利用しており、自分の役割を忘れてしまいそうになるぐらいに多用しています。
会議に向けたアジェンダ作成、それに伴うデータ分析、示唆出しまで、一言のプロンプトで完了してしまうのは革命的だと感じています。</li>
</ul>
</li>
</ul>
<h1>Mizoguchi Hiroki</h1>
<p>![Mizoguchi_Hirokiさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/mizoguchi.webp =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>KINTOを開発するグループで新車サブスクのWebフロントエンド開発チームに所属しています</li>
<li>フロントエンドチームにいますがバックエンドもインフラも全般好きです</li>
<li>自転車に乗って走り回るのが趣味です！走り回りすぎて最近骨折しましたが、治ったら懲りずに走り回ろうと思っています</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>東京6名・大阪3名のフロントエンドエンジニア9名で構成されています</li>
<li>バックエンド・フロントエンド・PdM・QAなど職種によってチームが分かれていて、開発する機能ごとに各チームから数名集って開発を進めるような体制になっています</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>行動力がある大人が集まっているという印象でした。経験値からくる冷静さと、周りを巻き込んでやりたいこと・やるべきことを進めるアクティブさを持った人が多い印象を受けています</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>チームメンバーそれぞれで異なったタスクを進めることが多いので、主にモクモクと作業しています。協力が必要なことや相談したいことをslackや対面で声を掛けると皆さん積極的に会話に参加してくれるのでコミュニケーションは円滑です</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>とにかく開放感があって、外の景色を見渡せるところが気に入っています</li>
<li>オフィス全体が車をテーマにデザインされていて、遊び心があるところが気に入っています。イベントも頻繁に開催しているので、ぜひ覗きにきてください</li>
</ul>
</li>
<li><strong>YYさん ⇒　Mizoguchi Hirokiさんへの質問</strong><blockquote>
<p>フロントエンド開発において、AIと人とどのように作業を分担されていますか？</p>
</blockquote>
<ul>
<li>私は大枠の設計は完全に人間、業務ロジック設計やコードのレイヤー分割などはAIの提案をもとに対話して決定、具体的な実装は殆どAIに任せるなど具象度に応じてAIへの依存が高まっていくような分担になっています。
地味にUIの見た目チェックや操作時の挙動確認は具象な作業なものの、人間がポチポチ画面操作して担当しています。（なんとかAIを使って自動化できないか模索中）</li>
</ul>
</li>
</ul>
<h1>Kosuke Kihara</h1>
<p>![Kosuke_Kiharaさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/Kosuke.webp =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>新サービス開発部 FACTORY EC開発G所属です。</li>
<li>趣味は自作キーボード・ヴィオラ・園芸、ヴィオラは市民オーケストラで演奏していました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>新サービス開発部 FACTORY EC開発Gで、TOYOTA/LEXUS UPGRADE FACTORYのEC基盤を開発・運用しています。</li>
<li>フロントエンド、バックエンド、PdM、SRE、QA、ディレクター、マネージャーなど合わせて15名ほどの体制です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>入社前に受けていた説明と大きく印象が異なることもなく、戸惑うことはなかったです。</li>
<li>あえて言えば、自分が勤務しているOsaka Tech Labでは特に遊び心を大事にしているところが良い意味でギャップに感じました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>チームではバーチャルオフィスのGatherを利用しており、リモートでも気軽に相談できる空気感があります。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>Osaka Tech Labのパークです。</li>
<li>ヨギボーを持っていってリラックスしながら仕事すると、頭が柔らかくなっていろんな発想ができる（気がする）。</li>
</ul>
</li>
<li><strong>Mizoguchi Hirokiさん ⇒　Kosuke Kiharaさんへの質問</strong><blockquote>
<p>最近AIを使ってうまくいった仕事や作業あれば教えてください！</p>
</blockquote>
<ul>
<li>JiraチケットやPRのURLから紐づくConfluence・Jira・Slack・コードを自動で追わせてまとめるスキルを作成しました。
案件の周辺コンテキストの理解にかかる時間を大幅に削減できています。</li>
</ul>
</li>
</ul>
<h1>やまそと</h1>
<p>![やまそとさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/yamasoto.webp =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>プラットフォーム開発部Cloud Infrastructure Gのやまそとです。</li>
<li>トヨタグループへのクラウド領域の技術支援を担当しています。</li>
<li>前職まではSES/SIerでバックエンドの開発エンジニアとして働いていましたが、気づいたらインフラエンジニアになっていました。</li>
<li>プライベートではビールとバイクにハマってます！</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>大阪4名東京2名の体制です</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>コミュニケーションが活発でアクティブな人が多いなーという印象でした</li>
<li>トップダウンではなくフラットに意見を言えますし、自律的に行動する人が多いのは良いギャップでした</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>普段はみんなそれぞれの案件に携わっていますが、社内のチームミーティングはワイワイやってます</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>OsakaTechLab勤務ですが、全体的にキレイでテンションが上がります</li>
<li>駅と繋がっていて雨に濡れずに済むので助かります</li>
</ul>
</li>
<li><strong>Kosuke Kiharaさん ⇒　やまそとさんへの質問</strong><blockquote>
<p>バイクが趣味とお聞きしましたが、最近バイクで行ったおすすめの場所などあれば教えてください！</p>
</blockquote>
<ul>
<li>去年の秋頃に兵庫の須磨に行きました！海沿いを走るのは気持ちよかったです。
あったかくなってきたので淡路島か琵琶湖にいきたいですね。</li>
</ul>
</li>
</ul>
<h1>やまと</h1>
<p>![やまとさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/yamato.jpeg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>my route開発部でAWSインフラアーキテクトとして働いています。</li>
<li>国内旅行が趣味で、アーケードゲームの全国行脚機能で43都道府県、スターバックスのアプリでは14都県巡っています。(2026年4月現在)</li>
<li>インドア趣味のほうでは某オンラインゲームの/playtimeが執筆時点で10,047時間でした。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>所属しているバックエンド開発チームでインフラを主に担当するのは私一人で、サーバサイドアプリケーションを開発する他のメンバーと密にコミュニケーションを取って仕事を進めています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>入社前の想像よりも、チームメンバーの一人ひとりが開発しているアプリケーションのことをもっとこうしたい！と考えていると感じました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>メンバーが2人以上出社すれば一緒にランチに行って雑談をしているので、仕事の依頼や質問もしやすく過ごしやすい雰囲気だと感じています。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>神保町オフィスは集中しやすくもあり孤独を感じるほど少なくもない、ほど良い出社率です。レストエリアがお洒落でアップルティーを取りに行くのがリフレッシュになります。</li>
</ul>
</li>
<li><strong>やまそとさん ⇒　やまとさんへの質問</strong><blockquote>
<p>おすすめの旅行先を教えてください！</p>
</blockquote>
<ul>
<li>美味しい酒・魚を求めるなら四国地方 or 日本海側、綺麗な景色を求めるなら海沿い、が良かったです！
その土地の名産であれば、味はもとよりお値段も都市圏より安くてたくさん食べられます。
ただし、食を堪能する旅には登山やハイキングも取り入れた方が、よいです（戒め）。</li>
</ul>
</li>
</ul>
<h1>I.Kobayashi</h1>
<p>![I.Kobayashiさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/Kobayashi.webp =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>クラウドセキュリティG所属のI.Kobayashiです。</li>
<li>KTCで利用しているクラウドのセキュリティ改善や改善活動の効率よくするためのツール開発などを行っています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>クラウドセキュリティGは現在、大阪2名、東京3名が在籍しています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>入社前にチーム状況・求められていることなど共有いただいていたのでギャップ全然ありませんでした。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>皆さん優しいので仕事がしやすいです。</li>
<li>利用したことないサービスや技術であっても一緒に調査してくれます！</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>１階コンビニ、２階レストランがあるので雨で外出たくない時によく利用しています！</li>
</ul>
</li>
<li><strong>やまとさん　⇒　I.Kobayashiさんへの質問</strong><blockquote>
<p>ご趣味は！（アウトドアでもインドアでも構いませんので！）</p>
</blockquote>
<ul>
<li>音楽・ポッドキャスト聴きながら目的もなく歩くのが好きです！</li>
</ul>
</li>
</ul>
<h1>HOKAMA</h1>
<p>![HOKAMAさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/HOKAMA.webp =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>開発支援部企画管理Gの外間です。</li>
<li>主に会社の予算管理や業務フローの調整などを担っている部署となります。</li>
<li>休みの日は小学3年生の息子の町クラブ（サッカー）でコーチをやっています。</li>
<li>趣味はフットサルとゴルフで夏になると日焼け止めを塗っても真っ黒になります。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>企画管理Gは室町2名、大阪1名、名古屋1名です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>ある程度のミッション内容を入社前に伺っていたので、あまりギャップは感じませんでした。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>全員中途採用なので落ち着いた雰囲気です。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>室町7階の休憩室が気に入っています。マッサージ機もあるので体を労わりながら仕事が出来るので！</li>
</ul>
</li>
<li><strong>I.Kobayashiさん　⇒　HOKAMAさんへの質問</strong><blockquote>
<p>室町周辺でおすすめのお店教えてください！(行ってみたいお店でも大丈夫です！)</p>
</blockquote>
<ul>
<li>室町オフィスから少しあるきますが、「新日本橋中華 龍龍龍龍 TETSU」の炒飯が美味しいです。
週一回は通ってます。</li>
</ul>
</li>
</ul>
<h1>きーた</h1>
<p>![きーたさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/kiita.webp =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>2026年1月入社のきーたです。</li>
<li>セキュリティ・プライバシー部に所属し、福岡オフィス（Fukuoka Tech Lab）で勤務しています。</li>
<li>アビスパ福岡が好きな方、お待ちしてます！</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>所属チームは3名体制です。</li>
<li>TFSグループが定める基準をベースとしたセキュリティのアセスメントを主に担当しています。</li>
<li>少人数なのでコミュニケーションも取りやすく、日々連携しながら進めています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>会社のカルチャーや雰囲気など、良い意味で入社前に抱いた印象とのギャップはありませんでした。</li>
<li>入社後のフォロー面談でも「ギャップはありましたか？」と聞かれますが、いつも「何もないですね」と答えていますｗ</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>「個々がプロフェッショナルでありつつ、しっかりチームで連携して動ける」といった印象です。</li>
<li>困ったときはすぐに相談に乗ってもらえるので助かっています。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>立地がいいところ。あとは地下街と繋がっていたら最高でした。</li>
</ul>
</li>
<li><strong>HOKAMAさん　⇒　きーたさんへの質問</strong><blockquote>
<p>これまで仕事で一番やらかしたことはどんなことですか？（言える範囲でお願いします）</p>
</blockquote>
<ul>
<li>言えることだと…、某大手メーカーさんの重要拠点のインフラを数時間止めてしまったこと、でしょうか。
あの経験があったおかげて、作業は人一倍慎重になりました！！</li>
</ul>
</li>
</ul>
<h1>sasanoshouta</h1>
<p>![sasanoshoutaさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/sasanoshouta.webp =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>AIファーストグループでAIエンジニアをしています、sasanoshoutaです。</li>
<li>社内外に対して生成AI活用の推進の為に折衝からPoC、実装までを幅広く行う業務に取り組んでいます。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>AIファーストグループは東京9名、名古屋3名、大阪1名の体制を敷いています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>入社間もなく、チーム内にはいい意味で上下の関係がなく、相互に取り組んでいることについて共有しながら技術的な共有や議論について交わすことができる印象を持ちました。</li>
<li>また、事前に自分への期待値や会社・チームの状況を聞いた上で役割を想像しながら入社しているので、ギャップはありませんでした。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>チーム全員が同じ取り組みをしている訳ではないですが、共通言語として「誰の為のものか」を全員が常に意識しながら目の前の事に集中して取り組んでいる雰囲気が常にあります。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>日本橋の室町という歴史あるエリアにあるオフィスで、オフィスの内装もモダンで働きやすいですが、周辺のロケーションも気に入っています。</li>
</ul>
</li>
<li><strong>きーたさん　⇒　sasanoshoutaさんへの質問</strong><blockquote>
<p>今年のサッカーW杯で日本以外に注目している国はありますか？</p>
</blockquote>
<ul>
<li>たくさんあります。
優勝候補スペイン・フランスや、逸材を輩出し続けているアフリカ勢の国々、初参加国の中でもノルウェー・ウズベキスタン・エジプトがどこまでいくのか、数大会振り出場のチェコあたりに注目したいと思ってます！</li>
</ul>
</li>
</ul>
<h1>satoshi</h1>
<p>![satoshiさんのプロフィール画像](/assets/blog/authors/i.kobayashi/2026-04-30-newcomer-202601/satoshi.webp =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>AIファーストグループの天野です！生成AIの活用推進を社内外に向けて活動しています。</li>
<li>非ソフトウェアエンジニアリング領域を中心に活動しています。</li>
<li>動画生成や記事執筆、顧客理解に対してのAI活用検証を行なっています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>AIファーストグループは東京9名、名古屋3名、大阪1名の体制を敷いています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>皆さん主体的に新しい事に取り組む方が多いなと感じました！</li>
<li>私もアイデアを出して試してみるのが好きなので、カルチャーに馴染みやすかったです。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>皆さん和気あいあいとした感じがありながらも、しっかりと目的感を持っている印象でした。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>オフィス周辺が綺麗なので帰宅時に優雅な感じに帰れる所です！</li>
</ul>
</li>
<li><strong>sasanoshoutaさん　⇒　satoshiさんへの質問</strong><blockquote>
<p>入社の決め手を教えてください！</p>
</blockquote>
<ul>
<li>AIの非ソフトウェア領域での活用や推進ができるポジションがあり、自分のやりたい事と重なった為です。
元々はソフトウェア領域でAIを活用していましたが、開発経験が浅く方向転換をしたかったので、私と同じような考えの方がいればAIファーストGオススメです！</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTOテクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[『ユーザーに寄りそわNight!』── エンジニアがユーザーを知るための社内勉強会 Vol.02レポート]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-04-27-yorisowa-night-vol02/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-04-27-yorisowa-night-vol02/</guid>
            <pubDate>Mon, 27 Apr 2026 01:00:00 GMT</pubDate>
            <description><![CDATA[社内勉強会「ユーザーに寄りそわNight! Vol.02」のレポート。開発者自身がユーザーと同じ環境でプロダクトを使ってみる取り組みから見えた、日常の開発に持ち帰れる気づきをまとめました。]]></description>
            <content:encoded><![CDATA[<p>こんにちは。KINTOテクノロジーズ（KTC）でKINTOの中古車ECサイトのディレクターをしている<a href="https://x.com/momentofudayo">かーびー</a>です。KINTO Technologiesでは<a href="https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025/">「ユーザーファースト」</a>を会社の重点方針のひとつに掲げ、全社でさまざまな取り組みが進んでいます。私も自分のチームで、ユーザーインタビューの録画をみんなで見る<a href="https://blog.kinto-technologies.com/posts/2026-01-14-user-interview-waiwai-session/">「ユーザーインタビューわいわい会」</a>を試すなど、お客様の一次情報に触れる場づくりに取り組んできました。</p>
<p>こうした取り組みをきっかけに、現在はユーザーファーストを社内に広めるための活動にも運営メンバーのひとりとして関わっています。そのひとつが、今回ご紹介する社内勉強会「ユーザーに寄りそわNight! Vol.02」です。</p>
<h2>自分たちのサービスを、ユーザーが使っているところを見たことはありますか？</h2>
<p>勉強会の中で参加者にこの質問をしたところ、約7割が「ない」と回答しました。</p>
<p><img src="/assets/blog/authors/y.nakamura/user02/survey-no.jpg" alt="アンケート結果：約7割が「ない」と回答"></p>
<p>関心がないのではなく、日常の開発フローの中にその機会がない。要件をヒアリングして、仕様に落とし込んで、品質の高いものを作って届ける。エンドユーザーがどんなふうにサービスを使っているかに触れる機会は、意外と少ないのが現実です。</p>
<p>しかもKINTOテクノロジーズの場合、関わるサービスはトヨタ自動車、株式会社KINTO、開発を担う私たちなど、複数の組織で成り立っています。本来なら1社の中で完結する「作って、使ってもらって、フィードバックをもとに改良する」という流れを、組織をまたいで回していく。ここが私たちの組織ならではの難しさだなと感じています。</p>
<p>関わる人が増えるほど、それぞれの立場や見えている景色は違ってきます。だからこそ、作っている一人ひとりがユーザーの姿を知っていることが大事になる。「あのお客様、こう言っていたよね」という共通の記憶がチームにあると、議論もかみ合いやすくなります。</p>
<p>言われたものを作るだけじゃなく、自分たちから価値を届けていく。「ユーザーに寄りそわNight!」は、ユーザーを知るために踏み出した社内チームの取り組みを紹介する勉強会です。</p>
<h2>方法論の講義ではなく、隣のチームの体験を共有する場</h2>
<p>この勉強会で大事にしているのは、 <strong>「私にもできそう！」</strong> と思えることです。</p>
<p>ユーザーリサーチの手法を網羅的に学ぶ場ではなく、他のチームの取り組みを聞いて「これなら自分のチームでもできそう」と感じてもらう。そんな場でありたいと考えています。</p>
<p>toCでもtoBでも、自分たちの仕事の先には必ず使う人がいます。その誰かに寄りそっていくことが、ユーザーファーストの根っこにある考え方だと捉えています。</p>
<p>こうした考えから、勉強会では実際にユーザーと向き合う取り組みをしたチームに登壇してもらい、何をやって、何に気づいたかを共有してもらう形式にしています。専門的な方法論の紹介ではなく、隣のチームの体験を聞くこと。そこから自分のチームでも試してみたいと思える、小さなきっかけが生まれる場になればと思っています。</p>
<h2>ユーザーに寄りそわNight! Vol.02：ユーザーと同じ環境で、プロダクトを使ってみる</h2>
<p>2026年3月に開催された第2回の勉強会では、実際にユーザーが使っているのと同じような環境で、自分たちもプロダクトをテストしてみるーーそんな取り組みをしているチームに登壇してもらいました。ユーザーファーストの取り組みとして、社内の各所で生まれている実践をキャッチして勉強会に繋げていく中で、この取り組みのことを知り、声をかけたのが始まりでした。</p>
<p>トヨタグループには「現地現物」——実際の現場に足を運び、自分の目で見て判断する——という考え方があります。登壇してくれたチームはこの考え方をユーザー理解にも活かしたいと、開発メンバー自身がユーザーと同じ状況に身を置いてプロダクトを使ってみる、という取り組みに挑戦していました。</p>
<h3>机の前の3秒、現場の3秒</h3>
<p>登壇でとくに印象に残ったのは、開発環境ではわからなかったことが、ユーザーと同じ状況で使ってみると次々に見えてきたという話でした。</p>
<p>たとえばアプリの表示にかかる時間。開発環境で3秒かかっても「ちょっと遅いな」と感じる程度だけれど、ユーザーが実際に使う状況で体験する3秒はまるで別物。急いでいるとき、周りに人がいるとき、落ち着いて待てないとき。クーラーの効いたオフィスで感じる3秒と、現場で感じる3秒は、同じ時間とは思えないくらい違って感じられた、と。</p>
<p>「仕様通りに動く」はずのものが、ユーザーと同じ状況に置かれるとまったく違う顔を見せる。データでは見えない課題が、身体で感じられる瞬間でした。</p>
<h3>「忖度を捨てる」という第一歩</h3>
<p>では、現場で気づいたことをどう日常の開発に持ち帰っていくか。パネルディスカッションで印象に残ったのは、「忖度を捨てる」という言葉でした。</p>
<blockquote>
<p>「アプリを使っていて『ここ遅いな』と思っても、『APIをたくさん呼んでるからしょうがないか』と開発者としての忖度をしてしまう。その忖度をあえて捨てて、純粋にユーザーとしてアプリを使ってみることが、まずできる第一歩」</p>
</blockquote>
<p>開発者として「これはしょうがないか」と自分で飲み込んでしまう場面は、きっと多くの人に心当たりがあると思います。その忖度を一度横に置いて、純粋にユーザーとしてアプリを触ってみる。大がかりな準備をしなくても、今日から始められる小さな一歩として、とても印象に残った言葉でした。</p>
<h2>これからも、小さな一歩を重ねていく</h2>
<p>Vol.02の懇親会では、「うちのチームでもこういうことをやってみたい、でもどう始めればいいんだろう？」という声や、登壇者を囲んで「どうやって社内を巻き込んでいったんですか？」と具体的な進め方を聞く姿が、あちこちで見られました。</p>
<p>アンケートのフリーコメント欄には、約半数の方が「これから自分のチームでやってみたいこと」を書き込んでくれました。印象的だったのは、toCのサービスを作っているチームだけでなく、業務システムやプラットフォームを担当する方々からも、具体的な一歩の言葉が並んだことです。</p>
<blockquote>
<p>「業務システムなのでユーザーがKINTO社員であり距離が近い。実際に業務をやらせてもらったり、フィードバックを貯める場を作ったりして、ユーザーファーストを実践する場を作りたい」</p>
</blockquote>
<blockquote>
<p>「忖度せずに改善アイデアを出し、検討する。アイデアを歓迎する空気を作っていきたい」</p>
</blockquote>
<p>自分たちの仕事の先にいる「使う人」は、toCのお客様だけではありません。社内の誰か、パートナー企業の誰か、ときには自分自身かもしれない。それぞれの現場で、それぞれの「寄り添い方」がある。そのことを、登壇してくれたチームの話と、参加者の声から改めて感じた回でした。</p>
<p>Vol.01の開催から半年、社内Slackチャンネルのメンバーは60人から99人に増え、「うちでもこういうことやってるよ！」と声をかけてくれる人も出てきています。これまで各チームの中に閉じていた取り組みが、少しずつ表に出てくるようになりました。</p>
<p>「ユーザーファースト」は2025年の注力テーマとして始まりましたが、ユーザーのことを考えるのはプロダクト開発の基礎の基礎。一年限りのテーマで終わらせず、Vol.03に向けた準備も進行中です。</p>
<p>大がかりな取り組みでなくても、まずは自分のプロダクトをユーザーとして使ってみることから。気づいたことを隣の人に話してみることから。一つひとつのチームで生まれる小さな一歩を、勉強会という場で共有し、また次の一歩へつなげていく。この取り組みの火を絶やさないよう、これからも続けていきます。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/y.nakamura/user02/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[「AI-Native Dev」始めました ─ 全社横断で挑むAIネイティブな文化づくり]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-04-21-introducing-ai-native-dev/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-04-21-introducing-ai-native-dev/</guid>
            <pubDate>Tue, 21 Apr 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[2026年2月に発足した全社横断プロジェクト「AI-Native Dev」の活動内容を紹介します。文化醸成と開発環境整備の2本柱で、AIネイティブな開発文化の定着を目指しています。]]></description>
            <content:encoded><![CDATA[<p>こんにちは、サイバーセキュリティと生成AI活用推進を担当しているたなちゅーです。この記事では、2026年2月に活動を開始したAI-Native Devプロジェクトについて紹介します。</p>
<h2><strong>活動の背景</strong></h2>
<h3><strong>2025年までの取り組み</strong></h3>
<p>KTCでは2024年の<a href="https://blog.kinto-technologies.com/posts/2024-01-26-GenerativeAIDevelopProject/">生成AI活用プロジェクト</a>を皮切りに、2025年は「AIファースト」「リリースファースト」を掲げ、AI活用は着実に進みました。</p>
<ul>
<li><strong>AIファースト</strong>：すべてのプロダクトへのAI統合、AIプロダクトの開発推進、グループ内でのAI活用ドライバー</li>
<li><strong>リリースファースト</strong>：「いかに速く届けるか」を文化として組織に定着させる</li>
</ul>
<h3><strong>「AIを使う」から「AIネイティブな開発・業務プロセス」へ</strong></h3>
<p>こうした取り組みを経て、昨年末に副社長の景山がテックブログ「<a href="https://blog.kinto-technologies.com/posts/2025-12-25-LookBack2025/">2025年の振り返りと2026年の展望：Agenticな未来へ</a>」で、2026年のキーワードとして「Agentファースト」と「AIエンジニアリングファースト（AI-Native Dev）」を掲げました。</p>
<ul>
<li><strong>Agentファースト</strong>：「対話するAI」から「行動するAI」へ。AIが自律的にタスクを遂行する世界を全社で実現する</li>
<li><strong>AIエンジニアリングファースト（AI-Native Dev）</strong>：AIネイティブな視点で開発・業務プロセスを再構築し、職種の壁を超える</li>
</ul>
<p>目指すのは、<strong>一人ひとりがAIネイティブな視点で開発や業務のプロセスを変えていくこと</strong>。その推進役として、2026年2月にAI-Native Devプロジェクトが発足。プロダクト開発からクラウドインフラ、コーポレート部門まで、10名超が合流した横断チームで活動を開始しています。</p>
<h2><strong>活動の2つの柱</strong></h2>
<p>個人の知見を組織全体で活かす仕組みと、それを支える開発環境の整備。この2つが揃って初めて組織として加速できると考え、活動を <strong>文化醸成</strong> と <strong>開発環境整備</strong> の2本立てで構成しています。</p>
<ul>
<li><strong>文化醸成</strong>：ナレッジの体系化・共有、AIツール利用状況の可視化、社内事例の発信など</li>
<li><strong>開発環境整備</strong>：AI時代のコードレビュー最適化、AI Agent / MCP基盤の整備、エンバイロメント（環境）エンジニアリングなど</li>
</ul>
<p><img src="/assets/blog/authors/tanachu/2026-04-21-introducing-ai-native-dev/01_ai-native-dev.png" alt="AI-Native Devの2本柱：文化醸成と開発環境整備"></p>
<h2><strong>Phase 1：まず土台をつくる</strong></h2>
<p>Phase 1として取り組んだのは、活動の土台となる2つの基盤です。</p>
<h3><strong>AI Native Hub</strong></h3>
<p>1つ目は、社内Wiki上に開設した生成AI活用の社内ポータル「AI Native Hub」です。</p>
<p><img src="/assets/blog/authors/tanachu/2026-04-21-introducing-ai-native-dev/02_ai-native-hub.png" alt="AI Native Hub"></p>
<p>職種別のAIツール活用ガイド、MCP・Skillsの使い方、社内事例などの情報を集約しています。また、コンテンツの運用にはGitHubを採用しています。Markdownで記述し、PRでレビューを回し、mainブランチにマージされると社内Wikiへ自動同期される仕組みです。運営チームだけが管理するのではなく誰でもナレッジを共有できる、社内全体で育てていくナレッジ集約場所を目指しています。</p>
<h3><strong>Claude Code Dashboard</strong></h3>
<p>2つ目は、Claude Codeの利用状況を可視化するダッシュボードです。Claude CodeのOpenTelemetryを活用しています。</p>
<p><img src="/assets/blog/authors/tanachu/2026-04-21-introducing-ai-native-dev/03_claude-code-dashboard.png" alt="Claude Code Dashboard"></p>
<p>ダッシュボードでは、MCPやSkillsの使用回数、利用者のトークン使用量、トークン使用量上位者のトレンドが見えます。自分の活用状況の振り返りやトークン使用量上位者との交流など、AIツール利用促進のきっかけになればと考えています。</p>
<h2><strong>Phase 2：実践と拡張</strong></h2>
<p>Phase 1は立ち上げと基盤整備。4月からのPhase 2は、その基盤の上で実践を加速するフェーズです。</p>
<h3><strong>文化醸成</strong></h3>
<p>文化醸成が目指すのは、AIネイティブな開発・業務のスタイルが組織に根づくことです。</p>
<ul>
<li><strong>もくもく会・ハンズオン会</strong>：気軽に情報交換できるオンラインの場を定期開催し、実践知を共有する</li>
<li><strong>AIネイティブな個人・部署へのインタビューとナレッジの横展開</strong>：先行事例を掘り起こし、他チームへ広げる</li>
<li><strong>AIネイティブな活動の可視化</strong>：AIネイティブ度合いを可視化し、活動の推進に活かす</li>
</ul>
<p>まず動き出したのが「もくもく会」です。週2回オンラインで開催して、ちょっとした困りごとやTipsなどを話しています。また、テーマを決めたハンズオン会も実施しており、初回の「Claude Codeを使い倒す設定を一緒にしよう会」には合計80名以上が参加しました。学びは集約して、後から参照できる形にしています。</p>
<h3><strong>開発環境整備</strong></h3>
<p>開発環境整備が目指すのは、AIエージェントを前提とした開発基盤を整えることです。</p>
<ul>
<li><strong>AI Agent / MCP基盤の整備</strong>：AI AgentやMCPの共有基盤の整備を進め、誰でも見つけて使える状態を目指します。</li>
<li><strong>AI時代に合わせたコードレビューの最適化</strong>：AIが生成したコードに対するレビュー観点や静的解析との連携など、AI前提のレビューフローを検討しています。</li>
<li><strong>エンバイロメント（環境）エンジニアリング</strong>：AIエージェントが安全に活動できる範囲の境界線設計やガードレールなどの整備に取り組んでいきます。</li>
</ul>
<p>既に社内ではエージェント開発・共有基盤「KTC Agent Store」を運用しており、現在は実行基盤をBedrock AgentCoreへの移行を進めています。AIエージェントとしてはAIインタビューという深堀りインタビューエージェントなどの開発が進行中です。</p>
<p><img src="/assets/blog/authors/tanachu/2026-04-21-introducing-ai-native-dev/04_ai_interview.png" alt="AI Interview"></p>
<h2><strong>ここまでの活動で感じたこと</strong></h2>
<p>一番の発見は、AIネイティブな働き方に既に踏み出しているメンバーの多さです。初回ハンズオン会には80名以上が参加し、チャットではおすすめ設定や活用Tipsが飛び交いました。この熱量をつなげれば、もっと大きな力になる。その点と点をつなぐことがAI-Native Devの役割だと改めて感じています。</p>
<p>また、AIネイティブな開発・業務スタイルが根づけば、日々の業務から生まれた余力が新たな価値創出へ向かう流れをつくれるはずです。「攻めのAI活用」と「守りの安全基盤」の両面をつなぎながら、その流れを組織全体で加速させていきます。</p>
<h2><strong>おわりに</strong></h2>
<p>AI-Native Devは始まったばかりです。土台を作るフェーズから、土台の上で走るフェーズへ。活動の進捗やナレッジは引き続きテックブログで発信していきます。</p>
<p>最後まで読んでいただきありがとうございました！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/tanachu/2026-04-21-introducing-ai-native-dev/coverimage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Android で秘匿情報を守る — Keystore, Cipher, DataStore による暗号化と永続化の実装例]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-04-17-keystore-cipher-datastore-encryption/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-04-17-keystore-cipher-datastore-encryption/</guid>
            <pubDate>Fri, 17 Apr 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[AndroidのKeystore、Cipher、DataStoreを使用して秘匿情報の暗号化と永続化を実装した際の実装詳細と注意点をまとめました]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>KINTOテクノロジーズの大沼です。
モビリティサービス「my route」アプリの開発に従事しています。</p>
<p>本記事では、AndroidのKeystore、Cipher、DataStoreを使用して秘匿情報の暗号化と永続化を実装した際の実装詳細とハマった点・注意点をまとめました。</p>
<p><a href="https://blog.kinto-technologies.com/posts/2025-06-16-encrypted-shared-preferences-migration/">こちら大杉さんの記事</a> では、Tink を使用したケースとパフォーマンス検証を紹介しているのでぜひご一読ください。</p>
<h2>💬 実装の前にディスカッション</h2>
<h3>🔍 本当に暗号化が必要なのか</h3>
<p><a href="https://www.youtube.com/watch?v=6ISJv6G5dBg">DroidKaigi 2025のyanzamさんのお話</a> でも触れられてましたが、Keystore がクラッシュするのは黙認しつつ最低限の機会頻度に抑えたいので、既存で暗号化しているデータが本当に暗号化する必要があるのかの議論をチームメンバーと交わしました。
案の定、不要に暗号化しているものもあり、議論することで最適なものに絞ることができました。</p>
<h3>🏗️ アーキテクチャ</h3>
<p>セキュリティに関するリファクタリングのコードレビューは、心理負荷が高いと考えています。
私のチームはこういう時、大枠の実装方針を事前に共有し合うことで、コードレビュー時の認識違いや負担が減らせます。</p>
<p>今回、データの暗号化とインターフェースを以下のようなスライドで共有し、大きな齟齬なくレビューを進めることができました。</p>
<p><img src="/assets/blog/authors/numami/numa_memo%20-%20%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3.jpg" alt="numa_memo - システムアーキテクチャ.jpg">
<img src="/assets/blog/authors/numami/numa_memo%20-%20%E3%82%A8%E3%83%A9%E3%83%BC%E3%83%8F%E3%83%B3%E3%83%89%E3%83%AA%E3%83%B3%E3%82%B0.jpg" alt="numa_memo - エラーハンドリング.jpg"></p>
<h2>🛠️ 実装の流れ</h2>
<p>ここからは、実際の実装手順を以下の流れで解説します。</p>
<ol>
<li><strong>依存関係の追加</strong> — DataStoreライブラリの導入</li>
<li><strong>Keystoreを使った暗号化キーの生成</strong> — AES/GCMの鍵をAndroid Keystoreで安全に管理</li>
<li><strong>Cipherを使った暗号化・復号化</strong> — 初期化ベクトル(IV)の扱いを含む暗号処理の実装</li>
<li><strong>DataStoreへの保存</strong> — 暗号化したデータをPreferences DataStoreで永続化し、Flowで読み出す</li>
</ol>
<h3>📚 依存関係の追加</h3>
<p>ライブラリにDataStoreを追加します。</p>
<pre><code class="language-gradle:build.gradle.kts">dependencies {
    // DataStore
    implementation(&quot;androidx.datastore:datastore-preferences:1.1.7&quot;)
}
</code></pre>
<h3>🔑 Keystoreを使った暗号化キーの生成</h3>
<pre><code class="language-kotlin">import android.security.keystore.KeyGenParameterSpec
import android.security.keystore.KeyProperties
import java.security.KeyStore
import javax.crypto.KeyGenerator

    ...
    
    fun getOrCreateSecretKey(): SecretKey? {
        try {
            // KeyStoreのインスタンス生成
            val keyStore =
                KeyStore.getInstance(ANDROID_KEY_STORE_PROVIDER).apply {
                    load(null) // KeyStoreを初期化するための必須の呼び出し
                }
            
            // KeyStoreにプロダクトの鍵が存在するか確認し、あれば取得し返す
            if (keyStore.containsAlias(PROJECT_KEY_STORE_ALIAS)) {
                val entry = keyStore.getEntry(PROJECT_KEY_STORE_ALIAS, null)
                if (entry is KeyStore.SecretKeyEntry) {
                    return entry.secretKey
                }
            }
            // KeyStoreにプロダクトの鍵が存在しなければ生成して保存し返す
            val params =
                KeyGenParameterSpec.Builder(
                    PROJECT_KEY_STORE_ALIAS,
                    KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT,
                )
                    .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                    .setKeySize(256)
                    .build()
            val keyGenerator =
                KeyGenerator.getInstance(
                    KeyProperties.KEY_ALGORITHM_AES,
                    ANDROID_KEY_STORE_PROVIDER,
                )
            keyGenerator.init(params)
            return keyGenerator.generateKey()
        } catch (e: Exception) {
            Firebase.crashlytics.recordException(e)
            return null
        }
}
</code></pre>
<h3>🔐 Cipherを使った暗号化・復号化</h3>
<pre><code class="language-kotlin">
import com.google.firebase.crashlytics.ktx.crashlytics
import com.google.firebase.ktx.Firebase
import java.util.Base64
import javax.crypto.Cipher
import javax.crypto.spec.GCMParameterSpec

interface CryptographyManager {
    fun encrypt(plaintext: String): String

    fun decrypt(encryptedString: String): String
}

private const val TRANSFORMATION = &quot;AES/GCM/NoPadding&quot;
private const val IV_SIZE_BYTES = 12
private const val TAG_SIZE_BITS = 128

class CryptographyManagerImpl : CryptographyManager {
    override fun encrypt(plaintext: String): String {
        return try {
            val cipher = Cipher.getInstance(TRANSFORMATION)
            cipher.init(Cipher.ENCRYPT_MODE, getOrCreateSecretKey())
            val ciphertext = cipher.doFinal(plaintext.toByteArray(Charsets.UTF_8))
            val ivAndCiphertext = cipher.iv + ciphertext // IVと暗号文をバイト配列として結合
            Base64.getEncoder().encodeToString(ivAndCiphertext)
        } catch (e: Exception) {
            try {
                CryptographyException.parse(e)
            } catch (cryptoException: CryptographyException) {
                Firebase.crashlytics.recordException(cryptoException)
                &quot;&quot;
            }
        }
    }

    override fun decrypt(encryptedString: String): String {
        return try {
            val cipher = Cipher.getInstance(TRANSFORMATION)
            val ivAndCiphertext = Base64.getDecoder().decode(encryptedString)
            // 復号化時に保存したIVを使う
            val spec =
                GCMParameterSpec(TAG_SIZE_BITS, ivAndCiphertext, 0, IV_SIZE_BYTES)
            cipher.init(Cipher.DECRYPT_MODE, getOrCreateSecretKey(), spec)
            val plaintext =
                cipher.doFinal(
                    ivAndCiphertext,
                    IV_SIZE_BYTES,
                    ivAndCiphertext.size - IV_SIZE_BYTES,
                )
            String(plaintext, Charsets.UTF_8)
        } catch (e: Exception) {
            try {
                CryptographyException.parse(e)
            } catch (cryptoException: CryptographyException) {
                Firebase.crashlytics.recordException(cryptoException)
                &quot;&quot;
            }
        }
    }
}
</code></pre>
<h3>💾 DataStoreへの保存</h3>
<pre><code class="language-kotlin">import android.content.Context
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

data class SecureDataPreferences(
    val textData: String,
)

object PreferencesKeys {
    private val TEXT_KEY = stringPreferencesKey(&quot;encrypted_text&quot;)
}

private val Context.dataStore: DataStore&lt;Preferences&gt; by preferencesDataStore(name = &quot;encrypted_prefs&quot;)

class SecureDataRepository(
    private val cryptographyManager: CryptographyManager
) {

    suspend fun saveTextData(data: String) {
        val encryptedData = cryptographyManager.encrypt(data)
        dataStore.edit { preferences -&gt;
            preferences[TEXT_KEY] = encryptedData
        }
    }
    
    private val secureDataFlow: Flow&lt;SecureDataPreferences&gt; =
        secureDataStore.data
            .catch { exception -&gt;
                if (exception is IOException) {
                    emit(emptyPreferences())
                } else {
                    throw exception
                }
            }
            .map {
                it.mapSecureDataPreferences()
            }

    private fun Preferences.mapSecureDataPreferences(): SecureDataPreferences {
        return SecureDataPreferences(
            textData = this[PreferencesKeys.TEXT_KEY]?.let { cryptographyManager.decrypt(it) } ?: &quot;&quot;,
            // ... Other data
        )
    }

    suspend fun getTextData(): String {
        return try {
            withTimeout(3000L) {
                secureDataFlow.map { it.textData }.first { it.isNotBlank() }
            }
        } catch (_: TimeoutCancellationException) {
            &quot;&quot;
        } catch (_: NoSuchElementException) {
            &quot;&quot;
        }
    }
}
</code></pre>
<h2>⚠️ ハマった点・注意点</h2>
<h3>1. 初期化ベクトル(IV)の保存</h3>
<p>暗号化時に生成されるIV（Initialization Vector）は、復号化時に必須です。
IVは暗号文と一緒に保存する必要があります。IVは秘密情報ではないため、平文で保存しても問題ありません。</p>
<p><strong>ハマったポイント:</strong> 最初の実装でIVを保存し忘れ、復号化時に<code>javax.crypto.AEADBadTagException</code>が発生しました。</p>
<h3>2. KeyStoreのキーのライフサイクル</h3>
<p>Android Keystoreに保存されたキーは、アプリをアンインストールすると削除されます。
また、デバイスのロック画面が解除されるまでキーにアクセスできない設定も可能です（<code>setUserAuthenticationRequired(true)</code>）。</p>
<p><strong>注意点:</strong> keyが存在しない場合の処理を適切に実装する必要があります。</p>
<h3>3. GCMモードのタグ長</h3>
<p>GCM（Galois/Counter Mode）を使用する場合、タグ長を正しく設定する必要があります。
一般的には128ビット（16バイト）が使用されます。</p>
<h3>4. エラーハンドリング</h3>
<p>復号化時にはさまざまなエラーが発生する可能性があります:</p>
<ul>
<li><code>KeyPermanentlyInvalidatedException</code>: キーが無効化された</li>
<li><code>AEADBadTagException</code>: 暗号文が改ざんされた、またはIVが間違っている</li>
<li><code>InvalidKeyException</code>: キーが無効</li>
</ul>
<p>これらのエラーをハンドリングし、必要に応じてデータをクリアし再生成するなどの対応が必要です。</p>
<h3>5. DataStoreの非同期処理</h3>
<p>DataStoreはすべての操作が非同期で行われます。
CoroutineまたはFlowを使用して適切に処理する必要があります。</p>
<p>DataStoreのソースコード内で、最新の値を1つだけ取得できる data.first() を使用することを推奨しています。</p>
<pre><code class="language-kotlin">// ViewModelでの使用例
viewModelScope.launch {
    repository.saveTextData(sensitiveData)
}

// Flowの監視
repository.secureDataFlow.map { it.textData }.first { it.isNotBlank() }
</code></pre>
<h3>6. 無限待機の防止</h3>
<p>DataStoreはディスクI/Oを伴う非同期処理です。first { it.isNotBlank() } は条件に一致する値が来るまで無限に待機しますが、
もしディスク読み込みが遅延したり、トークンが空のままだと、アプリがフリーズする可能性があり、タイムアウトを追加しました。</p>
<h3>7. ProGuard/R8の設定</h3>
<p>DataStore 1.1.5以降では、ProGuardルールがライブラリに内包されています。
巷の記事で ProGuardルール の記載が必要なことを目の当たりにしましたが、ルール記載なくリリースビルドしたところクラッシュせず、なぜ?
となっていたところ、リリースノート確認し気づきました。
今回、1.1.7 を使用しているため、DataStore専用のProGuardルールを追加する必要はありません。</p>
<p><a href="https://developer.android.com/jetpack/androidx/releases/datastore">https://developer.android.com/jetpack/androidx/releases/datastore</a></p>
<ul>
<li>バージョン1.2.0-beta01で修正された問題として記載：
&quot;Fix java.lang.UnsatisfiedLinkError when using DataStore in an app which is optimized with
R8&quot;</li>
<li>バージョン1.1.5で修正：
&quot;missing Proguard rules issue in the Android artifact of datastore-preferences-core&quot;</li>
</ul>
<h3>8. 標準のSharedPreferencesMigrationが使えない</h3>
<p>EncryptedSharedPreferencesは特殊な暗号化を使用していて、 標準のマイグレーションでは暗号化されたままのデータが転送される</p>
<p>また、EncryptedSharedPreferencesは 読み取り時に自動復号化されるのに対し、 今回は CryptographyManagerによる手動暗号化が必要です。
この部分を認知することができず、標準の標準のSharedPreferencesMigrationで実装し、テストしたところ復号できず判明しました。
マイグレーション時に適切な暗号化変換を実装しました。</p>
<h2>まとめ</h2>
<p>本記事では、Android Keystore、Cipher、DataStoreを組み合わせた秘匿情報の暗号化・永続化の実装について紹介しました。</p>
<ul>
<li><strong>実装前のディスカッションが重要</strong>: そもそも暗号化が必要なデータかをチームで議論することで、Keystoreへのアクセス頻度を最小限に抑えられた</li>
<li><strong>Keystoreの鍵管理</strong>: AES/GCMモードでの鍵生成とIV(初期化ベクトル)の保存を適切に行う必要がある</li>
<li><strong>DataStoreとの組み合わせ</strong>: Flowベースの非同期読み出しに対応するため、タイムアウトや無限待機の防止策が必要</li>
<li><strong>EncryptedSharedPreferencesからの移行</strong>: 標準のSharedPreferencesMigrationでは暗号化方式の違いにより復号できないため、手動でのマイグレーション実装が必要</li>
</ul>
<p>Keystoreのクラッシュは完全には避けられませんが、暗号化対象を最適化し、エラーハンドリングを適切に実装することで、安定したセキュアなデータ管理を実現できました。</p>
<h2>📣 追記: DataStore 1.3.0-alpha07 で暗号化サポートが追加</h2>
<p>本記事の執筆後、<a href="https://developer.android.com/jetpack/androidx/releases/datastore?hl=ja#1.3.0-alpha07">DataStore 1.3.0-alpha07</a>（2026年3月11日リリース）で、<strong>Tinkライブラリを使用した暗号化サポート</strong>が新たに追加されました。</p>
<p>新しい <code>androidx.datastore:datastore-tink</code> アーティファクトにより、<code>AeadSerializer</code> を使って既存のシリアライザをラップするだけで暗号化が実現できます。</p>
<pre><code class="language-kotlin">val aeadSerializer = AeadSerializer(
    aead = keysetHandle.getPrimitive(
        RegistryConfiguration.get(),
        Aead::class.java,
    ),
    wrappedSerializer = ExistingSerializer,
    associatedData = &quot;settings.json&quot;.encodeToByteArray(),
)
</code></pre>
<p>本記事で紹介したKeystore + Cipher による手動実装と比較すると、Tink統合によりボイラープレートが大幅に削減されます。ただし、現時点ではalpha版であるため、プロダクション導入の際はAPIの安定性を考慮する必要があります。今後のstable版リリースに注目です。</p>
<h2>参考資料</h2>
<ul>
<li><a href="https://developer.android.com/training/articles/keystore">Android Keystore System</a></li>
<li><a href="https://developer.android.com/topic/libraries/architecture/datastore">Jetpack DataStore</a></li>
<li><a href="https://developer.android.com/topic/security/data">暗号化されたファイルの使用</a></li>
<li><a href="https://developer.android.com/topic/security/best-practices">Android セキュリティのベスト プラクティス</a></li>
<li><a href="https://developer.android.com/jetpack/androidx/releases/datastore?hl=ja#1.3.0-alpha07">DataStore 1.3.0-alpha07 リリースノート</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/numami/keystore_cipher_datastore_cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[GitHub Actionsを意図せず大量実行させて社内CIを止めた話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-23-github-actions-runaway/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-23-github-actions-runaway/</guid>
            <pubDate>Wed, 01 Apr 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[ちょっとした操作ミスが連鎖して、GitHub Actionsのワークフローを大量実行させてしまった失敗談]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>KINTOテクノロジーズでインフラエンジニアをしているyassanです。</p>
<p>先日、GitHub Actionsのワークフローを意図せず大量に起動してしまい、<strong>社内のCI/CDパイプラインを約1時間にわたって止めてしまう</strong>という事故を起こしました。</p>
<p>この記事では、小さなミスがどう連鎖して大きな障害になったのか、そしてそこから何を学んだのかをお話しします。</p>
<h2>前提：コメント駆動のCI/CDパイプライン</h2>
<p>私たちのチームでは、Terraformのインフラコードを管理するリポジトリでGitHub Actionsを活用しています。</p>
<p>仕組みはシンプルで、PRにコメントを投稿すると、そのPRで変更されたディレクトリを検出して自動的に <code>terraform plan</code> を実行してくれるというものです。</p>
<p>ワークフローの概要を簡略化すると、以下のようなイメージです。</p>
<pre><code class="language-yaml">name: Terraform Plan

on:
  issue_comment:
    types: [created, edited]  # コメントの新規作成・編集時に発火

jobs:
  plan:
    # PRへのコメントで、本文にコマンド文字列を含む場合に実行
    if: |
      github.event.issue.pull_request
      &amp;&amp; contains(github.event.comment.body, &#39;/command&#39;)
    runs-on: ubuntu-latest
    steps:
      - name: PRの変更ディレクトリを検出
        # ...
      - name: 対象ディレクトリごとに terraform plan を実行
        # ...
      - name: 結果をPRにコメント
        # ...
</code></pre>
<p>通常であれば、PRの変更範囲は1〜2ディレクトリ程度。数分で完了する軽い処理です。</p>
<h2>やらかしの連鎖</h2>
<h3>火種：いつもの感覚でリベースしたら、対象が35ヶ所に膨れ上がった</h3>
<p>普段のPRは <code>main</code> ブランチに向けて作成しています。しかしこの日に限って、別の作業ブランチをベースにしたPRを作っていました。</p>
<p>ここで、いつもの癖で何も考えずにリベースを実行。すると、そのブランチにあった<strong>他のメンバーのコミット</strong>が差分に混入してしまいました。</p>
<p>本来1ディレクトリだったplanの対象が、一気に<strong>35ディレクトリ</strong>に膨れ上がりました。</p>
<h3>延焼：消火しようとしたらガソリンだった</h3>
<p>35ディレクトリ分のplanが走ってしまったことに気づき、「余計な結果コメントを非表示にして整理しよう」と考えました。</p>
<p>そこでGitHub APIを使って、不要な34件のコメントのうち20件を非表示（minimize）にしていきました。</p>
<p>その操作がワークフローのトリガーになるとも知らずに、非表示にするだけだと軽い気持ちで実施しました。</p>
<p>結果として、思いがけず20件 × 35ディレクトリ = <strong>約700回のワークフロー</strong>が一斉に走り出しました。</p>
<h3>種明かし：大量のトリガー</h3>
<p>GitHub APIの <code>minimizeComment</code> でコメントを非表示にすると、GitHub上では <strong>「コメントの編集」イベント</strong> として扱われます。ちなみに、Web UIから手動でhideした場合はこのイベントは発生しません。</p>
<p>そして、非表示にしたコメントの本文には、ワークフローのトリガーとなるコマンド文字列が含まれていました。</p>
<p>つまり、<strong>1件非表示にするたびに、35ディレクトリ分のplanが再び起動</strong>してしまう状況だったのです。</p>
<pre><code class="language-mermaid">graph TD
    A[結果コメントを非表示にする] --&gt;|editイベント発火| B[ワークフローがコメント本文を読む]
    B --&gt;|トリガー文字列を検出| C[35ディレクトリ分のplanが起動]
    C --&gt; D[結果コメントが投稿される]
    D --&gt;|さらに非表示にすると...| A
    style A fill:#ff6b6b,color:#fff
    style C fill:#ff6b6b,color:#fff
</code></pre>
<h3>誤判断：PRを閉じれば止まると思った</h3>
<p>約10分後、大量のワークフローが走っていることに気づきました。パニックになった私は「PRを閉じれば止まるはず」と考え、すぐにPRをクローズしました。</p>
<p>「これで大丈夫」と安心して、別の作業に戻りました。</p>
<h3>発覚：社内から悲鳴が上がる</h3>
<p>さらに約10分後。社内のチャットに「GitHub Actionsが動かない」「CIがずっとキュー待ちになっている」という報告が上がり始めました。</p>
<p>慌ててGitHubを確認すると、クローズしたはずのPRに<strong>まだ結果コメントが投稿され続けていました</strong>。</p>
<p>実は、PRをクローズしても <strong>実行中のワークフローはキャンセルされません</strong>。</p>
<p>それどころか、クローズされたPRに対してもコメントイベントは発火するため、PRクローズ自体にワークフローを止める効果はないのです。</p>
<p>これにより、共有ランナーの枠を食いつぶしてしまい、他チームのCIが動かなかったわけです。</p>
<p>私はすぐにGitHub Actionsの画面から、実行中のワークフローを手動で片っ端からキャンセル。ようやくキュー溜まりが解消し、社内のCI/CDが正常に戻りました。</p>
<p>あとから確認したところ、恐ろしいことに<strong>約3,000分（50時間相当）のActions実行時間を、わずか1時間の間に消費していた</strong>ことがわかりました。</p>
<h2>何が起きていたのか</h2>
<p>今回の事故は、4つのミスが連鎖して起きました。</p>
<table>
<thead>
<tr>
<th>#</th>
<th>やったこと</th>
<th>何が起きたか</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>別ブランチベースのPRでリベース</td>
<td>他人のコミット混入で対象35ディレクトリに膨張</td>
</tr>
<tr>
<td>2</td>
<td>結果コメントを非表示にして整理</td>
<td>非表示=編集イベント → ワークフロー再起動 × 20回</td>
</tr>
<tr>
<td>3</td>
<td>PRをクローズして安心</td>
<td>起動済みワークフローは止まらない</td>
</tr>
<tr>
<td>4</td>
<td>20分間気づかず放置</td>
<td>社内CI/CDが1時間停止</td>
</tr>
</tbody></table>
<p>一つ一つは「ちょっとした判断ミス」や「仕様を知らなかった」程度のことですが、それが連鎖することで大きな障害になりました。</p>
<h2>ワークフロー変更による再発防止</h2>
<h3>1. トリガー条件の見直し</h3>
<p>ワークフローのトリガーから <code>edited</code>（編集）イベントを削除し、<code>created</code>（新規作成）のみに限定しました。これにより、コメントの編集や非表示でワークフローが起動することはなくなりました。</p>
<pre><code class="language-diff">on:
  issue_comment:
-    types: [created, edited]
+    types: [created]
</code></pre>
<h3>2. コマンド判定ロジックの厳格化</h3>
<p>コメント本文にコマンド文字列が「含まれているか」ではなく、「先頭から始まっているか」で判定するように変更しました。さらに、イベント種別の二重チェックも追加しています。</p>
<pre><code class="language-diff">jobs:
  run_plan:
    if: |
      github.event.issue.pull_request
+      &amp;&amp; github.event.action == &#39;created&#39;
-      &amp;&amp; contains(github.event.comment.body, &#39;/command&#39;)
+      &amp;&amp; startsWith(github.event.comment.body, &#39;/command&#39;)
</code></pre>
<h3>3. 同時実行の制御</h3>
<p><code>concurrency</code> グループを設定し、同一PRでのワークフローの並列実行を防止しました。後から起動したワークフローが、先行するものをキャンセルして最新のplanだけが実行されるようになっています。</p>
<pre><code class="language-yaml">concurrency:
  group: plan-${{ github.event.issue.number }}
  cancel-in-progress: true
</code></pre>
<h2>組織としての課題</h2>
<p>今回の事故で、ワークフロー単体の修正だけでは防ぎきれない課題も見えてきました。</p>
<ul>
<li>共有ランナーの同時実行数が急増しても気づく仕組みがない</li>
<li>ワークフローのトリガー設計に関する共通のガイドラインがない</li>
<li>暴走に気づいたとき、誰がどう止めるかの手順が整備されていない</li>
</ul>
<p>これを踏まえてコーポレートITグループと連携して以下による改善を進めていきたいと考えています。</p>
<ul>
<li>ランナー使用状況の監視強化（同時実行数がしきい値を超えた際の Slack アラート）</li>
<li>ARMランナーやハイスペックランナーへの切り替えによる処理効率の改善</li>
<li>ワークフロートリガー設定のベストプラクティス策定・既存ワークフローの一括監査</li>
</ul>
<h2>この経験から学んだこと</h2>
<p><strong>「止めたつもり」が一番怖い。</strong></p>
<p>PRを閉じればワークフローも止まると思い込んでいましたが、実際にはそうではありませんでした。慌てているときほど、思い込みで行動してしまいがちです。</p>
<p><strong>ワークフローのトリガー条件は、「最悪のケース」で考える。</strong></p>
<p>GitHub APIを使ったコメントの非表示は編集イベントとして扱われること、結果コメントの本文にトリガー文字列が含まれること。どちらも普段は問題にならない仕様ですが、組み合わさったときに暴走を引き起こしました。</p>
<p><strong>小さなミスは連鎖する。</strong></p>
<p>リベースのミス、コメント整理の操作、PRクローズへの過信、確認不足。どれか一つでも正しく対処できていれば、ここまでの事故にはなりませんでした。失敗が起きたとき、焦らずに「今何が動いているのか」を確認することが大事だと痛感しました。</p>
<h2>おわりに</h2>
<p>今回の事故は、自分の操作で社内の開発フローを止めてしまうという、なかなかにつらい経験でした。</p>
<p>ただ、この失敗をきっかけにワークフローのトリガー設計を見直し、同様の暴走が起きない仕組みに改善できました。外注開発なら責任問題になりかねない失敗も、内製開発なら改善のきっかけにできる。それがこの経験で得た一番の実感です。</p>
<p>この記事が、同じようなCI/CDの落とし穴を避けるための参考になれば幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[1時間30分かかっていたデータ取り込み処理をたった5分で終わらせる技術〜ISUCONは役にたつ〜]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-31-improvement-importing-data-performance/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-31-improvement-importing-data-performance/</guid>
            <pubDate>Tue, 31 Mar 2026 01:00:00 GMT</pubDate>
            <description><![CDATA[1時間30分かかっていたデータ取り込み処理をたった5分で終わらせるようにできました。ISUCONの知識は役に立ちます。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは、KINTOテクノロジーズのFACTORY EC開発グループでバックエンドエンジニアをやっている、うえはら(<a href="https://x.com/penpen_77777">@penpen_77777</a>)です。
今回はWebサービスを決められたレギュレーションの中で限界まで高速化を図るチューニングバトル「ISUCON」で得た知識を活用して、FACTORYでマスタデータ反映に1時間30分かかっていた処理をたった5分で終わらせるようにした方法についてご紹介します。</p>
<p>「ISUCON」は、LINEヤフー株式会社の商標または登録商標です。
ISUCON is a trademark or registered trademark of LY Corporation.
<a href="https://isucon.net">https://isucon.net</a></p>
<h2>今回の課題</h2>
<p>FACTORYでは商品や車種などのマスタデータをExcelファイルに取りまとめ、
そのExcelファイルをもとに本番環境のDBにデータを反映しています(=マスタ反映)。</p>
<p>このマスタ反映に90分かかっており、マスタ運用作業のボトルネックになっていました。
例えば本番環境への反映の前に検証環境でマスタデータに問題ないかを確認しているのですが、
データの誤りに気づいて修正してもマスタ反映に90分かかるため、データが正しく直せたかどうかすぐに確認できない状況でした。</p>
<p>そこで、マスタ反映を高速化することで運用作業の効率化を図ることにしました。</p>
<h2>マスタデータ反映</h2>
<p>マスタ反映は、Excelで管理されているマスタデータを元に、最終的にマスタ反映コンテナがDBに書き込むという流れになっています。</p>
<p>上記の流れを図に示します。
<img src="/assets/blog/authors/uehara/2026-03-31-improvement-importing-data-performance/import-master-flow.svg" alt=""></p>
<p>図中では以下のような流れでマスタ反映が進みます。</p>
<ol>
<li>マスタ運営担当者が、原本となるExcelファイルに車種や商品情報を入力する</li>
<li>出来上がったExcelファイルをマスタ管理ツールにアップロードする</li>
<li>マスタ管理ツールがバリデーションをかけ、問題があれば担当者に通知する</li>
<li>Excelがアップロードされると裏でLambda関数が実行され、ExcelファイルからCSVファイルに変換される</li>
<li>DBに反映したい段階で、マスタデータをFACTORY本体に連携するため、CSVをレプリケーションバケットに保存する</li>
<li>レプリケーションバケットにファイルが保存されるとFACTORY本体でステートマシンが起動し、マスタ反映コンテナを起動する</li>
<li>マスタ反映コンテナがCSVを読み取ってSQLを組み立て、DBの各テーブルにレコードを読み書きする</li>
</ol>
<p>今回高速化の対象としたのは、7のマスタ反映コンテナの処理です。</p>
<h2>パフォーマンスチューニングをどのように進めたか追体験する</h2>
<p>今回のマスタ反映に関するパフォーマンス問題についてどのように解決したかサンプルコードで見ていきましょう。
実際のマスタ反映処理はKotlinで記述されていますが、サンプルコードの方では筆者が慣れているGoを使います。
また、使用するマスタデータはFACTORYの実際に使われているデータではありません。
ですが、似た構造のマスタデータを使うので、実際に筆者が行ったパフォーマンスチューニングと同じ方法で高速化できます。
もしよろしければ皆さんも手を動かしながら試してみてください。
<img src="/assets/blog/authors/uehara/2026-03-31-improvement-importing-data-performance/master-import-container.svg" alt=""></p>
<h3>入力</h3>
<p>ECサイトで管理している商品データを反映したいと考えてみましょう。
表では省略していますが、全部で50万件程度のデータとなります</p>
<table>
<thead>
<tr>
<th>product_code<br>商品を一意に識別するコード</th>
<th>product_name<br>商品の表示名</th>
<th>category_code<br>商品が属するカテゴリのコード</th>
<th>supplier_code<br>仕入先コード</th>
<th>status_code<br>商品の販売状態</th>
<th align="right">unit_price<br>単価（円）</th>
</tr>
</thead>
<tbody><tr>
<td>P1001</td>
<td>ボールペン 黒</td>
<td>CAT01</td>
<td>SUP01</td>
<td>active</td>
<td align="right">150</td>
</tr>
<tr>
<td>P1002</td>
<td>ボールペン 赤</td>
<td>CAT01</td>
<td>SUP01</td>
<td>active</td>
<td align="right">150</td>
</tr>
<tr>
<td>P1003</td>
<td>シャープペンシル</td>
<td>CAT01</td>
<td>SUP02</td>
<td>discontinued</td>
<td align="right">300</td>
</tr>
<tr>
<td>P2001</td>
<td>A4コピー用紙 500枚</td>
<td>CAT02</td>
<td>SUP03</td>
<td>active</td>
<td align="right">450</td>
</tr>
<tr>
<td>P2002</td>
<td>A3コピー用紙 500枚</td>
<td>CAT02</td>
<td>SUP03</td>
<td>active</td>
<td align="right">780</td>
</tr>
</tbody></table>
<p>人間にとって分かりやすいように表で示しましたが、システムにはcsvの形で入力されます。</p>
<pre><code class="language-csv">product_code,product_name,category_code,supplier_code,status_code,unit_price
P1001,ボールペン 黒,CAT01,SUP01,active,150
P1002,ボールペン 赤,CAT01,SUP01,active,150
P1003,シャープペンシル,CAT01,SUP02,discontinued,300
P2001,A4コピー用紙 500枚,CAT02,SUP03,active,450
P2002,A3コピー用紙 500枚,CAT02,SUP03,active,780
</code></pre>
<h3>出力</h3>
<p>入力されたデータを以下のように<code>product</code>テーブルに入れることにします。
category_codeやsupplier_codeやstatus_codeは外部テーブルで保持される値となるため、idに変換した上で保存されます。
外部テーブルにはすでにレコードが反映されているとします。</p>
<table>
<thead>
<tr>
<th align="right">product_id</th>
<th>product_code</th>
<th>product_name</th>
<th align="right">category_id</th>
<th align="right">supplier_id</th>
<th align="right">status_id</th>
<th align="right">unit_price</th>
</tr>
</thead>
<tbody><tr>
<td align="right">1</td>
<td>P1001</td>
<td>ボールペン 黒</td>
<td align="right">1</td>
<td align="right">1</td>
<td align="right">1</td>
<td align="right">150</td>
</tr>
<tr>
<td align="right">2</td>
<td>P1002</td>
<td>ボールペン 赤</td>
<td align="right">1</td>
<td align="right">1</td>
<td align="right">1</td>
<td align="right">150</td>
</tr>
<tr>
<td align="right">3</td>
<td>P1003</td>
<td>シャープペンシル</td>
<td align="right">1</td>
<td align="right">2</td>
<td align="right">2</td>
<td align="right">300</td>
</tr>
<tr>
<td align="right">4</td>
<td>P2001</td>
<td>A4コピー用紙 500枚</td>
<td align="right">2</td>
<td align="right">3</td>
<td align="right">1</td>
<td align="right">450</td>
</tr>
<tr>
<td align="right">5</td>
<td>P2002</td>
<td>A3コピー用紙 500枚</td>
<td align="right">2</td>
<td align="right">3</td>
<td align="right">1</td>
<td align="right">780</td>
</tr>
</tbody></table>
<pre><code class="language-mermaid">erDiagram
    Product {
        string product_id PK &quot;商品ID&quot;
        string product_code UK &quot;商品コード&quot;
        string product_name &quot;商品名&quot;
        string category_id FK &quot;カテゴリID&quot;
        string supplier_id FK &quot;仕入先ID&quot;
        string status_id FK &quot;ステータスID&quot;
        int unit_price &quot;単価（円）&quot;
    }

    Category {
        string category_id PK &quot;カテゴリID&quot;
        string category_code UK &quot;カテゴリコード&quot;
        string category_name &quot;カテゴリ名&quot;
    }

    Supplier {
        string supplier_id PK &quot;仕入先ID&quot;
        string supplier_code UK &quot;仕入先コード&quot;
        string supplier_name &quot;仕入先名&quot;
    }

    Status {
        string status_id PK &quot;ステータスID&quot;
        string status_code UK &quot;ステータスコード&quot;
        string status_name &quot;ステータス名&quot;
    }

    Category ||--o{ Product : &quot;has&quot;
    Supplier ||--o{ Product : &quot;supplies&quot;
    Status ||--o{ Product : &quot;applies&quot;
</code></pre>
<h2>改善前のコード</h2>
<p>サンプルコードの全体構成を以下の図に示します。
ハンズオンをサクッとできるようにテストデータの準備等の必要な作業を行ったのち、本題のマスタ反映が実行されるようになっています。testcontainersでMySQLコンテナを起動しテスト用のCSVを生成した後、main.goがそのCSVを読み取ってDBにマスタ反映を行います。
<img src="/assets/blog/authors/uehara/2026-03-31-improvement-importing-data-performance/sample-code.svg" alt=""></p>
<p>今回使用するサンプルコードを以下に示します。以下の4つのコードを同じディレクトリに配置してください。</p>
<p>:::details main.go (改善対象のコード)</p>
<pre><code class="language-go">package main

import (
	&quot;context&quot;
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;os&quot;
	&quot;time&quot;

	_ &quot;github.com/go-sql-driver/mysql&quot;
	&quot;github.com/gocarina/gocsv&quot;
	&quot;github.com/jmoiron/sqlx&quot;
)

func main() {
	ctx := context.Background()

	// MySQLコンテナを起動
	connStr, cleanup, err := startMySQLContainer(ctx)
	if err != nil {
		log.Fatal(err)
	}
	defer cleanup()

	db, err := sqlx.Open(&quot;mysql&quot;, connStr)
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// テーブル・マスターデータを作成
	if err := setupTables(db); err != nil {
		log.Fatal(err)
	}

	// サンプルCSVを生成（50万行）
	csvFilename := &quot;data.csv&quot;
	if err := generateSampleCSV(csvFilename, 500000); err != nil {
		log.Fatal(err)
	}

	// 1. CSVを読み取る
	file, err := os.Open(csvFilename)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	var products []Product
	if err := gocsv.UnmarshalFile(file, &amp;products); err != nil {
		log.Fatal(err)
	}
	fmt.Printf(&quot;CSV読み込み完了: %d 行\n&quot;, len(products))

	importStart := time.Now()

	for i, product := range products {
		// 2. 読んでない行があれば1行読み取る、なければ終了
		lineNum := i + 2

		// 3. category_codeをcategory_idに変換
		var category Category
		if err := db.Get(
			&amp;category,
			`SELECT * FROM categories WHERE code = ?`,
			product.CategoryCode,
		); err != nil {
			log.Fatalf(&quot;行 %d: category_code %q の検索に失敗: %v&quot;, lineNum, product.CategoryCode, err)
		}

		// 4. supplier_codeをsupplier_idに変換
		var supplier Supplier
		if err := db.Get(
			&amp;supplier,
			`SELECT * FROM suppliers WHERE code = ?`,
			product.SupplierCode,
		); err != nil {
			log.Fatalf(&quot;行 %d: supplier_code %q の検索に失敗: %v&quot;, lineNum, product.SupplierCode, err)
		}

		// 5. status_codeをstatus_idに変換
		var status Status
		if err := db.Get(
			&amp;status,
			`SELECT * FROM statuses WHERE code = ?`,
			product.StatusCode,
		); err != nil {
			log.Fatalf(&quot;行 %d: status_code %q の検索に失敗: %v&quot;, lineNum, product.StatusCode, err)
		}

		// 6. ProductRowに変換
		row := ProductRow{
			ProductCode: product.ProductCode,
			ProductName: product.ProductName,
			CategoryID:  category.ID,
			SupplierID:  supplier.ID,
			StatusID:    status.ID,
			UnitPrice:   product.UnitPrice,
		}

		// 7. UPDATE文を実行する
		result, err := db.NamedExec(`
			UPDATE products
			SET product_name = :product_name,
				category_id = :category_id,
				supplier_id = :supplier_id,
				status_id = :status_id,
				unit_price = :unit_price
			WHERE product_code = :product_code`,
			row,
		)
		if err != nil {
			log.Fatalf(&quot;行 %d: productsの更新に失敗: %v&quot;, lineNum, err)
		}

		rowsAffected, err := result.RowsAffected()
		if err != nil {
			log.Fatalf(&quot;行 %d: 更新件数の取得に失敗: %v&quot;, lineNum, err)
		}

		// 8. UPDATE対象がなければINSERTする
		if rowsAffected == 0 {
			_, err = db.NamedExec(`
				INSERT INTO products (product_code, product_name, category_id, supplier_id, status_id, unit_price)
				VALUES (:product_code, :product_name, :category_id, :supplier_id, :status_id, :unit_price)`,
				row,
			)
			if err != nil {
				log.Fatalf(&quot;行 %d: productsの登録に失敗: %v&quot;, lineNum, err)
			}
		}

		if (lineNum-1)%1000 == 0 {
			rate := float64(lineNum-1) / time.Since(importStart).Seconds()
			fmt.Printf(&quot;進捗: %d / %d 行 (%.0f 行/秒)\n&quot;, lineNum-1, len(products), rate)
		}
		// 9. 2に戻る
	}

	fmt.Printf(&quot;完了: %d 行 (所要時間: %v)\n&quot;, len(products), time.Since(importStart))
}
</code></pre>
<p>:::</p>
<p>:::details models.go (csv, dbを操作するのに必要な構造体を定義)</p>
<pre><code class="language-go">package main

type Product struct {
	ProductCode  string `csv:&quot;product_code&quot;`
	ProductName  string `csv:&quot;product_name&quot;`
	CategoryCode string `csv:&quot;category_code&quot;`
	SupplierCode string `csv:&quot;supplier_code&quot;`
	StatusCode   string `csv:&quot;status_code&quot;`
	UnitPrice    int    `csv:&quot;unit_price&quot;`
}

type Category struct {
	ID   int    `db:&quot;id&quot;`
	Code string `db:&quot;code&quot;`
	Name string `db:&quot;name&quot;`
}

type Supplier struct {
	ID   int    `db:&quot;id&quot;`
	Code string `db:&quot;code&quot;`
	Name string `db:&quot;name&quot;`
}

type Status struct {
	ID   int    `db:&quot;id&quot;`
	Code string `db:&quot;code&quot;`
	Name string `db:&quot;name&quot;`
}

type ProductRow struct {
	ProductCode string `db:&quot;product_code&quot;`
	ProductName string `db:&quot;product_name&quot;`
	CategoryID  int    `db:&quot;category_id&quot;`
	SupplierID  int    `db:&quot;supplier_id&quot;`
	StatusID    int    `db:&quot;status_id&quot;`
	UnitPrice   int    `db:&quot;unit_price&quot;`
}
</code></pre>
<p>:::</p>
<p>:::details setup.go（DB初期化・CSV生成）</p>
<pre><code class="language-go">package main

import (
	&quot;context&quot;
	&quot;encoding/csv&quot;
	&quot;fmt&quot;
	&quot;math/rand&quot;
	&quot;os&quot;
	&quot;strconv&quot;
	&quot;time&quot;

	&quot;github.com/jmoiron/sqlx&quot;
	&quot;github.com/testcontainers/testcontainers-go&quot;
	&quot;github.com/testcontainers/testcontainers-go/modules/mysql&quot;
	&quot;github.com/testcontainers/testcontainers-go/wait&quot;
)

func startMySQLContainer(ctx context.Context) (connStr string, cleanup func(), err error) {
	mysqlContainer, err := mysql.Run(ctx,
		&quot;mysql:8.0&quot;,
		mysql.WithDatabase(&quot;testdb&quot;),
		mysql.WithUsername(&quot;user&quot;),
		mysql.WithPassword(&quot;password&quot;),
		testcontainers.WithWaitStrategyAndDeadline(3*time.Minute,
			wait.ForListeningPort(&quot;3306/tcp&quot;).
				WithStartupTimeout(3*time.Minute),
		),
	)
	if err != nil {
		return &quot;&quot;, nil, err
	}

	connStr, err = mysqlContainer.ConnectionString(ctx)
	if err != nil {
		_ = mysqlContainer.Terminate(ctx)
		return &quot;&quot;, nil, err
	}

	cleanup = func() {
		_ = mysqlContainer.Terminate(ctx)
	}
	return connStr, cleanup, nil
}

func generateSampleCSV(filename string, rows int) error {
	file, err := os.Create(filename)
	if err != nil {
		return err
	}
	defer file.Close()

	writer := csv.NewWriter(file)
	defer writer.Flush()

	if err := writer.Write([]string{&quot;product_code&quot;, &quot;product_name&quot;, &quot;category_code&quot;, &quot;supplier_code&quot;, &quot;status_code&quot;, &quot;unit_price&quot;}); err != nil {
		return err
	}

	categoryCodes := []string{&quot;CAT01&quot;, &quot;CAT02&quot;, &quot;CAT03&quot;}
	supplierCodes := []string{&quot;SUP01&quot;, &quot;SUP02&quot;, &quot;SUP03&quot;}
	statusCodes := []string{&quot;active&quot;, &quot;discontinued&quot;, &quot;pending&quot;}

	for i := 0; i &lt; rows; i++ {
		record := []string{
			fmt.Sprintf(&quot;P%d&quot;, 1000+i+1),
			fmt.Sprintf(&quot;商品_%d&quot;, i+1),
			categoryCodes[rand.Intn(len(categoryCodes))],
			supplierCodes[rand.Intn(len(supplierCodes))],
			statusCodes[rand.Intn(len(statusCodes))],
			strconv.Itoa(rand.Intn(10000) + 100),
		}
		if err := writer.Write(record); err != nil {
			return err
		}
	}

	return nil
}

func setupTables(db *sqlx.DB) error {
	tables := []string{
		`CREATE TABLE IF NOT EXISTS categories (
			id INT AUTO_INCREMENT PRIMARY KEY,
			code VARCHAR(10) UNIQUE NOT NULL,
			name VARCHAR(100) NOT NULL
		)`,
		`CREATE TABLE IF NOT EXISTS suppliers (
			id INT AUTO_INCREMENT PRIMARY KEY,
			code VARCHAR(10) UNIQUE NOT NULL,
			name VARCHAR(100) NOT NULL
		)`,
		`CREATE TABLE IF NOT EXISTS statuses (
			id INT AUTO_INCREMENT PRIMARY KEY,
			code VARCHAR(20) UNIQUE NOT NULL,
			name VARCHAR(100) NOT NULL
		)`,
		`CREATE TABLE IF NOT EXISTS products (
			id INT AUTO_INCREMENT PRIMARY KEY,
			product_code VARCHAR(50) UNIQUE NOT NULL,
			product_name VARCHAR(255) NOT NULL,
			category_id INT NOT NULL,
			supplier_id INT NOT NULL,
			status_id INT NOT NULL,
			unit_price INT NOT NULL,
			FOREIGN KEY (category_id) REFERENCES categories(id),
			FOREIGN KEY (supplier_id) REFERENCES suppliers(id),
			FOREIGN KEY (status_id) REFERENCES statuses(id)
		)`,
	}

	for _, table := range tables {
		if _, err := db.Exec(table); err != nil {
			return err
		}
	}

	masterData := []string{
		`INSERT IGNORE INTO categories (code, name) VALUES
			(&#39;CAT01&#39;, &#39;文房具&#39;), (&#39;CAT02&#39;, &#39;食品&#39;), (&#39;CAT03&#39;, &#39;電化製品&#39;)`,
		`INSERT IGNORE INTO suppliers (code, name) VALUES
			(&#39;SUP01&#39;, &#39;株式会社A商事&#39;), (&#39;SUP02&#39;, &#39;株式会社B産業&#39;), (&#39;SUP03&#39;, &#39;株式会社C物産&#39;)`,
		`INSERT IGNORE INTO statuses (code, name) VALUES
			(&#39;active&#39;, &#39;販売中&#39;), (&#39;discontinued&#39;, &#39;販売終了&#39;), (&#39;pending&#39;, &#39;販売準備中&#39;)`,
	}

	for _, data := range masterData {
		if _, err := db.Exec(data); err != nil {
			return err
		}
	}

	return nil
}
</code></pre>
<p>:::</p>
<p>:::details go.mod</p>
<pre><code class="language-plain">module csv-import-example

go 1.24.5

require (
	github.com/go-sql-driver/mysql v1.9.3
	github.com/gocarina/gocsv v0.0.0-20240520201108-78e41c74b4b1
	github.com/jmoiron/sqlx v1.4.0
	github.com/testcontainers/testcontainers-go v0.40.0
	github.com/testcontainers/testcontainers-go/modules/mysql v0.40.0
)

require (
	dario.cat/mergo v1.0.2 // indirect
	filippo.io/edwards25519 v1.1.0 // indirect
	github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
	github.com/Microsoft/go-winio v0.6.2 // indirect
	github.com/cenkalti/backoff/v4 v4.3.0 // indirect
	github.com/containerd/errdefs v1.0.0 // indirect
	github.com/containerd/errdefs/pkg v0.3.0 // indirect
	github.com/containerd/log v0.1.0 // indirect
	github.com/containerd/platforms v0.2.1 // indirect
	github.com/cpuguy83/dockercfg v0.3.2 // indirect
	github.com/davecgh/go-spew v1.1.1 // indirect
	github.com/distribution/reference v0.6.0 // indirect
	github.com/docker/docker v28.5.1+incompatible // indirect
	github.com/docker/go-connections v0.6.0 // indirect
	github.com/docker/go-units v0.5.0 // indirect
	github.com/ebitengine/purego v0.8.4 // indirect
	github.com/felixge/httpsnoop v1.0.4 // indirect
	github.com/go-logr/logr v1.4.3 // indirect
	github.com/go-logr/stdr v1.2.2 // indirect
	github.com/go-ole/go-ole v1.2.6 // indirect
	github.com/google/uuid v1.6.0 // indirect
	github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.7 // indirect
	github.com/klauspost/compress v1.18.0 // indirect
	github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
	github.com/magiconair/properties v1.8.10 // indirect
	github.com/moby/docker-image-spec v1.3.1 // indirect
	github.com/moby/go-archive v0.1.0 // indirect
	github.com/moby/patternmatcher v0.6.0 // indirect
	github.com/moby/sys/sequential v0.6.0 // indirect
	github.com/moby/sys/user v0.4.0 // indirect
	github.com/moby/sys/userns v0.1.0 // indirect
	github.com/moby/term v0.5.0 // indirect
	github.com/morikuni/aec v1.0.0 // indirect
	github.com/opencontainers/go-digest v1.0.0 // indirect
	github.com/opencontainers/image-spec v1.1.1 // indirect
	github.com/pkg/errors v0.9.1 // indirect
	github.com/pmezard/go-difflib v1.0.0 // indirect
	github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
	github.com/shirou/gopsutil/v4 v4.25.6 // indirect
	github.com/sirupsen/logrus v1.9.3 // indirect
	github.com/stretchr/testify v1.11.1 // indirect
	github.com/tklauser/go-sysconf v0.3.12 // indirect
	github.com/tklauser/numcpus v0.6.1 // indirect
	github.com/yusufpapurcu/wmi v1.2.4 // indirect
	go.opentelemetry.io/auto/sdk v1.2.1 // indirect
	go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
	go.opentelemetry.io/otel v1.38.0 // indirect
	go.opentelemetry.io/otel/metric v1.38.0 // indirect
	go.opentelemetry.io/otel/sdk v1.38.0 // indirect
	go.opentelemetry.io/otel/trace v1.38.0 // indirect
	golang.org/x/crypto v0.43.0 // indirect
	golang.org/x/sys v0.38.0 // indirect
	google.golang.org/grpc v1.78.0 // indirect
	google.golang.org/protobuf v1.36.11 // indirect
	gopkg.in/yaml.v3 v3.0.1 // indirect
)
</code></pre>
<p>:::</p>
<p>高速化するためにmain.goを改善していきます。
main.goの処理の流れをまとめると以下の通りです。</p>
<ol>
<li>csvを読み取る<pre><code class="language-csv">product_code,product_name,category_code,supplier_code,status_code,unit_price
P1001,ボールペン 黒,CAT01,SUP01,active,150
P1002,ボールペン 赤,CAT01,SUP01,active,150
...
</code></pre>
</li>
<li>読んでない行があれば1行読み取る、なければ終了<pre><code class="language-csv">P1001,ボールペン 黒,CAT01,SUP01,active,150
</code></pre>
</li>
<li>category_codeをcategory_idに変換<pre><code class="language-sql">SELECT * FROM categories WHERE code = &#39;CAT01&#39;
-- =&gt; id=1, code=&#39;CAT01&#39;, name=&#39;文房具&#39;
</code></pre>
</li>
<li>supplier_codeをsupplier_idに変換<pre><code class="language-sql">SELECT * FROM suppliers WHERE code = &#39;SUP01&#39;
-- =&gt; id=1, code=&#39;SUP01&#39;, name=&#39;株式会社A商事&#39;
</code></pre>
</li>
<li>status_codeをstatus_idに変換<pre><code class="language-sql">SELECT * FROM statuses WHERE code = &#39;active&#39;
-- =&gt; id=1, code=&#39;active&#39;, name=&#39;販売中&#39;
</code></pre>
</li>
<li>ProductRowに変換</li>
<li>UPDATE文を実行する<pre><code class="language-sql">UPDATE products SET product_name = &#39;ボールペン 黒&#39;, category_id = 1, supplier_id = 1, status_id = 1, unit_price = 150 WHERE product_code = &#39;P1001&#39;
</code></pre>
</li>
<li>UPDATE対象がなければINSERTする<pre><code class="language-sql">INSERT INTO products (product_code, product_name, category_id, supplier_id, status_id, unit_price) VALUES (&#39;P1001&#39;, &#39;ボールペン 黒&#39;, 1, 1, 1, 150)
</code></pre>
</li>
<li>2に戻る</li>
</ol>
<h3>実行してみる</h3>
<p>まずは現状を把握するため反映にどれくらい時間がかかるかみてみましょう。
testcontainersでMySQLコンテナを起動するため、事前にDocker Desktopを起動しておいてください。
また、依存パッケージを取得するために<code>go mod tidy</code>を実行してから<code>go run .</code>を実行します。</p>
<pre><code class="language-bash">go mod tidy
go run .
</code></pre>
<p>このコードを実行してみると以下のような実行結果が得られます。
なんとDBへの反映に47分かかってしまいました。</p>
<pre><code>$ go run .
CSV読み込み完了: 500000 行
進捗: 1000 / 500000 行 (338 行/秒)
進捗: 2000 / 500000 行 (329 行/秒)
進捗: 3000 / 500000 行 (320 行/秒)
進捗: 4000 / 500000 行 (326 行/秒)
進捗: 5000 / 500000 行 (328 行/秒)
進捗: 6000 / 500000 行 (328 行/秒)
進捗: 7000 / 500000 行 (329 行/秒)
進捗: 8000 / 500000 行 (328 行/秒)
進捗: 9000 / 500000 行 (319 行/秒)
...
進捗: 500000 / 500000 行 (176 行/秒)
完了: 成功 500000 行, エラー 0 行 (所要時間: 47m23.503716s)
</code></pre>
<h2>実際のFACTORYのマスタ反映の負荷状況</h2>
<p>実際のFACTORYでの本番環境への反映では90分もの時間がかかっていました。
FACTORY本番のRDSでの負荷を計測するため、以下にDatabase Insightsの結果を示します。
<img src="/assets/blog/authors/uehara/2026-03-31-improvement-importing-data-performance/before.png" alt=""></p>
<p>図ではクエリ別にAAS(平均アクティブセッション)が示され、AASが高い順に並んでいます。
AASが高いほどDBに負荷がかかっており、低いほどDBに負荷がかかっていないというように解釈すればokです。</p>
<p>赤枠がマスタ反映時に実行されているSQLになりますが、</p>
<ol>
<li>特定のテーブルに対するSELECTの実行回数が多い(1秒あたりに200回程度実行されている)</li>
<li>SELECTよりも負荷は小さいものの、UPDATEも同程度の頻度で実行されている</li>
</ol>
<p>このように計測の結果、マスタ反映時に叩かれるSQL、特にSELECTが原因だなというように見当をつけ、改善を進めていきました。</p>
<h2>原因を探る</h2>
<p>これだけの時間がかかる原因を探ってみましょう。
ここではコード中で実行されるクエリに着目してみます。
実行されているクエリは以下の通りです。</p>
<table>
<thead>
<tr>
<th>#</th>
<th>クエリ</th>
<th align="right">ループ中(回)</th>
<th align="right">合計(回)</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td><code>SELECT * FROM categories WHERE code = ?</code></td>
<td align="right">1 × 50万ループ = 50万</td>
<td align="right">50万</td>
</tr>
<tr>
<td>2</td>
<td><code>SELECT * FROM suppliers WHERE code = ?</code></td>
<td align="right">1 × 50万ループ = 50万</td>
<td align="right">50万</td>
</tr>
<tr>
<td>3</td>
<td><code>SELECT * FROM statuses WHERE code = ?</code></td>
<td align="right">1 × 50万ループ = 50万</td>
<td align="right">50万</td>
</tr>
<tr>
<td>4</td>
<td><code>UPDATE products SET ... WHERE product_code = ?</code></td>
<td align="right">1 × 50万ループ = 50万</td>
<td align="right">50万</td>
</tr>
<tr>
<td>5</td>
<td><code>INSERT INTO products (...) VALUES (...)</code></td>
<td align="right">最大1 × 50万ループ = 最大50万</td>
<td align="right">最大50万</td>
</tr>
<tr>
<td></td>
<td>合計</td>
<td align="right">最大250万</td>
<td align="right">最大250万</td>
</tr>
</tbody></table>
<p>1ループあたりの実行回数は少ないですが、今回はCSVが50万行あることから50万ループ実行され、最大で合計250万クエリ実行されることになります。</p>
<p>実行されるクエリが多いと、インデックスを貼って単体のクエリが高速にしたとしても、ちりつもで遅くなってしまいます。
特にDBは別サーバに分離されることが多く、ネットワークの通信帯域の影響も受けてしまいます。</p>
<p>なので高速化の方針としては実行されるクエリをいかに削減するかということを考えれば良さそうです。</p>
<h2>実行されるクエリを削減するためには？</h2>
<h3>SELECT編</h3>
<p>実行されるクエリを削減するにはいくつかの手段がありますが、まずはオンメモリキャッシュを取り上げてみたいと思います。
オンメモリキャッシュは、時間のかかる処理の実行結果をあらかじめメモリ上に乗っけてしまい、結果が欲しい時にはメモリ上のデータから引っ張り出すことで高速化する手法です。ISUCONでは常套手段といっても良いほど典型的なパターンです。
今回でいくと時間のかかる処理とはDBへの問い合わせにあたります。
<img src="/assets/blog/authors/uehara/2026-03-31-improvement-importing-data-performance/cache-db.svg" alt=""></p>
<p>オンメモリでキャッシュするには、キャッシュ対象のデータが、キャッシュ中に書き換えられないほうが実装しやすいです。
キャッシュ中に実データに書き込みがある場合、キャッシュを書き込みに追随させるためデータの更新が必要になります。排他制御を考慮する必要があり、実装が困難になります。</p>
<p>productsテーブルを更新する際にはcategories, suppliers, statusesテーブルはすでに更新が完了しており、書き込みはありません。なのでproductsテーブルを更新する前にキャッシュしておけば問題なさそうです。</p>
<p>ということで先ほどのコードにキャッシュ処理を加えます。</p>
<p>CSV読み取り直後にSELECTを行い全件をメモリ上に載せます。
code→IDへ高速にデータを引きたいので、スライスではなくここでは<code>map[string]int</code>に載せてあげます。map型はキーにひもづくデータの取得で$O(1)$の計算量で高速にデータを引くことができます。</p>
<pre><code class="language-diff">        fmt.Printf(&quot;CSV読み込み完了: %d 行\n&quot;, len(products))

+       // マスターデータをmapに読み込み（code → id）
+       var categories []Category
+       if err := db.Select(&amp;categories, &quot;SELECT * FROM categories&quot;); err != nil {
+               log.Fatal(err)
+       }
+       categoryMap := make(map[string]int, len(categories))
+       for _, c := range categories {
+               categoryMap[c.Code] = c.ID
+       }
</code></pre>
<p>code→IDが欲しいタイミングで、先ほど定義したmap型の変数を使うように書き換えます</p>
<pre><code class="language-diff">                // 3. category_codeをcategory_idに変換
-               var category Category
-               if err := db.Get(&amp;category, &quot;SELECT * FROM categories WHERE code = ?&quot;, product.CategoryCode); err !=
nil {
-                       log.Printf(&quot;行 %d: category変換エラー: %v&quot;, i+2, err)
+               categoryID, ok := categoryMap[product.CategoryCode]
+               if !ok {
+                       log.Printf(&quot;行 %d: category変換エラー: code %q が見つかりません&quot;, i+2, product.CategoryCode)
                        errorCount++
                        continue
                }
</code></pre>
<p>他の修正も加えると以下のような差分になります。
:::details オンメモリキャッシュ化の全体差分</p>
<pre><code class="language-diff">diff --git a/main.go b/main.go
index c3705d8..c3c16cf 100644
--- a/main.go
+++ b/main.go
@@ -52,6 +52,34 @@ func main() {
 	}
 	fmt.Printf(&quot;CSV読み込み完了: %d 行\n&quot;, len(products))

+	// マスターデータをmapに読み込み（code → id）
+	var categories []Category
+	if err := db.Select(&amp;categories, &quot;SELECT * FROM categories&quot;); err != nil {
+		log.Fatal(err)
+	}
+	categoryMap := make(map[string]int, len(categories))
+	for _, c := range categories {
+		categoryMap[c.Code] = c.ID
+	}
+
+	var suppliers []Supplier
+	if err := db.Select(&amp;suppliers, &quot;SELECT * FROM suppliers&quot;); err != nil {
+		log.Fatal(err)
+	}
+	supplierMap := make(map[string]int, len(suppliers))
+	for _, s := range suppliers {
+		supplierMap[s.Code] = s.ID
+	}
+
+	var statuses []Status
+	if err := db.Select(&amp;statuses, &quot;SELECT * FROM statuses&quot;); err != nil {
+		log.Fatal(err)
+	}
+	statusMap := make(map[string]int, len(statuses))
+	for _, s := range statuses {
+		statusMap[s.Code] = s.ID
+	}
+
 	importStart := time.Now()

 	for i, product := range products {
@@ -59,41 +87,29 @@ func main() {
 		lineNum := i + 2

 		// 3. category_codeをcategory_idに変換
-		var category Category
-		if err := db.Get(
-			&amp;category,
-			`SELECT * FROM categories WHERE code = ?`,
-			product.CategoryCode,
-		); err != nil {
-			log.Fatalf(&quot;行 %d: category_code %q の検索に失敗: %v&quot;, lineNum, product.CategoryCode, err)
+		categoryID, ok := categoryMap[product.CategoryCode]
+		if !ok {
+			log.Fatalf(&quot;行 %d: category_code %q の検索に失敗&quot;, lineNum, product.CategoryCode)
 		}

 		// 4. supplier_codeをsupplier_idに変換
-		var supplier Supplier
-		if err := db.Get(
-			&amp;supplier,
-			`SELECT * FROM suppliers WHERE code = ?`,
-			product.SupplierCode,
-		); err != nil {
-			log.Fatalf(&quot;行 %d: supplier_code %q の検索に失敗: %v&quot;, lineNum, product.SupplierCode, err)
+		supplierID, ok := supplierMap[product.SupplierCode]
+		if !ok {
+			log.Fatalf(&quot;行 %d: supplier_code %q の検索に失敗&quot;, lineNum, product.SupplierCode)
 		}

 		// 5. status_codeをstatus_idに変換
-		var status Status
-		if err := db.Get(
-			&amp;status,
-			`SELECT * FROM statuses WHERE code = ?`,
-			product.StatusCode,
-		); err != nil {
-			log.Fatalf(&quot;行 %d: status_code %q の検索に失敗: %v&quot;, lineNum, product.StatusCode, err)
+		statusID, ok := statusMap[product.StatusCode]
+		if !ok {
+			log.Fatalf(&quot;行 %d: status_code %q の検索に失敗&quot;, lineNum, product.StatusCode)
 		}

 		row := ProductRow{
 			ProductCode: product.ProductCode,
 			ProductName: product.ProductName,
-			CategoryID:  category.ID,
-			SupplierID:  supplier.ID,
-			StatusID:    status.ID,
+			CategoryID:  categoryID,
+			SupplierID:  supplierID,
+			StatusID:    statusID,
 			UnitPrice:   product.UnitPrice,
 		}
</code></pre>
<p>:::</p>
<p>DBに問い合わせる代わりにメモリ上のキャッシュにデータを問い合わせるため、
SELECTの150万回分がなくなり、残りのUPDATE/INSERTの最大100万回にまで削減できました。</p>
<table>
<thead>
<tr>
<th>#</th>
<th>クエリ</th>
<th align="right">ループ前(回)</th>
<th align="right">ループ中(回)</th>
<th align="right">合計(回)</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td><code>SELECT * FROM categories</code></td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">1</td>
</tr>
<tr>
<td>2</td>
<td><code>SELECT * FROM suppliers</code></td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">1</td>
</tr>
<tr>
<td>3</td>
<td><code>SELECT * FROM statuses</code></td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">1</td>
</tr>
<tr>
<td>4</td>
<td><code>UPDATE products SET ... WHERE product_code = ?</code></td>
<td align="right">0</td>
<td align="right">1 × 50万ループ = 50万</td>
<td align="right">50万</td>
</tr>
<tr>
<td>5</td>
<td><code>INSERT INTO products (...) VALUES (...)</code></td>
<td align="right">0</td>
<td align="right">最大1 × 50万ループ = 最大50万</td>
<td align="right">最大50万</td>
</tr>
<tr>
<td></td>
<td>合計</td>
<td align="right">3</td>
<td align="right">最大100万</td>
<td align="right">最大100万3</td>
</tr>
</tbody></table>
<p>これでどれくらい高速化できたか見てみましょう。</p>
<pre><code>CSV読み込み完了: 500000 行
進捗: 1000 / 500000 行 (282 行/秒)
進捗: 2000 / 500000 行 (302 行/秒)
進捗: 3000 / 500000 行 (330 行/秒)
進捗: 4000 / 500000 行 (360 行/秒)
進捗: 5000 / 500000 行 (378 行/秒)
(略)
進捗: 496000 / 500000 行 (409 行/秒)
進捗: 497000 / 500000 行 (409 行/秒)
進捗: 498000 / 500000 行 (409 行/秒)
進捗: 499000 / 500000 行 (407 行/秒)
進捗: 500000 / 500000 行 (405 行/秒)
完了: 成功 500000 行, エラー 0 行 (所要時間: 20m35.34731075s)
</code></pre>
<p>以上のように時間を半減させることができました。</p>
<h3>INSERT/UPDATE編</h3>
<p>SELECTの実行回数は削減できましたが、まだ100万回ものSQLが実行されています。
残りのINSERT/UPDATEの高速化にチャレンジしてみます。</p>
<p>INSERT/UPDATEの実行回数を削減する手段としてはupsertに変更することが挙げられます。</p>
<h3>UPSERTとは</h3>
<p>UPSERTとはINSERTとUPDATEを組み合わせた単語で、INSERT時に対象レコードが存在しない場合はINSERTと、すでに存在する場合はUPDATEをかける処理です。
MySQLではINSERT ON DUPLICATE KEY UPDATEとREPLACE構文が使えますが、今回は前者の構文を使ってみます。</p>
<p>今回でいくと以下のUPDATE文を実行し、</p>
<pre><code class="language-sql">UPDATE products
SET product_name = ?,
    category_id  = ?,
    supplier_id  = ?,
    status_id    = ?,
    unit_price   = ?
WHERE product_code = ?
</code></pre>
<p>UPDATE対象が存在しなければINSERTを行っています。</p>
<pre><code class="language-sql">INSERT INTO products (
  product_code,
  product_name,
  category_id,
  supplier_id,
  status_id,
  unit_price
)
VALUES (?, ?, ?, ?, ?, ?)
</code></pre>
<p>INSERT ON DUPLICATE KEY UPDATEを使用すると2つのクエリを1つにまとめることができます。</p>
<pre><code class="language-sql">INSERT INTO products (
  product_code,
  product_name,
  category_id,
  supplier_id,
  status_id,
  unit_price
)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
  product_name = VALUES(product_name),
  category_id  = VALUES(category_id),
  supplier_id  = VALUES(supplier_id),
  status_id    = VALUES(status_id),
  unit_price   = VALUES(unit_price)
</code></pre>
<p>これだけで100万回→50万回までクエリの実行回数を削減できます。</p>
<table>
<thead>
<tr>
<th>#</th>
<th>クエリ</th>
<th align="right">ループ前(回)</th>
<th align="right">ループ中(回)</th>
<th align="right">合計(回)</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td><code>SELECT * FROM categories</code></td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">1</td>
</tr>
<tr>
<td>2</td>
<td><code>SELECT * FROM suppliers</code></td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">1</td>
</tr>
<tr>
<td>3</td>
<td><code>SELECT * FROM statuses</code></td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">1</td>
</tr>
<tr>
<td>4</td>
<td><code>INSERT INTO products (...) ON DUPLICATE KEY UPDATE ...</code></td>
<td align="right">0</td>
<td align="right">1 × 50万ループ = 50万</td>
<td align="right">50万</td>
</tr>
<tr>
<td></td>
<td>合計</td>
<td align="right">3</td>
<td align="right">50万</td>
<td align="right">50万3</td>
</tr>
</tbody></table>
<p>コードでは以下のように修正しています
:::details UPSERT化の差分</p>
<pre><code class="language-diff">diff --git a/main.go b/main.go
index c3c16cf..0da4db0 100644
--- a/main.go
+++ b/main.go
@@ -113,36 +113,23 @@ func main() {
 			UnitPrice:   product.UnitPrice,
 		}

-		// 7. UPDATE文を実行する
-		result, err := db.NamedExec(`
-			UPDATE products
-			SET product_name = :product_name,
-				category_id = :category_id,
-				supplier_id = :supplier_id,
-				status_id = :status_id,
-				unit_price = :unit_price
-			WHERE product_code = :product_code`,
+		// 7. UPSERT（INSERT or UPDATE）を実行する
+		_, err := db.NamedExec(`
+			INSERT INTO products (
+				product_code, product_name, category_id, supplier_id, status_id, unit_price
+			) VALUES (
+				:product_code, :product_name, :category_id, :supplier_id, :status_id, :unit_price
+			)
+			ON DUPLICATE KEY UPDATE
+				product_name = VALUES(product_name),
+				category_id  = VALUES(category_id),
+				supplier_id  = VALUES(supplier_id),
+				status_id    = VALUES(status_id),
+				unit_price   = VALUES(unit_price)`,
 			row,
 		)
 		if err != nil {
-			log.Fatalf(&quot;行 %d: productsの更新に失敗: %v&quot;, lineNum, err)
-		}
-
-		rowsAffected, err := result.RowsAffected()
-		if err != nil {
-			log.Fatalf(&quot;行 %d: 更新件数の取得に失敗: %v&quot;, lineNum, err)
-		}
-
-		// 8. UPDATE対象がなければINSERTする
-		if rowsAffected == 0 {
-			_, err = db.NamedExec(`
-				INSERT INTO products (product_code, product_name, category_id, supplier_id, status_id, unit_price)
-				VALUES (:product_code, :product_name, :category_id, :supplier_id, :status_id, :unit_price)`,
-				row,
-			)
-			if err != nil {
-				log.Fatalf(&quot;行 %d: productsの登録に失敗: %v&quot;, lineNum, err)
-			}
+			log.Fatalf(&quot;行 %d: productsのUPSERTに失敗: %v&quot;, lineNum, err)
 		}

 		if (lineNum-1)%1000 == 0 {
</code></pre>
<p>:::
実行してみましょう。</p>
<pre><code>CSV読み込み完了: 500000 行
進捗: 1000 / 500000 行 (636 行/秒)
進捗: 2000 / 500000 行 (642 行/秒)
進捗: 3000 / 500000 行 (658 行/秒)
進捗: 4000 / 500000 行 (661 行/秒)
進捗: 5000 / 500000 行 (652 行/秒)
(略)
進捗: 497000 / 500000 行 (650 行/秒)
進捗: 498000 / 500000 行 (650 行/秒)
進捗: 499000 / 500000 行 (650 行/秒)
進捗: 500000 / 500000 行 (650 行/秒)
完了: 成功 500000 行, エラー 0 行 (所要時間: 12m48.924974166s)
</code></pre>
<p>この修正だけで10分程度まで早くすることができました。</p>
<h3>bulk化する</h3>
<p>upsertに変更して50万回までSQLの実行回数を削減できました。
さらにSQLの実行回数を削減するためにSQLをbulk化してみます。</p>
<p>bulk化とはDBに対して複数のレコードに対する操作を1つのSQLにまとめて実行することを言います。
以下のUPSERT化したSQLはいまだ50万回叩かれています。</p>
<pre><code class="language-sql">INSERT INTO products (
  product_code,
  product_name,
  category_id,
  supplier_id,
  status_id,
  unit_price
)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE
  product_name = VALUES(product_name),
  category_id  = VALUES(category_id),
  supplier_id  = VALUES(supplier_id),
  status_id    = VALUES(status_id),
  unit_price   = VALUES(unit_price)
</code></pre>
<p>このSQLを1行ずつ入れていくのではなく、ある程度のレコード数で固めてから送ることで
SQLの実行回数を減らせるわけです。
今回は1000レコード分ずつSQLをまとめて送ることにしてみましょう。
すると500000/1000=500回までSQLの実行回数を削減できます。</p>
<table>
<thead>
<tr>
<th>#</th>
<th>クエリ</th>
<th align="right">ループ前(回)</th>
<th align="right">ループ中(回)</th>
<th align="right">合計(回)</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td><code>SELECT * FROM categories</code></td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">1</td>
</tr>
<tr>
<td>2</td>
<td><code>SELECT * FROM suppliers</code></td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">1</td>
</tr>
<tr>
<td>3</td>
<td><code>SELECT * FROM statuses</code></td>
<td align="right">1</td>
<td align="right">0</td>
<td align="right">1</td>
</tr>
<tr>
<td>4</td>
<td><code>INSERT INTO products (...) VALUES (...), (...), ... ON DUPLICATE KEY UPDATE ...</code></td>
<td align="right">0</td>
<td align="right">50万ループ / 1000 = 500</td>
<td align="right">500</td>
</tr>
<tr>
<td></td>
<td>合計</td>
<td align="right">3</td>
<td align="right">500</td>
<td align="right">503</td>
</tr>
</tbody></table>
<p>どれくらい固めるかを表す数値をバッチサイズと呼びますが、この場合バッチサイズは1000となります。</p>
<p>:::details バルクUPSERT化の差分</p>
<pre><code class="language-diff">diff --git a/main.go b/main.go
index 0da4db0..daf2689 100644
--- a/main.go
+++ b/main.go
@@ -80,8 +80,8 @@ func main() {
 		statusMap[s.Code] = s.ID
 	}

-	importStart := time.Now()
-
+	// code → id 変換してProductRowスライスを構築
+	var rows []ProductRow
 	for i, product := range products {
 		// 2. 読んでない行があれば1行読み取る、なければ終了
 		lineNum := i + 2
@@ -104,16 +104,29 @@ func main() {
 			log.Fatalf(&quot;行 %d: status_code %q の検索に失敗&quot;, lineNum, product.StatusCode)
 		}

-		row := ProductRow{
+		// 6. ProductRowに変換
+		rows = append(rows, ProductRow{
 			ProductCode: product.ProductCode,
 			ProductName: product.ProductName,
 			CategoryID:  categoryID,
 			SupplierID:  supplierID,
 			StatusID:    statusID,
 			UnitPrice:   product.UnitPrice,
+		})
+	}
+	fmt.Printf(&quot;変換完了: %d 行\n&quot;, len(rows))
+
+	// バルクUPSERT（1000行ずつ）
+	const batchSize = 1000
+	importStart := time.Now()
+
+	for i := 0; i &lt; len(rows); i += batchSize {
+		end := i + batchSize
+		if end &gt; len(rows) {
+			end = len(rows)
 		}
+		batch := rows[i:end]

-		// 6. UPSERT（INSERT or UPDATE）を実行する
 		_, err := db.NamedExec(`
 			INSERT INTO products (
 				product_code, product_name, category_id, supplier_id, status_id, unit_price
@@ -126,17 +139,16 @@ func main() {
 				supplier_id  = VALUES(supplier_id),
 				status_id    = VALUES(status_id),
 				unit_price   = VALUES(unit_price)`,
-			row,
+			batch,
 		)
 		if err != nil {
-			log.Fatalf(&quot;行 %d: productsのUPSERTに失敗: %v&quot;, lineNum, err)
+			log.Fatalf(&quot;バッチ %d-%d: UPSERTに失敗: %v&quot;, i+1, end, err)
 		}

-		if (lineNum-1)%1000 == 0 {
-			rate := float64(lineNum-1) / time.Since(importStart).Seconds()
-			fmt.Printf(&quot;進捗: %d / %d 行 (%.0f 行/秒)\n&quot;, lineNum-1, len(products), rate)
+		if end%10000 == 0 || end == len(rows) {
+			rate := float64(end) / time.Since(importStart).Seconds()
+			fmt.Printf(&quot;進捗: %d / %d 行 (%.0f 行/秒)\n&quot;, end, len(rows), rate)
 		}
-		// 8. 2に戻る
 	}

 	fmt.Printf(&quot;完了: %d 行 (所要時間: %v)\n&quot;, len(products), time.Since(importStart))
</code></pre>
<p>:::</p>
<p>では実行してみましょう。</p>
<pre><code>CSV読み込み完了: 500000 行
変換完了: 500000 行 (エラー 0 行)
進捗: 10000 / 500000 行 (56843 行/秒)
進捗: 20000 / 500000 行 (72234 行/秒)
進捗: 30000 / 500000 行 (78721 行/秒)
進捗: 40000 / 500000 行 (73047 行/秒)
進捗: 50000 / 500000 行 (76230 行/秒)
進捗: 60000 / 500000 行 (78932 行/秒)
進捗: 70000 / 500000 行 (81193 行/秒)
(略)
進捗: 460000 / 500000 行 (83997 行/秒)
進捗: 470000 / 500000 行 (83998 行/秒)
進捗: 480000 / 500000 行 (84197 行/秒)
進捗: 490000 / 500000 行 (83433 行/秒)
進捗: 500000 / 500000 行 (83642 行/秒)
完了: 成功 500000 行, エラー 0 行 (所要時間: 5.977838667s)
</code></pre>
<p>わずか6秒程度で完了するようになりました！
元々50分かかっていた処理だと考えると、かなり高速化されたのではないかと思います。</p>
<h2>改善後の実際のFACTORYでのDBの負荷状況</h2>
<p>改善の結果を先述のDatabase InsightsのAASで確認してみましょう。
<img src="/assets/blog/authors/uehara/2026-03-31-improvement-importing-data-performance/after.png" alt=""></p>
<p>赤枠がマスタ反映時に実行されているSQLになりますが、</p>
<ol>
<li>改善前に負荷がかかっているSQLとして挙げられていたSELECTがなくなって、ボトルネックを解消した</li>
<li>INSERTはまだいるが実行回数が減り、AASも減った</li>
</ol>
<p>このように実際のFACTORYのDBの計測からも負荷が減ったことがわかります。
この改善の結果、5分程度で反映が終わるようになりました！
改善前は90分かかっていたと考えるとめちゃくちゃ高速化できました！</p>
<h2>まとめ</h2>
<p>今回の改善の変遷をまとめると以下の通りです。</p>
<table>
<thead>
<tr>
<th>ステップ</th>
<th>施策</th>
<th align="right">所要時間</th>
<th align="right">SQL実行回数(最大)</th>
</tr>
</thead>
<tbody><tr>
<td>改善前</td>
<td>-</td>
<td align="right">47分</td>
<td align="right">250万回</td>
</tr>
<tr>
<td>1. オンメモリキャッシュ</td>
<td>SELECTをメモリ参照に置換</td>
<td align="right">20分</td>
<td align="right">100万回</td>
</tr>
<tr>
<td>2. UPSERT化</td>
<td>UPDATE+INSERTを1クエリに統合</td>
<td align="right">13分</td>
<td align="right">50万回</td>
</tr>
<tr>
<td>3. バルクUPSERT化</td>
<td>1000行ずつまとめて実行</td>
<td align="right">6秒</td>
<td align="right">500回</td>
</tr>
</tbody></table>
<p>パフォーマンスチューニングでとった方法はどれもISUCONではよく出てくる典型的な対応策です。
まさかISUCONで培った知識を使って業務でこれほどまでの結果を出せるとは思いもしませんでした。
ISUCONは業務でも役に立ちます。
これからもISUCONで腕を磨きつつ、業務でのボトルネックを改善していきたいと考えています。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uehara/2026-03-31-improvement-importing-data-performance/thumb.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[MediaPipe Instant Motion Trackingを用いた、AndroidにおけるARエフェクトの実現]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-27-korenani-guide-mediapipe-ar/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-27-korenani-guide-mediapipe-ar/</guid>
            <pubDate>Fri, 27 Mar 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[MediaPipeを用いてARエフェクトをAndroidで実装する方法を技術的に解説]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1>はじめに</h1>
<p>はじめまして。
KINTO テクノロジーズで KINTO Unlimited Android アプリを開発している JR.Liang です。</p>
<p>本記事では、KINTO Unlimited アプリにて提供する「これなにガイド」スキャン機能の AR エフェクトについて、Android における技術的な検証を紹介します。
特に MediaPipe のソリューションを用いて幅広い Android デバイスで AR エフェクトを実現した実装にフォーカスします。</p>
<h2>これなにガイドとは</h2>
<p>「これなにガイド」は AR（拡張現実）を活用して、車内スイッチの用途や使い方をテキストと動画で案内する機能です。紹介動画をご覧ください。
<a href="https://youtube.com/watch?v=E8zfNzuHr7g&embeds_referring_euri=https%3A%2F%2Fcorp.kinto-jp.com%2F&source_ve_path=MjM4NTE">https://youtube.com/watch?v=E8zfNzuHr7g&amp;embeds_referring_euri=https%3A%2F%2Fcorp.kinto-jp.com%2F&amp;source_ve_path=MjM4NTE</a>
上記の紹介動画は iOS アプリでの動作を示しています。スイッチ上に表示された黄色の丸 🟡 が、AR 技術で実現した仮想コンテンツです。</p>
<p>機能全体の仕組みは以下の流れです。本記事では 3 番目（描画）に関する内容を扱います。</p>
<pre><code>1. アプリのカメラを起動、カメラ画像を取得
2. 機械学習における物体認識を用いて、車内のスイッチを検出
3. 検出した座標を元に、ボタンとテキストをフレーム上に描画
4. ボタンをタップして、当該スイッチのテキストと動画を表示
</code></pre>
<h2>Android AR 技術検証の経緯</h2>
<p>当初の Android 版「これなにガイド」のスキャン機能では、Canvas を利用して毎フレーム検出される座標に描画する実装でした。そのため検出の時間差により、スマホ（カメラ）を動かすと描画のズレが生じていました。
<img src="/assets/blog/authors/JR.Liang/v1.gif" alt="v1">
<em>2D Canvas</em>
幸い、MediaPipe のソリューションである Instant Motion Tracking モジュールで<strong>素早くかつ安定した</strong> AR エフェクトを実現できることがわかり、Android への導入を検証しました。
<img src="/assets/blog/authors/JR.Liang/v2.gif" alt="v2">
<em>3D OpenGL</em></p>
<h1>MediaPipe Instant Motion Tracking</h1>
<p>MediaPipe は Google が開発したオープンソースの ML フレームワークで、顔検出・手のトラッキング・姿勢推定などリアルタイム映像処理のソリューションを提供します。</p>
<p>その中の Instant Motion Tracking は、現実世界のシーン上に 3D 仮想コンテンツをリアルタイムで正確に配置できる AR トラッキング機能です。初期化や厳密なキャリブレーションが不要で、静止面や動いている面の上にコンテンツを置くことが可能です。
@<a href="https://github.com/google-ai-edge/mediapipe/blob/master/docs/solutions/instant_motion_tracking.md">card</a></p>
<h2>Android + MediaPipe AR アーキテクチャ</h2>
<pre><code class="language-mermaid">graph TB
    A(Android CameraX) --&gt; |Camera Frame| B(Instant Motion Tracking)
    B --&gt; |Camera Image| C(TensorFlow Object Detection)
    C --&gt; |Detections Information| B(Instant Motion Tracking)
    B --&gt; |Output Stream| D(Android Surface Rendering)
</code></pre>
<p>CameraX で取得したフレームを Instant Motion Tracking に渡し、TensorFlow Lite で物体検出した情報を元に AR コンテンツを描画・追従させるパイプラインです。</p>
<h2>MediaPipe ライブラリの作成</h2>
<p>MediaPipe では Bazel を使用してパッケージをビルドします。Android に適合する AAR として書き出してアプリに組み込みます。
<a href="https://chuoling.github.io/mediapipe/getting_started/android_archive_library.html">https://chuoling.github.io/mediapipe/getting_started/android_archive_library.html</a></p>
<p>AAR をビルドする <code>BUILD</code> ファイルを作成し、<code>instant_motion_tracking</code> を基盤とした定義を記述します。</p>
<pre><code>load(&quot;//mediapipe/java/com/google/mediapipe:mediapipe_aar.bzl&quot;, &quot;mediapipe_aar&quot;)

mediapipe_aar(
    name = &quot;mediapipe_ar&quot;,
    calculators = [&quot;//mediapipe/graphs/instant_motion_tracking:instant_motion_tracking_deps&quot;]
)
</code></pre>
<p>MediaPipe は C++ が中核のため、C++ ランタイムである libc++_shared.so を AAR に同梱する必要があります。
<a href="https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/third_party/BUILD#L399-L403">https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/third_party/BUILD#L399-L403</a></p>
<p>また Instant Motion Tracking では画像処理ライブラリ OpenCV を利用し、AR トラッキングを行います。
<a href="https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/WORKSPACE#L649-L655">https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/WORKSPACE#L649-L655</a></p>
<p>上記サードパーティのライブラリを含めて、以下のコマンドで AAR をビルドします。</p>
<pre><code>bazel build -c opt --strip=ALWAYS \
    --host_crosstool_top=@bazel_tools//tools/cpp:toolchain \
    --fat_apk_cpu=arm64-v8a \
    --linkopt=-Wl,-z,max-page-size=16384 \
    //path/to/the/aar/build/mediapipe_ar:mediapipe_ar.aar
</code></pre>
<ul>
<li>市場に流通している Android デバイスは主に arm64-v8a アーキテクチャのため、AAR のサイズを抑える目的で <code>fat_apk_cpu=arm64-v8a</code> にします。</li>
<li>C++ ライブラリの 16KB page-size に対応するため、<code>max-page-size=16384</code> を追加します。</li>
</ul>
<p>また AAR を利用するにはグラフ構造を定義するファイル（<code>binarypb</code>）が必要です。</p>
<pre><code>bazel build -c opt mediapipe/graphs/instant_motion_tracking:instant_motion_tracking.binarypb
</code></pre>
<h1>Instant Motion Tracking の導入</h1>
<p>AAR をアプリに組み込んで、Android 側の実装を解説していきます。
下記は AAR に組み込んだ instant_motion_tracking の全体構造です。</p>
<p><img src="/assets/blog/authors/JR.Liang/graph.png" alt="Graph"></p>
<h2>instant_motion_tracking.pbtxt の構成</h2>
<p>グラフ定義ファイル <code>instant_motion_tracking.pbtxt</code> は、Calculator（処理ノード）・入出力ストリーム・サイドパケットの 3 要素で構成されます。</p>
<h3>Calculator</h3>
<p>各 Calculator がパイプライン上でどの処理を担うかを示します。</p>
<table>
<thead>
<tr>
<th>Calculator</th>
<th>役割</th>
</tr>
</thead>
<tbody><tr>
<td><strong>ImageTransformationCalculator</strong></td>
<td>カメラフレームを 320×320（FIT）にリサイズ。物体検出モデルの入力サイズに合わせる</td>
</tr>
<tr>
<td><strong>GpuBufferToImageFrameCalculator</strong></td>
<td>GPU テクスチャを CPU の <code>ImageFrame</code> に変換。TensorFlow Lite 推論に使用</td>
</tr>
<tr>
<td><strong>StickerManagerCalculator</strong></td>
<td>Sticker Proto をパースし、初期アンカーの座標・回転・スケール・レンダリング種別に分解</td>
</tr>
<tr>
<td><strong>RegionTrackingSubgraph</strong></td>
<td>ボックストラッキングでアンカー位置を追従。内部に <code>TrackedAnchorManagerCalculator</code>（アンカー管理）と <code>BoxTrackingSubgraphGpu</code>（GPU トラッキング）を持つ</td>
</tr>
<tr>
<td><strong>MatricesManagerCalculator</strong></td>
<td>トラッキング結果・回転・スケール・FOV・アスペクト比から OpenGL 用 4×4 モデル行列を生成</td>
</tr>
<tr>
<td><strong>GlAnimationOverlayCalculator</strong></td>
<td>モデル行列とテクスチャを用いて、元のカメラフレーム上に AR コンテンツを OpenGL で描画し <code>output_video</code> として出力</td>
</tr>
</tbody></table>
<h3>input_stream / output_stream</h3>
<p><code>input_stream</code> はフレームごとに Android 側から送信するデータ、<code>output_stream</code> はグラフの処理結果です。</p>
<table>
<thead>
<tr>
<th>ストリーム名</th>
<th>C++ 型</th>
<th>方向</th>
<th>用途</th>
</tr>
</thead>
<tbody><tr>
<td><code>input_video</code></td>
<td>GpuBuffer</td>
<td>Input</td>
<td>カメラフレーム</td>
</tr>
<tr>
<td><code>sticker_proto_string</code></td>
<td>String(Serialized Proto)</td>
<td>Input</td>
<td>ステッカーの座標・スケール等（Sticker Proto）</td>
</tr>
<tr>
<td><code>sticker_sentinels</code></td>
<td>vector<int></td>
<td>Input</td>
<td>座標をリセットするステッカー ID の配列</td>
</tr>
<tr>
<td><code>gif_textures</code></td>
<td>vector<AssetTextureFormat></td>
<td>Input</td>
<td>AR コンテンツの Bitmap テクスチャ配列</td>
</tr>
<tr>
<td><code>gif_aspect_ratios</code></td>
<td>vector<float></td>
<td>Input</td>
<td>各テクスチャのアスペクト比</td>
</tr>
<tr>
<td><code>output_video</code></td>
<td>GpuBuffer</td>
<td>Output</td>
<td>AR 描画済みフレーム</td>
</tr>
</tbody></table>
<h3>input_side_packet</h3>
<p><code>input_side_packet</code> は初期化時に一度だけ渡す定数で、グラフ実行中は変化しません。</p>
<table>
<thead>
<tr>
<th>パケット名</th>
<th>用途</th>
</tr>
</thead>
<tbody><tr>
<td><code>vertical_fov_radians</code></td>
<td>カメラの垂直 FOV（ラジアン）</td>
</tr>
<tr>
<td><code>aspect_ratio</code></td>
<td>カメラのアスペクト比</td>
</tr>
<tr>
<td><code>width</code> / <code>height</code></td>
<td>カメラ解像度</td>
</tr>
<tr>
<td><code>gif_texture</code></td>
<td>デフォルトテクスチャ（1x1 プレースホルダ）</td>
</tr>
<tr>
<td><code>gif_asset_name</code></td>
<td>AR テクスチャ描画用のポリゴンメッシュ（<code>.obj</code>）ファイル名</td>
</tr>
</tbody></table>
<p>Android への導入に当たって、公式サンプルのコードを参考にします。
<a href="https://github.com/google-ai-edge/mediapipe/tree/master/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking">https://github.com/google-ai-edge/mediapipe/tree/master/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking</a></p>
<h2>1. 初期化</h2>
<p>MediaPipe を使用する前に、ネイティブライブラリの読み込みとアセットマネージャーの初期化が必要です。</p>
<pre><code class="language-kotlin">companion object {
    init {
        System.loadLibrary(&quot;mediapipe_jni&quot;)
        System.loadLibrary(&quot;opencv_java4&quot;)
    }
}

// onCreate 相当の処理
AndroidAssetUtil.initializeNativeAssetManager(context)
</code></pre>
<ul>
<li><code>mediapipe_jni</code>: MediaPipe のコア処理を行う JNI ライブラリ</li>
<li><code>opencv_java4</code>: AR トラッキングに使用する OpenCV ライブラリ</li>
<li><code>initializeNativeAssetManager</code>: ネイティブコードからアセット（binarypb 等）にアクセスするために必要</li>
</ul>
<h2>2. カメラを起動する</h2>
<p>公式サンプルを参考に、以下の順序でパイプラインを構築します。
<strong>データフロー：</strong> CameraX → ExternalTextureConverter → FrameProcessor → SurfaceView</p>
<h3>2.1 EGL 環境と FrameProcessor の初期化</h3>
<pre><code class="language-kotlin">val eglManager = EglManager(null)
val frameProcessor = FrameProcessor(
    context,
    eglManager.nativeContext,
    &quot;instant_motion_tracking.binarypb&quot;,
    &quot;input_video&quot;,
    &quot;output_video&quot;
).apply {
    videoSurfaceOutput.setFlipY(true)
    setInputSidePackets(
        mapOf(
            &quot;gif_asset_name&quot; to packetCreator.createString(&quot;gif.obj.uuu&quot;),
            &quot;vertical_fov_radians&quot; to packetCreator.createFloat32(fovRadians),
            &quot;aspect_ratio&quot; to packetCreator.createFloat32(resolution.width.toFloat() / resolution.height.toFloat()),
            &quot;width&quot; to packetCreator.createInt32(resolution.width),
            &quot;height&quot; to packetCreator.createInt32(resolution.height),
            &quot;gif_texture&quot; to packetCreator.createRgbaImageFrame(createBitmap(1, 1))
        )
    )
}
</code></pre>
<ul>
<li><code>EglManager</code>: OpenGL ES の EGL コンテキストを作成・管理。MediaPipe のグラフ内 GPU Calculator（<code>GlAnimationOverlayCalculator</code> 等）が OpenGL で描画するために必要</li>
<li><code>FrameProcessor</code>: EGL コンテキストを受け取り、グラフの読み込み・入出力ストリームの管理・フレームごとのグラフ実行を行う<ul>
<li><code>instant_motion_tracking.binarypb</code>: <code>.pbtxt</code> を Bazel でコンパイルしたグラフ定義バイナリ</li>
<li><code>input_video</code>: MediaPipe グラフへカメラフレームを入力</li>
<li><code>output_video</code>: グラフで処理（AR 描画など）された映像を出力</li>
<li><code>videoSurfaceOutput.setFlipY(true)</code>: OpenGL とカメラの Y 軸方向が逆のため、出力映像を上下反転して正しい向きにする</li>
<li><code>setInputSidePackets</code>: グラフの <code>input_side_packet</code> に対応する定数をまとめて設定。カメラの FOV・アスペクト比・解像度など、グラフ実行中に変化しない値を初期化時に一度だけ渡す</li>
</ul>
</li>
<li><code>gif_asset_name</code> は AR テクスチャを描画するための<strong>ポリゴンメッシュ（頂点データ）</strong>、ここでは公式サンプルの<code>gif.obj.uuu</code>を利用</li>
</ul>
<h3>2.2 カメラ映像の変換パイプライン構築</h3>
<pre><code class="language-kotlin">val externalTextureConverter = ExternalTextureConverter(eglManager.context, 2).apply {
    setFlipY(true)
    setConsumer(frameProcessor)
    setDestinationSize(resolution.width, resolution.height)
}
val cameraHelper = object : CameraXPreviewHelper() {
    override fun getCameraCharacteristics(context: Context?, lensFacing: Int?) = cameraCharacteristics
}.apply {
    setOnCameraStartedListener(onCameraStartedListener)
    startCamera(
        context,
        lifecycleOwner,
        CameraHelper.CameraFacing.BACK,
        externalTextureConverter.surfaceTexture,
        Size(resolution.height, resolution.width)
    )
}
</code></pre>
<ul>
<li><code>ExternalTextureConverter</code>: カメラの <code>GL_EXTERNAL_OES</code> テクスチャを MediaPipe が処理できる標準テクスチャに変換<ul>
<li><code>setFlipY(true)</code>: カメラ映像の上下反転を補正</li>
<li><code>setDestinationSize(resolution.width, resolution.height)</code>: パイプラインの処理サイズはポートレート座標（例: <code>960×1280</code>）で指定</li>
</ul>
</li>
<li><code>CameraXPreviewHelper</code>: CameraX でバックカメラを起動し、Converter の SurfaceTexture に出力<ul>
<li><code>startCamera(targetSize = Size(resolution.height, resolution.width))</code>: CameraX はセンサー座標（ランドスケープ）を期待するため、width と height を入れ替えて渡す</li>
</ul>
</li>
</ul>
<p>公式サンプルでは <code>CameraXPreviewHelper</code> をそのまま使用し、内部で <code>CameraManager</code> からカメラ特性を取得します。
<a href="https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/mediapipe/java/com/google/mediapipe/components/CameraXPreviewHelper.java#L558-L560">https://github.com/google-ai-edge/mediapipe/blob/v0.10.32/mediapipe/java/com/google/mediapipe/components/CameraXPreviewHelper.java#L558-L560</a>
本実装では <code>getCameraCharacteristics</code> をオーバーライドし、事前に取得済みの <code>CameraCharacteristics</code> を直接渡します。これにより FOV やアスペクト比の算出に使うカメラ情報を、アプリ側で一元管理できます。</p>
<h3>2.3 出力先SurfaceViewの設定</h3>
<pre><code class="language-kotlin">SurfaceView(context).apply {
    holder.addCallback(object : SurfaceHolder.Callback {
        override fun surfaceCreated(holder: SurfaceHolder) {
            frameProcessor.videoSurfaceOutput.setSurface(holder.surface)
        }

        override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
            val displaySize = cameraHelper.computeDisplaySizeFromViewSize(Size(width, height))
            val (displayWidth, displayHeight) = if (cameraHelper.isCameraRotated) {
                displaySize.height to displaySize.width
            } else {
                displaySize.width to displaySize.height
            }
            externalTextureConverter.setDestinationSize(displayWidth, displayHeight)
        }

        override fun surfaceDestroyed(holder: SurfaceHolder) {
            frameProcessor.videoSurfaceOutput.setSurface(null)
        }
    })
}
</code></pre>
<ul>
<li><code>SurfaceHolder.Callback</code>: SurfaceView のライフサイクルに応じて FrameProcessor の出力先を管理<ul>
<li><code>surfaceCreated</code>: FrameProcessor の出力先として Surface を設定</li>
<li><code>surfaceChanged</code>: 画面回転・サイズ変更時に出力解像度を調整</li>
<li><code>surfaceDestroyed</code>: リソース解放</li>
</ul>
</li>
</ul>
<h2>3. 検出座標をグラフに渡す</h2>
<p>物体検出（TensorFlow Lite 等）で得られた座標を MediaPipe グラフに渡し、AR コンテンツを配置します。</p>
<h3>3.1 グラフから変換済み画像を取得</h3>
<p>MediaPipe グラフ内で <code>ImageTransformationCalculator</code> と <code>GpuBufferToImageFrameCalculator</code> によって変換された画像を <code>addPacketCallback</code> で受け取り、物体検出に使用します。</p>
<pre><code class="language-kotlin">frameProcessor.addPacketCallback(&quot;transformed_input_video_cpu&quot;) { packet -&gt;
    packet ?: return@addPacketCallback
    // 変換済み画像を物体検出（TensorFlow Lite）に渡す
    val bitmap = PacketGetter.getBitmapFromRgba(packet)
    objectDetector.detect(bitmap) { detections -&gt;
        // 検出結果を処理
    }
}
</code></pre>
<ul>
<li><code>transformed_input_video_cpu</code>: 変換後の画像を出力するストリーム名</li>
</ul>
<h3>3.2 座標の正規化</h3>
<p>物体検出結果のピクセル座標を、MediaPipe が期待する正規化座標に変換します。</p>
<pre><code class="language-kotlin">// ピクセル座標 → 正規化座標 (0.0〜1.0)
val normalizedX = pixelX / imageWidth.toFloat()
val normalizedY = pixelY / imageHeight.toFloat()
</code></pre>
<h3>3.3 Sticker Proto の構造</h3>
<p>Instant Motion Tracking では、AR オブジェクトの位置情報を Protocol Buffers 形式で定義します。</p>
<pre><code class="language-protobuf">message Sticker {
  int32 id = 1;        // ユニークID
  float x = 2;         // 正規化X座標 (0.0〜1.0)
  float y = 3;         // 正規化Y座標 (0.0〜1.0)
  float rotation = 4;  // 回転角度
  float scale = 5;     // スケール
  int32 render_id = 6; // レンダリングID
}

message StickerRoll {
  repeated Sticker sticker = 1;
}
</code></pre>
<h3>3.4 フレームごとにパケットを送信</h3>
<p><code>setOnWillAddFrameListener</code> を使用して、各フレーム処理前に検出座標をグラフへ送信します。</p>
<pre><code class="language-kotlin">frameProcessor.setOnWillAddFrameListener { timestamp -&gt;
    with(frameProcessor.graph) {
        // 検出された物体の座標情報をパケットとして送信
        val stickerRoll = StickerRoll.newBuilder()
            .addAllSticker(detectedObjects.map { detection -&gt;
                Sticker.newBuilder()
                    .setId(detection.id)
                    .setX(detection.normalizedX)  // 0.0〜1.0
                    .setY(detection.normalizedY)  // 0.0〜1.0
                    .setScale(detection.scale)
                    .build()
            })
            .build()

        val stickersPacket = packetCreator.createSerializedProto(stickerRoll)
        addPacketToInputStream(&quot;sticker_proto_string&quot;, stickersPacket, timestamp)
    }
}
</code></pre>
<ul>
<li><code>FrameProcessor.setOnWillAddFrameListener</code>: 各フレームがグラフに送られる直前に呼ばれるコールバック</li>
<li><code>FrameProcessor.graph.addPacketToInputStream</code>: 入力ストリームにパケットを追加</li>
<li><code>sticker_proto_string</code>: グラフ定義で指定された入力ストリーム名</li>
</ul>
<h2>4. テクスチャ（Bitmap）の描画と送信</h2>
<p>位置情報と同時に、AR コンテンツとして描画する Bitmap テクスチャもグラフに渡します。</p>
<h3>4.1 Bitmap テクスチャの生成</h3>
<p>検出された各スイッチに対して、丸アイコンとラベルテキストを含む Bitmap を生成します。</p>
<pre><code class="language-kotlin">val bitmap = createBitmap(width.toInt(), height.toInt()).apply {
    with(Canvas(this)) {
        concat(Matrix().apply {
            preScale(-1.0f, 1.0f, width / 2f, height / 2f) // X軸を反転して描画
        })
        drawCircle(circleX, circleY, CIRCLE_RADIUS, circlePaint)
        drawRect(rectLeft, rectTop, rectRight, rectBottom, backgroundPaint)
    }
}
</code></pre>
<p><code>Matrix().preScale(-1.0f, 1.0f)</code> で Bitmap を左右反転しています。以下の IMU 行列に合わせるためです。</p>
<pre><code class="language-cpp">float imu_matrix[9] = {
  -1.0f, 0.0f, 0.0f,  // X軸 → 反転(-X)
   0.0f, 0.0f, 1.0f,  // Y軸 → Z軸へ
   0.0f, 1.0f, 0.0f   // Z軸 → Y軸へ
};
</code></pre>
<p>この行列は OpenGL モデル行列（4x4）の回転成分として使われ、Y/Z 軸の入れ替えと X 軸反転でテクスチャをカメラ平面に平行に固定します。</p>
<p>本来はデバイスの IMU センサーから回転行列を受け取り、端末の傾きに追従させます。
<a href="https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L218-L220">https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L218-L220</a>
本実装では固定値にすることで<strong>常にカメラ正面を向く</strong>（ビルボード効果）ようにし、<code>(0,0)</code> の <code>-1.0</code> による X 軸反転を Bitmap 側の <code>preScale(-1.0f, 1.0f)</code> で打ち消します。</p>
<h3>4.2 テクスチャの送信</h3>
<pre><code class="language-kotlin">// テクスチャ画像（Bitmap配列）
val texturesPacket = packetCreator.createRgbaImageFrameVector(
    renderStickers.map { it.bitmap }.toTypedArray()
)
addPacketToInputStream(&quot;gif_textures&quot;, texturesPacket, timestamp)
// アスペクト比（テクスチャの縦横比）
val aspectRatiosPacket = packetCreator.createFloat32Vector(
    renderStickers.map { it.aspectRatio }.toFloatArray()
)
addPacketToInputStream(&quot;gif_aspect_ratios&quot;, aspectRatiosPacket, timestamp)
</code></pre>
<ul>
<li><code>PacketCreator.createRgbaImageFrameVector</code>: 複数の Bitmap を RGBA 形式のパケットに変換</li>
<li><code>gif_textures</code>: テクスチャ画像の入力ストリーム</li>
<li><code>gif_aspect_ratios</code>: 各テクスチャのアスペクト比（正しいスケーリングに必要）</li>
</ul>
<p>公式サンプルでは <code>createRgbaImageFrame</code> を使用して<strong>単一のテクスチャ</strong>をグラフに渡します。
<a href="https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L608-L610">https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L608-L610</a>
本実装では、複数の検出オブジェクトに対応するため <code>createRgbaImageFrameVector</code> で<strong>複数テクスチャを同時に送信</strong>し、<code>gif_aspect_ratios</code> も <code>createFloat32Vector</code> で<strong>各テクスチャに対応するアスペクト比の配列</strong>を渡すよう拡張します。これにより、検出された各スイッチに異なるラベル（テキスト付きBitmap）を正しい縦横比で表示できます。</p>
<p>ここまでで AR コンテンツをカメラ上に表示できました。</p>
<h2>5. 座標の更新</h2>
<p>トラッキング中のステッカー座標を更新するには、新しい座標を持つ <code>sticker_proto_string</code> と、リセット対象の ID を含む <code>sticker_sentinels</code> を同一 timestamp で送信します。<code>TrackedAnchorManagerCalculator</code> が該当 ID のトラッキングボックスを破棄し、新しい座標でトラッキングを再開します。</p>
<pre><code class="language-kotlin">// 更新した座標で Sticker Proto を再構築
val stickersPacket = packetCreator.createSerializedProto(stickerRoll)
addPacketToInputStream(&quot;sticker_proto_string&quot;, stickersPacket, timestamp)

// リセット対象のステッカー ID を送信
val stickerSentinels = packetCreator.createInt32Vector(updateIds)
addPacketToInputStream(&quot;sticker_sentinels&quot;, stickerSentinels, timestamp)
</code></pre>
<p>公式サンプルでは <code>sticker_sentinel</code> で<strong>単一のステッカー ID</strong> を送信します。
<a href="https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L342-L344">https://github.com/google-ai-edge/mediapipe/blob/0.10.32/mediapipe/examples/android/src/java/com/google/mediapipe/apps/instantmotiontracking/MainActivity.java#L342-L344</a>
本実装では <code>sticker_sentinels</code> として <code>createInt32Vector</code> で<strong>複数のステッカー ID を配列</strong>で渡すよう拡張し、物体検出で座標が更新された複数のステッカーを同時にリセットできるようにします。</p>
<h1>最後に</h1>
<p>以上が MediaPipe Instant Motion Tracking を用いた技術的な実装解説でした。決して容易に導入できる手法ではありませんが、本機能の要件に対して Android に最も適した解決策だと考えています。
以前に ARCore の検証も行いましたが、ARCore は SLAM 技術による事前の 3D マッピングに時間を要し、<strong>素早くかつ安定した</strong> AR エフェクトの実現には適さなかったため、検証を断念しました。
両フレームワークの違いを以下にまとめます。AR 技術の検討で参考になれば幸いです。</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>Instant Motion Tracking</th>
<th>ARCore</th>
</tr>
</thead>
<tbody><tr>
<td>仕組み</td>
<td>2D ボックストラッキング + OpenGL 描画</td>
<td>環境マッピング + 平面検出（SLAM）</td>
</tr>
<tr>
<td>デバイス要件</td>
<td>OpenGL ES 対応であれば動作</td>
<td>ARCore 対応デバイスのみ（Google 認定必須）</td>
</tr>
<tr>
<td>安定性</td>
<td>検出座標に依存するため補正が必要</td>
<td>空間認識が高精度で安定</td>
</tr>
<tr>
<td>導入コスト</td>
<td>Bazel ビルド・C++ Calculator のカスタマイズが必要</td>
<td>SDK 導入のみで比較的容易</td>
</tr>
<tr>
<td>オープンソース</td>
<td>あり（Apache 2.0）</td>
<td>なし（プロプライエタリ）</td>
</tr>
<tr>
<td>カスタマイズ性</td>
<td>Calculator の追加・変更で柔軟に拡張可能</td>
<td>SDK の API 範囲内に限定</td>
</tr>
<tr>
<td>パフォーマンス</td>
<td>軽量（2D トラッキングベースのため CPU/GPU 負荷が低い）</td>
<td>高負荷（環境の 3D 空間マッピングを常時実行）</td>
</tr>
<tr>
<td>学習コスト</td>
<td>高い（Bazel・C++・OpenGL・Protocol Buffers の知識が必要）</td>
<td>低い（Android SDK の知見で導入可能）</td>
</tr>
</tbody></table>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/JR.Liang/mediapipe.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[FDEが届ける「ニンベンのついた自働化」 ― AI Agent時代の新しい協業の形]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-23-AIエージェントによる業務の自働化/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-23-AIエージェントによる業務の自働化/</guid>
            <pubDate>Mon, 23 Mar 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[AI Agent（AIエージェント）の時代における「ニンベンのついた自働化」をキーワードに、KTCの現場事例やForward Deployed Engineer（FDE）という協業モデルを通じて、業務の知識を持つ人とAIの共創の形を紹介します]]></description>
            <content:encoded><![CDATA[<p>こんにちは！KINTOテクノロジーズ（以下、KTC）のAIファーストグループで、生成AIの社内活用推進を担当している和田です。普段は生成AIを使った業務価値の創出から、社内の教育研修、技術の手の内化まで、「AIを現場に届ける」仕事をしています。</p>
<p>今回お話ししたいのは、<strong>AI Agent（AIエージェント）</strong> というトレンドです。KTCのようなテックカンパニーの内側で何が起きているのか。そして、ITやAIの知識を持つ我々と、業務の知識を持つ方々（それは時によってメーカーの設計技術者さんだったり、販売店の営業さんだったりします）との「協業の形」がどう変わろうとしているのか。「ニンベンのついた自働化」というキーワードを軸に、お伝えしていきます。</p>
<hr>
<h2>1. はじめに ― なぜ今「エージェント」なのか</h2>
<p>生成AIの進化を振り返ると、大きく3つのフェーズがあったと考えています。</p>
<table>
<thead>
<tr>
<th>時期</th>
<th>フェーズ</th>
<th>特徴</th>
</tr>
</thead>
<tbody><tr>
<td>2022〜2023年</td>
<td>チャットAI</td>
<td>1問1答。「質問すれば答えてくれる」体験が広がる</td>
</tr>
<tr>
<td>2024年</td>
<td>RAG全盛期</td>
<td>RAG（Retrieval-Augmented Generation：社内データ等を検索しながら回答を生成する手法）で「自社の情報を知っているAI」が登場</td>
</tr>
<tr>
<td>2025年〜</td>
<td>AI Agent</td>
<td>AIが自ら考え、ツールを使い、複数ステップの仕事をこなす</td>
</tr>
</tbody></table>
<p><img src="/assets/blog/authors/s.wada/20260323/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%897.webp" alt="生成AIは「チャット」から「エージェント」へ"></p>
<p>Agentを実現するOSSの老舗であるLangChainをはじめ、エージェントという概念自体は2023年頃にはすでに存在していました。しかし当時は、LLMそのものの&quot;地頭&quot;がまだ追いついていませんでした。指示を正しく理解できない、途中で迷子になる、ツールの使い方を間違える ― そんな状態を覚えている方もいると思います。</p>
<p>ここ1〜2年でLLM（Large Language Model：大規模言語モデル）の精度が飛躍的に向上したことで、ようやくエージェントが「実用に耐える」レベルになってきました。これは毎日エージェントを使い、自身の業務を常に効率化し続けてきた私の実感です。</p>
<p>2026年の今、多くの企業がエージェント技術を「PoCから社会実装へ」と動き始めています。試すフェーズは終わり、業務に組み込むフェーズに入りつつある。だからこそ、「どう組み込むか」の設計思想が問われています。</p>
<hr>
<h2>2. 目指す姿 ― 「ニンベンのついた自働化」とはどんな状態か</h2>
<p>KTCが所属するトヨタグループでは昔から「自働化」という概念が大切にされています。「動」ではなく「働」。機械が異常を検知したら自ら止まり、不良を後工程に流さない。問題を顕在化させ、人が原因を究明し対処できる状態をつくる。人を機械の番人にせず、本来人間にしかできない判断や改善に集中させる。自動化の中に「人の知恵」を埋め込む思想です。</p>
<p>・・・とはいうものの、AIエージェントの時代における「ニンベンのついた自働化」とは、一体どんな状態でしょうか？</p>
<p>私はこう定義しています。</p>
<h3><strong>人間の役割が明確になっている</strong></h3>
<p>エージェントが作業している間、人はより創造的・判断的な仕事に集中できている。たとえば、エージェントがログ分析をしている間に、人間は対応方針の意思決定に集中する、といった状態です。</p>
<h3><strong>エージェントの「持ち物」が事前に整っている</strong></h3>
<p>必要な権限、参照すべきデータ、判断基準 ― これらを人間が先回りして渡している。エージェントに手待ちをさせない環境設計です。</p>
<h3><strong>「やってはいけないこと」の境界線が設計されている</strong></h3>
<p>例えば「データの参照はOK、削除はNG」「提案はするが、最終承認は必ず人間」といったガードレールが明確に引かれている。</p>
<h3><strong>業務を知る人がフロー全体をデザインしている</strong></h3>
<p>技術者だけでは、業務の「行間」は読めません。何年・何十年と積み上げてきたドメイン知識を持つ人が、AIとの協業設計に参加している状態です。</p>
<p>この4つが揃ったとき、AIは「勝手に動く怖いもの」ではなく、「信頼して任せられるチームメイト」になる。それが「ニンベンのついた自働化」の姿だと考えています。</p>
<hr>
<h2>3. 進め方の指針 ― PoCを現場に届けるための3ステップ</h2>
<p>「エージェント、作ってみたけど現場に浸透しない」</p>
<p>これは本当によく起きる現象です。理由はシンプルで、<strong>技術的に動くものを作ることと、それが業務に根付くことは、まったく別の話</strong> だからです。</p>
<p>私がエージェント開発の中で踏む3つのステップを紹介します。</p>
<p><img src="/assets/blog/authors/s.wada/20260323/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%8919.webp" alt="生成AIを用いた業務改善のプロセス"></p>
<h3>ステップ1：課題を「正しく」見つける</h3>
<p>ここでの「正しく」とは、AIで解くべき課題かどうかを見極めるという意味です。</p>
<p>何年もかけて磨き上げられてきた課題解決の型は、道具が変わっても色褪せません。トヨタグループが大切にする問題解決のアプローチ ― 「現状把握」「真因追求」は、AI活用の文脈でもそのまま有効です。</p>
<p>ただし、一つ重要な判断軸が加わります。<strong>「全てをAIでやろうとしない」</strong> ということ。</p>
<p>たとえば、月に数回しか発生しない作業を自動化しても、構築・運用コストに見合わないことがあります。逆に、毎日30分かかる定型作業は、多少精度が荒くてもエージェント化する価値がある。費用対効果とスケール感を冷静に見極めることが、このステップの肝です。</p>
<h3>ステップ2：試す・作り込む</h3>
<p>AIエージェントの構造は、実はシンプルです。大きく2つの要素で成り立っています。</p>
<ul>
<li><strong>プロンプト</strong>：エージェントへの「指示書」。あなたの役割はこれで、こういう手順で仕事をしてください、という設計図です。非エンジニアの方は「新人に渡す業務マニュアル」をイメージしていただくとわかりやすいかもしれません。</li>
<li><strong>ツール</strong>：エージェントが使える「道具箱」。ウェブ検索、社内データの参照、計算、メール送信など、LLM単体では苦手なことを補う機能群です。</li>
</ul>
<p>・・・ただし、「シンプルな構造 = 簡単に完成する」ではありません。</p>
<p>プロンプトの書き方ひとつで、エージェントの振る舞いは劇的に変わります。ツールの選び方、渡すデータの粒度、エラー時のフォールバック設計。この作り込みの工程に、全体の工数の大半がかかると言っても過言ではありません。</p>
<h3>ステップ3：業務フローに「組み込む」</h3>
<p>ここが最も重要で、かつ最も見落とされやすいステップです。</p>
<p>完成したエージェントを業務フローのどこに置くか。誰が使うか。既存のツールとどう共存させるか。例外が起きたときに誰がフォローするか。</p>
<p>これらの問いに答えられるのは、<strong>ドメインの知識を持つ人だけ</strong> です。</p>
<p>ここで言う「ドメイン知識」とは、特定の業務ノウハウだけを指しているわけではありません。業務フローを再設計するための価値判断基準、組織の意思決定経路や力学、そして現場の肌感覚 ― これらすべてを含む、長年の経験から培われた知の総体です。</p>
<p>たとえば自動車・モビリティの領域で考えると、その重要性がよくわかります。</p>
<h4><strong>現場の業務ノウハウ</strong></h4>
<p>整備士が持つ「この車種のこの年式は、ここが壊れやすい」という経験則。販売店の営業が持つ「この地域では◯月に需要が伸びる」という季節感覚。こうした知識は、個別業務に深く根ざしています。</p>
<h4><strong>価値判断と優先順位の基準</strong></h4>
<p>「納車までのリードタイムを短縮するよりも、お客様への中間報告の頻度を上げるほうが満足度に効く」「この検査工程は品質上絶対に省略できないが、書類作成の順序は変えられる」。業務フローを再設計するとき、何を守り何を変えてよいかを判断できるのは、その業務の「重み」を知っている人だけです。</p>
<h4><strong>組織の事情と意思決定の経路</strong></h4>
<p>「この変更はA部門だけでは通らない、B部門の部長の合意が要る」「この申請は制度上オンラインで完結するが、実質は事前の根回しが必要」。どんなに優れたエージェントを作っても、組織の中で動かせなければ意味がない。その道筋を知っているのも、ドメインの力です。</p>
<hr>
<p>これらの知識は構造化されていません。業務マニュアルにも社内ドキュメントにも、ましてやLLMの学習データにも十分には載っていない。だからこそ、エージェントを開発する技術者だけでは業務フローの設計はできないし、業務を知る「人」が設計に参加する必要があるのです。</p>
<p>具体的な場面で言えば、「この申請は月末に集中するから、そのタイミングでエージェントが下書きを用意しておいてくれると助かる」「この承認フローは部長の口頭確認が実質必要だから、エージェントの自動承認は外したほうがいい」 ― こうした判断は、何年も現場で業務を回してきた人にしかできません。</p>
<p>だからこそ、ステップ3は技術者と業務担当者の「共同作業」になります。ここに「ニンベンのついた自働化」の真価があると考えています。</p>
<h2>4. よくある落とし穴 ― 「動くけど根付かない」を避けるために</h2>
<p>セクション3で「<a href="#3.-%E9%80%B2%E3%82%81%E6%96%B9%E3%81%AE%E6%8C%87%E9%87%9D-%E2%80%95-poc%E3%82%92%E7%8F%BE%E5%A0%B4%E3%81%AB%E5%B1%8A%E3%81%91%E3%82%8B%E3%81%9F%E3%82%81%E3%81%AE3%E3%82%B9%E3%83%86%E3%83%83%E3%83%97">正しい進め方</a>」を紹介しましたが、現場では逆のパターン ― つまり、やってしまいがちな失敗 ― も数多く見てきました。エージェントが「技術的には動いているのに、業務に根付かない」とき、原因はたいてい次の3つのどれかに行き着きます。</p>
<h3>落とし穴1：「全部AIで」と決めつけてしまう</h3>
<p>エージェントの可能性に惹かれるあまり、「AIに丸投げ」してしまうケースです。</p>
<p>一見すると大胆で魅力的に聞こえます。しかし、業務フローの中には「人の判断が入ることで価値が生まれている」工程が必ずあります。たとえば、クレーム対応における熟練オペレーターの声色の判断や、契約書レビューでのベテラン法務担当の「この条項は先方の意図と違う気がする」という直感。データ上は自動化できそうに見えても、その判断こそが顧客との信頼関係を支えている。こうした工程をAIに丸ごと置き換えると、効率は上がっても、守るべきものが静かに失われていきます。</p>
<p>ステップ1の「<a href="#%E3%82%B9%E3%83%86%E3%83%83%E3%83%971%EF%BC%9A%E8%AA%B2%E9%A1%8C%E3%82%92%E3%80%8C%E6%AD%A3%E3%81%97%E3%81%8F%E3%80%8D%E8%A6%8B%E3%81%A4%E3%81%91%E3%82%8B">AIで解くべき課題かどうかの見極め</a>」が甘いと、ここにはまります。</p>
<h3>落とし穴2：ドメインエキスパート不在のまま業務フローを設計する</h3>
<p>エンジニアだけで「こう組み込めば効率的だろう」と業務フローを設計してしまうケース。技術的には合理的でも、現場の実態と噛み合わない、机上の空論で設計が進行してしまいます。</p>
<p>セクション3で挙げた「<a href="#%E7%B5%84%E7%B9%94%E3%81%AE%E4%BA%8B%E6%83%85%E3%81%A8%E6%84%8F%E6%80%9D%E6%B1%BA%E5%AE%9A%E3%81%AE%E7%B5%8C%E8%B7%AF">組織の事情と意思決定の経路</a>」。これを知っているのは、何年もその業務を回してきた人だけです。エンジニアがどれほど優れていても、この層の知識は外から取得できません。</p>
<h3>落とし穴3：「作って渡す」で終わりにしてしまう</h3>
<p>「エージェント、完成しました。マニュアルも書きました。あとはよろしくお願いします」。</p>
<p>この引き渡し方は、ほぼ確実に定着しません。エージェントは従来のシステムとは違い、使い方や問いかけ方によって振る舞いが変わります。現場の人が「こう聞けばこう返る」という感覚を掴むまでには、作った人と一緒に使ってみる期間が要ります。</p>
<p>もうひとつ見落とされがちなのが、<strong>UI/UXの設計</strong> です。エージェントと聞くと、つい「チャットUI」を思い浮かべがちですが、チャットはあくまで暫定的なインターフェースにすぎません。現場の人が本当に求めているのは「チャットで何でも聞ける」体験ではなく、「いつもの業務の流れの中で、自然にAIの力が効いている」体験です。それはボタンひとつで起動するワークフローかもしれないし、既存ツールの中に溶け込んだ提案機能かもしれない。チャットUIで得たフィードバックを手がかりに、ユーザーが本当に求める体験を作り込んでいく ― この工程を「渡して終わり」にすると、永遠にチャットの域を出られません。</p>
<p>使っていく中で「ここはもう少しこうしてほしい」というフィードバックが生まれる。そのフィードバックをその場で反映できる ― この即応性が、エージェントが業務に馴染むかどうかの分岐点になります。</p>
<hr>
<p>これらの落とし穴に共通するのは、<strong>技術と業務の間に「翻訳者」がいない</strong> ということです。</p>
<p>エージェントにせよ何にせよ、<strong>使ってもらってなんぼ</strong> です。どれだけ精緻に作り込んでも、現場で使われなければ価値はありません。そして「使われる」ためには、技術的な完成度よりも、業務への馴染み方のほうがはるかに重要です。エンジニアとドメインエキスパートが同じ机で一緒に考える体制さえあれば、これらの失敗の多くは防げます。</p>
<p>次のセクションでは、その「一緒に考える」を実現するための協業モデルについてお話しします。</p>
<hr>
<h2>5. 今後の展望 ― Forward Deployed Engineer（FDE）という協業の形</h2>
<p>最後に、「ニンベンのついた自働化」を現場に届けるための、IT企業との新しい協業モデルについてお話しします。</p>
<p><a href="https://blog.palantir.com/a-day-in-the-life-of-a-palantir-forward-deployed-software-engineer-45ef2de257b1">Forward Deployed Engineer（FDE）</a> とは、エンジニア自身が顧客の現場に入り込み、課題のヒアリングから実装・運用定着まで一気通貫で担う職種です。</p>
<p>起源は米国の Palantir Technologies が確立した FDSE（Forward Deployed Software Engineer） とされています。名前の由来は軍事用語の「Forward Deployed（前線展開）」で、「製品を納品するだけでは使われない、エンジニアが現場に入って初めて価値が生まれる」という哲学から生まれました。</p>
<p>従来のIT企業では、エンジニアは社内でシステムを開発し、営業・PM・カスタマーサクセスを介して顧客と接するのが一般的です。FDEはこの構造を変え、エンジニアが顧客と直接対話しながら、要件定義・実装・定着支援までをすべて担います。コンサルタントと異なるのは「自ら手を動かす」点です。</p>
<p>具体的には、<strong>作れるエンジニア自身が、課題を持っている現場に直接入り込んで、一緒に考える</strong>。プロトタイプを一緒に触りながら、同じ机で議論する。セクション4で挙げた<a href="#%E8%90%BD%E3%81%A8%E3%81%97%E7%A9%B43%EF%BC%9A%E3%80%8C%E4%BD%9C%E3%81%A3%E3%81%A6%E6%B8%A1%E3%81%99%E3%80%8D%E3%81%A7%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB%E3%81%97%E3%81%A6%E3%81%97%E3%81%BE%E3%81%86">「作って渡す」で終わりにしてしまう</a>という落とし穴の裏返しとも言えます。作って渡すのではなく、作りながら一緒に使う。その距離感が、エージェントの定着を左右します。</p>
<p><img src="/assets/blog/authors/s.wada/20260323/%E3%82%B9%E3%83%A9%E3%82%A4%E3%83%8931.webp" alt="価値創出を加速する：Forward Deployed Model"></p>
<table>
<thead>
<tr>
<th>役割</th>
<th>担うこと</th>
</tr>
</thead>
<tbody><tr>
<td><strong>FDE</strong>（IT側）</td>
<td>技術的な複雑さを引き受ける。AIの限界と可能性を正直に伝える。「これはできます、これは今は難しいです」を明確にする。</td>
</tr>
<tr>
<td><strong>ドメインエキスパート</strong>（業務側）</td>
<td>業務の文脈を提供する。「このデータならここから取れる」「この件は誰に聞けばいい」「この申請は私が通します」という現場の力を発揮する。</td>
</tr>
</tbody></table>
<p>この2つが掛け合わさったとき、初めて「ニンベンのついた自働化」が現場に根付く。私はそう信じています。</p>
<p>KTCは、この「FDEとドメインエキスパートの共創」を、自分たちの現場で実践し続けていきます。困りごとを見つけ、試し、形にして、届ける。そのサイクルの中で得た知見を、こうした場で発信していくことが、私にできる貢献のひとつだと考えています。</p>
<hr>
<p>ここまで読んでいただき、ありがとうございました！</p>
<p>「AIエージェント」という言葉が少し身近になり、「うちの現場でも何かできそうだな」と感じていただけたなら、この記事を書いた甲斐があります。</p>
<p>ぜひ一緒に、「ニンベンのついた自働化」を実装していきましょう。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/s.wada/20260323/cover.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[回帰テストにおけるPlaywright vs Autify NoCodeWeb 徹底比較]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-12-回帰テストにおけるPlaywright vs Autify 徹底比較/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-12-回帰テストにおけるPlaywright vs Autify 徹底比較/</guid>
            <pubDate>Thu, 12 Mar 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[回帰テストにおけるPlaywright vs Autify NoCodeWeb の比較について紹介させていただきます]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>Webアプリケーションの回帰テストを自動化する際、適切なツールの選択は品質保証とチームの生産性に大きく影響します。</p>
<h2>プロジェクト背景</h2>
<p>KINTOテクノロジーズ（以下、KTC）では、これまでAutify NoCodeWebを活用して回帰テストの自動化を進め、品質保証体制を構築してきました。Autify NoCodeWebのノーコードプラットフォームは、QA専任メンバーが中心となってテスト自動化を迅速に導入する上で非常に有効であり、多くの成果を上げてきました。
しかし、プロジェクトの成長に伴い、新たな課題も見えてきました:</p>
<ul>
<li>より高速なテスト実行が求められるようになった</li>
<li>CSVファイルの編集・アップロードなど、複雑なファイル操作を伴うテストシナリオの増加</li>
<li>データ駆動テストによる大量のテストパターンの実行ニーズ</li>
<li>エンジニアチームの拡大により、コードベースのテスト資産の管理が可能になった</li>
</ul>
<p>このような背景から、現在のKTCの体制と要件に最適なツールを再検討する必要が生じました。本記事では、これまでお世話になってきたAutify NoCodeWebと、新たな選択肢としてのPlaywrightを、実際の回帰テストシナリオにおいて詳細に比較します。
どちらのツールも優れた特徴を持っており、組織の状況によって最適な選択は異なります。本記事が、皆様のツール選定の一助となれば幸いです。</p>
<h1>ツール概要</h1>
<h2>Playwright</h2>
<ul>
<li>開発元: Microsoft</li>
<li>タイプ: オープンソースのE2Eテストフレームワーク</li>
<li>対応言語: JavaScript/TypeScript、Python、.NET、Java
対応ブラウザ: <ul>
<li>PC：Chromium（Chrome、Edge）、Firefox、WebKit（Safari相当）</li>
<li>モバイル：デバイスエミュレーション（Chromium、WebKit）
　※実機のモバイルブラウザ操作は非対応</li>
</ul>
</li>
<li>特徴: コードベースで柔軟性が高く、高速な実行速度</li>
</ul>
<h2>Autify NoCodeWeb</h2>
<ul>
<li>開発元: オーティファイ株式会社（日本企業）</li>
<li>タイプ: ノーコードAI搭載テスト自動化プラットフォーム</li>
<li>対応ブラウザ:<ul>
<li>PC：Chrome、Edge、Firefox、Safari（WebKit）</li>
<li>モバイル：iOS、Android</li>
</ul>
</li>
<li>特徴: 操作をレコーディングしてテストシナリオを作成、AI による要素認識と自動修復機能</li>
</ul>
<h1>ツール選択のためのデシジョンフローチャート</h1>
<p>自社に最適なツールを選ぶ際の判断フローを視覚化しました。このフローチャートを参考に、組織の状況に応じた選択を行ってください。</p>
<pre><code class="language-mermaid">  graph TD
  Start[QAチームにプログラミング可能なエンジニアがいる]
  Start --&gt;|No| AutifyNoCodeWeb1[Autify NoCodeWeb: ノーコードで容易、迅速な導入、AI自動修復]
  Start --&gt;|Yes| Speed{実行速度を重視?}
  
  Speed --&gt;|Yes| Playwright1[Playwright: 高速、柔軟、無料]
  Speed --&gt;|No| Requirements{要件に応じて選択}
  
  Requirements --&gt;|インフラ管理は避けたい| AutifyNoCodeWeb2[Autify NoCodeWeb]
  Requirements --&gt;|メール連携や頻繁なUI変更がある| AutifyNoCodeWeb2
  Requirements --&gt;|コストを優先したい| Playwright2[Playwright]
  Requirements --&gt;|データ駆動テストや複雑なファイル操作がある| Playwright2
</code></pre>
<h2>フローチャートの使い方</h2>
<p>このデシジョンフローは、以下の優先順位で判断することを推奨しています：</p>
<ul>
<li>チーム構成の確認: まず、開発チームにプログラミング可能なエンジニアがいるかを確認します。エンジニアリソースが限られている場合は、Autify NoCodeWebが最適な選択となります。</li>
<li>実行速度の重視度: エンジニアがいる場合、次に実行速度の重要性を評価します。CI/CDパイプラインでの高速フィードバックが重要な場合、Playwrightが適しています。</li>
<li>詳細要件の評価: 実行速度がそれほど重要でない場合は、具体的なテスト要件に基づいて判断します：</li>
</ul>
<pre><code class="language-mermaid"> graph LR
  C1[インフラ管理は避けたい] --&gt; AutifyNoCodeWeb[Autify NoCodeWeb]
  C2[メール連携や頻繁なUI変更がある] --&gt; AutifyNoCodeWeb[Autify NoCodeWeb]
  C3[コストを優先したい] --&gt; Playwright
  C4[データ駆動テストや複雑なファイル操作がある] --&gt; Playwright
</code></pre>
<ul>
<li>ハイブリッドアプローチの検討: 上記の要件が混在している場合、両ツールを併用するハイブリッドアプローチも有効な選択肢です。</li>
</ul>
<h1>機能別詳細比較</h1>
<table>
<thead>
<tr>
<th align="left">#</th>
<th align="left">比較項目</th>
<th align="left">Playwright</th>
<th align="left">Autify NoCodeWeb</th>
</tr>
</thead>
<tbody><tr>
<td align="left">1</td>
<td align="left"><strong>CSVの編集とアップロード</strong></td>
<td align="left">✅ 可能</td>
<td align="left">⚠️ 制限あり</td>
</tr>
<tr>
<td align="left">2</td>
<td align="left"><strong>特定ファイルのダウンロード</strong></td>
<td align="left">✅ 可能</td>
<td align="left">⚠️ 検証に制限</td>
</tr>
<tr>
<td align="left">3</td>
<td align="left"><strong>特定ステップのスクリーンショット</strong></td>
<td align="left">✅ 柔軟なカスタマイ즈可能</td>
<td align="left">✅ 自動取得で便利</td>
</tr>
<tr>
<td align="left">4</td>
<td align="left"><strong>画面上の文字状態の判断</strong></td>
<td align="left">✅ 詳細な検証可能</td>
<td align="left">✅ AI認識で安定</td>
</tr>
<tr>
<td align="left">5</td>
<td align="left"><strong>データ駆動テストの循環使用</strong></td>
<td align="left">✅ 可能</td>
<td align="left">⚠️ 制限あり</td>
</tr>
<tr>
<td align="left">6</td>
<td align="left"><strong>異なる画面間の切り替え</strong></td>
<td align="left">✅ 完全対応</td>
<td align="left">✅ 対応</td>
</tr>
<tr>
<td align="left">7</td>
<td align="left"><strong>外部メール内容の確認</strong></td>
<td align="left">✅ API連携で対応可能</td>
<td align="left">✅ 統合機能で便利</td>
</tr>
<tr>
<td align="left">8</td>
<td align="left"><strong>動的要素のロケート</strong></td>
<td align="left">✅ 高精度な制御</td>
<td align="left">✅ 高精度な制御 / JS指定</td>
</tr>
<tr>
<td align="left">9</td>
<td align="left"><strong>画面の比較(VRT)</strong></td>
<td align="left">✅ ピクセル単位の精密比較</td>
<td align="left">✅ AI支援で大規模変更に対応</td>
</tr>
<tr>
<td align="left">10</td>
<td align="left"><strong>スクリプトの実装難易度</strong></td>
<td align="left">⚠️ プログラミングスキル必要</td>
<td align="left">✅ ノーコードで容易</td>
</tr>
<tr>
<td align="left">11</td>
<td align="left"><strong>スクリプトの修正難易度</strong></td>
<td align="left">✅ テキスト編集で迅速</td>
<td align="left">⚠️ GUI操作が必要</td>
</tr>
<tr>
<td align="left">12</td>
<td align="left"><strong>スクリプトの実行速度</strong></td>
<td align="left">✅ 基準速度 (高速)</td>
<td align="left">⚠️ 比較的遅い傾向</td>
</tr>
</tbody></table>
<h2>1. CSVの編集とアップロード</h2>
<h3>Playwrightの場合:</h3>
<p><code>input[type=&quot;file&quot;]</code> 要素に対して<code>setInputFiles()</code> メソッドを使用することで、CSVファイルのアップロードが柔軟に実装できます。また、ファイルの動的生成やデータ駆動テストとの組み合わせも可能です。コードベースの利点を活かし、複雑なファイル操作シナリオに対応できます。</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>基本的なファイルアップロード機能は提供されていますが、複雑なCSV編集を伴うシナリオには制約があります。シンプルなファイルアップロードであれば、ノーコードで簡単に実装できる点は大きなメリットです</p>
<h2>2. 特定ファイルのWebページからのダウンロード</h2>
<h3>Playwrightの場合:</h3>
<p><code>page.waitForEvent(&#39;download&#39;)</code> を使用してダウンロードイベントを捕捉し、ファイル名や内容の検証まで完全に制御できます。ダウンロードしたファイルの内容を自動的に検証するシナリオも実装可能です</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>ダウンロード操作の記録と実行は可能です。基本的なダウンロード動作の確認には十分対応しており、ノーコードで実装できる利点があります。より詳細なファイル検証が必要な場合は、他の手段との組み合わせを検討する必要があります。</p>
<h2>3. 特定ステップのスクリーンショット</h2>
<h3>Playwrightの場合:</h3>
<p><code>page.screenshot()</code> や<code>locator.screenshot()</code> を使用して、任意のタイミングで全画面または特定要素のスクリーンショットを取得できます。保存先やファイル名も自由に設定可能で、細かい制御が必要な場合に優れています</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>全てのテストステップで自動的にスクリーンショットが撮影されるため、設定の手間が不要です。テスト失敗時の原因調査が容易になり、特にテスト自動化に不慣れなメンバーでも、確実に証跡を残せる点が優れています。</p>
<h2>4. 画面上の文字状態の判断</h2>
<h3>Playwrightの場合:</h3>
<p><code>expect(locator).toHaveText()</code> 、<code>toContainText()</code> 、<code>toBeVisible()</code> など、豊富なアサーションメソッドで文字列の存在、内容、表示状態を詳細に検証できます。正規表現による柔軟なパターンマッチングも可能で、複雑な検証ロジックに対応できます。</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>テキストの存在確認や表示状態の検証が可能です。特にAIによる要素認識により、画面デザインが変更されても同じテキスト要素を識別できる点が優れています。HTMLの細かい変更に強く、メンテナンスコストを削減できます</p>
<h2>5. データ駆動テストの循環使用</h2>
<h3>Playwrightの場合:</h3>
<p>テストデータを配列やCSVファイルから読み込み、<code>test.describe()</code> やforループを使用して複数のデータセットで同じテストロジックを実行できます。テストの再利用性が非常に高く、大量のテストパターンを効率的に実行できます。</p>
<pre><code class="language-javascript">   // CSVファイルからデータを読み込む
    testData = await readCSV(&#39;C:\\××××××××\\testData4.csv&#39;);
   
    for (const data of testData) {
      const {
        password,
        surname,
        katakanaSurname,
        yearOfBirth,
        monthOfBirth,
        dayOfBirth,
        sex,
        postCode1,
        postCode2,
        cellphoneNumber1,
        cellphoneNumber2,
        cellphoneNumber3,
        typeOfHousing,
        yearsOfResidence,
        numberOfPeople1,
        numberOfPeople2,
        annualIncome,
        purposeOfUser,
        licenseNumber,
        route,
        fileName,
        profession,
        corporateName,
        positionOfCorporateName,
        nameOfCorporate,
        katakanaNameOfCorporate,
        department,
        postCodeOfCorporate1,
        postCodeOfCorporate2,
        cellphoneNumberOfCorporate1,
        cellphoneNumberOfCorporate2,
        cellphoneNumberOfCorporate3,
        lengthOfWork
      } = data;
</code></pre>
<h3>Autify NoCodeWebの場合:</h3>
<p>個別のテストシナリオを作成することで、複数のパターンに対応できます。ノーコードで各シナリオを管理できるため、プログラミングの知識がなくても運用可能な点がメリットです。ただし、データ量が多い場合はシナリオ数が増加します。</p>
<h2>6. 異なる画面間の切り替え</h2>
<h3>Playwrightの場合:</h3>
<p>複数タブ、複数ウィンドウ、iframe間の切り替えを完全にサポートしています。<code>page.context().pages()</code> で全ページを取得したり、<code>page.waitForEvent(&#39;popup&#39;)</code> で新しいページを待機することができます。複雑な画面遷移ロジックも実装可能です。</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>画面遷移やタブ切り替えの操作を記録・実行できます。基本的な画面間の移動には十分対応しており、ノーコードで実装できる利点があります。</p>
<h2>7. 外部メール内容の確認（URLのクリックなど）</h2>
<h3>Playwrightの場合:</h3>
<p>メールテストAPIサービス（例：MailSlurp、Mailinator）と連携してメール内容を取得し、URLを抽出してナビゲーションすることが可能です。柔軟な連携が可能ですが、追加の実装とAPI費用が必要になる場合があります。</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>メール検証機能がプラットフォームに組み込まれており、追加の設定や実装なしでメール内のリンクをクリックしたり、内容を確認したりできます。この統合機能は大きな強みであり、特に非エンジニアのQAメンバーでも簡単に利用できる点が優れています。</p>
<h2>8. XPathなどによる頻繁に変動する要素のロケート</h2>
<h3>Playwrightの場合:</h3>
<p>CSS Selector、XPath、text、roleなど、多様なロケーター戦略をサポートしています。複数のロケーターを組み合わせたり、厳密な条件指定が可能で、動的要素に対しても高い精度で特定できます。</p>
<pre><code class="language-python">            # ENT番号取得
            xpath1 = &#39;//*[@id=&quot;app&quot;]/div/main/div/div[1]/div[1]/div[2]/div/div[3]/div[1]&#39;
            display_text1 = (await page.locator(f&#39;xpath={xpath1}&#39;).text_content() or &#39;&#39;).strip()
            last1 = display_text1[-5:]
            shinsa_number = &#39;97016QAP00&#39; + last1

            # メールアドレス取得
            xpath2 = &#39;//*[@id=&quot;app&quot;]/div/main/div/div[1]/div[1]/div[2]/div/div[7]/div[2]&#39;
            display_text2 = (await page.locator(f&#39;xpath={xpath2}&#39;).text_content() or &#39;&#39;).strip()

            # 名前取得
            xpath0 = &#39;//*[@id=&quot;app&quot;]/div/main/div/div[1]/div[1]/div[2]/div/div[7]/div[1]/a&#39;
            display_text0 = (await page.locator(f&#39;xpath={xpath0}&#39;).text_content() or &#39;&#39;).strip()
            lastname = display_text0[4:]
</code></pre>
<h3>Autify NoCodeWebの場合:</h3>
<p>AIによる要素認識を採用しており、HTMLが変更されても要素を識別しようとします。この機能は画面の小規模な変更に対して非常に強く、手動でのメンテナンスを大幅に削減できます。特にデザイン調整が頻繁に行われる開発フェーズでは、この自動修復機能が大きな価値を発揮します。複雑な動的要素については、認識精度を確認しながら運用することが推奨されます。
そして、Javascriptによって要素の指定も簡単にできます。</p>
<pre><code class="language-javascript">            function getEmailInputValue() {
              var selector = &quot;#__next &gt; main &gt; div &gt; div &gt; div.o-emailPasswordForm &gt; div &gt; div.m-inputField &gt; div:nth-child(1) &gt; div &gt; div.m-inputField__container &gt; div &gt; div &gt; input[type=email]&quot;;
              var element = document.querySelector(selector);
              if (!element) {
                throw new Error(&quot;Error: cannot find the element with selector(&quot; + selector + &quot;).&quot;);
              }
              return element.value;
            }

            // 実行例
            console.log(getEmailInputValue());
</code></pre>
<h2>9. 画面の比較（ビジュアルリグレッションテスト）</h2>
<h3>Playwrightの場合:</h3>
<p><code>toHaveScreenshot()</code> メソッドでピクセルレベルの画面比較が可能です。差分の許容範囲を設定したり、特定領域をマスクしたりできます。細かい視覚的変更の検出に優れており、意図しないUI変更を確実に捉えます</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>画面全体の変更を検出し、AIが変更箇所を識別します。特に大規模なデザイン変更時には、変更点の確認とテストシナリオの更新が比較的容易です。AIによる変更の影響分析機能により、どのテストシナリオを更新すべきかの判断がしやすく、大規模リニューアル時のメンテナンス工数を削減できる点が優れています。</p>
<h2>10. スクリプトの実装難易度</h2>
<h3>Playwrightの場合:</h3>
<p>JavaScript/TypeScriptなどのプログラミング言語とテストフレームワークの知識が必要です。習得には一定の時間がかかりますが、公式ドキュメントが充実しており、コミュニティも活発です。エンジニアチームが確立されている組織に適しています。</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>ブラウザ操作を記録するだけでテストシナリオが作成できるため、プログラミング経験がない非エンジニアでも容易に使用できます。この実装の容易さは、Autify NoCodeWebの最大の強みの一つです。QA専任メンバーが主体となってテスト自動化を推進できるため、エンジニアリソースが限られている組織や、迅速にテスト自動化を開始したい場合に特に有効です。</p>
<h2>11. スクリプトの修正難易度</h2>
<h3>Playwrightの場合:</h3>
<p>テキストエディタでスクリプトを直接編集できるため、小規模な修正は数秒で完了します。バージョン管理システム（Git）との親和性も高く、差分確認やロールバックが容易です。複数人での並行開発やコードレビュー文化とも相性が良いです。</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>GUI上で操作を再記録するか、手動で修正する必要があります。ただし、AIによる自動修復機能により、画面の小規模な変更には自動的に対応されるため、実際の修正作業は最小限に抑えられます。この自動修復機能は、メンテナンスコストの削減に大きく貢献します。</p>
<h1>12. スクリプトの実行速度</h1>
<h3>Playwrightの場合:</h3>
<p>ヘッドレスモードでの実行やネットワークリクエストの最適化により、非常に高速なテスト実行が可能です。並列実行にも標準対応しており、大規模なテストスイートでも短時間で完了します。</p>
<h3>Autify NoCodeWebの場合:</h3>
<p>クラウドベースのプラットフォームであり、ネットワークレイテンシーや処理のオーバーヘッドにより、Playwrightと比較して実行速度が遅くなる傾向があります。ただし、実際の速度差はテストケースの複雑さ、ネットワーク環境、Autify NoCodeWebのサーバー負荷などの要因によって大きく変動する可能性があります。大規模なテストスイートでは実行時間が増加する可能性がありますが、並列実行機能を活用することで全体の実行時間を最適化できます。</p>
<p>:::message
実行速度は環境やテストケースによって大きく異なるため、具体的な数値比較は控えます。各ツールの特性を理解し、実際の使用環境でのパフォーマンスを評価することをお勧めします。
:::</p>
<h1>追加の比較ポイント</h1>
<h2>📊 要約比較表</h2>
<table>
<thead>
<tr>
<th align="left">項目</th>
<th align="left">Playwright (エンジニア主導)</th>
<th align="left">Autify NoCodeWeb (QA・非エンジニア主導)</th>
</tr>
</thead>
<tbody><tr>
<td align="left"><strong>コスト</strong></td>
<td align="left">完全無料 (OSS)</td>
<td align="left">サブスクリプション型 (有料)</td>
</tr>
<tr>
<td align="left"><strong>導入障壁</strong></td>
<td align="left">プログラミングスキルが必要</td>
<td align="left">低い (ノーコードで即時開始)</td>
</tr>
<tr>
<td align="left"><strong>CI/CD</strong></td>
<td align="left">柔軟かつ強力な統合</td>
<td align="left">シンプルなAPI連携</td>
</tr>
<tr>
<td align="left"><strong>メンテナンス</strong></td>
<td align="left">コードベース・Git管理</td>
<td align="left">AIによる自動修復 (Self-healing)</td>
</tr>
</tbody></table>
<h2>1. コストと導入障壁</h2>
<h3>Playwright:</h3>
<ul>
<li>完全無料のオープンソース</li>
<li>学習コストは必要だが、長期的なランニングコストはゼロ</li>
<li>CI/CD環境への組み込みも容易</li>
<li>エンジニアチームの人件費は考慮が必要</li>
</ul>
<h3>Autify NoCodeWeb:</h3>
<ul>
<li>サブスクリプション型の有料サービス</li>
<li>初期導入が簡単で、迅速にテスト自動化を開始できる</li>
<li>テストシナリオ数や実行回数に応じた費用体系</li>
<li>インフラ管理コストが不要</li>
<li>エンジニアリソースが限られている場合、トータルコストで優位性がある場合も</li>
</ul>
<h2>2.チーム構成との適合性</h2>
<h3>Playwrightが適しているチーム:</h3>
<ul>
<li>エンジニア主導のQA体制が整っている</li>
<li>コードレビュー文化が定着している</li>
<li>複雑なテストロジックや高度なカスタマイズが必要</li>
<li>Git等のバージョン管理システムを活用している</li>
</ul>
<h3>Autify NoCodeWebが適しているチーム:</h3>
<ul>
<li>QA専任メンバーが中心（プログラミング経験が少ない）</li>
<li>エンジニアリソースが限られている</li>
<li>迅速にテスト自動化を開始したい</li>
<li>メンテナンスコストを抑えたい（AIによる自動修復活用）</li>
<li>ノーコードでテスト資産を管理したい</li>
</ul>
<h2>3. CI/CD統合</h2>
<h3>Playwright:</h3>
<ul>
<li>GitHub Actions、GitLab CI、Jenkins など主要CI/CDツールとの統合が容易</li>
<li>テスト結果のレポート生成、アーティファクト保存が柔軟</li>
<li>並列実行、シャーディングなど高度な実行戦略が可能</li>
<li>開発フローに深く統合できる</li>
</ul>
<h3>Autify NoCodeWeb:</h3>
<ul>
<li>APIを介したCI/CD統合が可能</li>
<li>独自のテスト実行環境を使用</li>
<li>クラウドベースのため、インフラ管理不要
CI/CD統合の設定がシンプル</li>
</ul>
<h2>4.メンテナンス性と長期運用</h2>
<h3>Playwright:</h3>
<ul>
<li>スクリプトをバージョン管理できる</li>
<li>リファクタリングやスクリプトの再利用が容易</li>
<li>コミュニティが活発で、最新のベストプラクティスにアクセスしやすい</li>
<li>長期的なスクリプト資産の管理に優れる</li>
</ul>
<h3>Autify NoCodeWeb:</h3>
<ul>
<li>AIによる要素の自動認識で、画面変更時のメンテナンス工数を削減</li>
<li>自動修復機能により、軽微な変更への対応が自動化される</li>
<li>プラットフォーム上での一元管理が可能</li>
<li>ノーコードのため、担当者の変更による影響が少ない</li>
</ul>
<h1>それぞれのツールが特に優れているシーン</h1>
<h2>Playwrightが最適なケース</h2>
<ul>
<li>大量のデータパターンテスト: 同一ロジックで数百〜数千パターンのテストデータを処理する必要がある場合</li>
<li>高頻度の実行: CI/CDパイプラインで1日に何度もテストを実行し、迅速なフィードバックが必要な場合</li>
<li>複雑なファイル操作: CSV編集、複数ファイルの同時アップロード、ダウンロードファイルの内容検証など</li>
<li>エンジニア主導のQA: 開発チームとQAチームが密接に連携し、テストスクリプトもコードレビューの対象とする場合</li>
<li>長期的な資産管理: テストスクリプトをソースコードと同様に管理し、継続的に改善していく場合</li>
</ul>
<h2>Autify NoCodeWebが最適なケース</h2>
<p>迅速な導入: プログラミング経験のないQAメンバーが、短期間でテスト自動化を開始したい場合
メール連携テスト: 外部メールの検証を含むシナリオが多い場合
頻繁なUI変更: デザイン調整が頻繁に行われる環境で、AI自動修復機能を活用したい場合
インフラ管理の負担軽減: テスト実行環境の構築・管理リソースが限られている場合
ノーコード資産管理: テスト資産をコード化せず、ビジュアルに管理したい場合
大規模リニューアル: 画面全体の大幅な変更時に、AIによる影響分析と効率的な更新が必要な場合</p>
<h2>KTCにおける選択理由</h2>
<p>KTCでは、これまでAutify NoCodeWebによって品質保証の基盤を築いてきましたが、プロジェクトの成長と共にいろいろな課題が顕在化しました。（上記のプロジェクト背景で述べた課題）
これら課題を解決する選択肢として、Playwrightを導入することにしました。ただし、これはAutify NoCodeWebを完全に置き換えるものではありません:</p>
<p>Playwrightが担う領域: データ駆動テスト、高速実行が求められるCI/CD統合、複雑なファイル操作を伴うシナリオ
Autify NoCodeWebが引き続き価値を発揮する領域: メール連携テスト、ノーコードで管理すべきシナリオ、QA専任メンバーが主導するテスト</p>
<p>両ツールの強みを活かしたハイブリッドアプローチにより、KTCの品質保証体制をさらに強化していく予定です。</p>
<h1>まとめ：どちらを選ぶべきか</h1>
<h2>Playwrightの主な強み:</h2>
<ul>
<li>高速な実行速度</li>
<li>柔軟なカスタマイズ性</li>
<li>精密な要素制御とデータ駆動テスト</li>
<li>オープンソースでコストゼロ</li>
<li>バージョン管理システムとの親和性</li>
</ul>
<h2>Autify NoCodeWebの主な強み:</h2>
<ul>
<li>ノーコードで実装が容易</li>
<li>AIによる自動修復でメンテナンスコスト削減</li>
<li>統合されたメール検証機能</li>
<li>非エンジニアでも運用可能</li>
<li>インフラ管理不要</li>
</ul>
<p>最適な選択は、組織の状況によって異なります:</p>
<ul>
<li>エンジニアリソースが限られ、迅速にテスト自動化を開始したい → Autify NoCodeWeb</li>
<li>エンジニアチームが確立され、高度なカスタマイズと高速実行が必要 → Playwright</li>
<li>両方のメリットを活かしたい → ハイブリッドアプローチ</li>
</ul>
<p>どちらのツールも、現代のWebアプリケーション開発において品質を担保するための重要な選択肢です。本記事の比較内容を参考に、自社のチーム構成、スキルセット、プロジェクト要件、予算、長期的な運用計画などを総合的に考慮して、最適なツールを選定してください。
Autifyは世界中で支持されているノーコードテスト自動化プラットフォームであり、特にエンジニアリソースが限られている組織において、品質保証体制を迅速に構築できる優れたソリューションです。KTCも、Autifyのサービスを通じて多くの成果を上げてきました。
今後も、両ツールの進化に注目し、それぞれの強みを最大限に活用していくことが重要です。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ro/6.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[技術者と障害当事者がチームで実現するアクセシビリティの「当たり前品質」：デブサミ2026参加レポート]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-11-devsumi-accessibility-as-standard/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-11-devsumi-accessibility-as-standard/</guid>
            <pubDate>Wed, 11 Mar 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[2月19日に参加したGovTech東京のアクセシビリティセッションの様子をレポートします]]></description>
            <content:encoded><![CDATA[<p>こんにちは。Engineering OfficeのAccessibility Advocate、辻勝利です。
少し前になりますが、2月19日にDevelopers Summit 2026（デブサミ2026）に参加し、一般財団法人GovTech東京によるセッション「アクセシビリティを“あたりまえ品質”に！！」を傍聴してきました。</p>
<p>登壇者の一人である松村道生さんは私の知人であり、同時期に新たな環境へ身を投じた仲間でもあります。彼がGovTech東京という組織において、どのようにアクセシビリティ推進を開発プロセスに組み込んでいるのか、その実践を参考にしたいと考えたのが参加の動機でした。</p>
<p>30分という限られた時間でしたが、アクセシビリティを「付加価値」ではなく「当然備わっているべき品質」と定義し、組織的に取り組む姿勢が非常に明確なセッションでしたので、今回はその内容を簡単にお伝えします。</p>
<h2>1. 効率化の裏側にある「課題」の実態</h2>
<p>セッションの前半では、視覚障害当事者でもある松村さんより、現在のデジタル化・効率化がもたらした課題が共有されました。</p>
<p>近年、サービスの効率化や自動化が「良いこと」として捉えられる傾向があり、様々なところで実際にいろいろなサービスの効率化が図られています。
もちろん、人材不足などの様々な要因により致し方ないと考えられる側面もありますが、下記の事例は私たち視覚障害者の「それでは済まされない現実」をあらわにする内容で、私も一つ一つうなずきながら聞きました。</p>
<ul>
<li>マイナンバー設定の課題： 役所にスクリーンリーダー環境が整備されていなかったため、秘匿すべきパスワードを職員に口頭で伝えて代筆・設定してもらうしかなかった経験。</li>
<li>対面サービスの減少： 駅の「みどりの窓口」削減により自動券売機が主流となったことで、独力での切符購入が困難になった現状。</li>
<li>行政申請の壁： コロナ禍のワクチン接種予約など、視覚障害者が独力で完結できない設計のままリリースされたサービスの実態。</li>
</ul>
<p>これらの事例を通じて、「世の中を便利にするための自動化が、結果として一部の都民を排除してしまっている」という切実な現状が示されました。</p>
<h2>2. 行政サービスにおける「唯一性」と責任</h2>
<p>特に印象に残ったのが、行政サービス特有の責任に関するお話でした。</p>
<p>民間サービスであれば、もし「サービスA」がアクセシビリティの問題で使えなくても、ユーザーは代替手段として「サービスB」を選択できる可能性があります。しかし、行政サービスである「東京アプリ」は唯一無二の存在であり、他に選択肢がありません。</p>
<p>「使えないから他を使う」という逃げ道がない以上、最初から全都民が等しく使える状態でリリースしなければならない。この「代替不可能な公共インフラとしての責任感」が、GovTech東京がアクセシビリティを最優先事項に据える最大の根拠であることを再認識しました。</p>
<h2>3. シフトレフト：開発工程へのアクセシビリティの組み込み</h2>
<p>山内晨吾さんが担当されたパートでは、これらの課題を「後付け」ではなく、開発の最上流から解決する「シフトレフト」の実践手法が紹介されました。</p>
<ul>
<li>デザイン段階からの設計（Figma）： コンポーネント単位で要件を定義し、UI設計時に品質を確保。</li>
<li>テストコードによる自動検証： 機械的にチェック可能な項目を自動化し、デグレード（品質低下）を防止。</li>
<li>AIレビューの活用： LLM（大規模言語モデル）等を活用し、コードレビュー段階でアクセシビリティの不備を検知。</li>
</ul>
<p>GovTech東京では、山内さん（エンジニア）と松村さん（当事者視点）が密に連携し、技術的な仕組みと実際の課題の体験が双方向でフィードバックされる体制が確立されています。チームとして高度に機能していることが、発表の端々から伝わってきました。</p>
<h2>4. 「なくては困る」を基準にする開発文化</h2>
<p>セッションの核となっていた、アクセシビリティを「あったらいいね（魅力品質）」から「なくては困る（当たり前品質）」へ変えていくという視点は、私が取り組んでいる「アクセシビリティを社内文化にする」という活動とも強く共鳴するものです。</p>
<p>この業界で20年以上アクセシビリティの啓発に従事していますが、当事者意識（オーナーシップ）と技術的な合理性がこれほど高いレベルで融合した発表には、なかなか出会えるものではありません。</p>
<h2>おわりに</h2>
<p>イベント終了後の「Ask the Speaker」では、お二人に直接ご挨拶する機会を得ました。現場で格闘している方々と対話し、今後の連携の可能性についても言葉を交わせたことは大きな収穫でした。</p>
<p>今回のセッションで得た知見を、私自身のプロジェクトにおける「アクセシビリティの文化定着」にも確実に活かしていきたいと考えています。</p>
<hr>
<p>参考リンク</p>
<ul>
<li><a href="https://event.shoeisha.jp/devsumi/20260218/session/6457">デブサミ2026 セッション詳細</a></li>
<li><a href="https://codezine.jp/news/detail/23205">CodeZine：アクセシビリティのシフトレフトを実現！「東京アプリ」の開発プロセス改善</a></li>
</ul>
<hr>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ソフトウェアの依存関係アップデートはRenovateにした理由]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-11-ソフトウェアの依存関係アップデートはRenovateにした理由/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-11-ソフトウェアの依存関係アップデートはRenovateにした理由/</guid>
            <pubDate>Wed, 11 Mar 2026 01:00:00 GMT</pubDate>
            <description><![CDATA[ソフトウェアの依存関係アップデートをDependabotと迷い続けた結果、Renovateに決定した理由について紹介します。]]></description>
            <content:encoded><![CDATA[<h1>ソフトウェアの依存関係アップデートはRenovateにした理由</h1>
<p>DBREグループで、DevSecOps担当を自称している栗原です。</p>
<p>タイトルの通り、ソフトウェア依存モジュールのアップデートにRenovateを採用しました。GitHub Dependabotと迷い続けましたが、この記事で紹介するDependabotにはない3つの利点が非常に魅力的だったため、Renovateを採用するにいたりました。Renovateを紹介している記事はよく見かけるので、あまり語られていないおすすめの実行方法についてと、私が惹かれた3つのポイントについて説明します。</p>
<h2>Renovateとは</h2>
<p><a href="https://github.com/renovatebot/renovate">Renovate</a>は、ソフトウェアの依存関係を自動でアップデートしてくれるOSSツールです。Dependabotと同様に、リポジトリのルートに設定ファイル（<code>renovate.json</code>）を配置して、Renovateを実行すると、依存関係のアップデートPRを自動で送ってくれます。</p>
<p>2026年3月現在は無料で利用可能ですが、<a href="https://www.mend.io/renovate/">Mend社による買収</a>後、将来的に有償化される可能性がある点は留意しておく必要があります。ただし、現時点ではOSSとして活発に開発が続いており、セルフホスティングも可能なため、柔軟な運用が可能です。</p>
<p>類似機能である、Dependabotとの詳細比較は<a href="https://docs.renovatebot.com/bot-comparison/">公式のbot比較ページ</a>に譲りますが、Dependabotより高機能なのは間違いないです。個人的には設定の柔軟性が圧倒的に高く、複数リポジトリでの設定の共通化など、エンタープライズでの利用に適していると感じています。</p>
<p>ちなみに、この記事ではSCMはGitHubであることを前提にしておりますが、GitLabなど他のSCMを使われている方にも参考になるかと思います。</p>
<h2>おすすめの実行方法</h2>
<p>他社さんの記事などをみかけると、<a href="https://github.com/apps/renovate">Mend Renovate App</a>（一番手っ取り早い）、<a href="https://github.com/renovatebot/github-action">公式のGitHub Actions</a>が紹介されていることが多いですが、私がおすすめしたいのは、<a href="https://docs.renovatebot.com/self-hosted-configuration/">CLIでの実行</a>です。</p>
<p>Renovateは依存定義ファイル（package.json）だけではなく、lockfile（package-lock.json）も更新してくれますが、その際に実行環境にインストールされているパッケージマネージャ（npm）を実行して実現します。つまり実際の開発環境と同じツールを使えるのがベストなわけです。前者の2つは、プレビルドされた環境はあるものの、厳密にやろうとすると、Renovate実行用のコンテナをカスタマイズするなどが必要ですが、CLI実行であれば、他のワークフローで使っている環境セットアップの処理がそのまま転用できます。</p>
<p>特に我々は<a href="https://blog.kinto-technologies.com/posts/2023-05-29-serverless-with-monorepo-nx/">Monorepo</a>を採用しており、複数の言語、パッケージマネージャ（Go、Python uv、Node.js yarn等）を使っているプロジェクトでは、それぞれのツールのバージョンを揃える必要があるため、CLI実行の恩恵が大きいです。</p>
<p>こちらは実際に我々が使っているGitHub Actionsです。</p>
<pre><code class="language-yaml">name: Update Deps Via Renovate
on:
  schedule:
    - cron: &#39;0 * * * *&#39;
  workflow_dispatch:

concurrency:
  group: &quot;${{ github.event.repository.name }}-update-deps-via-renovate&quot;
  cancel-in-progress: false

env:
  LOG_LEVEL: ${{ vars.RENOVATE_LOG_LEVEL || &#39;&#39; }}

jobs:
  renovate:
    runs-on: ubuntu-latest
    steps:
      # 他のワークフローとも共通化しているセットアッププロセス
      - name: checkout codebase and setup runtime
        id: setup-runtime
        uses: kinto-dev/action-dbre-setup-runtime@v3
        with:
          # 後ほど紹介しますが、共通renovate設定ファイルを利用するため、
          # 通常のGITHUB_TOKENではなく、GitHub Appのインストールアクセストークンを利用
          github-app-id: ${{ vars.GH_APP_ID }}
          github-app-private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}
          go-project: &quot;true&quot;
          python-uv-project: &quot;true&quot;

      # GitHub Actionsであれば、Node.jsランタイムがデフォルトでインストールされているので、npxで直接renovateを実行可能。
      - name: run renovate
        run: npx --yes --package renovate -- --token=&quot;${{ steps.setup-runtime.outputs.github-app-install-token }}&quot; &quot;${{ github.repository }}&quot;
</code></pre>
<p>以上がおすすめの実行方法です。それでは次章からおすすめの機能を紹介していきます。</p>
<h2>おすすめ機能1: インラインスクリプトもアップデートの対象にできる</h2>
<p>Dependabotでは基本的に設定ファイル（package.jsonやgo.mod等）に定義された依存関係のみがアップデート対象になりますが、Renovateは<a href="https://docs.renovatebot.com/modules/manager/regex/">Custom Manager</a>機能により、正規表現でマッチさせた任意の文字列をアップデート対象にできます。</p>
<p>例えば、以下のようにnpm scriptとしてDocker Hubのイメージをタグ指定して実行しているケースを考えてみます。</p>
<pre><code class="language-json">// package.json
{
  &quot;scripts&quot;: {
    &quot;gha_lint&quot;: &quot;docker run -i --init --rm -v $INIT_CWD/.github/workflows:/workflows rhysd/actionlint:1.7.7 -color $(ls .github/workflows/*.yml | awk -F &#39;/&#39; &#39;{print \&quot;/workflows/\&quot;$NF}&#39;)&quot;
  }
}
</code></pre>
<p>このようなインラインスクリプトに依存モジュールのバージョンがハードコードされるケースも、Renovateはアップデートの対象にしてくれます。renovate.jsonに以下の設定を追加するだけで実現できます。</p>
<pre><code class="language-json">&quot;customManagers&quot;: [
    {
      &quot;customType&quot;: &quot;regex&quot;,
      &quot;fileMatch&quot;: [
        &quot;^package\\.json$&quot;
      ],
      &quot;matchStrings&quot;: [
        &quot;docker run [^;]*? (?&lt;depName&gt;[^:\\s]+):(?&lt;currentValue&gt;[^\\s]+)&quot;
      ],
      &quot;datasourceTemplate&quot;: &quot;docker&quot;,
      &quot;versioningTemplate&quot;: &quot;docker&quot;,
      &quot;depTypeTemplate&quot;: &quot;shell-script-docker-inline&quot;
    }
]
</code></pre>
<p>この設定により、<code>rhysd/actionlint:1.7.7</code>の部分が検出され、新しいバージョンがリリースされると自動でPRが作成されます。正規表現でマッチングするため、Dockerfile、シェルスクリプト、CI/CDの設定ファイルなど、あらゆるファイルに対して適用可能です。Dependabotではカバーできない領域まで自動アップデートの対象にできるのは、運用負荷の軽減に大きく貢献します。</p>
<h2>おすすめ機能2: ローカルで設定ファイルをデバッグできる</h2>
<p><a href="https://docs.renovatebot.com/configuration-options/#packagerules">アップデートPRのグルーピング</a>など、ファインチューニングをしようと思うと、設定ファイルのトライアンドエラーがつらいです。これはDependabotでも同じだと思いますが、Renovateは開発PCでも動かせるCLIがあるため、手元でカジュアルに設定ファイルとにらめっこが可能です。</p>
<pre><code class="language-bash">$ LOG_LEVEL=debug npx renovate --platform=local --dry-run=full | tee renovate-dryrun.txt
</code></pre>
<p>この<code>--dry-run</code>オプションを使うと、実際にPRを作成せずに、どのような更新が検出されるかをローカルで確認できます。設定を変更して即座に結果を確認できるため、トライアンドエラーのサイクルが非常に高速です。</p>
<p>設定ファイルのvalidatorも付随しています。</p>
<pre><code class="language-bash">$ npx --yes --package renovate -- renovate-config-validator
</code></pre>
<p>このコマンドで、renovate.jsonの構文エラーや設定の妥当性をチェックできます。CI/CDに組み込んでおけば、設定ミスによる実行エラーを事前に防ぐことができます。</p>
<p>えっ...しょぼくない？と思われたかもしれませんが、Dependabotの場合は設定を変更するたびにGitHubにpushして結果を待つ必要があり、フィードバックループが長いです。Renovateはローカルで即座に確認できるため、スピーディーに設定ファイルを完成させることができました。個人的には大きなメリットであると考えます。</p>
<h2>おすすめ機能3: 設定ファイルを共通化できる</h2>
<p>苦労して完成させた設定ファイルをSSOT（Single Source of Truth）にしたいですよね。Renovateには<a href="https://docs.renovatebot.com/config-presets/">Config Presets</a>という、設定ファイルの共通化機能があります。</p>
<p>共通設定リポジトリに<code>default.json</code>を配置し、そこにベースとなる設定を記述します。例えば、PRのラベル設定、スケジュール設定、グルーピングルールなど、組織として統一したい設定をまとめておきます。</p>
<p>利用側の設定ファイルはこれだけで済みます。</p>
<pre><code class="language-json">{
  &quot;$schema&quot;: &quot;https://docs.renovatebot.com/renovate-schema.json&quot;,
  &quot;extends&quot;: [&quot;github&gt;kinto-dev/dbre-renovate-config&quot;]
}
</code></pre>
<p><code>github&gt;</code>プレフィックスでGitHubリポジトリを指定するだけで、共通設定を読み込むことができます。ブランチやタグを指定することも可能です（例: <code>github&gt;kinto-dev/dbre-renovate-config#v1.0.0</code>）。</p>
<p>もちろん、利用側リポジトリ特有の設定を拡張することもできます。</p>
<pre><code class="language-json">{
  &quot;$schema&quot;: &quot;https://docs.renovatebot.com/renovate-schema.json&quot;,
  &quot;extends&quot;: [&quot;github&gt;kinto-dev/dbre-renovate-config&quot;],
  &quot;ignorePaths&quot;: [
    &quot;backup/**&quot;
  ]
}
</code></pre>
<p>この機能により、複数リポジトリで共通の設定を使いつつ、各リポジトリ固有の要件にも対応できます。組織で管理するリポジトリが増えれば増えるほど、この機能の恩恵は大きくなります。</p>
<h2>まとめ</h2>
<p>以上、Renovateのおすすめの実行方法と、Dependabotにはない3つの魅力的な機能を紹介させていただきました！</p>
<p>改めてまとめると以下の通りです。</p>
<ol>
<li><strong>CLI実行で開発環境と同じツールを使える</strong> - 既存のCI/CDワークフローを流用できる</li>
<li><strong>インラインスクリプトもアップデート対象にできる</strong> - Custom Managerで正規表現マッチング</li>
<li><strong>ローカルでデバッグできる</strong> - 高速なフィードバックループで設定を洗練できる</li>
<li><strong>設定ファイルを共通化できる</strong> - 複数リポジトリで一貫した運用が可能</li>
</ol>
<p>最近全部AIでいいんじゃないかと思うことも多々ありますが、決められた定型作業であれば非AIツールもまだまだ有益だと信じて、ツールボックスを拡充していければと思います。お読みいただきありがとうございます。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AWS Configの記録頻度最適化でコストを大幅削減]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-10-aws-config-cost-optimization/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-10-aws-config-cost-optimization/</guid>
            <pubDate>Tue, 10 Mar 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[AWS Configの記録頻度を最適化し、セキュリティ要件を維持しながら月間コストを約80%削減した取り組みを紹介します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは、KINTOテクノロジーズ CloudSecurityグループの小林です。</p>
<p>皆さん、AWS Configのコストが高いなと思ったことはありませんか？
今回、記録方式の最適化で約80%のコスト削減を実現しました。
本記事ではその過程と得られた知見を共有します。</p>
<h2>本記事の対象読者</h2>
<ul>
<li>AWS Configのコストが気になっている方</li>
<li>AWSのコスト最適化に取り組んでいる方</li>
<li>セキュリティ要件とコストのバランスを考えている方</li>
<li>大規模なAWS環境を運用している方</li>
<li>Control TowerやOrganizationsを使って複数アカウントを管理している方</li>
</ul>
<h2>AWS Configとは</h2>
<p>AWS Configは、AWSリソースの設定変更を記録・追跡するサービスです。</p>
<p>主な用途は以下の通りです：</p>
<ul>
<li>リソース設定の変更履歴を記録</li>
<li>コンプライアンスルールへの準拠状況を監視</li>
<li>セキュリティ基準違反の検知</li>
<li>設定変更の監査証跡として活用</li>
</ul>
<h2>背景：なぜ見直しが必要だったのか</h2>
<p>AWS Configは便利なサービスですが、
記録回数に応じて課金されるため、大規模環境ではコストが大きくなりがちです。</p>
<p>主な要因は以下の通りです。</p>
<ul>
<li>記録対象リソースの増加（特にネットワーク関連リソース）</li>
<li>連続記録モードによる頻繁な記録</li>
</ul>
<h3>Control Tower環境での課題</h3>
<p>弊社では AWS Control Towerを使用して複数アカウントを管理しています。
AWS Configのコスト削減を検討する中で、取りうる選択肢は以下の2つでした。</p>
<p><strong>選択肢1: 現状維持（全て連続記録）</strong></p>
<ul>
<li>コストが高いまま</li>
<li>Control Towerのベストプラクティスに従う</li>
</ul>
<p><strong>選択肢2: StackSet (aws-control-tower-customizations) で記録頻度を最適化</strong></p>
<p><strong>AWSソリューション:</strong> <a href="https://github.com/aws-solutions/aws-control-tower-customizations">aws-control-tower-customizations</a></p>
<ul>
<li>大幅なコスト削減が期待できる</li>
<li>Control Towerが作成したConfigリソースを変更することになる</li>
</ul>
<h3>Control Tower のベストプラクティスとの兼ね合い</h3>
<p>AWS Control Towerの公式ドキュメントには、以下のような記載があります：</p>
<blockquote>
<p>「AWS Control Towerによって作成されたリソースを変更または削除しないでください。」</p>
</blockquote>
<p>出典元: <a href="https://docs.aws.amazon.com/ja_jp/controltower/latest/userguide/getting-started-guidance.html">AWS Control Tower リソースの作成と変更に関するガイダンス</a></p>
<p>この記載により、選択肢2の採用には慎重な姿勢を取らざるを得ませんでした。</p>
<p><strong>具体的な懸念事項は以下の通りです。</strong></p>
<ul>
<li>Configレコーダーの変更がControl Towerの動作に影響しないか</li>
<li>ドリフト検出機能が正しく動作するか</li>
<li>コンプライアンスレポートの正確性が保たれるか</li>
<li>ランディングゾーンの更新やOUの再登録が必要にならないか</li>
</ul>
<p>このため、コスト削減の必要性は認識していたものの、実施に踏み切れない状況が続いていました。</p>
<h3>記録回数の実態</h3>
<p>記録回数を調査したところ、以下のリソースタイプの記録回数が特に多いことがわかりました。</p>
<p><strong>記録回数が多かったリソースタイプ TOP4：</strong></p>
<ol>
<li>EC2 NetworkInterface - ネットワークインターフェースの状態変化</li>
<li>EC2 Subnet - サブネットの状態変化</li>
<li>EC2 SecurityGroup - セキュリティグループの関連付け変化</li>
<li>Config ResourceCompliance - コンプライアンスチェック</li>
</ol>
<p><strong>なぜこれらの記録回数が多いのか？</strong></p>
<p>連続記録モードでは、リソースに何らかの変更（内部状態の変化も含む）があるたびに記録されます。
これらのリソースの記録が多い原因は、ENIの作成/削除を起点とした連鎖的な記録にありました。</p>
<p><img src="/assets/blog/authors/i.kobayashi/2026-03/01.png" alt="ENI変更を起点とした連鎖的な記録の流れ"></p>
<p><strong>① EC2 NetworkInterface（根本原因）</strong></p>
<ul>
<li>ECSタスクの起動/停止に伴いENIが頻繁に作成・削除されており、そのたびにConfigの記録が発生<ul>
<li>VPC接続を有効化したLambda関数の場合も同様です。</li>
</ul>
</li>
</ul>
<p><strong>② EC2 Subnet（ENI に連動）</strong></p>
<ul>
<li>ENIの作成・削除に伴い、対象のサブネットの設定項目が記録<ul>
<li>VPC接続を有効化したLambda関数の作成時も同様です。</li>
</ul>
</li>
</ul>
<p><strong>③ EC2 SecurityGroup（ENI に連動）</strong></p>
<ul>
<li>ENIの作成・削除に伴い、その ENIに関連付けられたSecurityGroupの設定項目も記録</li>
</ul>
<p><strong>④ Config ResourceCompliance（すべてに連動）</strong></p>
<ul>
<li><code>AWS::Config::ResourceCompliance</code>は、Configルールによって評価されたリソースのコンプライアンス状態の変化を記録するリソースタイプです。<ul>
<li>上記の各リソースで新しい設定項目が記録されるたびにConfigルールの評価が走り、その結果がResourceComplianceとして記録されます。</li>
</ul>
</li>
</ul>
<p><strong>まとめると:</strong>
ENIの変更が起点となり、関連するSubnet、SecurityGroupの記録が連鎖的に発生し、
さらにそれぞれのConfigルール評価が走ることで、記録回数が増加していました。
コンテナやサーバーレスを多用している環境ほど、この傾向は顕著になります。</p>
<h2>解決策：記録頻度の最適化</h2>
<p>検証の結果、選択肢2の<a href="https://github.com/aws-solutions/aws-control-tower-customizations">aws-control-tower-customizations</a>はControl Towerの検出コントロールやドリフト検出に影響しないことが判明しました。
こちらのソリューションはControl Tower側で変更があった場合にもドリフトが発生しないよう設計されているため、安全に記録頻度の変更を展開できると判断し、選択肢2の実施に踏み切りました。</p>
<h3>方針：リソースタイプごとに記録方式を分ける</h3>
<p>すべてのリソースを一律に変更するのではなく、コスト構造を分析した上でリソースタイプごとに最適な記録方式を選択しました。</p>
<p><strong>日次記録に変更したリソース：</strong></p>
<ul>
<li>EC2 NetworkInterface</li>
<li>EC2 Subnet</li>
<li>EC2 SecurityGroup</li>
</ul>
<p><strong>連続記録のまま維持したリソース：</strong></p>
<ul>
<li>上記以外のリソースタイプ</li>
<li>Config ResourceCompliance<ul>
<li><a href="https://docs.aws.amazon.com/ja_jp/config/latest/developerguide/select-resources-console.html#select-resources-console-considerations">日次記録非対応</a></li>
</ul>
</li>
</ul>
<p>連続記録は記録回数ベース、日次記録はリソース数ベースの課金です。
記録回数がリソース数に対して大幅に多いリソースタイプだけを日次記録に変更し、
それ以外は連続記録のまま維持するのが最もコスト効率が良い方法です。</p>
<h3>展開方法</h3>
<p>記録頻度の変更は<a href="https://github.com/aws-solutions/aws-control-tower-customizations">aws-control-tower-customizations</a>を利用し、管理アカウント上でCloudFormationテンプレートを展開することで、Control Tower管理下の全アカウントに一括適用しました。</p>
<h3>セキュリティへの影響</h3>
<p>日次記録にすると、変更の途中経過は記録されません。
また、Configルールの評価タイミングはルールのトリガー方式（変更通知トリガーか、定期評価か）によって異なります。</p>
<p>スケジュールベースの定期評価ルールは、記録頻度にかかわらず設定された評価間隔で実行されます。
一方で、設定変更検知ベース（変更通知トリガー）のルールについては、日次記録の場合、評価に利用される設定情報が最大24時間前の状態となるため、実際の設定違反検知が最大24時間遅延しうる点に注意が必要です。</p>
<p>ただし、弊社環境では以下のサービスと併用することで、セキュリティ要件は維持できると判断しました。</p>
<ul>
<li>CloudTrailでAPIレベルの変更履歴は引き続き記録される</li>
<li>Security Hubでのセキュリティ準拠チェック</li>
<li>GuardDutyでの異常検知</li>
<li>SIEMサービスを利用した通知・分析</li>
</ul>
<h2>結果</h2>
<p>以下はCost Explorerでの日別コスト推移です。
切り替え前後でコストが大幅に低下していることがわかります。</p>
<p><img src="/assets/blog/authors/i.kobayashi/2026-03/02.png" alt="Cost Explorer日別コスト推移"></p>
<h2>学び・Tips</h2>
<h3>1. コスト構造の理解が重要</h3>
<p>AWS Configの料金は「記録回数」に基づくため、以下を理解することが重要です。</p>
<ul>
<li>どのリソースタイプが多く記録されているか</li>
<li>なぜそのリソースが頻繁に記録されるのか</li>
<li>記録頻度を変更できるリソースはどれか</li>
</ul>
<p>リソースタイプ別の記録回数は、CloudWatch メトリクス（AWS/Config ネームスペース）の<code>ConfigurationItemsRecorded</code>をResourceType別に確認できます。
AIエージェントにCloudWatchを参照させて調査することもできます。
また、リソース数は以下のコマンドで取得できます。</p>
<pre><code class="language-bash">aws configservice get-discovered-resource-counts --region ap-northeast-1
</code></pre>
<h3>2. この最適化が向いていないケース</h3>
<ul>
<li>すべてのリソースでリアルタイム記録が必須</li>
<li>変更の途中経過も記録が必要</li>
<li>セキュリティ要件が厳しく、日次記録では不十分</li>
<li>Configルールを利用した自動修復などを利用している</li>
</ul>
<h2>まとめ</h2>
<p>今回は記録回数の多いリソースタイプを特定し、日次記録に切り替えることで約80%のコスト削減を実現しました。
セキュリティ要件はCloudTrail、Security Hub、SIEMなどで補完できるため、実運用上の問題もありません。</p>
<p>本記事が同様の課題を抱えている方の参考になれば幸いです。</p>
<h2>参考資料</h2>
<ul>
<li><a href="https://aws.amazon.com/jp/config/pricing/">AWS Config 料金</a></li>
<li><a href="https://docs.aws.amazon.com/config/latest/developerguide/stop-start-recorder.html">AWS Config の記録モード</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Claude Code のサンドボックス機能を徹底検証してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-09-claude-code-sandbox/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-09-claude-code-sandbox/</guid>
            <pubDate>Mon, 09 Mar 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[Claude Codeのサンドボックスとpermissions.denyの2段構えについて、仕組みの解説から10種類のバイパス試行まで、macOS環境で検証した結果をまとめました]]></description>
            <content:encoded><![CDATA[<p>こんにちは！
Principal Generative AI Engineerの森田です。私の所属するAIファーストGでは、社内の生成AI活用にとどまらず、販売店やトヨタグループにおけるAI活用支援を行っております。</p>
<p>KINTOテクノロジーズでは、AIファーストを掲げ、全社員が必要な生成AIツールを申請し利用することができます。開発に関するものだけでもClaude Code、GitHub Copilot、Devin、Kiroなど、開発者が選べる環境が整っています。</p>
<p>今回は、社内でも特に利用者が多いClaude Codeのサンドボックス機能について調査しました。サンドボックスとは、Bashコマンドの実行をファイルシステム・ネットワークの両面からOSレベルで隔離するセキュリティ機能です。</p>
<h2>はじめに</h2>
<p>Claude Codeを使っていると、こんな場面に遭遇しないでしょうか。</p>
<p>コードの修正やコマンドの実行を任せると、操作のたびに「許可しますか？（Y/N）」と確認が入ります。意図しない操作を防ぐための仕組みなので当然ではあるのですが、これが何十回と続くと正直つらい。かといって、確認なしの自動承認モードにするのは怖い。プロンプトインジェクションやサプライチェーン攻撃など、外部からの脅威を考えると、何でも自動承認するわけにはいきません。</p>
<p>毎回確認していたら承認疲れで結局よく読まずに「Y」を押し続けてしまう。これが一番よくないパターンです。私自身、まさにこの状態に陥っていました。</p>
<p><img src="/assets/blog/authors/kazuaki.morita/2026-03-09-claude-code-sandbox/image01.png" alt=""></p>
<p>そんな中、社内の勉強会で同僚の太田さんがサンドボックス機能を紹介していました。ファイルシステムとネットワークの操作範囲をOSレベルで制限することで、「この範囲内なら自由にやらせていい。万が一おかしな操作があっても、被害を最小限に抑えることができる」という状態を作れるという説明でした。</p>
<p>承認疲れから解放されつつ、セキュリティも確保できる。早速自分でも追加調査を行い、実際にどこまで堅牢なのかを手元で検証してみました。本記事はその結果をまとめたものです。なお、検証はmacOS（Seatbelt）環境で行っています。</p>
<h2>サンドボックスとは</h2>
<p>Claude Codeのサンドボックスは、Bashコマンドの実行をファイルシステム・ネットワークの両面からOSレベルで隔離するセキュリティ機能です。</p>
<table>
<thead>
<tr>
<th>領域</th>
<th>デフォルトの制限</th>
</tr>
</thead>
<tbody><tr>
<td>ファイルシステム</td>
<td>カレントディレクトリ配下は読み書き可能。それ以外は読み取り専用</td>
</tr>
<tr>
<td>ネットワーク</td>
<td>許可されたドメインのみアクセス可（ホワイトリスト形式）</td>
</tr>
</tbody></table>
<p>OSのネイティブ機能で強制されるのが大きな特徴です。macOSではSeatbelt（カーネルレベルのサンドボックス機構）、Linux/WSL2ではbubblewrapが使われます。</p>
<h3>なぜ自動承認が安全になるのか</h3>
<p>サンドボックスが有効な状態では、書き込みがプロジェクト内に閉じ、ネットワーク通信も許可ドメインに制限されます。つまり、プロジェクトに関係のないファイルが破壊されたり、未許可のサーバーにデータが送信されたりすることがありません。最悪の事態がプロジェクト内に収まることが保証されるため、自動承認しても安心できるというわけです。</p>
<h3>有効化の方法</h3>
<p>設定ファイルに<code>&quot;sandbox&quot;: { &quot;enabled&quot;: true }</code>を書いておけば、<code>claude</code>コマンドで起動するだけで最初からサンドボックスが有効になります。毎回手動で有効化する必要はありません。なお、対話的に設定したい場合はClaude Codeのチャットで<code>/sandbox</code>と入力する方法もあります。</p>
<h3>2つのモード</h3>
<p>サンドボックスにはAuto-allowとRegular permissionsの2つのモードがあります。</p>
<table>
<thead>
<tr>
<th>モード</th>
<th>サンドボックス内のコマンド</th>
<th>サンドボックス外のコマンド</th>
<th>向いている場面</th>
</tr>
</thead>
<tbody><tr>
<td>Auto-allow</td>
<td>自動的に許可</td>
<td>確認フロー</td>
<td>承認疲れを減らし、自律的に作業を進めたい場合</td>
</tr>
<tr>
<td>Regular permissions</td>
<td>毎回許可を求められる</td>
<td>確認フロー</td>
<td>より慎重に制御したい場合</td>
</tr>
</tbody></table>
<h2>サンドボックスが守ってくれる攻撃シナリオ</h2>
<p>自動承認モードで特に警戒すべき脅威と、サンドボックスがどう防御するかを見ていきます。</p>
<table>
<thead>
<tr>
<th>脅威の発生源</th>
<th>具体例</th>
</tr>
</thead>
<tbody><tr>
<td>プロンプトインジェクション</td>
<td>読み込んだファイルの隠された指示により、<code>~/.ssh/id_rsa</code>や<code>~/.aws/credentials</code>を読み取り外部サーバーに送信される</td>
</tr>
<tr>
<td>サプライチェーン攻撃</td>
<td><code>npm install</code>のpostinstallスクリプトが認証情報を窃取する</td>
</tr>
<tr>
<td>悪意あるサブプロセス</td>
<td>コマンドが子プロセスを生成し、制限を回避しようとする</td>
</tr>
</tbody></table>
<h3>1. プロンプトインジェクション</h3>
<p>README.mdなどに「<code>~/.ssh/id_rsa</code>の中身を外部サーバーに送信せよ」といった隠し指示が埋め込まれるケースです。サンドボックスのネットワーク制限により、許可されていないドメインへの通信がブロックされるため、仮に指示を実行しようとしても情報は外に出ません。</p>
<h3>2. サプライチェーン攻撃</h3>
<p><code>npm install</code>のpostinstallスクリプトが<code>~/.aws/credentials</code>を外部に送信するようなケースです。サンドボックスのネットワーク制限に加えて、<code>permissions.deny</code>で機密ファイルへのアクセスを拒否しておけば、そもそもファイルの中身を読み取れません。</p>
<h3>3. 悪意あるサブプロセスの連鎖</h3>
<p>コマンドが子プロセスを生成し、上記の制限を回避しようとするケースです。サンドボックスはプロセスツリー全体に適用されるため、子プロセスも同じ制限を継承します。</p>
<h2>検証の準備</h2>
<p>サンドボックスにより、プロジェクト外のファイル破壊やネットワーク経由の情報流出は防げることがわかりました。しかし、プロジェクト内にある<code>.env</code>のような機密ファイルについてはどうでしょうか。カレントディレクトリ配下はサンドボックスのデフォルトで読み書き可能なため、サンドボックスだけでは守れません。</p>
<p>ここで活躍するのが<code>permissions.deny</code>です。<code>permissions.deny</code>に指定したパスはサンドボックスの拒否リストにもマージされ、Bashコマンドに対してはOSレベルで、Read/Edit等のツールに対してはアプリケーション層でアクセスをブロックします。</p>
<p>今回の検証では、<code>permissions.deny</code>で保護したファイルに対して、Claude Codeにあらゆる手段でアクセスを試みさせ、実際にブロックされるかを確認します。試行するバイパス手法は以下の通りです。</p>
<table>
<thead>
<tr>
<th>#</th>
<th>手法</th>
<th>狙い</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Node.jsスクリプト</td>
<td>別言語ランタイムからの読み取り</td>
</tr>
<tr>
<td>2</td>
<td>シンボリックリンク経由</td>
<td>リンクで保護パスを迂回</td>
</tr>
<tr>
<td>3</td>
<td>ファイルコピー（cp）</td>
<td>コピーによる間接的な読み取り</td>
</tr>
<tr>
<td>4</td>
<td>Python</td>
<td>さらに別の言語ランタイム</td>
</tr>
<tr>
<td>5</td>
<td>macOS<code>open</code>コマンド</td>
<td>OS標準コマンドでの読み取り</td>
</tr>
<tr>
<td>6</td>
<td>macOS<code>ditto</code>コマンド</td>
<td>ファイル複製ユーティリティ</td>
</tr>
<tr>
<td>7</td>
<td>バイナリダンプ（xxd）</td>
<td>子プロセス経由のバイナリ読み取り</td>
</tr>
<tr>
<td>8</td>
<td>tarでアーカイブ化</td>
<td>アーカイブ経由の読み取り</td>
</tr>
<tr>
<td>9</td>
<td>Readツール直接</td>
<td>Claude Code内蔵ツール</td>
</tr>
<tr>
<td>10</td>
<td>Grepツール</td>
<td>Claude Code内蔵ツール</td>
</tr>
</tbody></table>
<p>用意した<code>.claude/settings.json</code>は以下の通りです。</p>
<pre><code class="language-json">{
  &quot;permissions&quot;: {
    &quot;deny&quot;: [
      &quot;Edit(.claude/**)&quot;,
      &quot;Read(.env)&quot;,
      &quot;Edit(.env)&quot;,
      &quot;Read(./secrets/**)&quot;,
      &quot;Edit(./secrets/**)&quot;
    ]
  },
  &quot;sandbox&quot;: {
    &quot;enabled&quot;: true,
    &quot;autoAllowBashIfSandboxed&quot;: true,
    &quot;allowUnsandboxedCommands&quot;: false,
    &quot;network&quot;: {
      &quot;allowedDomains&quot;: [
        &quot;github.com&quot;,
        &quot;api.github.com&quot;
      ]
    }
  }
}
</code></pre>
<p><code>permissions.deny</code>で<code>.env</code>と<code>./secrets/**</code>を明示的にブロックし、検証用のダミーファイルとして<code>.env</code>（ダミーの秘密情報）と<code>secrets/credentials.json</code>を配置しました。</p>
<p><code>allowUnsandboxedCommands: false</code>は、コマンドがサンドボックスの制限に引っかかって失敗した場合の挙動を制御します。デフォルトの<code>true</code>ではサンドボックスの外で再実行を試みますが、<code>false</code>にすると失敗したらそのまま失敗。サンドボックスの外には一切出られなくなります。</p>
<p>なお、今回はファイルシステム制限に焦点を当てており、ネットワーク制限の検証は対象外です。</p>
<h2>検証結果</h2>
<h3>基本的なアクセス制御</h3>
<p>サンドボックスを有効にした状態で、Claude Codeにファイルの一覧を確認させたところ、<code>.env</code>と<code>secrets/</code>は一覧にすら表示されませんでした。</p>
<pre><code>sandbox/
├── .claude/
│   └── settings.json
├── src/
│   └── app.js
├── CLAUDE.md
└── TESTS.md
</code></pre>
<p>実際には<code>.env</code>と<code>secrets/</code>が存在しますが、<code>ls</code>でもGlobツールでも見えません。<code>secrets/</code>配下にどんなファイルがあるかすらわからない状態です。</p>
<h3>バイパス出来ないかClaude Codeで検証</h3>
<p>Claude Codeに「<code>.env</code>をどうにかして読み取ってほしい」と依頼し、あらゆる手法を試させました。</p>
<p>代表的な出力を2つ紹介します。</p>
<p>1. Node.jsスクリプトでの試行では<code>EPERM</code>が返りました。</p>
<pre><code>$ node src/read_env.js
Failed to read .env: EPERM: operation not permitted, open &#39;/path/to/sandbox/.env&#39;
</code></pre>
<p>5. macOSの<code>open</code>コマンドでは、ファイルが存在しないかのように振る舞いました。</p>
<pre><code>$ open .env
The file .env does not exist.
</code></pre>
<p>他の手法もすべて同様にブロックされました。結果の一覧は以下の通りです。</p>
<table>
<thead>
<tr>
<th>#</th>
<th>手法</th>
<th>結果</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Node.jsスクリプト</td>
<td><code>EPERM: operation not permitted</code></td>
</tr>
<tr>
<td>2</td>
<td>シンボリックリンク経由</td>
<td><code>Operation not permitted</code></td>
</tr>
<tr>
<td>3</td>
<td>ファイルコピー（cp）</td>
<td><code>Operation not permitted</code></td>
</tr>
<tr>
<td>4</td>
<td>Python</td>
<td><code>PermissionError: Operation not permitted</code></td>
</tr>
<tr>
<td>5</td>
<td>macOS<code>open</code>コマンド</td>
<td><code>The file .env does not exist.</code></td>
</tr>
<tr>
<td>6</td>
<td>macOS<code>ditto</code>コマンド</td>
<td><code>Cannot get the real path for source</code></td>
</tr>
<tr>
<td>7</td>
<td>バイナリダンプ（xxd）</td>
<td><code>Operation not permitted</code></td>
</tr>
<tr>
<td>8</td>
<td>tarでアーカイブ化</td>
<td><code>Cannot stat: Operation not permitted</code></td>
</tr>
<tr>
<td>9</td>
<td>Readツール直接</td>
<td>ブロック</td>
</tr>
<tr>
<td>10</td>
<td>Grepツール</td>
<td>ブロック</td>
</tr>
</tbody></table>
<p><code>permissions.deny</code>に指定したパスはOSカーネルレベルでブロックされるため、プログラミング言語やコマンドを変えても回避できません。Bashツールから起動されるプロセスはすべて同じポリシーを継承します。</p>
<h2>まとめ</h2>
<p>Claude Codeのセキュリティは、サンドボックスと<code>permissions.deny</code>の2段構えで成り立っています。</p>
<p>サンドボックスは、書き込みをプロジェクト内に閉じ、ネットワーク通信を許可ドメインに制限します。これにより、プロジェクト外のファイル破壊や未許可サーバーへのデータ送信が防がれ、自動承認モードを安心して利用できます。</p>
<p>さらに、特定のファイルやディレクトリをClaude Codeから見せたくない場合は<code>permissions.deny</code>が有効です。今回の検証では<code>.env</code>を題材に10種類のバイパスを試行し、すべてブロックされることを確認しました。<code>permissions.deny</code>のルールはサンドボックスの拒否リストにマージされ、Bashコマンドに対してはOSカーネルレベルで、Read/Edit等のツールに対してはアプリケーション層で強制されるため、プログラミング言語やコマンドを変えても回避できません。</p>
<p>実運用では、サンドボックスの読み取り専用アクセスはプロジェクト外にも及ぶ点に注意が必要です。たとえば<code>~/Documents</code>や<code>~/Desktop</code>にはClaude Codeに見せる必要のないファイルがあるはずです。<code>permissions.deny</code>でこれらのディレクトリを拒否しておけば、意図しない読み取りを防げます。</p>
<p>Claude Codeを日常的に使っている方は、ぜひサンドボックスの導入を検討してみてください。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Compose化した画面の初期化処理、initブロックでやるか？LaunchedEffect(Unit)内でやるか？]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-01-22-how-to-initialize/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-01-22-how-to-initialize/</guid>
            <pubDate>Tue, 03 Mar 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[Compose化した画面の初期化処理、initブロックでやるか？LaunchedEffect(Unit)内でやるか？]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>my route開発部のAndroidエンジニア、Romie(<a href="https://twitter.com/Romie_1112">@Romie_1112</a>)です。
my routeのAndroidチームではUIの実装をxmlからJetpack Compose(以下Compose)へと粛々と切り替えております。
現在は地域別の特集コンテンツを並べた画面をCompose化しています。 希望の順番で並べ替えることもできます。
以下の順番で初回表示を行います。</p>
<pre><code class="language-初回表示時">1. 画面遷移する
2. 希望の順番を初期値：おすすめ順に設定する
3. リクエストの時に希望の順番をAPIに渡す
4. データを取得する
5. 取得したデータの一覧を表示する
</code></pre>
<p>実装する中で<code>4. データを取得する</code>処理について迷ったので、今回はそのお話をしたいと思います。</p>
<h1>初期化の実装方法</h1>
<p>これまでの実装は、希望の順番を渡してAPIを叩いた結果を<code>LiveData</code>で通知し、<code>observe</code>で監視して値を取得してから画面を表示していました。
そのため、値を取得する前の初期化処理は実装されていませんでした。
しかし今回Compose化に伴いUiStateの値が変わればリアクティブプログラミングで即Fragmentに反映する<code>StateFlow</code>に変えることにし、<code>LaunchedEffect(Unit)</code>内で初期化するよう実装しました。
ここで初期化の実装にあたり、私は次に挙げる2つの方法で迷いました。</p>
<h2>1. initブロックで初期化する場合</h2>
<p>intiブロックで初期化する場合、以下のような実装になります。</p>
<pre><code class="language-kotlin:UiState">data class FeatureSummaryListUiState(
    val featureSummaryList: List&lt;一覧のアイテム&gt; = emptyList(),
)
</code></pre>
<pre><code class="language-kotlin:ViewModel">private val _sortType = MutableStateFlow(おすすめ順)
private val _uiState = MutableStateFlow(FeatureSummaryListUiState())
val uiState = _uiState.asStateFlow()

init {
    viewModelScope.launch {
        _sortType.collectLatest { sortType -&gt;
            val summary = (APIを叩いてデータを取得)
            _uiState.update {
                it.copy(
                    featureSummaryList = (設定したい初期値),
                )
            }
        }
    }
}
</code></pre>
<pre><code class="language-kotlin:Fragment">setContent {
    MyRouteTheme {
        val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
        FeatureSummaryListScreen(
            uiState = uiState,
        )
    }
}
</code></pre>
<p>initブロックについての記載を<a href="https://kotlinlang.org/docs/classes.html#initializer-blocks">公式リファレンス</a>[^1]から見てみましょう。</p>
<blockquote>
<p>The primary constructor initializes the class and sets its properties. In most cases, you can handle this with simple code.
If you need to perform more complex operations during instance creation, place that logic in initializer blocks inside the class body. These blocks run when the primary constructor executes.
Declare initializer blocks with the init keyword followed by curly braces {}. Write within the curly braces any code that you want to run during initialization:</p>
</blockquote>
<p>initブロックは引用にもあります通り、インスタンスが形成された時に実行されるものになります。
インスタンスが形成された時に一度だけ呼ばれますので、初期化の処理を書くのにぴったりです。
ただし、initブロックはインスタンス形成時に呼ばれるという性質上、単体テストで初期化がちゃんとできているか見ることが厳しく、また単体テストの記載に慣れていないとinitブロックを考慮したテストを書くのが大変です。</p>
<h2>2. LaunchedEffect(Unit)内で初期化する場合</h2>
<p>では、FragmentからViewModel内の初期化処理をコールした場合はどうでしょうか。
最初に一度だけ呼ぶ処理だとコードを読む人に明示するため<code>LaunchedEffect(Unit)</code>の中に書くことをお勧めします。</p>
<pre><code class="language-kotlin:UiState">data class FeatureSummaryListUiState(
    val featureSummaryList: List&lt;一覧のアイテム&gt; = emptyList(),
)
</code></pre>
<pre><code class="language-kotlin:ViewModel">private val _sortType = MutableStateFlow(おすすめ順)
private val _uiState = MutableStateFlow(FeatureSummaryListUiState())
val uiState = _uiState.asStateFlow()

fun initFeatureSummaryListUiState() { // initがfunになっています
    viewModelScope.launch {
        _sortType.collectLatest { sortType -&gt;
            val summary = (APIを叩いてデータを取得)
            _uiState.update {
                it.copy(
                    featureSummaryList = (設定したい初期値),
                )
            }
        }
    }
}
</code></pre>
<pre><code class="language-kotlin:Fragment">setContent {
    MyRouteTheme {
        val uiState = viewModel.uiState.collectAsStateWithLifecycle().value
        LaunchedEffect(Unit) {
            viewModel.initFeatureSummaryListUiState() // ここが違う！
        }
        FeatureSummaryListScreen(
            uiState = uiState,
        )
    }
}
</code></pre>
<p><a href="https://developer.android.com/develop/ui/compose/side-effects?hl=ja">Composeにおける副作用</a>[^2]に副作用(<code>LaunchedEffect</code>)の説明がございます。</p>
<blockquote>
<p>副作用とは、コンポーズ可能な関数の範囲外で発生するアプリの状態の変化を指します。
コンポーザブルのライフサイクルとプロパティ（予測できない再コンポジション、異なる順序でのコンポーザブルの再コンポジション、破棄可能な再コンポジションなど）により、コンポーザブルは副作用がないようにするのが理想的です。
ただし、スナックバーを表示するなどの1回限りのイベントをトリガーする場合や、特定の状態で別の画面に移動する場合などに、副作用が必要になることがあります。
これらのアクションは、コンポーザブルのライフサイクルを認識している制御された環境から呼び出す必要があります。</p>
</blockquote>
<p>そして、<a href="https://developer.android.com/develop/ui/compose/side-effects?hl=ja#rememberupdatedstate">こちらの章</a>[^3]により具体的な記載がございます。</p>
<blockquote>
<p>コールサイトのライフサイクルと一致する作用を作成するには、Unitやtrueのような決して変化しない定数をパラメータとして渡します。</p>
</blockquote>
<p>この実装には次の一般的なメリットがあります。</p>
<ul>
<li>再利用性：どの箇所からでも呼び出せる</li>
<li>テスト容易性：独立した関数で実装しているため単体テストがやりやすい</li>
</ul>
<p>また、プロジェクト内のメリットとして以下が挙げられます。</p>
<ul>
<li>既存のコードとの整合性：他の箇所を確認したところCompose化した画面の初期化は<code>LaunchedEffect(Unit)</code>内で行っていることが多く整合性が取りやすい</li>
</ul>
<p>ただし、デメリットもあります。</p>
<ul>
<li>UDFの法則に反する：ViewModel→Fragmentという単方向でデータが流れる[^4]べきなのにFragment→ViewModelとなってしまう[^5] </li>
<li>依存度が高まり疎結合が崩れる：FragmentでViewModelの処理が呼ばれると依存度が高まりMVVMの目的の１つである疎結合が崩れる</li>
<li>呼び忘れる恐れがある：<code>LaunchedEffect(Unit)</code>をはじめどこからでも呼び出せる代わりに呼び忘れる恐れがある</li>
</ul>
<h3>補足：発展編</h3>
<p>今回の内容についてより高度な議論をJaewoong Eum氏が<a href="https://proandroiddev.com/loading-initial-data-in-launchedeffect-vs-viewmodel-f1747c20ce62">こちらの記事</a>[^6]にて行っております。
Androidコミュニティに対してアンケートを取得した上で、Ian Lake氏のツイートを引用してinitブロックも<code>LaunchedEffect(Unit)</code>内での初期化もアンチパターンであり
<code>SharingStarted.WhileSubscribed(5_000)</code>を活用した初期値の設定を紹介しています。</p>
<p>ただ、私は以下の懸念について検討した上で今回は<code>SharingStarted.WhileSubscribed(5_000)</code>を使用しませんでした。
一般的な点では</p>
<ul>
<li>可読性の低下：複数のプロパティを持つUiStateを<code>SharingStarted.WhileSubscribed(5_000)</code>で管理すると実装が複雑になり却って可読性が下がる</li>
</ul>
<p>プロジェクト内の点では</p>
<ul>
<li>既存のコードとの整合性の低下：<code>LaunchedEffect(Unit)</code>内で初期化している画面が多いことから既存のコードとの整合性が取りづらくなる</li>
</ul>
<p>です。</p>
<p>Jaewoong Eum氏の記事は今回ご紹介したものも含めて非常に勉強になりますので、全て英語ですが興味のある方は是非読んでみてください。</p>
<h1>まとめ</h1>
<p>今回は<code>LaunchedEffect(Unit)</code>内で初期化したのですが、initブロックで初期化する場合と<code>LaunchedEffect(Unit)</code>内で初期化する場合、2つのメリットとデメリットを比較した上で、以下の点を重視しました。</p>
<blockquote>
<ul>
<li>テスト容易性：独立した関数で実装しているため単体テストがやりやすい</li>
<li>既存のコードとの整合性：他の箇所を確認したところCompose化した画面の初期化は<code>LaunchedEffect(Unit)</code>内で行っていることが多く整合性が取りやすい</li>
</ul>
</blockquote>
<p>また、希望の順番を変えて並べ替えを行った時以下の順番で再表示を行います。</p>
<pre><code class="language-希望の順番を変えて並べ替えを行った時">1. 並べ替えボタンを押下する
2. 希望の順番を任意の並べ替えに設定する
3. リクエストの時に希望の順番をAPIに渡す
4. データを取得する
5. 取得したデータの一覧を表示する
</code></pre>
<p>ここから<code>4. データを取得する</code>処理を1つの関数で実装し、初回表示時も希望の順番を変えて並べ替えを行った時も希望の順番をAPIに渡して関数を呼び出す形にした方がいいと考えました。
よって再利用性も重視しました。</p>
<blockquote>
<ul>
<li>再利用性：どの箇所からでも呼び出せる</li>
</ul>
</blockquote>
<p>理想を追求するといろんな方法が出てきますが、アンチパターンとされているものがあっても正解は1つではないですし、チーム内でレビューすること・後々の拡張性やテスト容易性を考慮しその都度1番良い実装を選択できると良いですね。
一番大切なのは、自分なりに理由や根拠を明確にして実装することです。</p>
<p>読んでいただきありがとうございました。それでは次の記事で。</p>
<p>[^1]: 出典元：<a href="https://kotlinlang.org/docs/classes.html#initializer-blocks">Classes: Constructors and initializer blocks: Initializer blocks</a>より一部抜粋
[^2]: 出典元：<a href="https://developer.android.com/develop/ui/compose/side-effects?hl=ja">Composeにおける副作用</a>より一部抜粋
[^3]: 出典元：<a href="https://developer.android.com/develop/ui/compose/side-effects?hl=ja#rememberupdatedstate">rememberUpdatedState: 値が変化しても再起動すべきでない作用の値を参照する</a>より一部抜粋
[^4]: ViewModel内の値をFragmentが参照できない(ViewModelで何が起きているかFragmentが知らない)状態
[^5]: FragmentがViewModel内で更新されている<code>featureSummaryList</code>を参照できる状態
[^6]: 出典元：<a href="https://proandroiddev.com/loading-initial-data-in-launchedeffect-vs-viewmodel-f1747c20ce62">Loading Initial Data in LaunchedEffect vs. ViewModel</a>より一部抜粋</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年11・12月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-03-02-newcomers-introduction-nov-dec/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-03-02-newcomers-introduction-nov-dec/</guid>
            <pubDate>Mon, 02 Mar 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年11月・12月に入社した8名の皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、2025年12月入社の齋藤です！</p>
<p>本記事では、2025年11月・12月に入社したメンバー8名に入社直後の感想をお伺いし、まとめました。
KINTOテクノロジーズ（以下、KTC）に興味のある方、そして、今回参加してくださったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>齋藤 諒太</h1>
<p>![齋藤 諒太さんのプロフィール画像](/assets/blog/authors/dowod/dowod.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>KINTO開発部でフロントエンドエンジニアとして働いています。新潟県出身で今は大阪市在住です。</li>
<li>業務としては主にKINTOのシミュレーションや申し込み、マイページの開発を行っています。</li>
<li>前職ではRailsやNext.jsで構成された比較メディアサイトの開発をフロントエンド・バックエンドの領域を問わず担当していました。</li>
<li>趣味は自作PC、ゲーセン、ペットの猫をこねることです。よろしくお願いします。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>Osaka Tech Labに3人、室町オフィスに6人の合計9人のチームです。</li>
<li>1週間単位のスプリントで開発を進めています。毎週のプランニングでタスクを決め、レトロスペクティブで成果と課題を振り返ります。</li>
<li>毎日デイリースクラムで進捗を共有し、互いの状況を把握することで効率的な開発体制を維持し、短いサイクルで改善を重ねています。</li>
</ul>
</li>
<li><strong>チームの雰囲気はどんな感じ？</strong><ul>
<li>拠点や勤務形態が多様でオンライン中心ですが、不明点があればすぐに質問でき、相談もチーム内外で活発に行われています。</li>
<li>課題や改善案があればADRを通じて提案できます。ADRはアーキテクチャに限らず、チームのルールや方針を幅広く決めるための仕組みとして活用しており、誰でもカジュアルに新しいアイデアを発信し、継続的な改善を進められる環境です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>これまで培った技術や知識を活かせる環境で働きたいと考え、以前から関心を持っていたKINTOのサブスクリプションサービスに、ユーザーとしてだけでなく開発者としても携わりたいと思い入社しました。</li>
<li>入社後、大きなギャップはありませんが、Osaka Tech Labは思っていたよりもまだ人数が少なく、落ち着いた雰囲気だった点はギャップかもしれません。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>JCTと駅直結の利便性です。外部イベントも開催され、気軽に参加できるうえ、雨でも濡れずに出社できます。</li>
</ul>
</li>
<li><strong>フクロウさん ⇒　齋藤 諒太さんへの質問</strong><blockquote>
<p>おすすめのアプリやサービスありますか？</p>
</blockquote>
<ul>
<li>10年以上1Passwordを使っています。覚えておくのは1Passwordのマスターパスワードだけで済み、強力なパスワードを自動生成して保存・同期してくれるのでとても楽です。さらにクレジットカード情報の管理機能や、パスワードの使い回し・漏えいを自動検出して通知するセキュリティ監査機能も備わっています。Windows、Mac、iOS、Androidに加え、ブラウザ拡張機能にも対応しており、ほぼすべての環境で使える点も魅力です。</li>
</ul>
</li>
</ul>
<h1>うえぽん</h1>
<p>![うえぽんさんのプロフィール画像](/assets/blog/authors/dowod/2026-03-02-newcomer-202511-12/uepon.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>デジタル戦略部DataOpsG所属となります！</li>
<li>前職はSESエンジニアとして多様な業種、システムにかかわってきました。</li>
<li>趣味は釣りで最近は月に1回程度しか行けていないので食卓と話題のネタを仕入れに行かなければ。という意気込みです！</li>
<li>よろしくお願いします。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>チーム内でもデータの蓄積を行う基盤チーム、蓄積したデータを提供する仕組みを扱うnicolaチームという構成になっていて、全体で9名の体制です。</li>
</ul>
</li>
<li><strong>チームの雰囲気はどんな感じ？</strong><ul>
<li>それぞれの強みを生かして日々業務や技術・知識習得に取り組んでいます。</li>
<li>共有の場では積極的に深掘りをしてチームとしての向上心が高いと感じています！</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>特定の分野で技術を磨き自身の強みとしたい！</li>
<li>フルスタックエンジニアとしての経験を活かせる！</li>
<li>入社前に丁寧な説明をしていただけて、業務環境についてギャップはなかったです。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>名古屋オフィスは駅の地下街から直結されているため悪天候の影響を受けずに出社できます！</li>
</ul>
</li>
<li><strong>齋藤 諒太さん ⇒　うえぽんさんへの質問</strong><blockquote>
<p>これまで多くの現場を経験されたとのことですが、特に印象に残っている現場はありますか？</p>
</blockquote>
<ul>
<li>銀行関係の現場なこともありセキュリティー意識がとても高かったです。（検証環境エリア、本番環境エリア共に作業者・作業理由・作業時間の事前申請必須など）</li>
<li>また、利用者がいない時間に更新するため、深夜当番と早朝当番を月1でやっていました。</li>
</ul>
</li>
</ul>
<h1>debugon</h1>
<p>![debugonさんのプロフィール画像](/assets/blog/authors/dowod/2026-03-02-newcomer-202511-12/debugon.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>Engineering Officeでアクセシビリティを社内文化にする仕事をしている辻です。KTCには辻さんが何人かいらっしゃるので、私のことは debugon と覚えてください。</li>
<li>AIで音楽を作るのが趣味です。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>それぞれの専門領域を持つメンバーが、東京、名古屋、福岡で活動するチームです。</li>
<li>専門的な知識を生かしつつ、他のメンバーの専門性との化学反応を生かし、社内の様々なチームの力を最大限に発揮できるように共創しています。</li>
</ul>
</li>
<li><strong>チームの雰囲気はどんな感じ？</strong><ul>
<li>複数拠点で活動するチームなので、オンラインやオフラインでコミュニケーションをしっかりとっています。</li>
<li>「食べ物」の話しが好きなメンバーが多いので、食べる話になるとSlackチャンネルが盛り上がります。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>モビリティカンパニーに文化としてアクセシビリティの考え方を広めたくて入社しました。</li>
<li>「一緒に良いものを作っていきたい」という考えの方がたくさんいらっしゃるので、とてもやりがいを感じています。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>トヨタ車の模型がたくさん置いてあって、それぞれの形を手で触って確認できたことがうれしかったです。</li>
</ul>
</li>
<li><strong>うえぽんさん ⇒　debugonさんへの質問</strong><blockquote>
<p>AIで音楽作成されるとのことですが、どんなジャンルの音楽が好きですか？制作に使うお気に入りのツールやソフトあれば教えてください！</p>
</blockquote>
<ul>
<li>音楽を作るときには Suno を使っています。ジャズが好きなのですが、気分のままにこれまでに聞いたいろいろなジャンルの音楽を思い出しながら作っています。</li>
</ul>
</li>
</ul>
<h1>なかぴー</h1>
<p>![なかぴーさんのプロフィール画像](/assets/blog/authors/dowod/2026-03-02-newcomer-202511-12/nakapy.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>障害者雇用枠で2025年12月に入社しました。在宅勤務です。</li>
<li>経歴としましてはSIerのエンジニアからキャリアをスタートして、事業会社の社内SE、PM、ITコンサルタントの経験があります。</li>
<li>伴走型のPMで、「餅は餅屋」をモットーに駆けずり回るスタイルでフットワークには定評がありました。</li>
<li>約1年半前に脳出血で左半身麻痺になりました。完全在宅の時短勤務で働けることが有難いです。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>開発支援部人事グループの中の労務総務チームです。チームは自分を含めて4名です。</li>
</ul>
</li>
<li><strong>チームの雰囲気はどんな感じ？</strong><ul>
<li>定例会では休日の様子も共有し合って和やかな雰囲気です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>入社動機：経験を活かしてエンジニアの方のサポートが出来そうだと感じたから。</li>
<li>入社前後のギャップ：1on1が多い。激務な職場が多かったのですが、今は業務量を調整してもらえて有難いです。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>室町オフィスがあるコレド室町はお洒落な商業ビルで駅直結なのでリハビリを頑張って出社したいです。</li>
</ul>
</li>
<li><strong>debugonさん ⇒　なかぴーさんへの質問</strong><blockquote>
<p>お気に入りのデスクアイテムや文房具は？</p>
</blockquote>
<ul>
<li>とにかく忘れないように、付箋を頻繁に使っています。シンプルなものが一番使いやすいです。</li>
</ul>
</li>
</ul>
<h1>miurat</h1>
<p>![miuratさんのプロフィール画像](/assets/blog/authors/dowod/2026-03-02-newcomer-202511-12/miurat.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>デジタル戦略部DataOpsGにデータエンジニアとしてジョインしました。</li>
<li>前職では、事業会社でデータ基盤構築やデジタルマーケティング関連の仕事に従事してきました。</li>
<li>趣味は、テニス、ゴルフでボールを打つことが好きです！</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>メンバーは東京・名古屋・大阪の3拠点あわせて計9名です！</li>
</ul>
</li>
<li><strong>チームの雰囲気はどんな感じ？</strong><ul>
<li>チーム全体の雰囲気は風通しが良く、相談や雑談もしやすい雰囲気です。</li>
<li>また、好奇心旺盛なメンバーが多く、最新のトレンドや業界ニュースなどを積極的に共有し合う文化が根付いています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>ビジョン・バリューに共感したからです！</li>
<li>入社後のギャップは、ドキュメントが整っているなと思いました！</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>大阪オフィスは、高層階の為、景色が綺麗です。また、駅直結なので、通勤が便利です！</li>
</ul>
</li>
<li><strong>なかぴーさん ⇒　miuratさんへの質問</strong><blockquote>
<p>データ分析で心がけていることは何ですか？</p>
</blockquote>
<ul>
<li>誰もがストレスなく使えるよう、複雑さを取り除いたシンプルな設計と、データの品質維持を心がけています。</li>
</ul>
</li>
</ul>
<h1>でこぽん</h1>
<p>![でこぽんさんのプロフィール画像](/assets/blog/authors/dowod/2026-03-02-newcomer-202511-12/dekopon.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>Cloud Infrastructure G にエンジニアとしてジョインしました。</li>
<li>前職では AWS 専業のエンジニアとしてシステム開発やお客様の内製化支援などを行っていました。</li>
<li>趣味はテニスや登山で、主に神奈川の山を登ってます！</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>10名程度の組織で、サービスを支えるインフラチーム、中長期的な課題への改善を繰り返すカイゼンチーム、そしてトヨタグループの CCoE を支えるソリューションチームがあり、私はソリューションチームに所属しています。</li>
</ul>
</li>
<li><strong>チームの雰囲気はどんな感じ？</strong><ul>
<li>和気あいあいな雰囲気です。</li>
<li>お昼は出社しているメンバーのほとんど全員で外に出て神保町のいろいろな美味しいお店を開拓しています。</li>
<li>二郎系ラーメンを食べる人が多くいます。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>ビジョンに対してチームで前向きに進んでいけそうな雰囲気に魅力を感じました。</li>
<li>入社後のギャップも特になく、自由な雰囲気で成果を出していくことができると思います。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>神保町オフィスに在籍しているのですが、周りに美味しいお店が無限にあります！</li>
</ul>
</li>
<li><strong>miuratさん ⇒　でこぽんさんへの質問</strong><blockquote>
<p>ストレス発散方法を教えてください！</p>
</blockquote>
<ul>
<li>同僚や友人とお酒を飲みに行くことです🍻</li>
</ul>
</li>
</ul>
<h1>Yanaggy</h1>
<p>![Yanaggyさんのプロフィール画像](/assets/blog/authors/dowod/2026-03-02-newcomer-202511-12/yanaggy.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>プロダクトマネージャーとして入社しました。</li>
<li>TOYOTA UPGRADE FACTORY/LEXUS UPGRADE FACTORYというクルマの「アップグレード」をWebで申し込めるサービスを担当しています。</li>
<li>漫画から小説までいろんな本を読むのが好きです。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>チームはFE/BEエンジニア、SRE、QA、ディレクター、PDMなど約10名からなり、東京、大阪にまたがっています。</li>
<li>PDMは東京1名、大阪1名の2名体制です。毎朝オンラインで話して案件状況や課題をシェアしながら案件を進めています。</li>
</ul>
</li>
<li><strong>チームの雰囲気はどんな感じ？</strong><ul>
<li>拠点は離れていますが、Slackの非同期コミュニケーション、オンラインでのデイリーMTG、ちょっとした相談など同期コミュニケーションを使い分けながら仕事を進めています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>これまでは金融やデジタルコンテンツのシステム開発をしており、次は実物のあるモノに関わる業界で仕事したかったのと、小寺さんがインタビューで語っていた「最初のクルマ、最後のクルマ」のコンセプトにひかれたからです。</li>
<li>良いギャップとしては職務・職種の経歴、年齢層など思ってたより様々な背景を持ったメンバと仕事できるのが刺激的です。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>大阪オフィスの机が広い。あとJCTと呼ばれているイベントを行える広いスペース。社内外の様々なイベントが開催されています。</li>
</ul>
</li>
<li><strong>でこぽんさん ⇒　Yanaggyさんへの質問</strong><blockquote>
<p>Osaka Tech Lab 周辺で一番お気に入りのランチもしくは居酒屋があれば教えてください！</p>
</blockquote>
<ul>
<li>九州ラーメン亀王。高校生の時から通っているラーメン店です。オフィスから少し離れていますがよく行きます。</li>
</ul>
</li>
</ul>
<h1>フクロウ</h1>
<p>![フクロウさんのプロフィール画像](/assets/blog/authors/dowod/2026-03-02-newcomer-202511-12/fukuro.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>開発支援部人事G採用チームに配属。</li>
<li>これまで在宅でバックオフィス業務に加え、デザインや動画制作などのクリエイティブ業務も経験してきました。</li>
<li>昨年まで療養期間がありましたが、体力づくりを経て、最近は本格的に筋トレに取り組もうと考えています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>開発支援部人事G採用チーム（7名）に所属しています。</li>
<li>採用計画に沿って、募集・面接・進捗共有などを進めながら、より良い採用に向けて日々改善しています。</li>
</ul>
</li>
<li><strong>チームの雰囲気はどんな感じ？</strong><ul>
<li>オンラインでのMTG参加はまだ多くありませんが、問題点の共有や改善に積極的に取り組む姿勢がうかがえます。</li>
<li>笑い声も絶えない和やかな雰囲気もあります。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>障害者雇用という立場ではありますが、面接時、他社に比べて良い意味で特別扱いされすぎず、他のメンバーと同じように見てもらえている点にとても好感。</li>
<li>入社後も必要な配慮は十分過ぎるほどありつつ、想像していたようなギャップは特に感じていません。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>完全在宅のためオフィス出社の機会はありませんが、社内外の様々なイベントに参加してみたいなと思っています。</li>
</ul>
</li>
<li><strong>Yanaggyさん ⇒　フクロウさんへの質問</strong><blockquote>
<p>体力・健康維持のためにやっていることはありますか？</p>
</blockquote>
<ul>
<li>基本的な体調管理はもちろん、障害の特性的に、体温と気温、食事の温度などは常に気にしています。</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTOテクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/dowod/2026-03-02-newcomer-202511-12/coverimage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[イラストをアートから「アセット」へ：DesignOps 視点で挑む、生成 AI プロンプトの標準化と工程化]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-02-27-designops-genai/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-02-27-designops-genai/</guid>
            <pubDate>Fri, 27 Feb 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[生成AIをDesignOpsの枠組みに組み込み、イラスト制作を属人的なアートから再現可能で標準化された“運用可能なアセット”へと進化させるプロセスと検証をまとめた取り組み。]]></description>
            <content:encoded><![CDATA[<h2><strong>はじめに｜なぜ“AI × DesignOps”なのか？</strong></h2>
<p>プロダクトが成長すればするほど、ビジュアルアセットの需要は指数関数的に増えていきます。しかし、デザイナーの数は（悲しいことに）指数関数的には増えません。</p>
<p>従来のイラスト制作は個人のスキルに依存しやすく、結果として品質のバラツキや制作スピードが開発ベロシティを阻害する「<strong>デザイン負債（Design Debt）</strong>」を生み出していました。DesignOps の本質は、デザインを「一点物のマニュアルアート」から「再現可能なシステム」へと昇華させることにあります。</p>
<p>私たちは今回、AI を単なる「便利な魔法の筆」ではなく、一貫性のあるデザインシステムを支える「<strong>レンダリングエンジン</strong>」として再定義する検証を行いました。</p>
<h2><strong>DesignOpsとは？</strong></h2>
<p>「DesignOps（デザインオプス）」という言葉、デザイナー以外にはまだ少し耳慣れないかもしれません。</p>
<p>簡単に言うと、<strong>DesignOps は</strong>「<strong>デザイン版の DevOps</strong>」です。 属人的になりがちなデザイン業務をプロセスとして整理し、担当者が変わってもチームとして一定の品質をデプロイできる状態を目指す考え方です。</p>
<p>今回の取り組みでは、生成 AI を「クリエイティブな遊び」としてではなく、プロダクトのコードベースの一部のように運用できるかを検証しました。特にエンジニア主体の組織においては、デザインも「<strong>ロジックとして扱えるか</strong>」が、持続可能な運用の鍵になります。</p>
<h2><strong>プロジェクトの背景：Unlimited App で直面した「成長の痛み」</strong></h2>
<p>Unlimited App ではこれまで、プロダクトデザイナーがイラストの世界観設計から品質の最終調整まで、いわば「職人のこだわり」をもって一貫して担ってきました。しかし、プロダクトが成長し、機能追加やコンテンツ拡張が加速するなか、必要とされるイラストの量と展開範囲は、私たちの想像を超えるスピードで拡大していきました。
<a href="https://kinto-jp.com/unlimited/app/">https://kinto-jp.com/unlimited/app/</a></p>
<p>その過程で、個人の努力だけではカバーしきれない「<strong>構造的なボトルネック</strong>」が浮き彫りになってきたのです。</p>
<ul>
<li><strong>工数の比例増加</strong>：表現を都度最適化する運用では、制作アセット数に比例して工数も積み上がっていく（まさに <em>O(n)</em> の世界です）。</li>
<li><strong>再現性の設計難度</strong>：クオリティの判断が個人の経験値に依存しやすく、「なぜこれが正解なのか」という基準を仕組みとして残しづらい。</li>
<li><strong>属人的なバランス調整</strong>：スピードと完成度のトレードオフを、常に個人の「さじ加減」で調整し続けなければならない。</li>
</ul>
<p>これらは決して個人のスキルの問題ではなく、プロダクトが次のステージへ進むために解決すべき「<strong>システム上の課題</strong>」でした。</p>
<p>そこで生まれたのが、「<strong>イラスト制作を個のスキルに依存する形から、再現性のある設計へとリファクタリングできないか？</strong>」という問いです。私たちはこの問いに対し、生成 AI という強力なエンジンを DesignOps のプロセスに組み込むことで、持続可能な制作体制の構築に挑みました。</p>
<blockquote>
<p><strong>※ [ ちょこっと技術解説 ]：<em>O(n)</em> とは？</strong><br/><br/>エンジニアがよく使う「計算量」を測る指標です。ここでは「描くイラストの枚数（<em>n</em>）」が増える分だけ、「制作時間」も正直に増えていくという<strong>線形の現実</strong>を指しています。<br/><br/>つまり、「<strong>単なる「魔法」ではなく、10 倍の依頼が来たら 10 倍の工数が必要になる</strong>」という、デザイナーにとっては少し切ない、そしてエンジニアにとっては「今すぐ最適化（リファクタリング）したい！」と血が騒ぐ状態のことです。</p>
</blockquote>
<h2><strong>今回の検証アプローチ｜“AIに寄せる” vs “AIを寄せる”</strong></h2>
<p>AI を活用する際、戦略は大きく 2 つに分かれます：</p>
<ul>
<li><strong>AI に寄せる（AI-Native Approach）</strong> AI が得意な表現（原生スタイル）をそのまま受け入れ、効率を最大化する。「AI っぽさ」を味方につける手法です。</li>
<li><strong>AI を寄せる（Brand-Centric Approach）</strong> 独自のブランドアイデンティティに基づき、AI の出力を厳格に制御する。</li>
</ul>
<p>Unlimited App はすでに確立された世界観を持つプロダクトです。そのため、前提条件として「AI を寄せる」<strong>アプローチを選択しました。これは、プロンプトを単なる命令文ではなく、ブランドの</strong>「デザイン・トークン（Design Tokens）」<strong>として定義し、AI という不確実な実行環境において</strong>「決定論的な出力（Deterministic Output）」を目指す、エンジニアリング的な挑戦でもあります。</p>
<p><img src="/assets/blog/authors/Amelie/260227/P02_CH04.jpg" alt="AI活用の2つのアプローチ比較図。「AIに寄せる（AI-Native Approach）」と「AIを寄せる（Brand-Centric Approach）」の難易度と成果の性質を対比。"> </p>
<h2><strong>イラスト標準化の設計思想とプロンプトアーキテクチャ</strong></h2>
<p>「AI なら、プロンプトひとつで何でも描いてくれるのでは？」——そんな期待は、運用フェーズに入った瞬間に崩れ去ります。実用的な DesignOps において、プロンプトは単なる「魔法の呪文」ではありません。明確な設計意図を AI へ伝えるための、厳密な「<strong>インターフェース</strong>」であるべきです。</p>
<p>標準化プロセスの構築にあたり、筆者は下図のような <strong>7つのステップ</strong>を策定しました。ビジュアル定義の抽出（Step 1）からリファレンスの整理（Step 4）までは、いわば「<strong>視覚的な仕様書</strong>（<strong>Visual Spec</strong>）」を書き上げる、設計の根幹を支えるフェーズです。</p>
<p><img src="/assets/blog/authors/Amelie/260227/P03_CH05.jpg" alt="イラスト標準化プロセスの7ステップ。ビジュアル定義（Step 1）から、プロンプト作成（Step 5）、反復・最適化（Step 7）までのワークフロー図。"> </p>
<h3><strong>Step 1〜4：プロンプトを「エンジニアリング」するための前処理</strong></h3>
<ul>
<li><p><strong>Step 1｜ビジュアル定義の抽出（Extracting Visual Tokens）</strong> 最初に取り組んだのは、デザインシステムとの整合性チェックです。2.5D の立体感、余白の持たせ方、低コントラストな配色……。これらを言語化する工程は、後に解説する 「<strong>Style Tokens</strong>（<strong>スタイル定数</strong>）」 の基盤となります。</p>
</li>
<li><p><strong>Step 2｜イラスト用途の分類（Defining the Scope）</strong> 生成 AI の活用範囲を「クリエイティブな表現」ではなく、「<strong>UX を補助するインフォグラフィック</strong>」 と定義しました。目的を限定することで、維持すべき「再現性」と、AI に任せるべき「表現幅」の境界線が明確になります。</p>
</li>
<li><p><strong>Step 3 &amp; 4｜リファレンスの構造解体（Deconstructing References）</strong> 大量のリファレンスを収集し、「好き嫌い」という感性ではなく、構図や影の強度といった「<strong>Visual Spec（視覚仕様）</strong>」として分解・整理しました。「なんとなく似ている」を「仕様通りである」に変えるための、最も泥臭く、かつ重要なリサーチ工程です。</p>
</li>
</ul>
<p>この Step 1〜4 の本質は、「<strong>AI に依存しない設計構造を人間側で作る</strong>」 ことにあります。</p>
<p>このプロセスで最もエキサイティングであり、かつ重要なのが <strong>Step 5 の「プロンプト作成」</strong> です。ここを単なる「作文」にせず、<strong>エンジニアリング的に構造化された工程</strong>として設計しました。</p>
<p>具体的には、プロンプトを以下の <strong>2 層構造（Layered Architecture）</strong> として定義しています：</p>
<p><img src="/assets/blog/authors/Amelie/260227/P04_CH05_2.jpg" alt="プロンプト設計の構造図。ビジュアルリファレンスからプロンプトジェネレータを経て、Style Tokensと Content Variablesの2層構造に落とし込む流れ。"> </p>
<h3><strong>Part 1：Style Tokens（スタイル定数層）</strong></h3>
<p>線画の太さ、立体感、陰影のルール、配色など、プロダクトの DNA を定義します。「何を描くか」に依存しない、<strong>再利用可能な Constant（定数）レイヤー</strong>です。</p>
<h3><strong>Part 2：Content Variables（コンテンツ変数層）</strong></h3>
<p>「車」「人物」「スマホを持つ手」など、画面ごとに差し替え可能な <strong>Dynamic Variable（動的変数）レイヤー</strong>です。</p>
<p>この 「<strong>スタイルとコンテンツの解耦（Decoupling）</strong>」 こそが、DesignOps 視点での解決策です。これにより、「何を描いても同じトーンで出力される」という、デザイナーにとっての聖杯（Holy Grail）を目指しました。</p>
<h3><strong>ツールごとに異なる「Prompt の最適解」</strong></h3>
<p>検証を進める中で面白いことが分かりました。AI ツールごとに「プロンプトの癖」が全く違うのです。以前は同一のプロンプトで比較していましたが、現在は「構造（Part 1 / Part 2）は共通、実装（実際の記述）は各ツールに最適化」<strong>という方針に切り替えました。 「プロンプトを共有する」のではなく、</strong>「プロンプトを設計するインターフェース（ルール）を共有する」。この方が、ツールの進化に柔軟に対応できる持続可能な仕組みになります。</p>
<p><img src="/assets/blog/authors/Amelie/260227/P05_CH05_3.jpg" alt="ChatGPTとGeminiにおけるプロンプト構造の具体的な記述例。Style Tokens（共通スタイル）とContent Variables（個別要素）の構成比較。"> </p>
<h2><strong>徹底検証：生成 AI 3 社の「性格診断」—— イラスト標準化の最適解を探る</strong></h2>
<h3><strong>2025年10月時点の Midjourney 検証</strong></h3>
<p>まずは、2025年10月に行った Midjourney（V7）での検証結果です。</p>
<p>当時の出力は、視覚的な完成度が非常に高く、一枚絵としての魅力は群を抜いていました。</p>
<p>しかし、標準化という観点では、いくつかの課題が見えてきました。</p>
<ul>
<li>装飾的な要素が多く、情報量がやや過剰</li>
<li>構図がダイナミックで、並べた際の統一感が出にくい</li>
<li>ブランドトーンよりも「生成AIらしさ」が前面に出る傾向</li>
</ul>
<p>つまり、<strong>創造性は高いが、制御性が低い。</strong></p>
<p>この特性はクリエイティブ用途には適していますが、UI内で量産・運用するインフォグラフィック用途には不向きであると判断しました。</p>
<p><img src="/assets/blog/authors/Amelie/260227/P06_CH06.jpg" alt="Midjourneyの検証比較。リファレンスなし、Image Prompts、Style References の3パターンによるイラストの再現性と精度の違い。"> </p>
<h3><strong>ChatGPT / Gemini へのシフト</strong></h3>
<p>Midjourney との比較を経て、検証の軸を「表現力」から「再現性」へとシフトしました。</p>
<p>同一のビジュアルリファレンスと構造化プロンプトを用い、ChatGPT および Gemini で出力を比較しました。</p>
<p>この時点で明確になったのは、</p>
<ul>
<li>ChatGPT は構図の安定性が高い</li>
<li>Gemini はクリーンだが、再解釈が入る傾向がある</li>
</ul>
<p>という違いでした。</p>
<p><img src="/assets/blog/authors/Amelie/260227/P07_Ch06_2.jpg" alt="ChatGPT (GPT 4o/5/5.2)とGemini (2.5/3 Pro)によるイラスト出力の比較。同一のリファレンスから生成された赤い車のバリエーション。"> </p>
<h3><strong>最新検証：観点別比較</strong></h3>
<p>プロンプトの構造を定義したところで、次なるステップは「どの実行エンジン（AI モデル）が最も安定して仕様通りに動くか」のベンチマークテストです。私たちは、構図・人物・色彩・命令遵守性の 4 つの観点から、プロダクト運用への適性を評価しました。</p>
<h4><strong>① 構図の安定性—— UI に馴染むか？</strong></h4>
<p>インフォグラフィックにおいて、余白と主体のサイズ感は「UI の整合性」に直結します。</p>
<p>ChatGPT は視点・余白・被写体のバランスが安定しており、複数枚を並べた際の一貫性が高い結果となりました。</p>
<p>一方 Gemini は、微妙な視点変更や背景処理の差異が発生しやすく、量産時の揺らぎがやや大きい傾向が見られました。</p>
<p><img src="/assets/blog/authors/Amelie/260227/P08_CH06_3.jpg" alt="構図の安定性比較。電柱に衝突した青い車のイラストを用いて、ChatGPTとGeminiの視点や背景処理の一貫性を検証する図。"> </p>
<h4><strong>② 人物表現の精度—— 意図が伝わるか？</strong></h4>
<p>「シートベルトを締める」「スマホを見る」といった具体的な動作の正確さを検証しました。</p>
<p>人物の顔パーツ・視線・身体バランスにおいて、ChatGPT は安定したクオリティを維持しました。</p>
<p>Gemini は柔らかいトーンで描写される一方、表情や骨格の一貫性に若干のばらつきが見られました。</p>
<p><img src="/assets/blog/authors/Amelie/260227/P09_CH06_4.jpg" alt="人物表現の精度比較。運転手の顔や身体バランスの良し悪しを、ChatGPTとGeminiの出力結果にチェックマークと×印で評価した比較図。"> </p>
<h4><strong>③ 用色とブランド整合性</strong></h4>
<p>ChatGPT は指定した色調レンジを忠実に守る傾向が強く、ブランドトーンとの整合性が高い結果となりました。</p>
<p>Gemini は自然なグラデーション処理を行う反面、色相・彩度が微妙に変化するケースがあり、厳密なトーン統制には追加調整が必要でした。</p>
<p><img src="/assets/blog/authors/Amelie/260227/P10_CH06_5.jpg" alt="用色とブランド整合性の比較。車の正面図を用い、GPT 5.2とGemini 2.5/3 Proにおける色調の守秘性とグラデーション処理の違いを提示。"> </p>
<h4><strong>④ 命令遵守性（Instruction Following）—— 仕様書通りに動くか？</strong></h4>
<p>最も大きな差はここでした。</p>
<p>ChatGPT はプロンプト構造（Part 1 / Part 2）をほぼそのまま出力に反映する傾向が強く、設計思想と出力結果の対応関係が明確でした。</p>
<p>Gemini は意図を解釈し、最適解を“再構成”する挙動が見られ、創造性は高いものの、決定論的制御はやや難しいという印象です。</p>
<blockquote>
<p>※ 正密に Gemini が過度な再解釈を試みるからこそ、私たちは Part 1（定数層）において、より厳密なビジュアルのガードレールを定義し、封鎖（Lockdown）する必要があるのです。</p>
</blockquote>
<h2><strong>各ツールの本性：創作のパートナーか、信頼の実行エンジンか</strong></h2>
<h3><strong>Midjourney：気分屋な天才アーティスト</strong></h3>
<p>2025 年 10 月時点の検証では、Midjourney は<strong>圧倒的な</strong>「<strong>映え</strong>」を誇りました。一枚絵としての完成度は素晴らしいのですが、標準化という観点では少し「個性が強すぎ」ました。</p>
<ul>
<li>情報量が多すぎて UI の邪魔をしてしまう。</li>
<li>構図がダイナミックすぎて、並べた時に統一感が出にくい。</li>
<li>つまり、「<strong>クリエイティブな爆発力はあるが、規律を守るのが苦手なタイプ</strong>」です。</li>
</ul>
<h3><strong>Gemini：再解釈を試みるクリエイティブ・ディレクター</strong></h3>
<p>Gemini 3 Flash などの最新検証では、非常にクリーンな UI イラストを生成してくれますが、時折「自分の色」を出したがる傾向があります。</p>
<ul>
<li>構図や余白が毎回少しずつズレる「自由奔放さ」。</li>
<li>プロンプトを忠実に守るというより、「<strong>意図を汲み取ってリミックスしてしまう</strong>」挙動が見られました。これは創作には良いですが、量産プロセスでは「予測可能性」を下げる要因になります。</li>
</ul>
<h3><strong>ChatGPT (DALL-E)：仕様書通りに動くシニアエンジニア</strong></h3>
<p>対照的に ChatGPT は、<strong>プロンプトの構造をそのまま出力に反映する能力</strong>（<strong>Instruction Following</strong>）が極めて高いことが分かりました。</p>
<ul>
<li>構図が安定し、用色も保守的でルール化しやすい。</li>
<li>まさに DesignOps における 「<strong>信頼できる実行エンジン</strong>」 です。</li>
<li><strong>「イラストを作る（Make）」ツールではなく、「運用する（Ops）」ツール</strong>としての適性が最も高いと判断し、現在は ChatGPT を中心に検証を進めています。</li>
</ul>
<blockquote>
<p>※ もちろん、表現の幅や偶発的なひらめきという点では、Midjourney や Gemini に軍配が上がる場面もあります。</p>
</blockquote>
<h2><strong>実装結果：Unlimited App で何が変わったのか？</strong></h2>
<p>確立した標準スタイルは、現在 Unlimited App 内の「車種別イラスト」や「レベル選択カード」などで試験的に運用されています。「<strong>AI で 8 割のベースを生成し、人間が最後の 2 割を整える</strong>」というフローにより、制作スピードと一貫性が（ついに！）両立し始めました。</p>
<p>しかし、この取り組みは Unlimited App という「実験場」だけで完結するものではありません。私たちが構築したプロンプトの 2 層構造は、いわばデザインの「共通プロトコル」です。将来的には、「スタイル定数（Part 1）」というコンフィグを各ブランドの DNA に合わせて差し替えるだけで、社内のあらゆるプロダクトやサービスへこの仕組みを横展開（スケール）させていくことを目見据えています。プロダクトを跨いで「一貫性のあるビジュアルを即座にデプロイできる」状態——これこそが、私たちが目指す真の DesignOps の姿です。</p>
<p><img src="/assets/blog/authors/Amelie/260227/P11_CH07.jpg" alt="Unlimited Appの実際のUI画面。生成されたイラストが、車種選択画面やレベル選択カードのアニメーションとして統合されている様子。"> </p>
<h2><strong>やってみて分かった課題</strong></h2>
<p>数ヶ月の検証で分かったのは、プロンプトには「<strong>賞味期限（Prompt Rot）</strong>」があるということです。ツールのアップデートにより、昨日まで動いていた魔法の言葉が、今日には効かなくなる。</p>
<p>そのため、プロンプトを一度作って終わりにするのではなく、<strong>継続的にリファクタリングしていく前提の設計</strong>が必要です。今後は、これらのメンテナンスを自動化する「AI Agent 的なアプローチ」も視野に入れています。</p>
<h2><strong>まとめ：AI × DesignOps は何を変えうるのか</strong></h2>
<p>今回の検証を通じて確信したのは、生成 AI は単なる「魔法」ではなく、<strong>DesignOps を再設計するための強力なトリガー</strong>であるということです。</p>
<p>標準化とは、自由を奪うことではありません。むしろ、「<strong>どこを固定し（Constants）、どこを柔軟にするか（Variables）</strong>」を定義することで、変化の激しいプロダクト開発においてクリエイティブな安定走行を可能にする行為です。</p>
<p>DesignOps は、デザインを「属人的な手癖」から「再現可能なプロセス」へと拡張します。この取り組みが、皆さんのプロダクトにおけるクリエイティブ運用のヒントになれば幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/Amelie/260227/P01_CH01.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[CloudFormation ネストスタックが DELETE_COMPLETE 状態で更新不能になった問題の解決記録]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-02-12-ResolvingAnIssueWhereACloudFormationNestedStackBecameUnupdatableInDeleteCompleteState/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-02-12-ResolvingAnIssueWhereACloudFormationNestedStackBecameUnupdatableInDeleteCompleteState/</guid>
            <pubDate>Thu, 12 Feb 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[AWS Amplify Gen 2 + CDK 環境で CloudFormation のネストスタックが DELETE_COMPLETE 状態になり更新不能になった問題を、AWS サポートの助言を得て2段階デプロイで解決した記録です。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは、 Cloud Infrastructure G の山中です！
AWS Amplify Gen 2 + CDK で構築した dev 環境で、CloudFormation のデプロイが失敗し続けるという問題に遭遇しました。エラーメッセージは以下の通りです：</p>
<pre><code>Stack:arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/amplify-XXXXX-CloudWatchLogsToS3Stack108915EF-XXXXX/XXXXX is in DELETE_COMPLETE state and can not be updated.
</code></pre>
<p>本記事では、この問題の原因究明から AWS サポートへの問い合わせ、そして最終的な解決までのプロセスを共有します。</p>
<h2>背景</h2>
<h3>実施したリファクタリング</h3>
<p>CDK コードで、あるリソース群を <code>cdk.Stack</code>（独立したネストスタック）から <code>Construct</code>（親スタック内のリソースグループ）へ変更するリファクタリングを行いました。</p>
<p>この変更を行った理由は、<strong>クロススタック参照の問題</strong>を解消するためです。</p>
<blockquote>
<p><strong>クロススタック参照とは？</strong>
CloudFormation で複数のスタック間でリソース（ARN など）を参照し合う仕組みです。便利ですが、参照元・参照先の削除順序によってはエラーが発生することがあります。</p>
</blockquote>
<p><strong>変更前：</strong> 独立したネストスタックとして定義</p>
<pre><code class="language-typescript">export class CloudWatchLogsToS3Stack extends cdk.Stack {
  // 独立したスタックとしてデプロイされる
}
</code></pre>
<p><strong>変更後：</strong> 親スタック内の <code>Construct</code> として定義</p>
<pre><code class="language-typescript">export class CloudWatchLogsToS3Stack extends Construct {
  // 親スタックの一部としてデプロイされる
}
</code></pre>
<p>一見シンプルな変更ですが、これが思わぬ問題を引き起こしました。</p>
<h2>発生した問題</h2>
<h3>問題の発生メカニズム</h3>
<p><code>Stack</code> → <code>Construct</code> への変更により、CDK の <code>Construct</code> 階層が変わり、CloudFormation の<strong>論理 ID</strong>（CloudFormation がリソースを識別するための内部的な名前）が変化しました。</p>
<p><strong>【変更前の構造】</strong></p>
<pre><code>CloudWatchLogsToS3Stack (Stack)
├── Firehose0
├── FirehoseRole0
└── ...
</code></pre>
<p><strong>【変更後の構造】</strong></p>
<pre><code>CloudWatchLogsToS3Stack (Amplify Stack)
└── CloudWatchLogsToS3StackResource (Construct)
    ├── Firehose0
    ├── FirehoseRole0
    └── ...
</code></pre>
<p>この結果、以下の連鎖的な問題が発生しました：</p>
<ol>
<li>CloudFormation は論理 ID が変わったため「新規リソース」として作成を試みる</li>
<li>しかし物理名（IAM ロール名、ロググループ名など）は既存のものと同じ</li>
<li>「リソースが既に存在する」エラーで <code>CREATE_FAILED</code></li>
<li>作成に失敗したネストスタックが <code>DELETE_COMPLETE</code> 状態になる</li>
<li><strong>親スタックが、削除済みネストスタックの ARN を参照し続ける(孤立した参照)</strong></li>
<li>以降のデプロイで「<code>DELETE_COMPLETE</code> 状態のスタックは更新できない」エラーが発生</li>
</ol>
<p>特に厄介なのは手順 5 の状態です。親スタックのテンプレートに「存在しないネストスタック」への参照が残り続けるため、何をしてもデプロイが失敗するようになります。</p>
<h3>エラーの詳細</h3>
<pre><code>CloudWatchLogsToS3Stack108915EF:
Stack:arn:aws:cloudformation:ap-northeast-1:XXXXXXXXXXXX:stack/amplify-XXXXX-CloudWatchLogsToS3Stack108915EF-XXXXX/XXXXX is in DELETE_COMPLETE state and can not be updated.
</code></pre>
<h2>試した対応（すべて失敗）</h2>
<p>自力で以下の対応を試みましたが、いずれも解決には至りませんでした。</p>
<table>
<thead>
<tr>
<th>試した対応</th>
<th>結果</th>
</tr>
</thead>
<tbody><tr>
<td>重複リソース（IAM ロール、ロググループ等）の手動削除</td>
<td>リソースは削除できたが、デプロイは失敗</td>
</tr>
<tr>
<td><code>DELETE_COMPLETE</code> 状態のネストスタックを AWS コンソールから削除</td>
<td>既に削除済みのため操作不可</td>
</tr>
<tr>
<td>AWS CLI でルートスタックのテンプレートから参照を除去して <code>update-stack</code></td>
<td>同じエラーで失敗</td>
</tr>
<tr>
<td><code>continue-update-rollback</code> コマンドで問題リソースをスキップ</td>
<td>スタック状態が <code>UPDATE_ROLLBACK_COMPLETE</code> のため使用不可</td>
</tr>
<tr>
<td>CDK コードで論理 ID を変更して新規作成</td>
<td>古い参照がルートスタックに残っているため失敗</td>
</tr>
</tbody></table>
<p>どの方法でも、<strong>親スタックが削除済みのネストスタック ARN を参照し続けている</strong>という根本問題を解決できませんでした。</p>
<h2>AWS サポートへの問い合わせ</h2>
<p>自力での解決が困難と判断し、AWS サポートに問い合わせました。</p>
<h3>問い合わせ内容（要約）</h3>
<ul>
<li>ルートスタックが <code>DELETE_COMPLETE</code> 状態のネストスタック ARN を参照し続けている</li>
<li>テンプレート更新、<code>continue-update-rollback</code>、論理 ID 変更など試したが解決せず</li>
<li>ルートスタックから古いネストスタック参照を除去していただくことは可能か</li>
</ul>
<h3>AWS サポートからの回答</h3>
<blockquote>
<p>原則として、AWS 側にてお客様のスタックを操作することは行なっておりません。</p>
<p>お問い合わせのエラーを解消いただくには、親スタックにて管理されているリソースから子スタックを削除いただいた後に、再度子スタックを作成いただく必要がございます。</p>
<p>手順1: 親スタックの CDK コードより既に削除されている子スタックを作成されている処理をコメントアウトいただき、CDK コードをデプロイすることで、親スタックにて管理されているリソースから子スタックを削除いただく。</p>
<p>手順2: 手順1におけるコメントアウトを外し、再度 CDK コードをデプロイいただく。</p>
</blockquote>
<p>つまり、2段階のデプロイで解決できるとのことでした。</p>
<h2>解決手順</h2>
<h3>手順1: 問題のスタック作成処理をコメントアウトしてデプロイ</h3>
<p>以下をコメントアウトしました：</p>
<pre><code class="language-typescript">// CloudWatch Logs → S3エクスポート設定
// ============================================================
// 【手順1】DELETE_COMPLETE 状態のネストスタック参照を削除するため、
// 一時的にコメントアウトしています。
// ============================================================
// const logsExportStack = backend.createStack(&quot;CloudWatchLogsToS3Stack&quot;);
// const logsExportStackInstance = new CloudWatchLogsToS3Stack(...);
// logsExportStack.addDependency(logsBucketStack);

// RumConstruct の Firehose 連携も無効化
const rumStackInstance = new RumConstruct(rumStack, &quot;RumStackResource&quot;, {
  // ...
  enableSubscriptionFilter: false,  // true → false
  // firehoseStreamArn: logsExportStackInstance.firehoseStreamArns.get(&#39;rum&#39;)!,
});

// rumStack.addDependency(logsExportStack);

// SubscriptionFiltersStack もコメントアウト
// if (appSyncApiId) {
//   const subscriptionFiltersStack = backend.createStack(&quot;SubscriptionFiltersStack&quot;);
//   ...
// }
</code></pre>
<p>デプロイ後の確認：</p>
<pre><code class="language-bash">aws cloudformation list-stack-resources \
  --stack-name amplify-XXXXX \
  --output json | jq -r &#39;.StackResourceSummaries[] | select(.LogicalResourceId | contains(&quot;CloudWatchLogsToS3&quot;) or contains(&quot;SubscriptionFilters&quot;))&#39;
</code></pre>
<p>→ 出力なし = 親スタックから参照が削除された ✅</p>
<h3>手順2: コメントアウトを解除して再デプロイ</h3>
<p>コメントアウトを全て解除し、元の状態に戻してデプロイしました。</p>
<p>結果：</p>
<pre><code class="language-bash">aws cloudformation describe-stacks --stack-name amplify-XXXXX \
  --query &quot;Stacks[0].StackStatus&quot;
# =&gt; &quot;UPDATE_COMPLETE&quot;
</code></pre>
<p>→ デプロイ成功 ✅</p>
<h2>学んだこと</h2>
<h3>1. CDK の Construct 階層変更は要注意</h3>
<p><code>Stack</code> → <code>Construct</code> への変更のような、一見シンプルなリファクタリングでも、CloudFormation の論理 ID が変わる可能性があります。</p>
<p>:::message
<strong>対策</strong>: 本番環境に適用する前に、必ず <code>cdk diff</code> で論理IDが変更されているかを確認しましょう。論理 ID の変更は「リソースの再作成」を意味するため、影響範囲を把握することが重要です。
:::</p>
<h3>2. 「孤立した参照」問題は厄介</h3>
<p>ネストスタックが <code>DELETE_COMPLETE</code> 状態になると、親スタック側にそのスタックへの参照が残り続けます。
その結果、CloudFormation は削除済みスタックを更新しようとして失敗し、通常の更新操作では復旧できない状態に陥ることがあります。</p>
<h3>3. 2段階デプロイが有効</h3>
<p>このような孤立した参照問題には、「問題箇所をコメントアウト → デプロイ → 解除 → 再デプロイ」という2段階の手順が有効です。1回目のデプロイで親スタックから参照を削除し、2回目で新規作成するという流れです。</p>
<h3>4. 困ったら AWS サポートへ</h3>
<p>自力で解決できない問題に遭遇した場合、AWS サポートへの問い合わせが有効です。今回は「AWS 側での直接操作はできない」という回答でしたが、代わりに的確な回避策を教えていただきました。</p>
<h2>まとめ</h2>
<p>CloudFormation のネストスタックが <code>DELETE_COMPLETE</code> 状態で更新不能になる問題は、以下の手順で解決できました：</p>
<ol>
<li><strong>手順1</strong>: 問題のスタック作成処理をコメントアウトしてデプロイ（親スタックから参照を削除）</li>
<li><strong>手順2</strong>: コメントアウトを解除して再デプロイ（スタックを新規作成）</li>
</ol>
<p>CDK/CloudFormation を使用している方で同様の問題に遭遇した場合、この記事が参考になれば幸いです。</p>
<h2>参考リンク</h2>
<ul>
<li><a href="https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-nested-stacks.html">AWS CloudFormation ユーザーガイド - ネストされたスタック</a></li>
<li><a href="https://docs.aws.amazon.com/cdk/v2/guide/home.html">AWS CDK 開発者ガイド</a></li>
<li><a href="https://docs.amplify.aws/gen2/">AWS Amplify Gen 2 ドキュメント</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/d.yamanaka/20260212_coverimage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Vibe Codingで作ったPoCを引き継ごうとしたら、1週間溶けた話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026_01_30_vibe_codingで引き継ぎが壊れた話/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026_01_30_vibe_codingで引き継ぎが壊れた話/</guid>
            <pubDate>Fri, 30 Jan 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[Claude CodeでVibe Codingして作ったPoCを別の担当者に引き継ごうとしたら、新環境でアプリが動かず原因解明に1週間かかった失敗談と、そこから得た教訓を共有します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>Vibe Coding、楽しいですよね。</p>
<p>Claude Codeに「こんな感じで作って」と伝えるだけで、AWSのリソースを使ったアプリがサクサク出来上がっていく。自分でコードを書く量は激減して、PoCなんてあっという間に完成する。</p>
<p>…と思っていた時期が、僕にもありました。</p>
<p>一人で作ったPoCを別の担当者に引き継ごうとしたら、<strong>新環境でアプリが動かない</strong>。原因を調べようにも、Vibe Codingで作ったから<strong>コードの中身を自分でも把握していない</strong>。結局、原因解明に<strong>約1週間</strong>溶かしました。</p>
<p>この記事では、何が起きたのか、なぜ時間がかかったのか、そしてどうすれば防げたのかを共有します。</p>
<h2>何を作っていたか</h2>
<p>Claude Codeを使って、一人でPoCを開発していました。</p>
<p>構成はこんな感じ：</p>
<ul>
<li><strong>Amazon DynamoDB</strong> – データストア</li>
<li><strong>Amazon Bedrock Agent</strong> – LLMを使った処理</li>
<li><strong>Amazon Cognito</strong> – 認証</li>
</ul>
<p>典型的なサーバーレス構成です。Vibe Codingでガンガン作って、旧環境（開発用AWSアカウント）ではちゃんと動いていました。</p>
<h2>事件：新環境で動かない</h2>
<p>引き継ぎのタイミングで、新環境（別のAWSアカウント）にデプロイして動作確認をしました。</p>
<p><strong>動かない。</strong></p>
<p>エラーは出るけど、原因がよくわからない。Claude Codeにデバッグさせても、的を射た回答が返ってこない。</p>
<p>「コードを読めばわかるでしょ」と思うかもしれませんが、Vibe Codingで作ったので<strong>自分でもコードの詳細を把握していない</strong>んですよね。どこを見ればいいかすらわからない。</p>
<h2>原因：AIが勝手に書いたフォールバック値</h2>
<p>結局、新環境と旧環境のデプロイ後の挙動の違いをClaude Codeに分析させて、やっと原因がわかりました。AWSのリソースやCloudWatchログを片っ端から参照させた結果です。</p>
<p>原因は<strong>環境変数のフォールバック値</strong>でした。</p>
<pre><code class="language-typescript">// ※ 以下はAIが生成した例示コードです。実際のコードとは異なります。

// config.ts
export const config = {
  dynamoTableName: process.env.DYNAMO_TABLE_NAME || &quot;dev-user-table&quot;,
  bedrockAgentId: process.env.BEDROCK_AGENT_ID || &quot;ABCD1234EF&quot;,
  cognitoUserPoolId: process.env.COGNITO_USER_POOL_ID || &quot;ap-northeast-1_XyZ123&quot;,
  cognitoClientId: process.env.COGNITO_CLIENT_ID || &quot;1a2b3c4d5e6f7g8h9i0j&quot;,
};
</code></pre>
<p>AIが「環境変数が設定されていない場合に備えて」と気を利かせて、フォールバック値を書いていたんです。</p>
<p>旧環境では、たまたまこのフォールバック値が指すリソースが存在していたので動いていた。でも新環境では別のAWSアカウントなので、当然そんなリソースは存在しない。だから動かない。</p>
<p>しかもエラーメッセージを見ても「リソースが見つかりません」としか出ないから、<strong>環境変数の問題だと気づけなかった</strong>。</p>
<h2>なぜ1週間もかかったのか</h2>
<p>正直に言います。</p>
<p><strong>自分がコードをほとんど読まなかったから</strong>です。</p>
<p>Vibe Codingの快適さに甘えて、AIが生成したコードをちゃんと確認していませんでした。だから問題が起きたときに「どこを見ればいいか」がわからない。</p>
<p>Claude Codeにデバッグさせても、ピンポイントで原因に辿り着けない。結局、新旧環境の挙動の違いをCloudWatchログレベルで比較させて、やっと「あ、ここか」となりました。</p>
<p>Vibe Codingで楽をした分、トラブル時のツケを払わされた感じです。</p>
<h2>引き継ぎ相手も困っていた</h2>
<p>自分だけじゃなく、引き継ぎ相手も困っていました。</p>
<ul>
<li>コード量が多くて、全体像を把握する時間が足りない</li>
<li>どこが重要なコードなのかわからない</li>
<li>ドキュメントもない</li>
</ul>
<p>最終的にAIにアーキテクチャ図を生成させて、やっと自分でも全体像がなんとなくわかった状態でした。コードだけ渡されても、正直<strong>自分でも説明できない</strong>。</p>
<p>これは引き継ぎとして完全に失敗です。</p>
<h2>教訓：Vibe Codingで引き継ぎを壊さないために</h2>
<p>この経験から得た教訓を共有します。</p>
<h3>1. AIには必要最小限の仕事だけさせる</h3>
<p>「あると便利かも」でコードを追加させない。依頼していない機能を勝手に実装されると、把握できないコードが増えるだけ。</p>
<h3>2. Agent.md（CLAUDE.md）で余計なことをさせない制御</h3>
<p>プロジェクトルールを明文化しておく。特に「やってはいけないこと」を書いておくのが重要。</p>
<h3>3. コードレビューを徹底する</h3>
<p>AIが生成したコードであっても、人間によるレビューは不可欠です。これにより、不要なコードや潜在的な問題を早期に発見し、コードの品質を維持することができます。</p>
<pre><code class="language-markdown">&lt;!-- ※ 以下はAIが生成した例示です。プロジェクトに応じてカスタマイズしてください。 --&gt;

# プロジェクトルール

## 環境変数
- 環境変数にフォールバック値（デフォルト値）を絶対に書かない
- 環境変数が未設定の場合は明示的にエラーを出すこと
- 環境変数の一覧は `.env.example` に記載し、常に最新化する

## コード規模
- 実装は必要最小限にする。「あると便利かも」で追加しない
- 1ファイル300行を超えたら分割を検討する
- 使われていないコードは即削除する

## ドキュメント
- アーキテクチャ図（`docs/architecture.md`）を常に最新に保つ
- 新しいAWSリソースを追加したら、必ずアーキテクチャ図を更新する
- READMEのセットアップ手順は、新環境で動くことを前提に書く

## やってはいけないこと
- ハードコードされた認証情報・リソースID
- 「とりあえず動く」ための回避策（後で必ず負債になる）
- 依頼されていない機能の追加
</code></pre>
<h3>3. 環境変数はフォールバック値なしで即エラーにする</h3>
<p>今回の問題を防ぐなら、こう書くべきでした：</p>
<pre><code class="language-typescript">// ※ 以下はAIが生成した例示コードです。実際のコードとは異なります。

// config.ts
const requireEnv = (key: string): string =&gt; {
  const value = process.env[key];
  if (!value) {
    throw new Error(`環境変数 ${key} が設定されていません`);
  }
  return value;
};

export const config = {
  dynamoTableName: requireEnv(&quot;DYNAMO_TABLE_NAME&quot;),
  bedrockAgentId: requireEnv(&quot;BEDROCK_AGENT_ID&quot;),
  cognitoUserPoolId: requireEnv(&quot;COGNITO_USER_POOL_ID&quot;),
  cognitoClientId: requireEnv(&quot;COGNITO_CLIENT_ID&quot;),
};
</code></pre>
<p>これなら環境変数が未設定のとき、すぐにエラーで気づける。</p>
<h3>4. ドキュメントを自動生成・自動更新する仕組みを作る</h3>
<p>コードだけでは引き継ぎできない。アーキテクチャ図やREADMEは必須。</p>
<p>できればコードの変更に合わせて自動更新される仕組みを作りたい。完璧は無理でも、「コードとドキュメントがズレている」状態は避けたい。</p>
<h2>まとめ</h2>
<p>Vibe Codingは楽しいし、生産性も上がる。でも<strong>引き継ぎ</strong>という観点では落とし穴がある。</p>
<ul>
<li>AIが「気を利かせて」書いたコードが、別環境で問題を起こす</li>
<li>自分でコードを把握していないから、トラブル時に対応できない</li>
<li>引き継ぎ相手もコードを理解できない</li>
</ul>
<p><strong>100%コントロールするのは無理でも、できるだけ手綱を握っておく</strong>のが大事だと痛感しました。</p>
<p>Vibe Codingをやるなら：</p>
<ul>
<li>Agent.mdで「やってはいけないこと」を明文化する</li>
<li>AIには最小限の仕事だけさせる</li>
<li>ドキュメントは最初から用意しておく</li>
<li>AIが生成したコードも必ず人間がレビューする</li>
</ul>
<p>この記事が、同じ轍を踏む人を一人でも減らせたら嬉しいです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/amano-satoshi/vibecoding_handover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[「障害」を新たな視点で捉え直す ～障害平等研修開催報告～]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-01-29-disability-equality-training/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-01-29-disability-equality-training/</guid>
            <pubDate>Thu, 29 Jan 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[Engineering Officeで開催した「障害平等研修」の様子を紹介します]]></description>
            <content:encoded><![CDATA[<p>こんにちは。KINTOテクノロジーズのEngineering OfficeでAccessibility Advocateとして働いている辻勝利です。</p>
<p>今回は、去る1月15日に同チームの皆様向けに開催した「障害平等研修（Disability Equality Training）」についての開催報告をしたいと思います。 そもそも「障害平等研修」とはなにか、なぜEngineering Officeの有志向けに最初の研修を開催したのかなど、お話しできればと思います。</p>
<h2>1. はじめに：なぜ「技術」の組織が「マインド」を学ぶのか</h2>
<p><img src="/assets/blog/authors/debugon/DET1.jpg" alt="障害平等研修の開始時に投影された、本日の目標とプログラム内容のプレゼンテーション画面。"><br>アクセシビリティの分野に携わる中で、私はある「失敗」を多く目にしてきました。それは、アクセシビリティが「障害者のための特別な対応」と定義された瞬間、優先度が下がり、多忙を理由に見送られてしまうという現実です。 これを変えるには、手法（How）の前に、アクセシビリティを追求する「意義（Why）」を社内文化として根付かせることが不可欠です。昨年11月にKINTOテクノロジーズに入社して以来、私が「文化形成」を最重視しているのはそのためです。その第一歩として、まずは身近なEngineering Officeのメンバーを対象に「障害平等研修（DET: Disability Equality Training）」を実施しました。</p>
<h2>2. 障害平等研修（DET）とは</h2>
<p><img src="/assets/blog/authors/debugon/DET2.jpg" alt="障害平等研修（Disability Equality Training）のタイトルが映し出された講義用モニター。">
DETは1990年代のイギリスで誕生しました。日本でも「障害者差別解消法」の施行や、東京2020大会のボランティア研修に採用されるなど、世界標準のプログラムとなっています。 最大の特徴は、障害者自身がファシリテーターを務めること、そして「教わる」のではなく「参加者同士の議論」を通じて気づきを得ることです。今回は約1時間のダイジェスト版として、「障害とは何か？」「障害はどこにあるのか？」という本質的なテーマを深掘りしました。</p>
<h2>3. 参加者の属性</h2>
<p><img src="/assets/blog/authors/debugon/DET3.jpg" alt="明るい会議室で、お菓子や飲み物を囲みながら、5名の参加者がリラックスした様子でテーブルを囲んでいる研修会場。">
今回はEngineering Officeを中心に、名古屋や福岡など各拠点から5名が室町オフィスに集結しました。普段リモートワークが多い私ですが、あえて対面形式を選んだのは、温度感のある深い対話の場を作りたかったからです。お菓子を囲み、リラックスした雰囲気の中で研修はスタートしました。</p>
<h2>4. 研修の様子：問題を「発見」するプロセス</h2>
<p><img src="/assets/blog/authors/debugon/DET4.jpg" alt="イラストを囲みながら、身振り手振りを交えて活発に意見を交わす参加者の様子。">
研修ではイラストやビデオを用い、日常に潜む「問題」を探し出しました。 印象的だったのは、参加者の皆さんがごく自然に、障害を「個人の問題」から「社会や環境の問題」へと転換して議論を進めていたことです。エンジニアリングに携わる方々らしく、目の前の事象を「解決すべき課題」として捉える姿勢が非常に頼もしく感じられました。</p>
<h2>5. 心境・視点の変化：アンケートが語る「パラダイムシフト」</h2>
<p><img src="/assets/blog/authors/debugon/DET5.jpg" alt="ワークシートのアップ。「障害とは、社会や人の対応不足が生み出す環境である」と手書きで記入されている。">
研修の前後で、参加者の「障害」に対する解釈は驚くほど変化しました。 最初は「心身の機能に関すること」や「それに伴って何かができないこと」という前提で話し始めていたメンバーが、ワークショップでの対話を重ねるうちに、自分たちの外側にある要因を含めた新たな視点で障害を捉え直そうとしている姿が印象的でした。 終了時には、参加者の口から「障害に対する考え方の前提がひっくり返った気がする」といった言葉が聞かれ、ファシリテーターとしてこの上なく手応えを感じた瞬間でした。</p>
<p>アンケートでも満足度・内容ともに10点満点中9〜10点という極めて高い評価をいただき、以下のような前向きな声をいただいています。</p>
<ul>
<li>「本人と環境という、2つの問題に目線が広がりました」  </li>
<li>「こういう考え方が一般常識になれば、世の中が変わると思う」  </li>
<li>「ぜひ後半もやりたい。他の拠点やチームにも広めたい」  </li>
<li>「議論を活発にするための心理的安全性についても検討していきたい」</li>
</ul>
<h2>6. 今後のアクション：誰もが「社会を変えるプレーヤー」に</h2>
<p><img src="/assets/blog/authors/debugon/DET6.jpg" alt="3名の参加者が机を囲み、手元の資料を指差しながら真剣に、かつ前向きな様子で話し合っている後ろ姿。">
モビリティの分野において「障害」について深く掘り下げることは、これからの移動の在り方を考える上で避けては通れないテーマです。
研修を通じて私たちが得た最大の収穫は、アクセシビリティを「誰かのための特別な対応」ではなく、「身近なところにあり自分たちが解決できるかもしれない課題」として捉え直したことです。自分の仕事のどこにバリアがあり、どこに解決の可能性があるのか。その気づきこそが、文化を変える第一歩になります。
誰もが社会のバリアを取り除く「プレーヤー」であると実感できる職場。その先に、KINTOテクノロジーズが「すべての人にとって働きやすく、価値を提供できる場所」になる未来を目指し、この対話の輪を他拠点や他部署へも広げていきたいと思っています。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[顧客の声を深く理解するために「ユーザーインタビューわいわい会」を試してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-01-14-user-interview-waiwai-session/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-01-14-user-interview-waiwai-session/</guid>
            <pubDate>Wed, 21 Jan 2026 01:00:00 GMT</pubDate>
            <description><![CDATA[ユーザーインタビューの録画をチームで一緒に視聴し、顧客理解とチームの温度感を揃えるために試験的に実施した取り組みと、そこから得られた気づきをまとめました。]]></description>
            <content:encoded><![CDATA[<p>こんにちは。KINTOテクノロジーズ（KTC）でKINTOの中古車ECサイトのディレクターをしている<a href="https://x.com/momentofudayo">かーびー</a>です。</p>
<p><a href="https://up.kinto-jp.com/">KINTO 中古車</a>とは、「KINTOの新車返却車両」の中から状態の良いクルマのみを、クルマのプロが厳選して提供する「高品質な中古車サブスクリプションサービス」です。</p>
<p>今回は、KINTOの中古車に関わる有志のメンバーで試験的に実施した「ユーザーインタビューわいわい会」の取り組みと、そこから得られた気づきや学びについてご紹介します。</p>
<h2>「ユーザーインタビューわいわい会」とは？</h2>
<p>私たちは、ご契約いただいているお客様が、どのような点に魅力を感じているのかを深く理解するため、継続的にユーザーインタビューを実施しています。</p>
<p>ただ、インタビュー担当者のハイライトや要約だけでは、お客様の姿や言葉の裏にある「熱量」といった生の声を、インタビューに参加していないチームメンバーには十分に伝えきれない場面があります。</p>
<p>そこで、メンバーそれぞれが直接お客様の声に触れ、課題への解像度を揃えることで「チームの温度感をより高めたい！」と考えました。そこで試験的に実施したのが、ユーザーインタビューの録画動画をみんなで視聴し、ディスカッションする会です。</p>
<p><img src="/assets/blog/authors/y.nakamura/user01/01.png" alt="ユーザーインタビュー説明会の様子"><br><em>KINTOの中古車に関わる有志メンバー13名で実施</em>  </p>
<h2>「ユーザーインタビューわいわい会」の概要</h2>
<p>今回はお昼の時間帯を使っての実施だったため、視聴しながら参加できるようにランチもあわせて用意しました。</p>
<h3>実際の流れ（約60分間）</h3>
<p>今回はテスト実施として、以下の時間配分で行いました。</p>
<ul>
<li>ユーザーインタビューの視聴：約35分</li>
<li>個人ワーク：約5分</li>
<li>テーブルごとに共有：約15分</li>
<li>全体振り返り：約5分</li>
</ul>
<p>結果として、共有や振り返りの時間がかなりタイトになりました。
特にテーブル共有では、話が盛り上がったところで時間切れになる場面もあり、「もっと話したかった」という声も聞かれました。</p>
<h3>意識したポイント</h3>
<p>発言力やドメイン知識の差によって意見が偏らないよう、「まず一人で書く→その後に共有する」という流れを採用しました。</p>
<p>また、普段はお客様の行動データ（定量）を見ていますが、数字だけでは「なぜその行動をしたのか」までは分かりません。そこで今回は、データから立てた仮説を事前に配布し、インタビュー（定性）で検証する形をとりました。「定量では見えないこと」に自然と目が向くような設計を意識しています。</p>
<h3>定量→定性で見えたこと（仮説検証の例）</h3>
<p>例えば、あるお客様のデータからは「<strong>車種Aを複数お気に入り登録</strong>していたが、最終的に<strong>車種Bで契約</strong>した」という事実が見えていました。</p>
<ul>
<li><strong>仮説1：</strong> <strong>納期や価格</strong>を優先し、Bの車種に切り替えたのではないか？</li>
<li><strong>結果：</strong> インタビューを通じて、こちらは<strong>概ね仮説通り</strong>であることが確認できました。</li>
</ul>
<p>一方で、データだけでは読み解けなかった大きなギャップもありました。</p>
<ul>
<li><strong>仮説2：</strong> 初回訪問から数十時間で契約しているため、<strong>納期最優先で即決</strong>したのではないか？</li>
<li><strong>結果：  仮説は外れていました。</strong> 実際には、数ヶ月にわたって外部サイトで徹底的にリサーチを重ねた「熟考の末」の訪問でした。</li>
<li><strong>真相：</strong>  納期も要素のひとつではありましたが、<strong>最終的な決め手は「サービスとしての信頼性」</strong>。納得感が醸成されたタイミングでサイトを訪れたため、結果として「即決」というデータとして現れていただけでした。</li>
</ul>
<p>このように、定量データだけでは見えない「意思決定の背景」や「迷いのプロセス」が、生の声を聞くことで具体的に浮かび上がってきました。</p>
<p><img src="/assets/blog/authors/y.nakamura/user01/02.png" alt="インタビュー視聴後にディスカッションする様子"></p>
<h2>「ユーザーインタビューわいわい会」を実施してみて</h2>
<h3>① お客様の判断の瞬間を共有できた</h3>
<p>本会終了後のアンケートでは、<strong>参加者全員から「印象に残った瞬間があった」という回答</strong>が得られました。</p>
<p>たとえば、 「KINTOって、Webで申込完結・車両保険も月額に入ってこの価格なんですよね？」「想像していたより、含まれているものが多いですね」 といった発言があり、サービスの説明を受ける中で、想定していたよりもコストパフォーマンスが高いと感じた様子が率直に語られました。</p>
<p>さらに、トヨタの正規販売店による整備・メンテナンスが月額料金に含まれている点について、車両トラブル時にすぐ対応してもらえたというエピソードも挙がり、購入後の体験にも満足していることがうかがえました。</p>
<p>こうした一連の声から、中古車であっても「安心して使える体験」と価格のバランスが、意思決定において重要な価値として受け取られていることを直接確認でき、チームにとって確かな手応えにつながりました。</p>
<p><img src="/assets/blog/authors/y.nakamura/user01/03.png" alt="インタビュー視聴後のアンケート結果"></p>
<h3>② 次の仮説が自然と生まれた</h3>
<p>ユーザーインタビュー視聴後には、</p>
<ul>
<li>車の知識レベルによって、選び方はどう違うのか？</li>
<li>コストパフォーマンスを、どの要素で評価しているのか？</li>
<li>他社との比較は、どの程度行われているのか？</li>
<li>契約前に家族とどのような会話をしているのか？</li>
<li>「中古車」というワードにどのような印象を持っているのか？</li>
</ul>
<p>といった<strong>問いや気づき、仮説が60件以上</strong>集まりました。</p>
<p>感想で終わるのではなく、次の仮説やアクションにつながる問いが、さまざまな職種のメンバーから自然に生まれたことは、実務につなげやすい状態をつくれたという意味でも、大きな収穫でした。</p>
<h3>③ チームに共通言語ができた</h3>
<p>本会後の会議では、 <strong>「ユーザーインタビューのお客様も同じことをおっしゃっていましたが…」</strong> といった会話が出るようになりました。</p>
<p>顧客像を共通の根拠として会話できる状態が生まれ、これを積み重ねていくことで、議論のスピードや精度も高まっていくのではないかと考えています。</p>
<h4>参加者の声（一部抜粋）</h4>
<blockquote>
<p>「アンケートだけでは本質的なニーズや背景に十分に踏み込めない場合があると感じました。直接ヒアリングの機会を持つことで、課題の根底にある思いや具体的な状況をより深く理解できることに気づきました。」</p>
</blockquote>
<h3>次回に向けて</h3>
<p>今回はテスト実施という位置づけで、60分という限られた時間の中で実施しました。取り組みとしての有用性を確認できた一方で、次回に向けて磨いていきたい点も見えてきました。</p>
<p>次回は、以下のような点を中心に改善を進めていく予定です。</p>
<ul>
<li>ディスカッション時間の拡大</li>
<li>参加人数を増やし、より多様な視点を集める</li>
</ul>
<p>「時間が足りなかった」という声は、それだけ共有したい気づきが多く生まれていたということでもあると感じています。この熱量を、次回の場づくりにつなげていけたらと思います。</p>
<p><img src="/assets/blog/authors/y.nakamura/user01/04.png" alt="インタビュー視聴後にディスカッションする様子"></p>
<h2>「ユーザーインタビューわいわい会」からの気づき</h2>
<p>今回の「わいわい会」を通じて、職種や視点が異なるチームで前提を揃えていくための、いくつかの気づきがありました。</p>
<h3>共通言語と「翻訳」の存在</h3>
<p>モビリティ業界という特性上、私たちのチームには多様な専門性を持つメンバーが集まっています。立場によって言葉の捉え方が異なるのは当然ですが、自分自身、どこかで <strong>前職のような少人数チームでの「阿吽（あうん）の呼吸」</strong> を前提にコミュニケーションを組み立てていた部分があったと、改めて気づかされました。</p>
<p>今回、大人数で対話をする中で気づいたのは、これまでは誰かが言葉のギャップを埋める「翻訳」を自然に担ってくれていたのではないか、ということです。チームの規模や多様性が増すほど、個人の属人的な「翻訳」に頼るのではなく、一次情報という「共通の土台」を仕組みとして提供することが重要になると実感しました。</p>
<h3>「一次情報」が対話の質を変える</h3>
<p>伝え方のスキルを磨くことも大切ですが、それ以上に <strong>「同じ一次情報を共有すること」自体が、前提を揃えるうえで非常に有効</strong>だと再確認しました。今回の「わいわい会」のように、ユーザーの声を「一緒に体験する」形を続けていくことで、以下のような効果を得られそうだと感じています。</p>
<ul>
<li><strong>解釈のズレを未然に防ぐ：</strong> 同じ体験を起点にすることを繰り返すことで、チーム内の前提の食い違いが起きにくくなる。</li>
<li><strong>多角的な視点を仕組みとして取り入れる：</strong> 職種や背景の違いから、一人では気づけない観点が自然と集まる。</li>
<li><strong>対話のハードルが下がる：</strong> 「あの時の、あのお客様のことば」という共通言語が増えていくため、その後の議論がスムーズになる。</li>
</ul>
<p>こうした小さな積み重ねを大切にしながら、対話の輪を少しずつ広げていけたらと思います。</p>
<h2>おわりに</h2>
<p>今回の「ユーザーインタビューわいわい会」は、社内で進められている<a href="https://blog.kinto-technologies.com/posts/2025-12-25-LookBack2025/#ktc%E3%81%AE%E6%8F%BA%E3%82%8B%E3%81%8E%E3%81%AA%E3%81%84%E3%80%8C%E5%9F%BA%E6%9C%AC%E7%9A%84%E4%BE%A1%E5%80%A4%E3%80%8D">ユーザーファースト</a>の取り組みとも、自然につながる実践でした。</p>
<p>ユーザー理解をチームにどう広げ、実務にどう落とし込んでいくか。その一つのヒントとして、この「わいわい会」の形も引き続き試していけたらと思います。</p>
<p>ユーザーファーストに関する全社的な取り組みについては、以下の記事でも紹介しています。ぜひあわせてご覧ください。</p>
<p><a href="https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025/">https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025/</a> </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/y.nakamura/user01/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Whyが届かない開発から脱却するために、私たちがやった40のこと]]></title>
            <link>https://blog.kinto-technologies.com/posts/2026-01-07-myroute-six-month-retrospective/</link>
            <guid>https://blog.kinto-technologies.com/posts/2026-01-07-myroute-six-month-retrospective/</guid>
            <pubDate>Wed, 07 Jan 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[「作るだけ」になっていた開発組織を変えるための40の取り組み]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは。<a href="https://top.myroute.fun/">my route</a> 開発部でバックエンドチームのリーダーをしている yf です。<br>my route 開発部では、昨年 7 月に組織体制が変わり、新しい形で開発を進めています。</p>
<p>その変化に備えて、6 月から少しずつ進めてきた取り組みが、
半年たった今、チームの空気や仕事の進め方に確かな変化をもたらしています。</p>
<p>この半年で扱ったテーマは約 40。
一つひとつは小さな改善ですが、積み重ねることで
「開発の役割」や「プロダクトとの向き合い方」が大きく変わってきました。</p>
<p>本記事では、私たちがどんなステップを踏み、
なぜその変化が起きたのかを、時系列[^1]で振り返ります。</p>
<h1>この記事はこんな人向け</h1>
<ul>
<li>開発が「実装担当」に閉じてしまっていることに違和感がある方  </li>
<li>仕様やスケジュールが決まった状態で渡ってきて、Why を理解しづらいと感じている方  </li>
<li>プロダクト思考を持ちたいが、日々の開発で手応えを持てていない方  </li>
<li>組織改善をしたいが、何から手を付ければいいかわからないリーダー・サブリーダー  </li>
<li>「誰かを責める」のではなく、「構造を変える」アプローチを探している方</li>
</ul>
<p>私たちが半年間かけて試行錯誤してきた取り組みが、
同じような悩みを持つチームのヒントになれば幸いです。</p>
<h1>当時の状況と、なぜそうなっていたのか</h1>
<p>取り組みを始める前、開発部には次のような状況がありました。</p>
<ul>
<li>要件や仕様が、開発フェーズの後半で共有されることが多かった  </li>
<li>開発期間が限られ、品質改善や振り返りに十分な時間を取れなかった  </li>
<li>スケジュールや設計の背景（Why）が、開発側に伝わりにくかった  </li>
<li>結果として、実装を中心に進めざるを得ない進め方になっていた</li>
</ul>
<p>これは、特定の誰かの判断ミスというよりも、<br><strong>&quot;役割分担とプロセスの構造がそうさせていた状態&quot;</strong> だったと振り返っています。</p>
<p>プロデューサーはプロダクトを良くしようとする責任感から、
設計やスケジュールをできるだけ具体化しようとしていました。</p>
<p>一方で、その分、開発に共有されるタイミングが遅くなり、
開発側には「How（どう作るか）」を中心とした情報が渡る構造になっていました。</p>
<p>その結果、
<strong>&quot;なぜこの機能を作るのか（Why）を理解した上で改善提案を行う余地が少なくなり、&quot;</strong>
開発が実装中心の役割に閉じてしまっていたのです。</p>
<p>この状況を変えるために、
私たちは <strong>&quot;開発組織から、プロダクト組織へ&quot;</strong> という
大きな方向転換に踏み出しました。</p>
<h1>半年間の全体像</h1>
<pre><code>目的定義 → プロセス設計 → 運用定着 → 連携強化 → 品質と戦略 → 組織文化として定着
</code></pre>
<p>まず、私たちが向き合っていた「仕事の流れ」の変化を、Before / After で示します。</p>
<p><img src="/assets/blog/authors/yushu.fukushima/2026-01-07-01.png" alt="figure1" title="「仕事の流れ」の変化"></p>
<h1>■ 6 月 —— “目的と役割の再定義”</h1>
<pre><code>組織変革の起点
</code></pre>
<p>まず取り組んだのは、</p>
<ul>
<li><strong>“私たちはどこに向かうのか“</strong>  </li>
<li><strong>“チームリーダーは何を担うのか“</strong></li>
</ul>
<p>という意図合わせでした。<br>開発に関わる関係チームのリーダー全員（PDM・QA・バックエンド・モバイル）で、理想像の輪郭を描き直しました。</p>
<h2>主なテーマ</h2>
<ol>
<li>独立プロダクト組織としての目的定義  </li>
<li>リーダー役割の再整理  </li>
<li>過渡期の案件対応  </li>
<li>仕事の流れ図（AS-IS）の棚卸し  </li>
<li>チーム役割の再設計  </li>
<li>会議体・Slack などコミュニケーション設計</li>
</ol>
<p>この段階で、別の部である <a href="https://blog.kinto-technologies.com/posts/2025-02-12-engineering-office/">Engineering Office</a> にも参画してもらいました。
開発部の中だけでは前提になっていた考えや、
見落としていたプロセスの歪みを、
第三者の視点から言語化・整理してもらえたことは、
その後の設計を進めるうえで大きな支えになりました。</p>
<h4>6 月 あらためてひとこと - “まず揃えないと、何も始まらない”</h4>
<p>この時点では、具体的な施策よりも
<strong>&quot;前提が揃っていないまま進むことの危険性&quot;</strong> を強く感じていました。</p>
<p>早く手を動かしたい気持ちを抑え、
あえて立ち止まって目的と役割を言語化したのは、
後戻りしないための投資だったと思っています。</p>
<h1>■ 7 月 —— “仕組みの再設計に着手”</h1>
<pre><code>新しい仕事の流れの原型ができた
</code></pre>
<p>6 月に定義した理想を、実際のプロセスに落とし込んだ月です。</p>
<ul>
<li>スプリント導入（計画・中間・レビュー・レトロスペクティブ）  </li>
<li>チーム間連携会の常設  </li>
<li>Jira運用ルールの再構築  </li>
<li>ストーリーチケット作成基準の統一  </li>
<li>Git ブランチ戦略の見直し  </li>
<li>リリースフロー整備  </li>
<li>成果物レビューの仕組み化  </li>
<li>問い合わせ・障害の暫定ルール化</li>
</ul>
<p><strong>“会議体・プロセスがゼロから設計されていくスピード感“</strong> があり、<br>部全体の透明性が大きく向上しました。</p>
<h4>7 月 あらためてひとこと - “理想を、現実の流れに落とす”</h4>
<p>6月に描いた理想は、そのままでは机上の空論でした。</p>
<p>リーダとして意識したのは「誰がやっても迷わない仕組み」になっているか。
プロセスを設計することは、メンバーの思考コストを下げることだと実感した月でした。</p>
<h1>■ 8 月 —— “新プロセスの定着と運用強化”</h1>
<pre><code>Jiraと仕事の流れが形になってきた
</code></pre>
<ul>
<li>新プロセスの試験運用  </li>
<li>新規案件でのトライアル導入  </li>
<li>UI 定例、Slack などコミュニケーション基準化  </li>
<li>問い合わせ・ツール改修フローの改善  </li>
<li>ロードマップレビューの開始</li>
</ul>
<p>プロセスが回り始めたことで、<br><strong>“現場から自然と改善提案が出る状態“</strong> が生まれはじめました。</p>
<h4>8 月 あらためてひとこと - “仕組みは、使われて初めて意味を持つ”</h4>
<p>新しいプロセスは、導入しただけでは根付きません。
この月は「守らせる」よりも <strong>&quot;使ってみてどうだったかを聞く&quot;</strong> ことに注力しました。</p>
<p>現場から改善案が出始めたとき、組織が一段階変わった感覚がありました。</p>
<h1>■ 9 月 —— “プロダクト思考と横断連携の強化”</h1>
<pre><code>チーム間連携が日常化
</code></pre>
<ul>
<li>ストーリー分割ワーク  </li>
<li>役割を越えたアイデア提案の促進  </li>
<li>リソースアサイン管理の透明化  </li>
<li>AI 活用案件の相談  </li>
<li>リリース承認ルートの改善</li>
</ul>
<p>リーダー陣の視点が<br><strong>“「自分の領域」から「プロダクト全体」“へ</strong><br>大きくシフトした月でした。</p>
<h4>9 月あらためてひとこと - “役割を越えることを、許可する”</h4>
<p>プロダクト全体の話をするとき、役割を理由に遠慮が生まれる場面がありました。</p>
<p>リーダとして意識したのは、「それはあなたの領域じゃない」という空気を消すこと。
横断連携は、仕組みだけでなく心理的安全性があって初めて機能すると学びました。</p>
<h1>■ 10 月 —— “運用・リスク管理の高度化”</h1>
<pre><code>問い合わせ・障害・運用プロセスが進化
</code></pre>
<ul>
<li>問い合わせ・障害フローの再整備  </li>
<li>運用体制の検討  </li>
<li>目的起点の進め方ワーク  </li>
<li>アジャイルトレーニング準備  </li>
<li>他部門との連携強化</li>
</ul>
<p><strong>“リスクを未然に防ぐ動き”</strong> が自然と発生する組織へと変化しました。</p>
<p><img src="/assets/blog/authors/yushu.fukushima/2026-01-07-02.png" alt="figure1" title="目的起点の進め方ワーク"></p>
<h4>10 月 あらためてひとこと - “問題が起きる前提で、組織をつくる”</h4>
<p>問い合わせや障害は、ゼロにはできません。</p>
<p>だからこそ <strong>&quot;起きたときにどう振る舞えるか&quot;</strong> を考えるフェーズに入りました。
個人の頑張りに依存せず、組織として耐性を持つことを意識し始めた月です。</p>
<h1>■ 11 月 —— “品質・戦略・成長のフェーズへ”</h1>
<pre><code>技術とビジネスがつながり始めた
</code></pre>
<ul>
<li>スプリントへの QA 導入方針の確定  </li>
<li>セキュリティ監査オーナーの役割整理  </li>
<li>ポストモーテム文化の定着  </li>
<li>UI/UX 改善の進め方刷新  </li>
<li>フィールドワーク成果の共有</li>
</ul>
<p>“案件をこなす組織” から<br><strong>“プロダクトを成長させる組織”</strong> へと進化。</p>
<h4>11 月 あらためてひとこと - “技術は、ビジネスとつながってこそ価値になる”</h4>
<p>品質やUXの議論が増えたことで、技術がプロダクト価値にどう貢献するかを
言葉にする機会が増えました。</p>
<p>リーダとしては、技術的な正しさと、事業としての判断をつなぐ役割を
より強く意識するようになりました。</p>
<h1>■ 12 月 —— “半年の蓄積が組織文化に変わり始めた”</h1>
<pre><code>目的起点で動ける組織に
</code></pre>
<ul>
<li>UI/UX 改善の長期方針の確立  </li>
<li>QA 導入プロセスの実運用  </li>
<li>ロードマップレビューの定着  </li>
<li>工程ごとのリードタイム測定開始</li>
</ul>
<p>6 月に掲げた<br><strong>“自律したプロダクト組織になる”</strong><br>という目標が、実際の動きとして現れてきました。</p>
<h1>半年で生まれた “4 つの変化”</h1>
<h3>① 情報の透明性</h3>
<p>プロダクト全体の状態が誰にでも見えるようになった。</p>
<h3>② 早期リスク検知</h3>
<p>問い合わせ・障害・運用課題が芽のうちに見つかるようになった。</p>
<h3>③ 横断連携の活性化</h3>
<p>PDM・QA・バックエンド・モバイルが互いに提案しあう文化が育った。</p>
<h3>④ 再現性のあるプロセス</h3>
<p><strong>“誰がやっても前に進む”</strong> 仕組みが整った。</p>
<h4>12 月 あらためてひとこと - “文化は、後から気づくもの”</h4>
<p>何か劇的なイベントがあったわけではありません。</p>
<p>ただ振り返ってみると、目的から考え、自然と連携し、改善を回す
<strong>&quot;当たり前の動き&quot;</strong> が定着していました。</p>
<p>組織文化は、作ろうとして作るものではなく、
積み重ねの結果として生まれるのだと感じています。</p>
<h1>もともとの課題は、どこまで変わったか</h1>
<p>取り組みを始めた当初、私たちは  「開発が実装中心の役割に閉じてしまっている」という課題を抱えていました。</p>
<p>半年間の取り組みによって、</p>
<ul>
<li>Why を共有したうえで議論できる場が増え</li>
<li>スケジュールや設計の背景を前提に、改善提案が出るようになり</li>
<li>開発が「決められたものを作る」だけの立場ではなくなってきた</li>
</ul>
<p>といった変化が確かに生まれました。</p>
<p>一方で、  すべてが理想通りに解消されたわけではありません。
プロダクト全体の意思決定への関与や、  戦略レイヤーでの議論は、まだ発展途上です。</p>
<p>それでも、  <strong>「なぜやるのかを考えながら作る」ことが当たり前になり始めた</strong> という点で、
当初感じていた課題は、確実に別のフェーズへ進んだと感じています。</p>
<h1>次の半年へ</h1>
<p>これからは、<br><strong>“プロダクトをつくる組織“から“プロダクトを成長させる組織“</strong><br>へさらに進化していきます。</p>
<ul>
<li>グロースハック文化の定着  </li>
<li>権限移譲と育成の体系化  </li>
<li>プロダクト戦略の内製化  </li>
<li>インシデント学習ループ強化  </li>
<li>開発体制の継続アップデート</li>
</ul>
<p>内部だけで議論すると主観に偏り、問題の本質を見誤ることがあります。<br>そのため今回は、部を横断して取り組めたことも私たちにとって大きな追い風となりました。</p>
<p>半年で大きく変化した組織が、次の半年でどこまで成長できるのか。<br>私自身、とても楽しみにしています。</p>
<h1>さいごに</h1>
<p>my route 開発部では、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://hrmos.co/pages/kinto-technologies/jobs/1811937538128224267">こちら</a>からご確認ください！</p>
<hr>
<p>[^1]: 半年で扱った 40 のテーマを月別の代表例として抜粋しています。  </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年10月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-26-newcomer-202510/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-26-newcomer-202510/</guid>
            <pubDate>Fri, 26 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年10月に入社した6人の皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、2025年10月入社のr.tesakiです！</p>
<p>本記事では、2025年10月入社のみなさまに入社直後の感想をお伺いし、まとめてみました。
KINTOテクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>S.N.</h1>
<p>![S.N.さんのプロフィール画像](/assets/blog/authors/tesaki/2025-12-24-newcomer-202512/S_N.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>KINTO中古車開発Gのバックエンドエンジニアとして入社しました。室町オフィス勤務です。</li>
<li>最近映画館の近くに引っ越したので、映画館で映画を観るのにハマっています！</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>KINTO中古車開発Gは、プロデュースチーム、フロントエンドチーム、バックエンドチームの3チーム体制です。</li>
<li>バックエンドチームは自分を含めて8名です。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>出社した日にはチームメンバーと一緒にランチに行くなど賑やかな雰囲気です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li><strong>入社動機：</strong> 大きなエンジニア組織＆物を扱うサービスに携わりたいと考えていたため。</li>
<li><strong>入社前後のギャップ：</strong> ミーティングや社内イベントなどで他拠点の方とも関わることが想像よりも多く、会社としての一体感があって良いと感じています！</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>室町オフィスは駅直結なので、天気を気にしなくて良いのが嬉しいです！</li>
</ul>
</li>
<li><strong>M.U.さん ⇒　S.N.さんへの質問</strong><blockquote>
<p>小学生の時の将来の夢（なりたい職業）は何でしたか？</p>
</blockquote>
<ul>
<li>小学校の卒業アルバムの写真撮影でエプロンをつけた記憶があるので、料理人になりたかったんだと思います！</li>
</ul>
</li>
</ul>
<h1>K.S.</h1>
<p>![K.S.さんのプロフィール画像](/assets/blog/authors/tesaki/2025-12-24-newcomer-202512/K_S.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>酒とカラオケをこよなく愛するアラフィフ。自称元プロゲーマー。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>コーポレートIT部所属。親会社のビッグプロジェクトに参画してシステムリプレースのリーダー。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>個々が際立っていてオリジナリティーのある人たちがワイワイしている感じ。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li><strong>入社動機：</strong> エンジニアの報酬が高いし、役職定年ないし、バリバリ働けそうだから。</li>
<li><strong>入社前後のギャップ：</strong> チームで働くというよりピンで活動することが多い気がする。意外に社長、副社長との距離が近いｗ</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>神保町オフィスは人が少なくて広々と使えるし、とても開放感があってよい！</li>
</ul>
</li>
<li><strong>S.N.さん ⇒　K.S.さんへの質問</strong><blockquote>
<p>一番好きなお酒を教えてください！</p>
</blockquote>
<ul>
<li>ビール🍺がサイコーです👌が、痛風が怖いので梅サワーで我慢してます🤷‍♂️</li>
</ul>
</li>
</ul>
<h1>r.tesaki</h1>
<p>![r.tesakiさんのプロフィール画像](/assets/blog/authors/tesaki/nekonote.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>オンプレのインフラエンジニアからスタートしてKTCではプラットフォーム開発部DBREグループのSREチームメンバーとして入社しました。Osaka Tech Lab所属です。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>Database を専門とするDBREチームと、プロダクト全体を担うSREチームの2チームに分かれて活動してます。KINTOや他の業務システムの開発チームと一緒に活動することが多いです。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>Osaka Tech Labはチームを跨いだ一体感があって、他のチームメンバーは東京にいますが孤立感はなく賑やかに感じてます。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li><strong>入社動機:</strong> 組織横断であったり、プロダクト専任であったりと色々な形のSREに挑戦できそうだったため。</li>
<li><strong>入社前後のギャップ:</strong> 聞いてた以上に東京へ行ける機会が多く、承認もスムーズに進むことです。各種イベント参加もしやすいです。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>JR大阪駅の改札を出て目の前にオフィスビルの入口があるところ。</li>
</ul>
</li>
<li><strong>K.S.さん ⇒　r.tesakiさんへの質問</strong><blockquote>
<p>10億当たったら何に使う？</p>
</blockquote>
<ul>
<li>猫を飼う人しか住めないマンションを立てて猫好きの楽園をつくる！</li>
</ul>
</li>
</ul>
<h1>ぬー</h1>
<p>![ぬーさんのプロフィール画像](/assets/blog/authors/tesaki/2025-12-24-newcomer-202512/nu.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>プラットフォーム開発部 Cloud Infrastructure G に所属しています。KINTO 関連システムの AWS インフラの構築や保守運用を担当しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>グループはインフラチーム、カイゼンチーム、ソリューションチームの3チーム体制で、同じインフラ領域でも別々の責務を担っています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>メンバーの仲が良く、雑談も多いです。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>プラットフォーム開発部はグループやチームがたくさんあり、思っていた以上に役割が細分化されていると感じました。</li>
<li>若手をリードしてほしいと言われて入社しましたが、今のところ(いい意味で)リードする必要性を感じないぐらい素晴らしいメンバーだと思います！</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>神保町オフィスは近隣に飲食店が豊富で、おいしいお店が多いところ</li>
</ul>
</li>
<li><strong>r.tesakiさん ⇒　ぬーさんへの質問</strong><blockquote>
<p>おすすめのキャンプグッズ教えてください！</p>
</blockquote>
<ul>
<li>特にこれ！というグッズは無いのですが、SOTO というメーカーの製品はコンパクトなものが多いのでおすすめです！</li>
<li>キャンプをしていると荷物が増えてきて、少しでも物を減らしたり同じ用途でも小さいものにしたくなるので。</li>
<li>(といっても自分では持っていなくて、今後買いたいなと思っているところですｗ)</li>
</ul>
</li>
</ul>
<h1>U.V.</h1>
<p>![U.V.さんのプロフィール画像](/assets/blog/authors/tesaki/2025-12-24-newcomer-202512/U_V.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>ビジネスディベロップメントGのU.V.です。 国内外のビジネス拡張を担当しております。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>5名で構成された、多国籍のチームです。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>マネジメントからの方針は明確ですが、各メンバーが自由に意見を述べ、自分の仕事に主体性を持って取り組めていると感じています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>オリエンテーションは分かりやすく、私が抱えていた疑問をすべて解消してくれました。</li>
<li>入社時点で有給休暇が付与されると伺い、とても驚きました。ありがとうございます。</li>
<li>毎月新しいプロジェクトが立ち上がり、仕事がとてもダイナミックで楽しんでいます。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>オフィスは混雑しておらず、木製の家具がとても可愛らしいです。</li>
</ul>
</li>
<li><strong>ぬーさん ⇒　U.V.さんへの質問</strong><blockquote>
<p>日本に来た動機と、日本に来て驚いたことや変だなーと思うことがあれば教えてください！</p>
</blockquote>
<ul>
<li>日本で留学生として過ごした時間がとても楽しく、自分の国とは大きく違う環境でキャリアを築きたいと思い、日本で働くことを決めました。</li>
<li>驚いたことの一つは、日本の方が電話でもお辞儀をすることです。</li>
</ul>
</li>
</ul>
<h1>M.U.</h1>
<p>![M.U.さんのプロフィール画像](/assets/blog/authors/tesaki/2025-12-24-newcomer-202512/M_U.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>モビリティプロダクト開発部で、販売店様との関係構築/プリセールスを担当しています</li>
<li>勤務先は今年開設されたばかりの福岡です</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>4名体制（私以外は東京のオフィスがベース）で全国の販売店様を担当しています</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>福岡では皆で協力しながら、オフィスを運営しています</li>
<li>出張で来られる方が多いので、部署を超えて会話する機会が多いですね</li>
<li>チームメンバーとは定期的にオンサイトでコミュニケーションを取っているので、リモートだからといって不自由は無いです</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>中規模Sierとスタートアップを経験したので、規模の大きな会社で新しいチャレンジをしてみたいと思ったからです</li>
<li>大きなギャップはなく、個々人のスキルが高くプロの集団だと感じました</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>勤務先の福岡テックラボは11月にオープンしたばかり！</li>
<li>海が見渡せる開放感のある景色とおしゃれな内装で、出社したくなるオフィスです</li>
</ul>
</li>
<li><strong>U.V.さん ⇒　M.U.さんへの質問</strong><blockquote>
<p>お仕事の中で、AI をどのように活用しているのか、興味深い取り組みがあれば教えていただけますか。</p>
</blockquote>
<ul>
<li>主に調査や資料作成など一般的な使い方です</li>
<li>情報をキャッチアップする際にコパイロットだと、社外情報＋社内資料も提案してくれるのが素敵ですね</li>
<li>販売店様の情報を調査する等であればGeminiの方が優秀だと感じています</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTOテクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年の振り返りと2026年の展望：Agenticな未来へ]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-25-LookBack2025/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-25-LookBack2025/</guid>
            <pubDate>Thu, 25 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年のKINTOテクノロジーズ（KTC）の取り組み総括および、2026年に向け、AI活用の深化と組織文化の変革に焦点が当てられた展望を語ります]]></description>
            <content:encoded><![CDATA[<p>KINTOテクノロジーズ（KTC）の景山です！ 年末恒例となりますが、2025年の振り返りと、来る2026年の展望について書きたいと思います。 </p>
<h2>2025年の振り返り：実装とカルチャーへの定着</h2>
<p>2025年の年初、私は「AIファースト」と「リリースファースト（最短リリース）」という2つのテーマを掲げました。 </p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/</a> </p>
<h3>1. AIファースト：実装の年</h3>
<p>世の中的にも生成AIの実装が当たり前となった2025年、KTCでも以下の3点を推進してきました。 </p>
<ul>
<li>すべてのプロダクトへのAIインテグレート</li>
<li>AIプロダクトを多く開発する</li>
<li>販売店やトヨタグループにおけるAI活用のドライバーとなる</li>
</ul>
<p>この1年で、社員のみなさんのマインドは劇的に変わりました。私の想像を超えるスピードで変化が進み、KTCがグループ内でもAI活用の先頭集団にいられたのは、みなさんの真剣な取り組みのおかげです。本当にありがとうございました！ </p>
<h3>2. リリースファースト：カルチャーへの浸透</h3>
<p>こちらはテクニカルな施策というよりも、「プロダクト開発の文化」として組織に深く根付きつつあります。「何を作るか」だけでなく「いかに速く届けるか」。この意識が、私の想定を軽々と飛び越え、みなさんの手によって現場の当たり前になりつつあることに頼もしさを感じています。 </p>
<h2>2026年の展望：自律と拡張の年</h2>
<p>テクノロジー業界の潮流は、「対話するAI」から「行動するAI」へと急速にシフトしています。 </p>
<p>この波を捉えるために、2026年は以下の2つのファーストに注力します。 </p>
<h3>1. Agentファースト（Agentic AI）</h3>
<p>2025年までのAIは、人間が指示を出して答えをもらう「賢いチャットボット」が主流でした。しかし、昨今の技術トレンドは、複雑な推論を行い、自律的にタスクを完遂する「AI Agent」へと完全に移行しています。 </p>
<p>これまでのAI活用は「個人の作業補助」に留まりがちで、組織全体の効率化には限界がありました。しかし、Agentは違います。抽象的な目的を与えれば、AI自らがタスクを分解し、ツールを操作し、人間のようにアクションを実行します。 </p>
<p>KTCでも一部の部署でAgent活用が始まっていますが、2026年はこれを全社展開します。 </p>
<ul>
<li><strong>CX変革</strong>：Web/App上で、コンシェルジュのように振る舞うAI</li>
<li><strong>Ops変革</strong>：運用・業務プロセスを自律的に回すAI</li>
<li><strong>Sales変革</strong>：クルマの販売プロセスそのものを再発明するAI</li>
</ul>
<p>世の中が「AIを使う」から「AIに任せる」へと変わる今、全部署でAgentを生み出し、組織の生産性を爆発的に高めていきましょう。 </p>
<h3>2. AIエンジニアリングファースト（AI-Native Dev）</h3>
<p>ソフトウェア開発の世界では今、GitHub Copilot WorkspaceやKiro、Cursor、Claude CodeのようなAIネイティブな開発環境が標準になりつつあります。「コードを書く」という行為そのものの定義が変わろうとしているのです。 </p>
<p>この変化は、これまでの技術革新とは比較にならないスピードで進んでいます。古いやり方に固執すれば、あっという間に陳腐化します。しかし逆に言えば、<strong>「AIエンジニアリング」を武器にすれば、職種の壁を超えられる</strong>ということです。 </p>
<p>企画、要件定義、設計、実装。AIが実装をサポートしてくれる今、PdMやPjMといった職種の方々も、従来はエンジニア領域とされていた仕事まで拡張できるチャンスです。会社は最新の武器（ツール・環境）を惜しみなく提供します。一人ひとりが「AIネイティブ」な視点でプロセスを再構築し、開発のあり方を抜本的に変えていきましょう。 </p>
<h2>KTCの揺るぎない「基本的価値」</h2>
<p>技術トレンドがいかに変わろうとも、KTCの競争力の根幹となる価値観は変わりません。 </p>
<ul>
<li><strong>インテンシティ（MoveFast・OwnerShip）</strong> </li>
<li><strong>リリースファースト</strong></li>
<li><strong>ユーザーファースト</strong></li>
</ul>
<p>これらの価値はKTCの各人により構成され、KTCが変化の激しい時代を生き抜くための基礎力です。会社としても支援しますが、何よりみなさん自身の実践にかかっています。引き続き、徹底していきましょう。</p>
<h2>KTCの強み、優位性：Vertical AIへの挑戦</h2>
<p>汎用的なAIモデルは一般化し、世界中の誰もが使えるようになりました。では、KTCはどこで勝つのか。それは「ドメイン特化（Vertical）戦略」です。 </p>
<ul>
<li><strong>AI (Agent)</strong> </li>
<li><strong>クラウド</strong> </li>
<li><strong>ドメイン知識 &amp; グロースノウハウ</strong></li>
</ul>
<p>AI×クラウドの技術力は前提条件です。ここは進化が速く、常に最先端をキャッチアップし続ける危機感が必要です。 その上で、我々には「トヨタグループのビジネス資産」があります。コネクティッドデータ、車両販売の知見、リアルな顧客接点。これらは他社が簡単に模倣できない資産です。 </p>
<p><strong>「AI×クラウド」というグローバルな武器で、「モビリティ」という独自の領域を深掘りする</strong>。業務知識を蓄え、それをAIで価値に変換するサイクルを回すことこそが、KTCだけの強みになります。 </p>
<h2>最後に</h2>
<p>2026年は、技術の進化がさらに加速し、社会実装のフェーズも一段階上がります。変化を恐れず、むしろその先頭に立つ気概を持って、一人ひとりがアップデートしていきましょう！ </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ノンデザイナーでも“表紙デザイン”は作れる？ AIと共創した技術書の表紙制作プロセス]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-25-ai-bookcover-process/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-25-ai-bookcover-process/</guid>
            <pubDate>Thu, 25 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[ChatGPTとMidjourneyを活用し、ノンデザイナーでもできるかもしれない表紙デザイン制作のプロセスを実例を通して紹介しています。]]></description>
            <content:encoded><![CDATA[<p>こんにちは！KINTOテクノロジーズのクリエイティブGでデザイナーをしているmayuです。
今回は、技術書典の表紙制作の過程をご紹介したいと思います。
Midjourneyなどの生成AIツールを活用しているので、「<strong>ノンデザイナーでもプロ並みの制作ができる？！</strong>」のがミソです。
デザイナーの方も、そうでない方もぜひご覧ください！</p>
<h2>技術書典用の表紙デザインを依頼された</h2>
<p>ある日、エンジニアのうえはらさんから
<strong>「KINTOテクノロジーズ(KTC)の有志エンジニアによる技術書を技術書典に出展したいので、表紙デザインを作ってほしい」</strong>
という依頼を受けました。</p>
<p>今回はクリエイティブGのデザイナーmomoiさんの提案で
<strong>「デザイナーを集めてライブペインティング方式で作成するのはどうか？」</strong>
ということで、ライブペインティングイベント内での制作が決定しました！</p>
<h3>技術書典ってなに？</h3>
<p>技術書典についてはうえはらさんが書いてくださった記事があるのでこちらをご覧ください。
<a href="https://blog.kinto-technologies.com/posts/2025-12-12-techbookfest19-report/">https://blog.kinto-technologies.com/posts/2025-12-12-techbookfest19-report/</a></p>
<h3>ライブペインティングってなに？</h3>
<p>ライブペインティングについてはmomoiさんが書いてくださった記事があるのでこちらをご覧ください。
<a href="https://blog.kinto-technologies.com/posts/2025-12-24-ai-live-painting/">https://blog.kinto-technologies.com/posts/2025-12-24-ai-live-painting/</a></p>
<h2>どうやって作ったか</h2>
<p>ざっくり作り方を説明すると、</p>
<p><strong>1. お題をChatGPTにインプットさせる</strong>
<strong>2. ChatGPTにプロンプトを書いてもらう</strong>
<strong>3. Midjourneyで画像を生成する</strong>
<strong>4. Midjourneyで解像度UP &amp; Photoshopで微修正する</strong>
<strong>5. illustratorで入稿データを作成する</strong></p>
<p>の5ステップです！
それぞれ紐解いていきますね。</p>
<h3>1. お題をChatGPTにインプットさせる</h3>
<p>今回のお題はこちらでした。</p>
<p><img src="/assets/blog/authors/mayu_jibu/251225/live-painting.png" alt="live-painting"> </p>
<p>お題は事前に知らされていなかったので、正直「ざっくりしたお題だな…」と思い最初は全くイメージが浮かびませんでした。笑</p>
<br>

<h3>2. ChatGPTにプロンプトを書いてもらう</h3>
<p>まずは、私の相棒ChatGPTにお題をインプットして、アイデア出しをしてもらうことにしました。</p>
<p><img src="/assets/blog/authors/mayu_jibu/251225/chatgpt-1.png" alt="chatgpt-1"> </p>
<p>ひとまず、プロンプトを出してくれたのでこれをMidjourneyに入力していきます。</p>
<br>

<h3>3. Midjourneyで画像を生成する</h3>
<p><img src="/assets/blog/authors/mayu_jibu/251225/midjourney-1.png" alt="midjourney-1"> </p>
<p>一回目の生成結果はこんな感じ！
るぴあは事前に配布されていた素材を<strong>Omni-Reference</strong>で読み込ませ、一貫性を保っています。
Omni-Referenceについてはmomoiさんが詳しく書いてくださっているのでこちらの記事をご覧ください。
<a href="https://blog.kinto-technologies.com/posts/2025-06-13-omni-reference/">https://blog.kinto-technologies.com/posts/2025-06-13-omni-reference/</a></p>
<br>

<p>ここまでやって、なんとなく世界観のイメージは湧いてきました。
でも、るぴあが棒立ちで動きがないので何か違うなーって感じ…
「どんなポーズがいいか？」とChatGPTに相談してみました。</p>
<p><img src="/assets/blog/authors/mayu_jibu/251225/chatgpt-2.png" alt="chatgpt-2"> </p>
<p>さすがチャッピー！！かなり具体的でこれならイメージに合うものが作れそう！
それぞれのポーズをプロンプトに落とし込んでもらいました。</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/chatgpt-3.png" alt="chatgpt-3"> </p>
<p>これを、もう一度Midourneyで生成してみます。</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/midjourney-2.png" alt="midjourney-2"> 
<img src="/assets/blog/authors/mayu_jibu/251225/midjourney-3.png" alt="midjourney-3"> </p>
<p>なかなかいい感じになりました！
先ほどの棒立ちるぴあと比べると、「未来感」が増した気がします。
ただ、表紙の上部にはタイトルを入れる予定なので、これだと頭と文字が被ってしまいそうなんですよね…
どうしよう…と考えた結果、</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/midjourney-4.png" alt="midjourney-4"> </p>
<p>座らせることにしました！笑
(るぴあを座らせるようChatGPTにプロンプトを書いてもらいました)
これなら画像の上部が空くので、タイトルも問題なく入りそうです。</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/midjourney-5.png" alt="midjourney-5"> </p>
<p>何回か作成すると、かなりしっくり来る画像が生成されました！！</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/midjourney-6.png" alt="midjourney-6"> </p>
<p>こちらです！
まさに私が求めていた「先進的で、モビリティ感のある画像」が生成されました！
画像の上部もいい感じに空いているので、タイトルもきちんと収まりそうです。
ここから微修正は入れますが、ベースのデザインはこれで決定しました。</p>
<h3>4. Midjourneyで解像度UP &amp; Photoshopで微修正する</h3>
<p>ライブペインティングイベント内では時間制限があったため、先ほど生成した画像をそのまま提出したのですが、ありがたいことに依頼者票・オーディエンス票ともに1位に選んでいただき、晴れて表紙デビューが決定しました！！！</p>
<p>となると、先ほどの画像を表紙用に綺麗に整えていく必要があります。
ここからはMidjourneyとAdobeのPhotoshopを使って画像を修正していきます。</p>
<p>まずは、Midjourneyを使って解像度を上げていきます。</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/midjourney-7.png" alt="midjourney-7"> </p>
<p>やり方はとても簡単で、画像を開いて右下の <strong>「Creation Actions」→「Upscale」→「Subtle」を一回クリックするだけ</strong>です。
(隣にある「Criative」は、今回は元の画像を保持したいので使いませんでしたが、ディティールを加えてクオリティアップを目指すならこちらもおすすめです！)
この操作により画質は保ったまま「<strong>896px × 1,344px</strong>」から「<strong>1792px × 2,688px</strong>」と、2倍の解像度になりました！</p>
<p>次に、Photoshopで微修正をしていきます。</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/photoshop-1.png" alt="photoshop-1"></p>
<p>気になるのはこの辺ですね。</p>
<ul>
<li>るぴあのヘッドホンに描かれている∞などのマーク</li>
<li>左下の暗号ぽいもの</li>
</ul>
<p>Photoshopで簡単に消していきたいと思います。</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/photoshop-2.png" alt="photoshop-2"></p>
<p>なげなわツールで不要な部分を囲ったら、「<strong>生成塗りつぶし</strong>」をクリックします。(※プロンプトは無しでOK)</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/photoshop-3.png" alt="photoshop-3"></p>
<p>綺麗に消えました！
左下部分も同様の操作を行っていきます。</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/photoshop-4.png" alt="photoshop-4">
<img src="/assets/blog/authors/mayu_jibu/251225/photoshop-5.png" alt="photoshop-5"></p>
<p>自然に馴染みました！</p>
<br>

<p><img src="/assets/blog/authors/mayu_jibu/251225/photoshop-6.png" alt="photoshop-6"></p>
<p>これで画像が仕上がったので、あとは入稿データを作成していきます。</p>
<br>

<h3>5. illustratorで入稿データを作成する</h3>
<p><img src="/assets/blog/authors/mayu_jibu/251225/nyuko-data.png" alt="nyuko-data"></p>
<p>こちらが入稿データです！
タイトルなど付け足して背表紙と裏表紙も作成したら完成です。</p>
<h2>制作を終えて</h2>
<p>制作を終えて感じたのは、AIのおかげでノンデザイナーでも高品質なビジュアルを作れるハードルが格段に下がったということです。
今回の表紙制作でも、ChatGPTでのアイデア出しやMidjourneyでの画像生成によって、ゼロから何かを生み出す負荷が大きく減りました。</p>
<p>その一方で、「何がいい」「何が悪い」の判断や、細かな世界観の調整など、AIだけでは完結しない部分も多くあります。
だからこそ、AIの力を借りつつ、人間が持つ感性を利用して方向性を選択することが重要だとも改めて感じました。</p>
<p>私は今年からAIプロジェクトメンバーとして携わってきて、さまざまなツールを使う中で、少しずつ「<strong>AIへの正しい頼り方</strong>」がわかってきた実感があります。
AIはまだ完全ではなく、人間に置き換わることはできませんが、高いクリエイティビティを持っていて、私にとって仕事に欠かせない存在になっています。
これからも、AIを心強いパートナーとして、うまく協業しながら制作に向き合っていきたいと思っています。
最後までお読みいただき、ありがとうございました！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/mayu_jibu/251225/main-image.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[React Compiler を検証してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-25-react-compiler-evaluation/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-25-react-compiler-evaluation/</guid>
            <pubDate>Thu, 25 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[React Compiler 導入後、効果が実感できないため有効・無効時でベンチマークを実施し、本当にメモ化されているのか、どれくらいのパフォーマンス差があるのかを検証しました。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO テクノロジーズ Advent Calendar 2025</a> の 25 日目の記事です 🎅🎄</p>
<h2>はじめに</h2>
<p>こんにちは！
KINTO 開発部 KINTO バックエンド開発 G マスターメンテナンスツール開発チーム、技術広報 G 兼務、Osaka Tech Lab 所属の high-g（<a href="https://x.com/high_g_engineer">@high_g_engineer</a>）です。フロントエンドエンジニアをやっています。</p>
<p>現在開発中のプロジェクトでは、RC 版の頃から React Compiler を導入しており、約 8 ヶ月が経ちました。
導入によって <code>useMemo</code> や <code>useCallback</code> を書かなくても自動でメモ化されるため、メモ化を意識する必要がなくなり、開発体験は向上しました。
しかし、実際にどの程度メモ化が正しく行われているのか、パフォーマンスにどれくらいの影響があるのかは、詳しく検証できていませんでした。
そこで本記事では、React Compiler の有効時の挙動や、有効時と無効時のパフォーマンス比較を検証してみることにしました。</p>
<h2>React Compiler とは</h2>
<p><a href="https://ja.react.dev/learn/react-compiler/introduction">https://ja.react.dev/learn/react-compiler/introduction</a></p>
<p>React Compiler は、ビルド時に自動的にメモ化を行うことで React アプリを最適化するツールです。
そのため、React Compiler を導入すれば、<code>useMemo</code> や <code>useCallback</code>、<code>React.memo</code> などを手動で書く必要がなくなります。</p>
<p>最初の安定版（v1.0）は 2025 年 10 月 7 日にリリースされ、この記事が執筆された時点で約 2 か月半が経過しています。</p>
<p>安定版リリースまでの経緯は以下の通りです。</p>
<ul>
<li>2023 年 3 月 - 「React Forget」として開発、Meta 社内の限定的な領域で production 利用開始</li>
<li>2023 年 10 月 - <a href="https://reactadvanced.com/2023/">React Advanced Conference 2023</a> で「React Forget」として公開発表</li>
<li>2024 年 2 月 - instagram 全体で production 展開完了、Meta 社内の他サービスへ展開、OSS 化準備と発表</li>
<li>2024 年 5 月 - <a href="https://conf2024.react.dev/">React Conf 2024</a> で experimental release を発表</li>
<li>2024 年 10 月 21 日 - Beta release を公開</li>
<li>2025 年 4 月 21 日 - Release Candidate (RC) を公開</li>
<li>2025 年 10 月 7 日 - v1.0 安定版リリース</li>
</ul>
<h2>React Compiler の設定方法</h2>
<p>Vite と React 19 を使用した環境での React Compiler の設定方法を紹介します。</p>
<h3>1. パッケージのインストール</h3>
<pre><code class="language-bash">pnpm add -D babel-plugin-react-compiler
</code></pre>
<h3>2. vite.config.ts の設定</h3>
<p><code>@vitejs/plugin-react</code> の <code>babel</code> オプションに <code>babel-plugin-react-compiler</code> を追加します。</p>
<pre><code class="language-ts">import react from &quot;@vitejs/plugin-react&quot;;
import { defineConfig } from &quot;vite&quot;;

// 設定オプション
const ReactCompilerConfig = {
  /* ... */
};

export default defineConfig({
  plugins: [
    react({
      babel: {
        plugins: [[&quot;babel-plugin-react-compiler&quot;, ReactCompilerConfig]],
      },
    }),
  ],
});
</code></pre>
<p>これだけで設定は完了です。後はビルド時に React Compiler が自動的にコードを解析し、必要な箇所にメモ化を適用してくれます。</p>
<p>React Compiler の設定オプションに関しては、本記事では説明を割愛します。
特に設定しなくても動作しますが、詳細を知りたい方は以下を参照してください。</p>
<p><a href="https://ja.react.dev/reference/react-compiler/configuration">https://ja.react.dev/reference/react-compiler/configuration</a></p>
<h2>ベンチマーク対象の React アプリ</h2>
<p>実際の開発プロジェクトで検証を試みましたが、使用しているライブラリに既にメモ化されたコンポーネントが多く含まれていて、純粋な比較が困難だったので、ベンチマーク専用のプロジェクトを作成しました。
ヘッダー、サイドメニュー、メインコンテンツのエリアで構成され、初期表示時の合計コンポーネント数は約 100 個です。</p>
<p><img src="/assets/blog/authors/high-g/20251225/img1.png" alt="ベンチマーク対象のプロジェクト"></p>
<p>最初に、React Compiler が無効の状態で、React Dev Tools でメモ化の状況を確認していきます。<code>Components</code> タブを見ると、メモ化されている場合に表示される「Memo」のラベルが一切ないことがわかります。</p>
<p><img src="/assets/blog/authors/high-g/20251225/img2.png" alt="React Dev Tools による Components の表示"></p>
<p>次に、React Dev Tools の設定で <code>Highlight updates when components render</code> にチェックを入れて、上位にあるボタンコンポーネントをクリックし再レンダリングの様子を確認すると、本来再レンダリングが不要な子孫コンポーネントにも再レンダリングが発生していることが分かります。</p>
<p><img src="/assets/blog/authors/high-g/20251225/img3.png" alt="コンポーネントが再レンダリングされた際の Highlight 表示"></p>
<h2>React Compiler のメモ化の挙動</h2>
<p>React Compiler を有効にして開発サーバーを立ち上げ、同じアプリがメモ化されているかを確認します。
React Dev Tools の <code>Components</code> タブを確認すると、「Memo」のラベルが数多く表示されていることが分かります。</p>
<p><img src="/assets/blog/authors/high-g/20251225/img4.png" alt="React Compiler を適用したプロジェクトのメモ化の様子"></p>
<p>同様に、上位にあるボタンコンポーネントをクリックし、React Dev Tools で再レンダリングの状態を確認すると、再レンダリングが必要最小限に抑えられていることが分かります。</p>
<p><img src="/assets/blog/authors/high-g/20251225/img5.png" alt="React Compiler を適用したプロジェクトにあるコンポーネントが再レンダリングされた際の Highlight 表示"></p>
<h2>React Compiler のパフォーマンスベンチマーク</h2>
<p>ここからは、React Compiler によるパフォーマンスの差を React Dev Tools の <code>Profiler</code> を用いて検証していきます。</p>
<p>検証では、下記の赤枠のボタンを約 1 秒間隔で 10 回連続クリックした際の、レンダリング時間の比較を行いました。</p>
<p><img src="/assets/blog/authors/high-g/20251225/img6.png" alt="Global Count ボタンに赤枠がついたダッシュボード画面"></p>
<p>このボタンはメインコンテンツの最上位に配置されているため、クリック時に多くの子孫コンポーネントへ再レンダリングの影響が波及します。</p>
<p><img src="/assets/blog/authors/high-g/20251225/img7.png" alt="Global Count ボタンに関する説明"></p>
<h3>React Compiler 無効時（メモ化なし）</h3>
<p><img src="/assets/blog/authors/high-g/20251225/img8.png" alt="React Compiler 無効時の Profiler の結果"></p>
<p><strong>計測データ</strong></p>
<ul>
<li>1回目: 29ms</li>
<li>2回目: 34.5ms</li>
<li>3回目: 36.1ms</li>
<li>4回目: 33.9ms</li>
<li>5回目: 36.3ms</li>
<li>6回目: 17.6ms</li>
<li>7回目: 35.1ms</li>
<li>8回目: 32.1ms</li>
<li>9回目: 33.3ms</li>
<li>10回目: 36.8ms</li>
</ul>
<p><strong>平均レンダリング時間</strong>
32.5ms</p>
<p>Flamegraph を見ると、すべての子孫コンポーネントがレンダリングされていることが分かります。また、MainContents 以外にもレンダリング時間が長いコンポーネント（黄色やオレンジ色で表示）が存在し、本来不要な再レンダリングが発生していることが確認できます。</p>
<h3>React Compiler 有効時（メモ化あり）</h3>
<p><img src="/assets/blog/authors/high-g/20251225/img9.png" alt="React Compiler 有効時の Profiler の結果"></p>
<p><strong>計測データ</strong></p>
<ul>
<li>1回目: 11.1ms</li>
<li>2回目: 12.1ms</li>
<li>3回目: 12.2ms</li>
<li>4回目: 12.1ms</li>
<li>5回目: 12.1ms</li>
<li>6回目: 12.1ms</li>
<li>7回目: 12.1ms</li>
<li>8回目: 12.0ms</li>
<li>9回目: 12.0ms</li>
<li>10回目: 12.6ms</li>
</ul>
<p><strong>平均レンダリング時間</strong>
12.0ms</p>
<p>Flamegraph を見ると、グレーの斜線で表示されているコンポーネントが多数確認でき、再レンダリングが必要最小限に抑えられていることが分かります。</p>
<h3>パフォーマンス改善の結果</h3>
<table>
<thead>
<tr>
<th></th>
<th>React Compiler 無効時</th>
<th>React Compiler 有効時</th>
<th>改善結果</th>
</tr>
</thead>
<tbody><tr>
<td>平均レンダリング時間</td>
<td>32.5ms</td>
<td>12.0ms</td>
<td><strong>約 2.7 倍高速化</strong></td>
</tr>
</tbody></table>
<p>React Compiler を有効にしただけで、非常に大きなパフォーマンス改善ができていることが確認できました。
今回はメモ化を全く行っていないプロジェクトとの比較のため、すべてのケースで同様の改善が見込めるわけではありませんが、導入効果は十分に期待できます。</p>
<h2>懸念点</h2>
<h3>ライブラリとの相性</h3>
<p>現在開発中のプロジェクトでは TanStack Table を利用しています。TanStack Table は新しい参照を意図的に生成することで再レンダリングをトリガーする設計のため、React Compiler によるメモ化が適用されると、再レンダリングが発生せず意図しない挙動を引き起こす可能性があります。</p>
<p>この問題に対しては、<code>&quot;use no memo&quot;</code> ディレクティブを追加して部分的に React Compiler を無効化することで対応が可能です。</p>
<pre><code class="language-ts">function TableComponent() {
  &quot;use no memo&quot;;

  const table = useReactTable({
    data,
    columns,
    getCoreRowModel: getCoreRowModel(),
  });

  return (
    &lt;table&gt;
      {/* TanStack Table の参照変化に依存する処理 */}
    &lt;/table&gt;
  );
}
</code></pre>
<p>react-hook-form など、同様に参照の変化に依存するライブラリを利用する場合は、要所で <code>&quot;use no memo&quot;</code> の記述が必要になるため、導入時にはご注意ください。</p>
<h3>ビルド速度低下、ビルドファイルサイズ上昇</h3>
<p>React Compiler を導入すると、再レンダリング時のパフォーマンスは向上しますが、メモ化のためのコードが追加されるため、ビルド速度やファイルサイズに多少影響が出ます。その結果、初回読み込みが少し遅くなる可能性があります。</p>
<h4>React Compiler 無効時のビルド結果</h4>
<ul>
<li>ビルド時間: 64ms</li>
<li>ファイルサイズ: 232KB</li>
</ul>
<p><img src="/assets/blog/authors/high-g/20251225/img10.png" alt="React Compiler 無効時のビルド結果"></p>
<h4>React Compiler 有効時のビルド結果</h4>
<ul>
<li>ビルド時間: 556ms</li>
<li>ファイルサイズ: 248KB</li>
</ul>
<p><img src="/assets/blog/authors/high-g/20251225/img11.png" alt="React Compiler 有効時のビルド結果"></p>
<h2>まとめ</h2>
<p>今回は React Compiler を有効にしたときのメモ化に関する挙動の確認と、パフォーマンスにどれくらい影響があるのかを検証してみました。
結果として、メモ化が正しく動作し、そのおかげでパフォーマンス改善も十分に期待できることが分かりました。</p>
<p>ただし、いくつか注意点もあります。<br>参照の変化に依存するライブラリを使用する場合は、<code>&quot;use no memo&quot;</code> で部分的に無効化が必要になることがあります。また、メモ化のコードが追加される分、ビルド速度やファイルサイズには多少影響が出ます。</p>
<p>とはいえ、これらは対処可能な範囲ですし、パフォーマンス改善だけでなくメモ化を意識しなくて済むというメリットは非常に大きいです。React Compiler の導入を迷われている方は、ぜひ試してみてください。</p>
<p>最後まで読んでいただきありがとうございました。</p>
<h2>参考記事</h2>
<p><a href="https://ja.react.dev/learn/react-compiler/introduction">https://ja.react.dev/learn/react-compiler/introduction</a>
<a href="https://ja.react.dev/reference/react-compiler/configuration">https://ja.react.dev/reference/react-compiler/configuration</a>
<a href="https://reactadvanced.com/2023/">https://reactadvanced.com/2023/</a>
<a href="https://conf2024.react.dev/">https://conf2024.react.dev/</a>
<a href="https://react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023">https://react.dev/blog/2023/03/22/react-labs-what-we-have-been-working-on-march-2023</a>
<a href="https://react.dev/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024">https://react.dev/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024</a>
<a href="https://react.dev/blog/2025/10/07/react-compiler-1">https://react.dev/blog/2025/10/07/react-compiler-1</a>
<a href="https://github.com/TanStack/table/issues/5567">https://github.com/TanStack/table/issues/5567</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/high-g/20251225/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[AIで本の表紙を1時間で作る。ライブペインティングを企画した話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-24-ai-live-painting/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-24-ai-live-painting/</guid>
            <pubDate>Wed, 24 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[AIを使ったライブペインティング形式で、1時間という制限の中で技術書の表紙デザインを作り切る社内イベントを、どのような狙いと設計で企画したのかを紹介する記事。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の24日目の記事です。</p>
<p>こんにちは！
KINTOテクノロジーズのクリエイティブグループでデザイナーをしている桃井（<a href="https://x.com/momoitter">@momoitter</a>）です。</p>
<p>先日、AIを使ったライブペインティング形式で、1時間の制限時間でお題に挑戦する社内イベント 「<strong>Vibe Painting Hour</strong>」 を開催しました。
今回はその第1回として、技術書典に出展する技術書の表紙デザインをテーマに実施しました。</p>
<p><em>※技術書典（ぎじゅつしょてん）とは、エンジニアなどの技術者が自身の知見や取り組みをまとめた技術書を持ち寄り、展示・頒布する技術書専門のイベントです。</em></p>
<p>私はこの「Vibe Painting Hour」で、企画・全体のアートディレクション・イベント設計を担当しました。
この記事では、<strong>なぜこのイベントを企画したのか／どのように設計したのか</strong>を中心に紹介します。</p>
<p><img src="/assets/blog/authors/momoi/251224/01_keyvisual.jpg" alt="keyvisual"></p>
<p>なお、「技術書典」についての詳細は、うえはらさんのこちらの記事をご覧ください。
<a href="https://blog.kinto-technologies.com/posts/2025-12-12-techbookfest19-report/">https://blog.kinto-technologies.com/posts/2025-12-12-techbookfest19-report/</a></p>
<p>実際にイベント内で表紙デザインがどのように作られていったかは、mayuさんのこちらの記事で詳しく紹介されています。
<a href="https://blog.kinto-technologies.com/posts/2025-12-25-ai-bookcover-process/">https://blog.kinto-technologies.com/posts/2025-12-25-ai-bookcover-process/</a></p>
<h2>概要</h2>
<p>今回実施したのは、<strong>AIツールを活用したライブペインティング形式のデザインイベント</strong>です。</p>
<p><strong>制限時間は1時間</strong>。
5名のデザイナーが同時に画像生成とデザインを行い、最終的に技術書典に出展する技術書の表紙デザインを、その場で決定しました。</p>
<p>完成したアウトプットだけでなく、生成の途中にある思考や試行錯誤のプロセスも含めて共有することを重視しています。</p>
<h2>目的</h2>
<p>このイベントには、いくつかの目的がありました。</p>
<h3>1. AI×デザインの「プロセス」を見せたい</h3>
<p>生成AIは、完成物だけを見ると「一瞬で作っている」「魔法のように出てくる」という印象を持たれがちです。
しかし実際には、</p>
<ul>
<li>プロンプトの試行錯誤</li>
<li>生成結果の取捨選択</li>
<li>どこで割り切るか、どこを粘るかという判断</li>
</ul>
<p>といった、人の思考が大きく関わっています。</p>
<p>デザイナーが<strong>どう考え、どう生成し、どう決めていくのか。</strong>
そのプロセスをライブで見せたい（「デザイナーってすごいんだぜ！」を見せたい）、というのが大きな目的でした。</p>
<h3>2. キャラクターを「みんなで育てる」フェーズに進みたかった</h3>
<p><img src="/assets/blog/authors/momoi/251224/02_rupia.jpg" alt="rupia">
今回の表紙デザインでは、「るぴあ」というキャラクターを使用しています。
これは私がキャラクターデザインし、これまでAIの検証や、弊社のSNS・イベントでの発信に使われてきたキャラクターです。</p>
<p>これまで、るぴあを使った表現はどうしても自分一人で作ることが多く、表現の幅が狭まりやすい状態でした。
今回はあえて、</p>
<ul>
<li>他のデザイナーが、るぴあをどう解釈するのか</li>
<li>同じキャラクターでも、どんな表現の違いが生まれるのか</li>
</ul>
<p>を見てみたい、という狙いがありました。</p>
<p>それは、<strong>キャラクターを「個人で作るもの」から「みんなで育てるもの」へ</strong>移行するための、一つのステップでもありました。</p>
<h2>企画の経緯</h2>
<p>企画のきっかけは、弊社のAI推進メンバーのこの一言でした。</p>
<blockquote>
<p>「デザイナーによるAIライブペインティングが見てみたい」</p>
</blockquote>
<p>それとちょうど同じタイミングで、技術書典に向けた技術書執筆企画が進行しており「表紙デザインを制作してほしい」という依頼がありました。</p>
<p>そこで、</p>
<ol>
<li>ライブペインティングのリクエスト</li>
<li>表紙デザインの制作</li>
</ol>
<p>この2つを<strong>同時に叶えてしまおう</strong>と考えたのが、今回のイベントの始まりです。</p>
<h2>イベントのネーミングについて</h2>
<p><img src="/assets/blog/authors/momoi/251224/03_naming.jpg" alt="naming">
イベント名は、以下の3つを掛け合わせて<strong>Vibe Painting Hour</strong>としています。</p>
<ul>
<li>AIを使って感覚的にコーディングする「Vibe Coding」から取った「Vibe」</li>
<li>ライブで制作するという意味での「Live Painting」</li>
<li>制限時間1時間を示す「Hour」</li>
</ul>
<h2>事前ヒアリングとお題の作成</h2>
<p>イベント前には、依頼主であるうえはらさんにヒアリングを行いました。</p>
<ul>
<li>技術書の想定読者</li>
<li>技術書全体のトーン</li>
<li>表紙に求める印象や方向性</li>
</ul>
<p>そこから、下記の方向性が見えてきました。</p>
<ul>
<li>弊社の認知度からして、「KINTOテクノロジーズだから手に取ってみよう」という動機は生まれづらいので、<strong>キャラクターで引っかかりを作る</strong></li>
<li>そのうえで、弊社の<strong>技術力や先進性</strong>を感じさせる</li>
<li>弊社ならではの特色である「<strong>モビリティ</strong>」感も出す</li>
</ul>
<p>ただし本番では、よりライブ感を重視する目的で<strong>お題は事前に共有せず当日に発表</strong>されます。</p>
<p>結果として当日は長い説明ができません。
そこで、ヒアリング内容をもとにお題を次の一文に圧縮し、当日デザイナーへ共有しました。</p>
<p><img src="/assets/blog/authors/momoi/251224/04_order.jpg" alt="order"></p>
<h2>使用ツールとイベント設計の工夫</h2>
<h3>画像生成</h3>
<p>使用するAIツールは指定せず、各デザイナーが慣れているものを選んでいます。</p>
<p>当日は主に以下のツールが使われていました。</p>
<ul>
<li>Midjourney</li>
<li>ChatGPT（画像生成）</li>
<li>Photoshop</li>
</ul>
<p>また、今回のイベントでは<strong>あらかじめキャラクター「るぴあ」の画像を参加デザイナー全員に配布</strong>しておき、各AIツールで<strong>参照画像（リファレンス）として使える状態</strong>を用意していました。</p>
<p>これにより、</p>
<ul>
<li>キャラクターの外見や雰囲気が大きくブレない</li>
<li>そのうえで、表現の解釈や世界観の違いが自然に出る</li>
<li>「同じキャラクターをどう料理するか」に集中できる</li>
</ul>
<p>という状態を作ることができました。</p>
<p><img src="/assets/blog/authors/momoi/251224/05_reference.jpg" alt="reference"></p>
<h3>レイアウト（Figma）</h3>
<p><img src="/assets/blog/authors/momoi/251224/06_figmaboard.jpg" alt="figmaboard">
表紙のレイアウト作業には<strong>Figma</strong>を使用しました。</p>
<p>あらかじめ、</p>
<ul>
<li>タイトル</li>
<li>ロゴ</li>
<li>ナンバリング（vol.01 など）</li>
</ul>
<p>を配置した<strong>表紙用のアートボードを、参加人数分用意</strong>しています。</p>
<p>これは、要素の配置までライブでやってしまうと1時間で終わらないためです。
イベント中は、<strong>画像生成に集中できる状態を作る</strong>ことを最優先にしました。</p>
<h3>なぜこの設計にしたのか</h3>
<p>ライブペインティングという形式上、
「自由度が高すぎると収拾がつかない」「制限しすぎると面白くない」というバランスがとても重要でした。</p>
<ul>
<li>キャラクターは固定（参照画像あり）</li>
<li>レイアウトは半固定（Figmaで事前準備）</li>
<li>表現と生成のアプローチは自由</li>
</ul>
<p>という設計にすることで、
<strong>1時間という短い時間でも、各デザイナーの個性がしっかり見える状態</strong>を作ることができたと思います。</p>
<h2>世界観について</h2>
<p><img src="/assets/blog/authors/momoi/251224/07_world.jpg" alt="world">
今回のイベントでは、世界観づくりも重視しました。
一回きりのイベントではなく、<strong>今後も続けられるフォーマットにしたい</strong>と考えていたためです。</p>
<p>設定はこんな感じです。</p>
<ul>
<li>ここは、とある町の「クリエイティブ相談所」</li>
<li>デザインに悩んだお客さんが相談に来る</li>
<li>そこには腕利きのデザイナーがいる</li>
<li>ただし彼らは気まぐれで、1時間以上は働きたがらない</li>
<li>報酬は甘いお菓子</li>
</ul>
<p>少し遊びのある設定ですが、この世界観があることでイベント全体に一体感が生まれました。</p>
<h2>イベントの流れ（60分）</h2>
<p><img src="/assets/blog/authors/momoi/251224/08_flow.jpg" alt="flow">
イベントは、1時間で「生成 → 判断 → 決定」まで行う構成です。</p>
<h4>1. オープニング（約10分）</h4>
<p>世界観説明、依頼主紹介、相談内容・お題の発表、デザイナー紹介</p>
<h4>2. 生成タイム（前半・約15分）</h4>
<p>各デザイナーが画像生成を開始／同時にBGM生成デモや依頼主へのインタビューを実施</p>
<h4>3. 中間チェック（約15分）</h4>
<p>途中経過を共有し、使用ツールや現在の方向性を紹介</p>
<h4>4. 生成タイム（後半・約15分）</h4>
<p>中間チェックを踏まえて最終調整</p>
<h4>5. 結果発表（約5分）</h4>
<p>完成案を並べ、依頼主が採用デザインを選定！</p>
<p>その場で技術書の表紙を決め切るところまで含めて、イベントとして完結させました。</p>
<h2>実際にどんな案が出て、どう決まったのか</h2>
<p><img src="/assets/blog/authors/momoi/251224/09_proposal.jpg" alt="proposal">
当日は、同じキャラクターを使い、同じお題を受け取りながら、<strong>驚くほど素敵で個性あふれる表紙案が生まれました。</strong>
キャラクターの距離感や視線の向き、世界観の切り取り方、「技術書らしさ」の表現など、<strong>各デザイナーの個性がはっきりと表れています。</strong></p>
<p><img src="/assets/blog/authors/momoi/251224/10_coverdesign.jpg" alt="coverdesign">
最終的には依頼主であるうえはらさんに選定していただき、
デザイナーのmayuさんのデザインが、実際に技術書典で使用する表紙として決定しました！</p>
<h2>アンケート結果</h2>
<p>イベント後に実施したアンケートでは、</p>
<ul>
<li>満足度：4.61 / 5</li>
<li>再参加したい：89%</li>
</ul>
<p>という結果でした。</p>
<p>特に多かった声は、</p>
<ul>
<li>生成プロセスが見られて勉強になった</li>
<li>ライブ感があって面白かった</li>
<li>世界観や演出が良かった</li>
</ul>
<p>といったものです。</p>
<p>これらの結果から、アウトプットだけでなく「生成していくプロセス」や「ライブ感」そのものがしっかり価値として受け取られていたことを感じました。</p>
<p>特に「また参加したい」という声が多かったのは、
この形式が一度きりではなく、今後も続けていけそうだという手応えにつながっています。</p>
<h2>まとめ</h2>
<p>AIを使えば、デザインは一瞬でできる。そんなイメージを持たれることもあります。
しかし実際には、<strong>考え、選び、決めるのは人</strong>です。</p>
<p>今回のライブペインティングでは、そのプロセスを1時間に凝縮して見せることができました。
今後も「クリエイティブのお悩みを、AIと一緒に解決する」そんな場を、形を変えながら続けていければと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/momoi/251224/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[“なんでも屋”じゃなくて“オフィスの基盤職”。IT企業 Team総務の仕事、全部見せちゃいます！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-24-teamsoumu/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-24-teamsoumu/</guid>
            <pubDate>Wed, 24 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[“なんでも屋”じゃなくて“オフィスの基盤職”。IT企業 Team総務の仕事、全部見せちゃいます！]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の 24日目の記事です🎅🎄</p>
<p>こんにちは╭Ꙭ╮⸝o
KINTOテクノロジーズ（以下、KTC）で人事グループに所属している、だーしー（<a href="https://qiita.com/dashiko">@dashiko</a>）です。<br>普段は総務・労務まわりの実務や役員秘書業務、そして各拠点にいるサポートメンバーを束ねるチームのリーダーを担当しています。</p>
<p>私はKTC設立準備の時期から在籍しており、気づけば早 5 年と 10 か月が経とうとしています（古株を通り越して、もはや化石です🐘）。
開発組織の規模も、おかげさまで当時は 30 名ほどだったところから、いまでは 400 名を超える組織へと成長しました。</p>
<p>そんな変化の波の中で、この会社で初めて総務の領域に飛び込み、 気づけばずっと “現場の総務” としてオフィスづくりに向き合ってきました。
この記事では、その総務業務の中でも、全社を横断して動いている総務チーム、 通称「Team総務」の活動にスポットをあててご紹介していきたいと思います📄</p>
<blockquote>
<p>※Team総務 ＝ 兄弟会社KINTOの総務 × KTC側の総務がタッグを組んだ、<br>全社横断でオフィスや安全・カルチャーづくりを動かしているチームです。</p>
</blockquote>
<p><img src="/assets/blog/authors/daasii/daasii1.png" alt="Team総務のイメージ画像">
<em>Team総務。戦闘着は三本線のジャージです</em></p>
<p>IT企業と聞くと、まず思い浮かぶのは💡</p>
<ul>
<li>エンジニア</li>
<li>デザイナー</li>
<li>PjM・PdM</li>
</ul>
<p>など思い浮かべる人が多いと思います。
その裏側で 「オフィス」という物理インフラ を支えているのが総務です。</p>
<p>とはいえ、社外から見ると、</p>
<blockquote>
<p>「IT企業の総務って、実際なにしてるの？」<br>「“なんでも屋”って聞くけど、実態は？」</p>
</blockquote>
<p>というのが正直なところかなと🤔</p>
<p>そこでこの記事では、弊社のTeam総務を例に、</p>
<ul>
<li>総務がどんな役割を担っているのか  </li>
<li>この1年、どんなことに走り回っていたのか  </li>
<li>その中で見えてきた “泥臭さ” と “おもしろさ”</li>
</ul>
<p>あたりを、ご紹介しつつ「IT企業の総務の中身、全部見せちゃいます」 をテーマにまとめていきます 📝</p>
<br>

<h2>Team総務って何やっているの？</h2>
<h3>⚙️オフィス運営：物理世界の SRE みたいな仕事</h3>
<p>総務は、ある意味 <strong>Office Reliability Engineering</strong> です👓</p>
<ul>
<li>複数拠点（東京・名古屋・大阪・福岡）のオフィス管理  </li>
<li>レイアウト変更・増床・移転・リニューアル  </li>
<li>通称：民族大移動（＝大規模なゾーニングや配置替え）の設計と実行  </li>
<li>椅子・デスク・共用備品などの什器管理  </li>
<li>入退館・セキュリティカードの管理</li>
</ul>
<p>現場レベルだと、</p>
<ul>
<li>座席表をPowerPointで地道に更新しながら  </li>
<li>各部署の関係者とすり合わせをして  </li>
<li>移転日・工事日・席替え当日などのマイルストーンを決めて、</li>
<li>自分たちのToDoに落としてひとつずつ潰していく</li>
</ul>
<p>という、「アナログとデジタルのあいだ」くらいの運用で回しています。<br>見た目は泥臭いけれど、やっていることはほぼプロジェクトワークです💡</p>
<h3>🌱働く環境づくり：快適性・利便性・安全性の最適化</h3>
<p>「ちょっとした不便」を拾って直すのも、総務の大事な役目です🛠️</p>
<ul>
<li>半期に1回、給茶機のフレーバーアンケートを実施してラインナップを調整  </li>
<li>空調・照明・騒音に関する相談の一次窓口  </li>
<li>季節や状況に応じた設置備品の調整  </li>
<li>植栽の配置、感染予防グッズ、サーキュレーターなどの設置など<blockquote>
<p>「1人の“気になる”は、10人の“言わないモヤモヤ”かも」</p>
</blockquote>
</li>
</ul>
<p>という前提で、個別の要望とフロア全体のバランスを見ながら、<br>全体最適の落としどころを探しています🔍
<img src="/assets/blog/authors/daasii/daasii2.png" alt="給茶機アンケート">
<em>みんなの推しフレーバーを教えて！給茶機アンケート</em></p>
<h3>📘ガバナンスとルール整備：やさしい「社内仕様書」をつくる</h3>
<p>オフィスを安定運用するには、
「ちょっとしたお作法」から「いざというときの行動」まで、ルールづくりも欠かせません🗒️</p>
<ul>
<li>各拠点のオフィスガイド</li>
<li>日常のルール／社内手続きの申請・承認フロー  </li>
<li>安全衛生・災害時の行動マニュアル  など</li>
</ul>
<p>これらも、できるだけIT企業らしく・読みやすく を意識して整えています。</p>
<ul>
<li>読まれる前提のルール（見出し・Q&amp;A・図解でサクッと理解できるように）  </li>
<li>つくって終わりではなく、フィードバックや状況に応じたアップデート</li>
</ul>
<p>ルールで縛るというよりは、 
「みんなが迷わず・気持ちよく動けるための社内仕様書をつくる」 という感覚で運用しています📚<img src="/assets/blog/authors/daasii/daasii3.png" alt="KINTO Technologies Navi（KTN）">
<em>社内ルール・ナレッジがぎゅっと詰まった「KINTO Technologies Navi（KTN）」<br>困ったらまずここを開けばだいたいなんとかなる、を目指して育てています</em></p>
<h3>🎉社内イベント・カルチャーづくり</h3>
<p>Team総務は、オフィス起点のイベント も実施しています。</p>
<ul>
<li>七夕🎋 ハロウィン🎃 クリスマス🎄など、オフィスで季節を感じられるイベント企画や装飾づくり  </li>
<li>社内掲示板で「総務のつぶやき」を連載し、各拠点オフィスのトピックやイベント報告や総務の舞台裏を発信</li>
</ul>
<p>こうした取り組みは、単なる「盛り上がりイベント」ではなく、</p>
<ul>
<li>初めての方も参加しやすい雰囲気づくり  </li>
<li>拠点・職種・役職をまたいでのコミュニケーションきっかけづくり</li>
<li>オフィスに出社したい！と思ってもらえる仕組みづくり</li>
</ul>
<p>など、カルチャーのOSアップデートの場として設計しています🧩</p>
<p><img src="/assets/blog/authors/daasii/daasii4.png" alt="総務のつぶやき">
<em>社内掲示板で連載している「総務のつぶやき」<br>各拠点の季節イベントやオフィスの小ネタをTeam総務目線でゆるっとお届け</em></p>
<h3>💪ちょっと泥臭いリアル編：総務はわりと物理もやる</h3>
<p>ここまで読むとスマートに聞こえますが、現場はかなり泥臭いです⚾<br>だいたい毎月どこかで汗かいてますヽ(´o｀；）ｗ</p>
<ul>
<li>長尺脚立に乗って備品を修理する  </li>
<li>床下電源の配線のために、オフィスの床下に潜り込む  </li>
<li>オフィス美化のために、定期的にフロアをぐるぐる巡回する  </li>
<li>季節装飾やオフィスイベント装飾を、手作りしたり自分たちの手で1枚ずつ貼って回る </li>
<li>座席が固定席運用なので、異動・入退社・兼務を反映した最新の座席表を毎月コツコツ更新する</li>
</ul>
<p>エンジニアがコードをデプロイしている裏側で、<br>総務は総務で 「オフィスという物理レイヤー」をデプロイ しているようなものです。  </p>
<p>総務は決してキラキラした仕事ばかりではありませんが、 泥臭く手を動かしているぶんだけ、 </p>
<blockquote>
<p>「誰かのための仕事」がくっきり見える職種 だと感じています。</p>
</blockquote>
<p><img src="/assets/blog/authors/daasii/daasii5.png" alt="床掘り写真">
<em>床下掘りの写真を探したら、5年前から床を掘っていました（笑）</em></p>
<br>

<h2>2025年の総務活動ハイライト</h2>
<p>色々と語ってしまいましたが、ここでざーっと今年1年の総務活動をカレンダー形式で振り返ってみましょう📅
<img src="/assets/blog/authors/daasii/daasii6.png" alt="総務活動カレンダー"></p>
<p><img src="/assets/blog/authors/daasii/daasii7.png" alt="Team総務2025年活動記録">
<em>今年1年の総務活動メモリーズ📷</em></p>
<p>こうして月ごとに並べてみると、</p>
<ul>
<li>オフィスの移転・開設・リニューアル・ゾーニング調整（全拠点…！）  </li>
<li>民族大移動レベルのレイアウト変更（年2回…！）  </li>
<li>七夕・ハロウィン・Xmasなど季節イベントの企画・運営</li>
</ul>
<p>まで、フルコースでやっていた1年だったことに、自分たちでもちょっと笑ってしまいます😆</p>
<p>「オフィスは生き物」とよく言いますが、<br>その内臓（レイアウト）や血管（動線・配線）、そして“気持ちの温度”をせっせと整えながら、<br>みんながいつも通り働ける日常を維持していた1年だったなと感じています✨</p>
<br>



<h2>来年挑戦したいこと</h2>
<p>来年以降、Team総務としてチャレンジしていきたいことを、<br>「こうなったらいいな」をそのまま宣言しておきます🔥</p>
<ul>
<li>オフィス改善アイデアを “プロダクトバックログ” 的に管理する  <ul>
<li>声を集めて、見える場所で優先度をつけて、ちゃんとリリースしていく</li>
</ul>
</li>
<li>手作業前提のオペレーションを「AI前提」に組み替えていく  <ul>
<li>バックオフィスこそAI活用の実験場にして、チェック・転記・集計などの単純作業はどんどんAIに任せていく</li>
</ul>
</li>
<li>「出社したくなる場所」をつくるオフィス企画  <ul>
<li>ただの“作業場所”ではなく、「行くとちょっと得する」「誰かと話したくなる」オフィスへ強化</li>
</ul>
</li>
<li>季節イベントを「年中行事」レベルまで定着させる  <ul>
<li>年のリズムとして、「あ、そろそろ七夕だ」「今年のハロウィンは何やるの？」が自然に出てくるくらいまで育てる</li>
</ul>
</li>
<li>利用状況やアンケートなど、データに基づいたオフィス運営を進める  <ul>
<li>勘と経験だけでなく、「数字を見ながら意思決定できる総務」を目指す</li>
</ul>
</li>
</ul>
<br>

<h2>おわりに</h2>
<p>あらためて並べてみると、総務の仕事は</p>
<ul>
<li>オフィス移転・リニューアル・民族大移動  </li>
<li>季節イベント・社内イベント  </li>
<li>安全・ガバナンス・危機管理  </li>
<li>座席調整・オフィス美化　…などなど</li>
</ul>
<p>“見えにくいけれど、なくなると困るもの” ばかりです。</p>
<p>でも、その「見えにくい仕事」を、<br>こうして少しだけ 透明化してみる のも悪くないなと思っています。</p>
<p>毎年その1年を全力で走り、最終的に行きついたのはこの感覚でした。</p>
<blockquote>
<p>すべての仕事には必ず「相手」がいて、<br>相手を思えば、どんな仕事も愛ある仕事になる。</p>
</blockquote>
<p>床下をはいずり回る日も、脚立にのぼる日も、<br>オフィス内駆け回ってる日も、Slack でひたすらアナウンス文を書いている日も、<br>その先には誰かの「働きやすさ」があります。</p>
<p>IT企業の総務は、ちょっと泥臭くて、でもわりと愛のある仕事です😌🫶</p>
<hr>
<blockquote>
<p>😲💬 「IT企業の総務って、けっこうプロジェクト型で、ちゃんと“職能”なんだな」</p>
</blockquote>
<p>と、この記事を読んで、そんな風に感じてもらえたら嬉しいです。</p>
<p>同じコーポレート領域（総務 / 労務 / 人事）のみなさんとも、
「それ、うちもやってる！」とか
「その視点なかった！」みたいな
あるあるや工夫を、いつかどこかで交換できたらいいなと思っています！</p>
<p><strong>“なんでも屋”じゃなくて“オフィスの基盤職”。</strong>
<strong>IT企業 Team総務の仕事、今年も全力で走りきりました🏃‍♀️💨🎄</strong></p>
<p>最後まで読んでいただき、ありがとうございました╭Ꙭ╮⸝o</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/daasii/daasii.samune.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[MCPを使ってNew RelicのデータにSlackからアクセスしよう！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-24-use-newrelic-mcp-from-slack/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-24-use-newrelic-mcp-from-slack/</guid>
            <pubDate>Wed, 24 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[New Relic MCPを使ってSlackからデータを取得する方法を紹介します]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1>はじめに</h1>
<p>この記事は <a href="https://qiita.com/advent-calendar/2025/newrelic">New Relic Advent Calendar 2025</a> の24日目の記事です。</p>
<p>こんにちは、SREチームの<a href="https://x.com/tgidgd">おさない</a>です。本記事では<a href="https://newrelic.com/jp/press-release/20251126">先日発表</a>されたNew Relic MCPサーバーをSlackから利用できるようにする方法について紹介します。</p>
<p>MCPサーバー自体はClaudeなどの対話型AIやCodex, Kiroなどのコーディングエージェントなど、さまざまなインターフェースで利用することができますが、Slackというインターフェースで使えるようにすることで、New Relicのデータ利活用の幅をさらに広げられると感じています。</p>
<h1>構成図</h1>
<p>今回は以下のような構成でSlackからNew Relic MCPを利用します。</p>
<p><img src="/assets/blog/authors/n.osanai/2025-12-24-use-newrelic-mcp-from-slack/architecture.png" alt="">
<em>今回作成する構成図</em></p>
<p>SlackでBotにメンションして質問することで、回答に必要な情報をNew Relic MCPを使って取得してスレッドに返信してくれます。</p>
<h1>構築手順</h1>
<p>:::message
本記事ではSlackからNew Relicのデータにアクセスできるようにするために最小限の内容を紹介しています。以下のような要素については省略していますので、必要に応じて対応してください。</p>
<ul>
<li>システムプロンプトのチューニング<ul>
<li>New Relicのアカウントが複数ある場合、どのアカウントから情報を探しますか？といった返答になりがちなので、システムプロンプトなどで「利用なアカウントから順番に調査してください。」といった文言を入れると動くようになりました。</li>
</ul>
</li>
<li>セキュリティ面の対策<ul>
<li>今回紹介する構成はユーザーのインプットをそのまま伝えているため、入力・出力内容のガードレールを設けることをオススメします。</li>
<li>また、IAM Roleの権限も必要最小限の設定にすることをオススメします。</li>
</ul>
</li>
<li>会話履歴の保持<ul>
<li>今回の構成では会話履歴を保持しておらず、単発でのやり取りになります。
:::</li>
</ul>
</li>
</ul>
<h2>New Relic APIキーの発行</h2>
<p>New Relicの<a href="https://one.newrelic.com/admin-portal/api-keys/home">API Keys</a>のページに遷移して、<code>Create a key</code>ボタンを押下します。
Key Typeは<code>User</code>を選択して作成し、発行されたAPIキーを控えておきます。</p>
<h2>Slack Appの作成と設定 - その1</h2>
<p>まずはSlack Appを作成します。
Slack Appの作成方法についてはさまざまな記事があるため詳細な説明は省きますが、<a href="https://api.slack.com/apps">こちら</a>から<code>From scratch</code>でSlack Appを作成してください。
作成したら<strong>OAuth &amp; Permissions</strong>のページに遷移し、Bot Token Scopesに<code>chat:write</code>の権限を追加します。
そして<strong>Install App</strong>から対象のSlackワークスペースにインストールし、発行された<strong>Bot User OAuth Token</strong>を控えておきます。</p>
<h2>アプリケーションの構築</h2>
<p>今回は<a href="https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/serverless-getting-started-hello-world.html">AWS SAM</a>と<a href="https://strandsagents.com/latest/">Strands Agents</a>を使って構築します。私が構築した際のSAM CLIのバージョンは1.149.0でした。
以下にtemplate.yamlとそれぞれのLambda関数のコード、requirements.txtを記載します。</p>
<p>:::details template.yaml</p>
<pre><code class="language-yaml">AWSTemplateFormatVersion: &#39;2010-09-09&#39;
Transform: AWS::Serverless-2016-10-31

Globals:
  Function:
    Timeout: 120

Resources:
  SlackEventHandlerFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: slack-event-handler-function
      CodeUri: src/
      Handler: slack_event_handler.lambda_handler
      Runtime: python3.14
      Role: !GetAtt SlackEventHandlerFunctionRole.Arn
      SnapStart:
        ApplyOn: PublishedVersions
      AutoPublishAlias: SnapStart
      Architectures:
      - x86_64
      Events:
        NewRelicBot:
          Type: Api
          Properties:
            Path: /event
            Method: post
      Environment:
        Variables:
          SQS_QUEUE_URL: !GetAtt SlackMessageSQS.QueueUrl

  SlackEventHandlerFunctionPermission:
    Type: AWS::Lambda::Permission
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !Ref SlackEventHandlerFunction
      Principal: apigateway.amazonaws.com
  SlackEventHandlerFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: slack-event-handler-function-role
      AssumeRolePolicyDocument:
        Version: &#39;2012-10-17&#39;
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/AmazonSQSFullAccess

  SlackAIExecutorFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: slack-ai-executor-function
      CodeUri: src/
      Handler: slack_ai_executor.lambda_handler
      Runtime: python3.14
      MemorySize: 256
      Role: !GetAtt SlackAIExecutorFunctionRole.Arn
      Architectures:
      - x86_64
      Events:
        SlackMessageSQS:
          Type: SQS
          Properties:
            Queue: !GetAtt SlackMessageSQS.Arn
            BatchSize: 1
            Enabled: true
      Environment:
        Variables:
          NEW_RELIC_API_KEY: &#39;&#39; # 発行したNew Relic APIキーを設定
          SLACK_BOT_TOKEN: &#39;&#39; # 発行したSlack Bot User OAuth Tokenを設定

  SlackAIExecutorFunctionRole:
    Type: AWS::IAM::Role
    Properties:
      RoleName: slack-ai-executor-function-role
      AssumeRolePolicyDocument:
        Version: &#39;2012-10-17&#39;
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - lambda.amazonaws.com
            Action:
              - sts:AssumeRole
      Path: /
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        - arn:aws:iam::aws:policy/service-role/AWSLambdaSQSQueueExecutionRole
        - arn:aws:iam::aws:policy/AmazonBedrockFullAccess

  SlackMessageSQS:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: slack-message-sqs.fifo
      FifoQueue: true
      VisibilityTimeout: 180
      MessageRetentionPeriod: 600
      ContentBasedDeduplication: true
      DeduplicationScope: messageGroup
      RedrivePolicy:
        deadLetterTargetArn: !GetAtt SlackMessageDLQ.Arn
        maxReceiveCount: 2

  SlackMessageDLQ:
    Type: AWS::SQS::Queue
    Properties:
      QueueName: slack-message-dlq.fifo
      FifoQueue: true
      VisibilityTimeout: 180
      MessageRetentionPeriod: 604800
</code></pre>
<p>※ NEW_RELIC_API_KEY, SLACK_BOT_TOKENは必要に応じてパラメータストアから取得するなどの方法で設定してください。
:::</p>
<p>:::details slack_event_handler.py</p>
<pre><code class="language-py">import json
import boto3
import hashlib
import os

sqs = boto3.client(&#39;sqs&#39;)
queue_url = os.environ.get(&#39;SQS_QUEUE_URL&#39;)

def lambda_handler(event, context):
    body = json.loads(event[&#39;body&#39;])

    if body.get(&#39;challenge&#39;) is not None:
        challenge = body[&#39;challenge&#39;]
        return response(&#39;200&#39;, challenge)

    if body[&#39;event&#39;].get(&#39;edited&#39;) is not None:
        return response(&#39;200&#39;, &#39;edited event ignored&#39;)

    if body[&#39;event&#39;][&#39;type&#39;] == &#39;app_mention&#39;:
        ts = body[&#39;event&#39;][&#39;ts&#39;]
        channel = body[&#39;event&#39;][&#39;channel&#39;]
        thread_ts = body[&#39;event&#39;].get(&#39;thread_ts&#39;)
        text = body[&#39;event&#39;].get(&#39;text&#39;)
        if text is None:
            return response(&#39;200&#39;, &#39;no text field&#39;)

        payload = {
            &#39;text&#39;: text,
            &#39;channel&#39;: channel,
            &#39;ts&#39;: thread_ts if thread_ts is not None else ts,
        }

        sqs.send_message(
            QueueUrl=queue_url,
            MessageBody=json.dumps(payload),
            MessageGroupId=channel,
            MessageDeduplicationId=hashlib.md5(text.encode()).hexdigest()
        )
    return response(&#39;200&#39;, &#39;ok&#39;)

def response(status_code, body):
    return {
        &#39;statusCode&#39;: status_code,
        &#39;body&#39;: body,
        &#39;headers&#39;: {
            &#39;Content-Type&#39;: &#39;text/plain&#39;,
        },
    }
</code></pre>
<ul>
<li>多重起動を防ぐために、同じ文言を送信した場合はSQSの重複排除機能によりメッセージが破棄されるようにしています</li>
<li>textには<code>&lt;@U12345678&gt;</code>のようにSlack AppのメンバーIDが含まれるため、SQSにメッセージを送信する前に必要に応じてトリミング処理を追加してください。
:::</li>
</ul>
<p>:::details slack_ai_executor.py</p>
<pre><code class="language-py">import json
import os
from contextlib import asynccontextmanager
from mcp.client.streamable_http import streamable_http_client, create_mcp_http_client
from strands.tools.mcp.mcp_client import MCPClient
from strands import Agent
from strands.models import BedrockModel
from slack_sdk import WebClient


SYSTEM_PROMPT = &quot;&quot;&quot;
あなたはNew Relicのデータを駆使して開発者をサポートする優秀なエンジニアです。
&quot;&quot;&quot;

@asynccontextmanager
async def _nr_transport():
    async with create_mcp_http_client(
        headers={
            &quot;api-key&quot;: os.environ.get(&#39;NEW_RELIC_API_KEY&#39;),
            &quot;include-tags&quot;: &quot;discovery,alerting,data-access,incident-response,performance-analytics,advanced-analysis&quot;,
        }
    ) as http_client:
        async with streamable_http_client(
            url=&quot;https://mcp.newrelic.com/mcp/&quot;,
            http_client=http_client,
            terminate_on_close=True,
        ) as streams:
            yield streams


nr_mcp_client = MCPClient(_nr_transport)
slack_client = WebClient(token=os.environ.get(&#39;SLACK_BOT_TOKEN&#39;))
model_id = &quot;jp.anthropic.claude-sonnet-4-5-20250929-v1:0&quot;

def lambda_handler(event, context):
    body = json.loads(event[&#39;Records&#39;][0][&#39;body&#39;])

    text = body[&#39;text&#39;]
    channel = body[&#39;channel&#39;]
    ts = body[&#39;ts&#39;]

    with nr_mcp_client:
        tools = nr_mcp_client.list_tools_sync()
        
        model = BedrockModel(model_id=model_id, region_name=&#39;ap-northeast-1&#39;, temperature=0)
        agent = Agent(system_prompt=SYSTEM_PROMPT, tools=tools, model=model)

        response = agent(text)
        post_response = slack_client.chat_postMessage(channel=channel, thread_ts=ts, text=str(response))
        if post_response[&#39;ok&#39;]:
            return True
        else:
            return False
</code></pre>
<p>:::</p>
<p>:::details requirements.txt</p>
<pre><code>annotated-types==0.7.0
anyio==4.12.0
attrs==25.4.0
boto3==1.42.13
botocore==1.42.13
certifi==2025.11.12
cffi==2.0.0
click==8.3.1
cryptography==46.0.3
docstring_parser==0.17.0
h11==0.16.0
httpcore==1.0.9
httpx==0.28.1
httpx-sse==0.4.3
idna==3.11
importlib_metadata==8.7.0
jmespath==1.0.1
jsonschema==4.25.1
jsonschema-specifications==2025.9.1
mcp==1.24.0
opentelemetry-api==1.39.1
opentelemetry-instrumentation==0.60b1
opentelemetry-instrumentation-threading==0.60b1
opentelemetry-sdk==1.39.1
opentelemetry-semantic-conventions==0.60b1
packaging==25.0
pycparser==2.23
pydantic==2.12.5
pydantic-settings==2.12.0
pydantic_core==2.41.5
PyJWT==2.10.1
python-dateutil==2.9.0.post0
python-dotenv==1.2.1
python-multipart==0.0.21
referencing==0.37.0
rpds-py==0.30.0
s3transfer==0.16.0
six==1.17.0
slack_sdk==3.39.0
sse-starlette==3.0.4
starlette==0.50.0
strands-agents==1.20.0
typing-inspection==0.4.2
typing_extensions==4.15.0
urllib3==2.6.2
uvicorn==0.38.0
watchdog==6.0.0
wrapt==1.17.3
zipp==3.23.0
</code></pre>
<p>※仮想環境でslack_sdkとstrands-agentsをインストールしてpip freezeしたもの
:::</p>
<p>この内容でsam buildおよびsam deployを実行してデプロイします。
デプロイしたらAWSコンソールから作成されたAPI Gatewayのステージを確認し、作成したエンドポイントのURLを控えておきます。
例： <code>https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/event</code></p>
<h2>Slack Appの設定 - その2</h2>
<p>先ほど作成したSlack Appの設定画面に戻り、<strong>Event Subscriptions</strong>のページに遷移します。</p>
<ul>
<li><strong>Enable Events</strong>をONにして、Request URLに先ほど控えたAPI GatewayのエンドポイントURLを設定<ul>
<li>Verifiedと表示されればOKです</li>
</ul>
</li>
<li>Subscribe to bot eventsの<strong>Add Bot User Event</strong>から<code>app_mention</code>を追加して保存<ul>
<li><strong>OAuth &amp; Permissions</strong>のBot Token Scopesに<code>app_mentions:read</code>の権限が自動で追加されます</li>
</ul>
</li>
</ul>
<p>※変更を保存したら、再度Slackワークスペースにインストールしてください</p>
<h1>動作確認</h1>
<p>以上で構築は完了です！
Slackの任意のチャンネルに作成したSlack Appを追加してメンションを飛ばしてみましょう。質問内容にもよりますが、1分程度で返信がくるはずです。</p>
<p><img src="/assets/blog/authors/n.osanai/2025-12-24-use-newrelic-mcp-from-slack/bot_sample.png" alt="">
<em>Slackでの動作イメージ</em></p>
<p>:::message
レスポンスのフォーマットがSlackのフォーマットに最適化されていないため、場合によっては見づらい表示になる可能性があります。
私は以下のような方法でSlackフォーマット(のよう)に変換してからポストしています。</p>
<pre><code class="language-py">def convert_to_slack_format(text: str) -&gt; str:
    # 太字
    text = text.replace(&quot;**&quot;, &quot;*&quot;)
    # 取り消し線
    text = text.replace(&quot;~~&quot;, &quot;~&quot;)
    # 冒頭に - が付いている場合
    text = re.sub(r&#39;^([ ]*)-&#39;, r&#39;\1•&#39;, text)
    # 改行の後に --- が付いている場合は削除
    text = re.sub(r&#39;\n---+\n&#39;, &#39;\n&#39;, text)
    # 改行の後に - が付いている場合
    text = re.sub(r&#39;(\n[ ]*)-&#39;, r&#39;\1•&#39;, text)
    return text
</code></pre>
<p>:::</p>
<p>MCPの機能は<a href="https://docs.newrelic.com/jp/docs/agentic-ai/mcp/tool-reference/">New Relic MCP ツールリファレンス</a>で確認できます。ツールを組み合わせることでさまざまな活用ができそうです。</p>
<ul>
<li>日頃ダッシュボードで確認している項目をSlack Reminderで定期的にBotにメンションして、要約してもらう</li>
<li>アラート発生時のメッセージでBotをメンションに含めることで自動で一次調査させる</li>
<li>特定のセッショントレースIDでのユーザーの行動をまとめてレポートを作成させる</li>
</ul>
<h1>おわりに</h1>
<p>New Relic MCPサーバーは本記事執筆時点ではPublic Previewの段階ですが、十分に実用的なレベルで利用でき、今後のアップデートが非常に楽しみです！</p>
<p>また、この仕組みはNew RelicのMCPサーバーに限らず、他のMCPサーバーに対しても応用可能なものになっているので、普段活用しているMCPサーバーを使いやすいインターフェースで使えるようにしてみてはいかがでしょうか？</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[仕事で使える画像生成AI入門 – 「クオリティ」と「安全性」を両立する、現場目線のはじめの一歩 –]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-23-image-generation-guide/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-23-image-generation-guide/</guid>
            <pubDate>Tue, 23 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[「クオリティ」と「安全性」を両立する、現場目線のはじめの一歩]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の23日目の記事です</p>
<h2><strong>はじめに：「AIなら一瞬でした！」…で、そのまま提出していませんか？</strong></h2>
<p>※本記事の内容は <strong>2025年12月時点</strong> の情報に基づいています。各サービスの仕様・規約は変更される可能性があるため、最新情報は公式サイトをご確認ください。</p>
<p>「企画書のイメージ画像、AIで作ってみました」「ブログのアイキャッチ、AIなら一瞬でした」</p>
<p>こんなフレーズを、最近あちこちで見かけるようになりました。実際、画像生成AIはビジネスパーソンにとってかなり強力な味方です。</p>
<p>ただ、正直に言うと、「なんとなく便利だから使っているだけ」で終わってしまっているケースも多いのではないでしょうか。</p>
<ul>
<li>とりあえずAIにお願いして出てきたものを、そのまま資料に貼る</li>
<li>なんとなく不安はあるけれど、深く考える時間もない</li>
<li>「みんな使ってるし…まあ大丈夫でしょ」と自分を納得させる</li>
</ul>
<p>私自身も、最初は完全にこのモードでした。</p>
<p>ですが、仕事で使う以上、「どこにリスクがありそうか」だけでもざっくり知っておくと、仕事の質が一段上がる感覚があります。</p>
<p>本記事では、「なんとなく使っている」状態から、「企業で働く一人として、責任を持って使いこなす」状態へアップデートしていくための実務的なポイントを、できるだけ現場目線で共有していけたらと思っています。</p>
<hr>
<h2>本記事の流れ</h2>
<ol>
<li>「便利さ」の裏にある、3つのモヤモヤを整理する</li>
<li>ビジネスパーソンにおすすめの「3つのAIツール」との付き合い方</li>
<li>「組織のルール」より前にできる、個人としての3つの工夫</li>
</ol>
<hr>
<h2><strong>「便利さ」の裏にある、3つのモヤモヤを整理する</strong></h2>
<p>まずは、「画像生成AIを使うときに、なんとなくモヤッとしているけど言語化できていない不安」を整理してみます。
画像生成AIのリスクは、大きく分けると次の <strong>3つのカテゴリー</strong> に置き換えられます。</p>
<ol>
<li><strong>法的リスク</strong>：この画像って「誰のもの」なんだっけ？</li>
<li><strong>ブランドリスク</strong>：「AIだから安全」ではない</li>
<li><strong>オペレーションリスク</strong>：「なんか不安だけど、聞ける人がいない」</li>
</ol>
<p>順番に見ていきます。</p>
<h3><strong>1. 法的リスク：この画像って「誰のもの」なんだっけ？</strong></h3>
<p>AIで画像を作ったとき、ふと頭をよぎる疑問があります。「この画像の著作権って、誰にあるんだろう？」「自分の作品として発表していいのか？」「クライアント案件で使っても大丈夫なのか？」——そんなことを考えたことはないでしょうか。</p>
<p>実際のところ、使用しているツール、どの国・地域の法律が適用されるか、そしてそのツールの利用規約や自社の契約の内容、これらの組み合わせによって解釈はかなり変わってきます。</p>
<p>なので法律の専門家でなくても、少なくとも</p>
<ul>
<li>「ツールごとに権利の扱いが違うらしい」</li>
<li>「商用利用OKかどうかは、利用規約を一度は見ておいた方がいい」</li>
</ul>
<p>くらいの感覚を持っておくだけでも、「ちょっと立ち止まるためのブレーキ」がちゃんとかかるようになるかと思います。</p>
<p>そして利用規約を読むのが難しい場合や判断に迷う場合は、独断で使わずに上長、情シス部門、法務担当などに「このツール、業務で使っても大丈夫ですか？」と一度聞いてみるのも一つの手です。
それだけでも、多くのトラブルを防ぎやすくなります。</p>
<h4><strong>「無自覚に似てしまう」リスク</strong></h4>
<p>さらに怖いのが、「無自覚に既存作品に似たものを作ってしまう」リスクです。</p>
<p>画像生成AIは膨大な画像データを学習して動いているので、こちらの意図とは関係なく、</p>
<ul>
<li>どこかで見たことがある構図</li>
<li>有名キャラクターにちょっと似たもの</li>
<li>某ブランドっぽいロゴ</li>
</ul>
<p>といったものが、それっぽく出てきてしまうことがあります。</p>
<p>そのときに、「AIが勝手に作ったんで…」という言い訳は、残念ながら通用しません。
外に出すのはあくまで「自分（自社）」だからです。
「この画像、本当に大丈夫かな？」と少しでも感じたら、一度立ち止まって、権利面を確認するという習慣をつけておくと安心です。</p>
<h3><strong>2. ブランドリスク：「AIだから安全」ではない</strong></h3>
<p>たとえ法的にはセーフでも、こんなケースはどうでしょうか。社内のトーン＆マナーとまったく合わないビジュアルを使ってしまったり、意図せずステレオタイプな表現が混じっていたり、社会的な配慮を欠く表現になってしまっていたり——。こうしたケースは、法的には問題なくても、ブランド毀損につながりかねません。</p>
<p>生成AIは、学習データの傾向を反映して、思わぬ偏見を含んだ画像を出力してしまうというケースも珍しくありません。
研究レベルでも、職業・性別・人種などに関するステレオタイプを強く反映してしまうことが指摘されています。</p>
<p>「AIで作ったからこそ、人間がチェックする」 という意識がとても大切で、<strong>最後は人間が「目を通す」「悩んだら誰かに見せる」こと</strong>を前提にした運用にしておくと安全度が大きく変わります。</p>
<h3><strong>3. オペレーションリスク：「なんか不安だけど、聞ける人がいない」</strong></h3>
<p>そして地味に効いてくるのが、この「運用まわり」のリスクです。</p>
<ul>
<li>社内でAIをちゃんと使いこなしている人がまだ少ない</li>
<li>どのツールを使っていいか、会社として決まっていない</li>
<li>生成した画像の保管場所がバラバラ</li>
<li>結局、「まあいいか」で自己判断になりがち</li>
</ul>
<p>例えば、個人の Google アカウントや OpenAI アカウントで業務用の画像を生成していると、退職時にデータが個人側に残ってしまったり、会社側が、どのアカウントで何が作られたか把握できない、といった問題が生じる可能性があります。 可能であれば、法人プランの利用を情シスや上長に相談することをおすすめします。</p>
<h4><strong>プロンプトに機密情報を書き込んでしまうリスク</strong></h4>
<p>もう一つ気にしておきたいのが、<strong>プロンプトに機密情報を書き込んでしまうかもしれない</strong>という点です。</p>
<p>たとえ「入力データを学習に利用しない」ことが明示されている法人向けプランを使っていたとしても、入力した情報は一度サービス提供者のサーバーを経由します。
OpenAI や Google、Adobe なども、ヘルプやポリシーで「機密情報は入力しないでください」と明記しています。</p>
<p>例を挙げると、次のような情報は入力を避けるべきです。</p>
<table>
<thead>
<tr>
<th>例</th>
<th>入力を避けるべき内容</th>
</tr>
</thead>
<tbody><tr>
<td>〇〇社向けの提案資料を作る</td>
<td>取引先の企業名・個人名</td>
</tr>
<tr>
<td>新製品『△△』のロゴ案を5パターン考える</td>
<td>未発表のプロジェクト名・製品名</td>
</tr>
<tr>
<td>売り上げの数値まとめる</td>
<td>社外秘の固有名詞・数値など</td>
</tr>
</tbody></table>
<p>こうした場面では、固有名詞を伏せて「大手自動車メーカーA社」「金融機関B社」、「来年発売予定の新商品」のように、<strong>抽象化して入力する</strong>ことを心がけるとリスクを下げられます。</p>
<hr>
<h2><strong>それでも、AIはちゃんと使えば最強の「相棒」になる</strong></h2>
<p>ここまでリスク寄りの話が続きましたが、「じゃあ使わない方がいいのか？」というと、そうではありません。</p>
<p><strong>「正しく怖がる」</strong> ことができれば、画像生成AIは本当に頼れる相棒になると感じています。</p>
<p>現場目線でいうと、特にこんなメリットがあります。</p>
<h3><strong>イメージの共有が圧倒的に早くなる</strong></h3>
<p>「こんな感じの世界観で」と口頭やテキストで説明するより、AIでざっくりイメージを出してしまった方が早い場面はたくさんあります。</p>
<h3><strong>「素材探し」からある程度解放される</strong></h3>
<p>ストックフォトサービスで延々とスクロールする代わりに、「夕暮れの高速道路を走る青いコンパクトカー」など、欲しいシチュエーションを直接プロンプトで指定できるのはメリットが大きいです。</p>
<h3><strong>アイデア出しの壁打ち相手になってくれる</strong></h3>
<p>「ちょっとやりすぎかも？」くらいの案を遠慮なく試せるので、思わぬ表現に出会えることもあります。</p>
<hr>
<p>大事なのは、「魔法の箱」として丸投げするのではなく、<strong>自分の意図を持って使う</strong>という感覚です。</p>
<p>AIはあくまで「相棒」であって、最終判断は自分がする。その意識があるだけで、活用の質がぐっと変わります。</p>
<hr>
<h2>ビジネスパーソンにおすすめの「3つのAIツール」との付き合い方</h2>
<p>ここからは、現場の目線で使いやすい 3 つのツールを、「どういうときに相性がいいか」という観点で整理してみます。</p>
<table>
<thead>
<tr>
<th>#</th>
<th>ツール</th>
<th>特徴</th>
</tr>
</thead>
<tbody><tr>
<td>①</td>
<td>ChatGPT</td>
<td>会話ベースでイメージを固める</td>
</tr>
<tr>
<td>②</td>
<td>Google Gemini</td>
<td>Google Workspace連携</td>
</tr>
<tr>
<td>③</td>
<td>Adobe Firefly</td>
<td>権利面の安心感</td>
</tr>
</tbody></table>
<hr>
<h3><strong>① ChatGPT</strong></h3>
<p><strong>→ 「言葉にしながらイメージを固めたい」ときに</strong></p>
<p>会話ベースで「もう少し柔らかい雰囲気に」「右の人物を消して」といった修正ができるのが強みです。</p>
<h4><strong>企業での利用について</strong></h4>
<p>企業で利用する場合、個人アカウント（Free / Plus / Pro）ではなく、以下の組織向けプランが推奨されます。</p>
<ul>
<li><strong>ChatGPT Business</strong>（※2025年8月に「Team」から名称変更されました）</li>
<li><strong>ChatGPT Enterprise</strong></li>
</ul>
<p>これらのプランでは、デフォルトで入力データが学習に使われない設定になっていると説明されています。</p>
<p>一方で、<strong>Free / Plus / Pro</strong> といった個人向けプランでは、デフォルトで会話内容がモデル改善に利用される設定だと説明されています。
設定画面でオプトアウトしない限り学習に利用される可能性があるため、業務利用時は特に注意が必要です。</p>
<h4><strong>相性が良いシーンの例</strong></h4>
<ul>
<li>企画の初期段階で、コンセプトの方向性を探りたいとき</li>
<li>「こんな感じ？」と壁打ちしながら、画像のバリエーションを試したいとき</li>
<li>テキストと画像をセットで考えたい（タイトル案＋キービジュアル案 など）とき</li>
</ul>
<p> <a href="https://openai.com/ja-JP/business-data/">公式サイト（ビジネスデータのプライバシー、セキュリティ、コンプライアンス）</a> </p>
<hr>
<h3><strong>② Google Gemini</strong></h3>
<p><strong>→ 「Google Workspace との連携」を重視したいときに</strong></p>
<p>「スライド用の背景画像をサッと欲しい」「提案資料の中に入れるイメージをその場で作りたい」といったニーズにフィットします。</p>
<h4><strong>企業での利用について</strong></h4>
<p>Google Workspace の商用プランでは、入力データや生成物はAIの学習に利用されません（商用データ保護が適用されます）。</p>
<p>一方で、<strong>個人アカウント</strong> から Gemini アプリを使う場合は、会話内容が製品改善やモデル改善に利用されることがあります。
業務で使うなら、自分がどの契約・どのアカウントで Gemini を使っているかを必ず確認しておきたいところです。</p>
<h4><strong>相性が良いシーンの例</strong></h4>
<ul>
<li>提案資料に差し込む、リアル寄りのイメージ画像が欲しいとき</li>
<li>すでに Google Workspace を業務で使っているチーム</li>
<li>ドキュメントやスライドの中で、そのままプロンプトを書いて画像を生成したいとき</li>
</ul>
<p> <a href="https://support.google.com/a/answer/15706919?hl=ja">公式サイト（Google Workspace の生成 AI に関するプライバシー ハブ）</a> </p>
<hr>
<h3><strong>③ Adobe Firefly（アドビ ファイアフライ）</strong></h3>
<p><strong>→「権利関係のクリーンさ」を最優先したいときに</strong></p>
<p>Photoshop や Illustrator でおなじみの Adobe が提供する画像生成AIです。</p>
<p><strong>最大の特徴</strong> は、Adobe が Firefly について、Adobe Stock などのライセンス済みコンテンツや著作権が消滅したパブリックドメイン画像など、<strong>権利的にコントロールされた素材を中心に学習している</strong> と公式に明示している点です。</p>
<p>もちろん、これだけで「何があっても絶対安心」とは言えませんが、コンプライアンスを重視する企業のWebサイト、広告クリエイティブ、大規模なキャンペーンビジュアルなどを作るときに、ひとつの&quot;安心材料&quot;として選びやすいツールです。</p>
<h4><strong>IP補償について</strong></h4>
<p>また、エンタープライズ向けの Firefly ソリューションやAdobe Stock の一部の生成機能では、一定の条件を満たした場合に、<strong>生成物に対するIP補償</strong> が提供される仕組みがあります（対象となるプランや条件は契約形態によって異なります）。
「会社でAdobeに入っているから大丈夫」と思い込まず、「<strong>自社の契約はIP補償の対象プランか？</strong>」を一度確認することをおすすめします。</p>
<h4><strong>相性が良いシーンの例</strong></h4>
<ul>
<li>既に Photoshop / Illustrator を使っていて、その延長で生成AIを使いたいとき</li>
<li>権利面・ブランド面への配慮が特に重要なプロジェクト</li>
<li>生成画像に Content Credentials（生成経路の情報）を付けて管理したいとき</li>
</ul>
<p> <a href="https://business.adobe.com/jp/products/firefly-business/firefly-ai-approach.html">公式サイト（包括的で安全に商用利用できるAIを活用したビジネス用コンテンツ制作）</a> </p>
<p> <a href="https://www.adobe.com/jp/ai/overview/firefly/gen-ai-approach.html">公式サイト（Adobe Fireflyによる生成AIへのアプローチ）</a> </p>
<hr>
<h2><strong>「組織のルール」より前にできる、個人としての3つの工夫</strong></h2>
<p>「うちの会社、まだAIのルールとか全然決まってないんだよね…」という方も多いと思います。</p>
<p>まずは、社内にすでにルールやガイドラインがないかを確認してみてください。
すでにある場合は、当然そちらが最優先です。</p>
<p>「確認したけど特にない」「これから整備される予定」という状況であれば、今日からできる小さな工夫を3つだけ挙げておきます。</p>
<hr>
<h3><strong>工夫1：使うツールの「利用規約をまず確認してみる」</strong></h3>
<p>細かいところまで読み込めなくても、少なくとも、次のようなポイントは一度チェックしておくと、いざという時に助かります。<a href="%E6%A5%AD%E5%8B%99%E4%B8%8A%E3%81%AE%E6%9C%80%E7%B5%82%E5%88%A4%E6%96%AD%E3%81%AB%E3%81%82%E3%81%9F%E3%81%A3%E3%81%A6%E3%81%AF%E3%80%81%E5%88%A9%E7%94%A8%E8%A6%8F%E7%B4%84%E3%81%AE%E8%A9%B2%E5%BD%93%E7%AE%87%E6%89%80%E3%82%92%E8%A9%B3%E7%B4%B0%E3%81%AB%E7%A2%BA%E8%AA%8D%E3%81%99%E3%82%8B%E3%81%8B%E3%80%81%E6%B3%95%E5%8B%99%E6%8B%85%E5%BD%93%E3%81%B8%E3%81%AE%E7%9B%B8%E8%AB%87%E3%82%92%E3%81%8A%E5%8B%A7%E3%82%81%E3%81%97%E3%81%BE%E3%81%99%E3%80%82%E6%9C%AC%E8%A8%98%E4%BA%8B%E3%81%A7%E7%B4%B9%E4%BB%8B%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%AF%E3%80%8C%E6%9C%80%E5%88%9D%E3%81%AE%E4%B8%80%E6%AD%A9%E3%80%8D%E3%81%A8%E3%81%97%E3%81%A6%E3%81%AE%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88%E3%81%A7%E3%81%82%E3%82%8A%E3%80%81%E3%81%93%E3%82%8C%E3%81%A0%E3%81%91%E3%81%A7%E5%8D%81%E5%88%86%E3%81%A8%E3%81%84%E3%81%86%E6%84%8F%E5%91%B3%E3%81%A7%E3%81%AF%E3%81%82%E3%82%8A%E3%81%BE%E3%81%9B%E3%82%93%E3%80%82">^1</a></p>
<p><strong>確認すべきポイント</strong></p>
<ul>
<li>商用利用はOKか</li>
<li>再配布・譲渡はどこまで許されているか</li>
<li>クレジット表記が必要かどうか</li>
<li>「AI生成です」と書く必要があるのか</li>
<li>この画像は「自分のもの」として扱っていいか<a href="%E5%90%84%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%81%AE%E5%88%A9%E7%94%A8%E8%A6%8F%E7%B4%84%E3%81%AB%E3%81%AF%E3%80%81%E7%94%9F%E6%88%90%E3%81%97%E3%81%9F%E7%94%BB%E5%83%8F%E3%81%AE%E6%89%B1%E3%81%84%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E8%A8%98%E8%BC%89%E3%81%8C%E3%81%82%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82">^2</a><ul>
<li>権利がユーザーに帰属するのか</li>
<li>それとも、サービス側からライセンスを付与される形なのか</li>
</ul>
</li>
</ul>
<p>ツールによって、</p>
<ul>
<li>「入力と出力はユーザーに帰属します」</li>
<li>「ユーザーに一定のライセンスを付与します」</li>
</ul>
<p>といった表現が分かれますし、OpenAI や Adobe のように、ビジネス向けサービスでIP補償を用意しているケースもあります。</p>
<p> <strong>ポイント</strong>
「なんとなく大丈夫そう」ではなく、最低限、上の項目について <strong>「どこに何が書いてあるか」だけでも見つけておく</strong> と、自分では気づかないリスクがグッと減ります。</p>
<hr>
<h3><strong>工夫2：「これはアウトかも？」と思ったら、一度人に見せる</strong></h3>
<ul>
<li>有名キャラに似ていないか</li>
<li>ブランドロゴっぽい要素が入っていないか</li>
<li>社会的な配慮を欠く表現になっていないか</li>
</ul>
<p>など、自分だけの判断では不安なときは、チームメンバー、デザイナー、上長などに「どう思う？」と一度聞いてみることが必要だと思います。自分では気づきにくいグレーゾーンを別の人があっさり見抜いてくれる、ということもよくあります。</p>
<hr>
<h3><strong>工夫3：プロンプトと生成物を「メモしておく」</strong></h3>
<p>「この画像どうやって作ったんだっけ？」という振り返りのためだけでなく、万が一、第三者から「この画像、似ていませんか？」と問い合わせが来たときに、「このツールで、このプロンプトで生成しました」と説明できる状態にしておくことが、自分や会社を守ることにつながります。</p>
<p>完璧な管理まではしなくても、以下の内容を記録しておくと説明しやすくなります。</p>
<table>
<thead>
<tr>
<th>記録レベル</th>
<th>内容</th>
</tr>
</thead>
<tbody><tr>
<td><strong>最低限</strong></td>
<td>どのツールを使ったか（ChatGPT、Gemini、Firefly など）、いつ生成したか（日付）</td>
</tr>
<tr>
<td><strong>推奨</strong></td>
<td>プロンプトの全文、生成した画像のファイル名と保存場所、使用目的（社内資料 / クライアント提案 / Webサイト など）</td>
</tr>
</tbody></table>
<p>スマホのメモアプリや、生成した画像と同じフォルダにテキストファイルを置いておくだけでも、何もしないよりはるかに役立ちます。</p>
<p><a href="https://ai-act-service-desk.ec.europa.eu/en/ai-act/article-50">EU AI Act</a> や <a href="https://www.mofa.go.jp/mofaj/ecm/ec/page5_000483.html">G7広島プロセス</a> など、生成AIの透明性や説明責任が重視されつつあります。
「どのツールで、どんな指示を出して、どの画像を使ったか」をざっくりでも追えるようにしておくことは、こうした流れに備える意味でも有効です。</p>
<hr>
<h2><strong>おわりに：「なんとなく」から「意識して使う」へ</strong></h2>
<p>画像生成AIは、使いこなせば本当に頼れる相棒になります。</p>
<p>でも、「便利だから」とただ流されるのではなく、リスクを意識しながら使うことで、仕事の質も、周囲からの信頼も、一段上がるはずです。</p>
<p>完璧なルールが整うのを待つ必要はありません。</p>
<ul>
<li>利用規約を見る</li>
<li>迷ったら誰かに聞く</li>
<li>使ったツールやプロンプトを軽く記録しておく</li>
</ul>
<p>こうした小さな実践の積み重ねが、</p>
<p>「なんとなく使っている人」と「<strong>責任を持って使いこなしている人</strong>」の差になっていくのだと思います。</p>
<hr>
<h2><strong>免責事項</strong></h2>
<p>本記事は、画像生成AIに関する一般的な情報の共有を目的としたものであり、
<strong>法律的な助言を行うものではありません。</strong></p>
<p>具体的な判断が必要なケースでは、</p>
<ul>
<li>各サービスの最新の利用規約や FAQ</li>
<li>所属組織のルール・ガイドライン</li>
<li>必要に応じて専門家（法務・弁護士等）</li>
</ul>
<p>に相談することをおすすめします。</p>
<hr>
<p>ただし、ここが少しややこしいところです。
「権利はユーザーに帰属」と書かれているサービスもあれば、「ユーザーにライセンスを付与」といった書き方のサービスもあります。
特にクライアントとの契約で「著作権の譲渡」が条件になっている場合は、自己判断せず、利用規約の該当部分を法務や上長に見せて「この条件で問題ないか」を確認することをおすすめします。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoshima/image-generation-guide/01/title.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[ユーザーファースト推進の一歩目として、人間中心設計専門家を取得してみた。]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-23_hcd-expert/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-23_hcd-expert/</guid>
            <pubDate>Tue, 23 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[ユーザーファースト推進に向けて、人間中心設計専門家（HCD専門家）資格を取得し、その学びとプロセスから得た気づきをまとめた記事です。]]></description>
            <content:encoded><![CDATA[<p>こんにちは！クリエイティブグループの大金（おおがね）です。</p>
<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の23日目のものです。</p>
<p>私の普段の業務は、ウェブディレクターとしてサブスクKINTOの中古車の情報設計や、アシスタントマネージャーとしてクリエイティブグループの組織マネジメントを行っています。</p>
<p>今年からは新たに、注力テーマである「ユーザーファースト」の推進担当に手を挙げ、Engineering Officeの守谷（emim）とともにユニットとして活動しています。得意領域で分担している彼女のパートは11日目に紹介されていますので、そちらの記事も覗いてみてください。
<a href="https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025/">https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025/</a></p>
<p>今日は、私が人間中心設計専門家（HCD専門家）の資格を取得した体験の紹介を通して、「ユーザーファースト」という姿勢に対する熱い思いをお伝えしたいと思います。</p>
<p>自社事業で「ユーザーファースト」を掲げながらも、それが組織の壁や既存のプロセスに阻まれ、なかなか本質的な価値に繋がらない… そんな課題を抱える事業責任者や開発担当者の方にこそ、ぜひ読んでいただきたい内容です。5分ほどお付き合いください！</p>
<h1>なぜ私が人間中心設計専門家の認定取得を目指したのか？</h1>
<p>「ユーザーファースト」。ウェブで事業展開を行っている事業会社では「聞いたことがない」ということはない重要ワードかとは思いますが、教科書的に「これができていたらユーザーファーストである」といった定義は明確になされていない、あるいはするのが難しいもので、各事業・事業体において望ましい姿はやや異なるかもしれません。</p>
<p>私たちはテックカンパニーとして株式会社KINTOとともにサービス開発、運営を行っていますが、全社に「ユーザーファースト」が行き渡ることを目指すにあたり、その言葉の意味合いや活動のゴール、プロセスを推進担当から見出していく必要がありました。</p>
<p>私が推進担当に手を挙げた理由は、前職で担当していたサービスがユーザーファースト戦略によって大きく躍進した経験があり、あの素晴らしい体験をKTCでも再現したいというものでした。そこには当然、人の巻き込みも必要です。</p>
<p>そこでユーザーファーストの推進担当として、専門領域の知識・実績があることを他者に示し、安心して参画していただく手段として、資格を取得することを思い至りました。この領域の資格としては現在、特定非営利活動法人 人間中心設計推進機構（HCD-net）が認定している人間中心設計スペシャリスト／専門家が適しているということで、受験の準備に入ったのがちょうど去年のこの時期でした。</p>
<h1>準備期間</h1>
<p>まずは認定を受けたい資格と、その受験資格の確認から入りました。</p>
<p>HCD-netの認定資格は2種類あり、スペシャリストと専門家となっています。いずれもHCDプロジェクトを推進する能力があることを、決められたレポート形式に沿って示すスタイルの認定試験となります。</p>
<p>HCDプロジェクトとは、ユーザーに対する仮説を立て、実際にインタビューやプロトタイピングを経て実証・検証のサイクルを回していくプロジェクトです。私の場合はウェブサービスでの実践経験しかありませんでしたが、特に問題ないようでした。</p>
<p>HCDプロジェクトを推進するスキルセットを持ち合わせ、独力で遂行できる人がスペシャリスト、これにプロジェクト全体や、組織やメンバーへのHCDの導入を含めて推進するリーダーシップやマネジメント力を持ち合わせている人が専門家として認定されます。私の場合は「ユーザーファーストの全社への推進」が目的ですので、専門家を目指すこととしました。</p>
<p>また受験資格も異なり、専門家については「人間中心設計・ユーザビリティ関連の実務経験5年以上を目安／実践事例が3例以上」となっています。パッと思いついて勉強を頑張ればすぐに取得できるものではない、ということがわかりました。過去に携わっていたプロジェクトも含めると要件を満たしているだろうと考え、受験することにしました。</p>
<h1>受験対策</h1>
<p>HCD-net主催のオンライン説明会に参加し、概要を把握しました。</p>
<p>過去に携わったHCDプロジェクトを概要から詳細に至るまで時系列で書き出し、コアコンピタンスの項目ごとに整理します。コアコンピタンスとは、例えば「利用状況の理解及び明示の能力」を「A1.調査・評価設計能力」などに区分けしたもので、プロジェクトの各プロセスをこの区分けに落とし込むことが求められます。</p>
<p>整理する内容は調査対象、目的、アクション、最終アウトプットでワンセット。これを10項目ｘ３例（審査に必要な必要最低数）、漏れなくダブりなく記載します。</p>
<h1>受験中</h1>
<p>期間内に指定のドキュメントを提出する形式の認定試験であり、他者からアドバイスを受けることは禁止となっています。正解がわからない中で自力で書き上げる必要があるため、当時の記録や記憶をたどりつつひたすら記載しては読み直し、修正を繰り返しました。</p>
<p>年末年始の休暇のほぼ全てを投入し、指定のドキュメントの確認をはじめてから２週間くらいで提出できました。</p>
<h1>合格後、何か変わったか</h1>
<p>取得した目的の通り、ユーザーファースト推進のシーンで専門家を名乗ることができています。
これまでの仕事を振り返り、可視化し、他者に伝わるドキュメントに起こすプロセスを経たことで、この分野について一通りは理解・実践しているという自信がついたという側面もあります。</p>
<p>プロジェクトは常に生き物であり、学びが尽きないものなので、まだまだこれから実践を続けて行きたいと考えています。</p>
<p>同時に、ユーザーファーストを自社の文化に昇華すべく、社内外に知識や実践例、Tipsなどを還元していく活動も邁進していきます。</p>
<p>もしあなたが、</p>
<ul>
<li>「単なるデザイン改善」ではなく、「サービスの本質的な価値と収益性」を追求したい</li>
<li>ユーザーの声を、組織と事業戦略を動かす力に変えたい</li>
</ul>
<p>そんなことを感じていたなら、HCDをテーマにしたイベント等にも今後参加しようと考えていますので、どこかでお会いした際には情報交換をさせてください。</p>
<p><img src="/assets/blog/authors/ogane/251223/01_illustration.jpg" alt="illustration"> </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ogane/251223/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ（Part 3）：SwiftUI連携と技術Tips]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-22-cmp-swiftui-hybrid-part3/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-22-cmp-swiftui-hybrid-part3/</guid>
            <pubDate>Mon, 22 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[DroidKaigi 2025で発表した「Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ」の内容をまとめました。Part 3では、SwiftUI連携、CMP vs Flutter比較、実践で直面した落とし穴、導入戦略を紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の22日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>こんにちは、KINTOテクノロジーズ Mobile Assistanceマネージャー&amp;KMPチームリードのYena Hwangです。</p>
<p>本記事は「Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ」シリーズの最終回Part 3です。Part 1ではハイブリッド開発を選んだ背景を、Part 2では具体的な実装方法を紹介しました。今回はSwiftUI連携の詳細、CMP vs Flutter比較、そして実践で直面した落とし穴と導入戦略を解説します。</p>
<blockquote>
<p><strong>本シリーズの構成</strong></p>
<ol>
<li>Part 1：なぜハイブリッドなのか</li>
<li>Part 2：実装ガイド</li>
<li><strong>Part 3：SwiftUI連携と技術Tips</strong> ← 現在の記事</li>
</ol>
</blockquote>
<h2>SwiftUIとCMPの相互埋め込み</h2>
<p>ハイブリッドアーキテクチャの核心は、SwiftUIとCompose Multiplatform（CMP）を自由に組み合わせられることです。ここでは、共通モジュールを実際にネイティブ画面に、どう統合するのかをご紹介します。</p>
<p><img src="/assets/blog/authors/yenahwang/p-swiftui-cmp-integration.png" alt="SwiftUI + CMP統合"></p>
<h3>ComposeをSwiftUIに埋め込む</h3>
<p>SwiftUIにComposeを埋め込む方法です。</p>
<p><img src="/assets/blog/authors/yenahwang/img01.png" alt="SwiftUI内にComposeを埋め込んだ画面"></p>
<p>まず、CMP側では<code>UIViewController</code>を返します。</p>
<pre><code class="language-kotlin">// AppComposeUI.kt
fun createProfileVC(
    user: User
): UIViewController {
    return ComposeUIViewController {
        ...
    }
}
</code></pre>
<p>そしてSwiftUI側では<code>UIViewControllerRepresentable</code>を使って、そのコントローラをラップします。</p>
<pre><code class="language-swift">// ComposeViewContainer.swift
import SwiftUI
import shared  // Import the shared KMP framework

struct ComposeViewContainer: UIViewControllerRepresentable {
    let user: User

    func makeUIViewController(context: Context) -&gt; UIViewController {
        // Use the Compose wrapper to create the UIViewController
        return AppComposeUIKt.createProfileVC(user)
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
    }
}
</code></pre>
<p>これでSwiftUIの中にCMPの画面が自然に表示されます。</p>
<p>以下は<code>ComposeViewContainer</code>に<code>user</code>を渡している例です。</p>
<pre><code class="language-swift">// ContentView.swift
struct ContentView: View {
    let user: User

    var body: some View {
        VStack {
            Text(&quot;SwiftUI Header&quot;).font(.headline).padding()
            // Embed the Compose UI inside SwiftUI.
            ComposeViewContainer(user: user)
                .frame(height: 250)
            List {
                Text(&quot;SwiftUI Item 1&quot;)
                Text(&quot;SwiftUI Item 2&quot;)
            }
        }
    }
}
</code></pre>
<p>このように書くだけで、<strong>CMPの画面をそのままSwiftUIのコンポーネントとして扱える</strong>ようになります。</p>
<h3>SwiftUIをComposeに埋め込む</h3>
<p>今回は、逆に、<strong>SwiftUIをComposeに埋め込む方法</strong>です。</p>
<p><img src="/assets/blog/authors/yenahwang/img02.png" alt="Compose内にSwiftUIを埋め込んだ画面"></p>
<p>まずKotlin（iosMain）側にエントリーポイントを用意します。SwiftUIをComposeに埋め込むための入口です。KotlinはSwiftUIを生成しません。Swiftから<code>() -&gt; UIViewController</code>のファクトリを受け取ります。</p>
<pre><code class="language-kotlin">// ComposeEntryPointWithUIViewController.kt
fun ComposeEntryPointWithUIViewController(
    createUIViewController: () -&gt; UIViewController
): UIViewController = ComposeUIViewController {
    Column(
        Modifier
            .fillMaxSize()
            .windowInsetsPadding(WindowInsets.systemBars),
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text(&quot;Embedding SwiftUI into Compose&quot;)
        UIKitViewController(
            factory = createUIViewController,
            modifier = Modifier.size(250.dp).border(2.dp, Color.Blue),
        )
    }
}
</code></pre>
<p>そしてCompose側では<code>UIKitViewController()</code>を使って、SwiftUIビューを表示します。</p>
<p>次にSwift側の実装です。ここでは<code>MySwiftUIView</code>を定義して、<code>UIHostingController</code>でラップします。</p>
<pre><code class="language-swift">// MySwiftUIView.swift
struct MySwiftUIView: View {
    let userName: String

    var body: some View {
        VStack(spacing: 8) {
            Text(&quot;Native SwiftUI Content&quot;).font(.headline)
            Text(&quot;Welcome, \(userName)&quot;)
        }
    }
}

// Pass SwiftUI to Kotlin entry point
struct ComposeViewControllerRepresentable: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -&gt; UIViewController {
        // Pass a factory to Kotlin that returns a UIViewController.
        // Here we wrap our SwiftUI view in a UIHostingController.
        return Main_iosKt.ComposeEntryPointWithUIViewController(createUIViewController: {
            UIHostingController(rootView: MySwiftUIView(userName: &quot;Yena Hwang&quot;))
        })
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
}
</code></pre>
<p>そして、そのコントローラを<code>ComposeEntryPointWithUIViewController</code>にファクトリとして渡します。このようにして、結果的に<strong>SwiftUIの画面をComposeの中に表示する</strong>ことができます。</p>
<h3>ネイティブUIコンポーネントの埋め込み：MapView</h3>
<p>次に、ネイティブUIコンポーネントを利用した例です。CMP側にネイティブUIコンポーネントを埋め込んだアプリの動作をご覧ください。このMapViewはiOSの<strong>ネイティブコンポーネント</strong>です。</p>
<p><img src="/assets/blog/authors/yenahwang/img03.png" alt="ネイティブMapViewを埋め込んだ画面"></p>
<pre><code class="language-kotlin">// MapContainer.kt
// commonMain
@Composable
expect fun MapContainer(
    modifier: Modifier = Modifier,
    dealers: List&lt;Dealer&gt;
)

// iosMain
@Composable
actual fun MapContainer(
    modifier: Modifier,
    dealers: List&lt;Dealer&gt;
) {
    UIKitView(
        factory = { // platform.MapKit
            MKMapView(frame = CGRectZero.readValue()).apply {
                showsUserLocation = true
                delegate = MapDelegate(
                    ...
                )
            }
        }
    )
}
</code></pre>
<p>共通コードでは<code>expect</code>関数を宣言し、iOS側では<code>actual</code>を実装しています。そして<code>UIKitView</code>を使って、<code>MKMapView</code>をそのまま埋め込んでいます。</p>
<p>このように、<strong>expect/actual</strong>パターンを使うことで、共通インターフェースを維持しながらプラットフォーム固有のネイティブコンポーネントを自由に埋め込むことができます。</p>
<h2>CMP vs Flutter：なぜCMPを選んだのか</h2>
<p>ここまでCMPとネイティブUIの使い分けについて見てきましたが、実は他のクロスプラットフォームのフレームワークでも同じような課題があります。</p>
<h3>ネイティブビュー統合方式の決定的な違い</h3>
<p>例えばFlutterの公式ドキュメントには、『Platform Viewにはパフォーマンス面でトレードオフがある』と明記されています。</p>
<table>
<thead>
<tr>
<th>区分</th>
<th>CMP</th>
<th>Flutter</th>
</tr>
</thead>
<tbody><tr>
<td>レンダリング</td>
<td>Inline Rendering 😄</td>
<td>Overlay 🥲</td>
</tr>
<tr>
<td>特徴</td>
<td>高速で効率的</td>
<td>重いビューでカクつき</td>
</tr>
</tbody></table>
<h3>技術的な違い</h3>
<p>CMPとFlutterでは、ネイティブUIと埋め込みUIの相互運用方式が異なり、パフォーマンスに大きな差が出ます。</p>
<table>
<thead>
<tr>
<th>Host UI → Embedded UI</th>
<th>Rendering path</th>
<th>Performance impact</th>
</tr>
</thead>
<tbody><tr>
<td>Native → CMP</td>
<td>Android: AndroidView inside Compose (same View system)<br>iOS: CMP Skia surface hosts a UIKit view via UIKitView</td>
<td>Very Low (Android) / Low–Moderate (iOS)</td>
</tr>
<tr>
<td>CMP → Native</td>
<td>Android: ComposeView in a ViewGroup (Compose runs on platform renderer)<br>iOS: SwiftUI/UIViewController hosts ComposeUIViewController (Skia surface inside)</td>
<td>Low (Android) / Moderate (iOS)</td>
</tr>
<tr>
<td>Native → Flutter</td>
<td>Flutter renders via Skia/Impeller; native view injected as PlatformView (texture/layer composition)</td>
<td>Moderate; visible stutter on heavy views (Map, WebView)</td>
</tr>
<tr>
<td>Flutter → Native</td>
<td>Native Activity/VC hosts Flutter engine surface</td>
<td>Extra startup cost (engine warm-up), stable after warm-up</td>
</tr>
</tbody></table>
<p><img src="/assets/blog/authors/yenahwang/p47.png" alt="CMP vs Flutter比較"></p>
<h3>CMPの強み</h3>
<ul>
<li>ネイティブMapViewをそのまま使用し、ネイティブAPIでアイコン/経路レンダリング</li>
<li>MKMapViewなどのネイティブコンポーネントを直接配置</li>
<li>コンポーネント単位で交換可能</li>
<li>ネイティブパフォーマンスとアクセシビリティ維持</li>
</ul>
<p><img src="/assets/blog/authors/yenahwang/p49.png" alt="ネイティブMapView統合"></p>
<h3>複雑なハイブリッドUIパフォーマンス比較</h3>
<table>
<thead>
<tr>
<th>Framework</th>
<th>Startup Time</th>
<th>Memory Usage</th>
<th>UI Smoothness</th>
</tr>
</thead>
<tbody><tr>
<td>Native</td>
<td>⭐⭐⭐</td>
<td>⭐⭐⭐</td>
<td>⭐⭐⭐</td>
</tr>
<tr>
<td>CMP + Native</td>
<td>⭐⭐⭐</td>
<td>⭐⭐</td>
<td>⭐⭐⭐</td>
</tr>
<tr>
<td>Flutter + Native</td>
<td>⭐⭐</td>
<td>⭐⭐</td>
<td>⭐⭐</td>
</tr>
</tbody></table>
<h2>実践で直面した落とし穴</h2>
<blockquote>
<h3>&quot;Every solution breeds new problems.&quot; — Arthur Bloch</h3>
</blockquote>
<p>ここまでCMPとネイティブUIの組み合わせの利点を見てきましたが、どんな技術にも課題はあります。私たちがKMP/CMP開発で実際に直面した落とし穴を紹介します。</p>
<h3>KMPロジックレイヤー</h3>
<p><strong>1. HTTP/TLS/リダイレクト/クッキー</strong></p>
<ul>
<li>症状：プラットフォーム別エンジンが異なり、クッキー永続性、リダイレクト、TLSピニングが異なる動作</li>
<li>解決：タイムアウト/キャッシュ/ロギング共有設定 + DIやexpect/actualでプラットフォーム別エンジン+セキュリティポリシー注入</li>
</ul>
<p><strong>2. 正規表現の違い</strong></p>
<ul>
<li>症状：JVMはjava.util.regex、iOS/Nativeは ICUベースエンジン使用。<code>\R</code>、可変幅lookbehindなどがiOSで異なる動作またはサポートなし</li>
<li>解決：複雑な機能を避ける、シンプルなサブパターンに分離、エンジン交換可能なアダプター構成</li>
</ul>
<p><strong>3. タイムゾーン &amp; DST</strong></p>
<ul>
<li>症状：プラットフォーム別タイムゾーンDBとフォーマッティング規則が異なる</li>
<li>解決：すべての計算はkotlinx-datetimeで、フォーマッティングはexpect/actualでプラットフォームに委任</li>
</ul>
<h3>CMP UIレイヤー</h3>
<p><strong>1. フォント &amp; タイポグラフィ（CJK/絵文字）</strong></p>
<ul>
<li>症状：プラットフォーム別フォールバックが異なり、行の高さ、省略、絵文字幅が異なる</li>
<li>解決：フォントバンドル、フォントファミリー/ウェイト明示的宣言、lineHeight/letterSpacing明示的設定</li>
</ul>
<p><strong>2. 省略/テキスト測定の違い</strong></p>
<ul>
<li>症状：iOS/SkikoテキストレイアウトがAndroidとピクセル単位で一致しない</li>
<li>解決：maxLines + TextOverflow.Ellipsis使用、「ぴったり」な仮定を避ける、主要画面スクリーンショットリグレッションテスト</li>
</ul>
<p><strong>3. スクロール &amp; ジェスチャー物理</strong></p>
<ul>
<li>症状：iOSの慣性がより「滑らか」で、ネストスクロールの感触が異なる。また、CMPのScrollの中にネイティブのScrollを入れると、内側のScrollが動作しない問題もある</li>
<li>注意：スクロール物理設定を抽象化してプラットフォーム別分岐、複雑なネストスクロールページは単純化。Scroll ViewのUXは設計段階から調整が必要</li>
</ul>
<h2>導入戦略：プロダクト段階別アプローチ</h2>
<p>CMP/KMPの真の強みは<strong>柔軟性</strong>です。共有コードとネイティブコードの比率を自由に調整できるため、プロトタイプでは再利用を最大化し、プロダクトが成熟したらネイティブ比率を高めることができます。</p>
<p><img src="/assets/blog/authors/yenahwang/p58-61.png" alt="導入戦略"></p>
<h3>新規プロダクト：共有比率を高くスタート</h3>
<table>
<thead>
<tr>
<th>段階</th>
<th>共有比率</th>
<th>目標</th>
<th>リソース状況</th>
</tr>
</thead>
<tbody><tr>
<td>Start Stage</td>
<td>~99%</td>
<td>迅速なリリース、アイデア検証</td>
<td>限られた開発リソース</td>
</tr>
<tr>
<td>Growth Stage</td>
<td>~80%</td>
<td>プロダクト拡張 + ネイティブUX強化</td>
<td>標準開発リソース</td>
</tr>
<tr>
<td>Maturity Stage</td>
<td>~50%</td>
<td>パフォーマンス最適化、長期保守性、チーム専門化</td>
<td>豊富な開発リソース</td>
</tr>
</tbody></table>
<h3>既存プロダクト：段階的導入</h3>
<p><strong>1. Start Small（1%）</strong>
最も安全なエントリーポイント。最も小さくリスクの低い領域にKMP/CMP導入</p>
<p><strong>2. Stepwise Expansion（20%）</strong>
安定性と確信を確保しながら導入範囲を拡張</p>
<p><strong>3. End Stage（~50%）</strong>
共有コードとネイティブコードの持続可能なバランスポイントに到達</p>
<h2>おわりに</h2>
<p>ハイブリッドアーキテクチャを導入して得られた成果をまとめると：</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>Before（従来・ネイティブ）</th>
<th>After（ハイブリッド）</th>
</tr>
</thead>
<tbody><tr>
<td>開発効率</td>
<td>両OS別々に実装</td>
<td>50%削減、一箇所でバグ修正</td>
</tr>
<tr>
<td>コスト</td>
<td>重複作業・すり合わせ必要</td>
<td>コミュニケーション・QA・保守削減</td>
</tr>
<tr>
<td>チーム</td>
<td>OS別に専門エンジニア必要</td>
<td>少人数で両プラットフォーム対応</td>
</tr>
<tr>
<td>品質</td>
<td>ネイティブ品質</td>
<td>ネイティブ品質を維持しながら共有</td>
</tr>
<tr>
<td>柔軟性</td>
<td>ネイティブのみ</td>
<td>必要な部分だけ共有可能</td>
</tr>
</tbody></table>
<p><strong>ネイティブの強みを活かしながらコードを共有できる</strong>——これがハイブリッド戦略の最大のメリットでした。</p>
<p>DroidKaigiで発表しながら、多くの方々が同じ悩みを持っていることを感じました。これからKMP・CMPの導入を検討しながら、両OSのネイティブコードも活かしたハイブリッドアーキテクチャを準備している方々に、私たちのチームの経験が少しでも参考になれば幸いです。</p>
<p>重複作業を減らし、両OSに効率よく対応しながら、高品質なアプリを素早くユーザーに届ける——KMP・CMPを活用したハイブリッド開発を、ぜひ検討してみてください。</p>
<hr>
<p>こちらは「Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ」シリーズのPart 3（最終回）です。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yenahwang/coverImage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[変化を起こしたい個人商店チーム─KINTOテクノロジーズ Engineering Officeの1年と、ゆかいななかまたち]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-22-engineering-office-reports/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-22-engineering-office-reports/</guid>
            <pubDate>Mon, 22 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズのEngineering Officeチームの1年間の活動と多様なメンバーを紹介します。]]></description>
            <content:encoded><![CDATA[<p>こんにちは、<a href="https://x.com/ahomu">@ahomu</a> です。この記事は<a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の22日目の記事です🎅🎄</p>
<p>2025年2月に<a href="https://blog.kinto-technologies.com/posts/2025-02-12-engineering-office/">KINTOテクノロジーズの開発組織を強くしたい Engineering Office のご紹介</a>という記事でご紹介した弊チームについて、発足から1年弱経過した現状とメンバー紹介を書きます。</p>
<h2>1年を振り返って</h2>
<p>今年の中頃にFindyさんの開発生産性カンファレンスでも弊チームについて登壇させてもらいました。</p>
<p><a href="https://findy-tools.io/articles/kinto-technologies-dev-productivity-con-2025/133">https://findy-tools.io/articles/kinto-technologies-dev-productivity-con-2025/133</a></p>
<p>我々は「トヨタグループの内製開発部隊たるKINTOテクノロジーズ（KTC）の開発力・組織力を高めるためにケイパビリティやカルチャーを獲得/変化するための働きかけ」という、抽象的なミッションを担っています。</p>
<h3>学びと理解と関係づくり</h3>
<p>登壇時にも言及したことですが、我々のような社歴の浅いメンバー/チームが横軸活動を進める上で大事なことは、独りよがりにならず、これまでとこれからをセットで示すことです。</p>
<ul>
<li>これまでのトレードオフを知ることで、現状の意味・意義を知る</li>
<li>問題や不足を見つけるのではなく、今できていることを知った上で伸び代を見つける</li>
</ul>
<p>EOは各人の領域でこの動きをしっかり行うことができたと思います。また、実際に頼りにしてもらえる機会も増えてきており、まだ小さいながらも着実に変化を起こせています。
全社のカバレッジとしては不足もありつつですが社内各所、各レイヤーとも様々な関係を築けました。</p>
<h3>個人商店と多様性のシナジー</h3>
<p>活動が本格化するにつれて専門性の異なる個人商店的なチームであることの明確な強みが出てきたように感じています。専門領域以外にも出身業界や属性に多様性があるチームなので、ひとりひとりが様々な視点をもって全社に向けてアプローチをしています。</p>
<p>その過程であつまった情報や知見を持ち寄ると、同じ対象にも色々な物の見方や、立場による見解の差異がよく見えてきます。所感と事実を正しく扱うことで、KTCの次の可能性や、より具体的な打ち手を考える中で大きな助けになっています。</p>
<p>また、ひとりひとりが社内営業であると同時に、組合として助け合うことで「この仕事はこのひとのほうが得意だからパスしちゃお」がうまくワークすることも増えています。個人的な経験だと同質性による強行突破を図るような仕事が多かったので、今のこのチームで多様性のメリットが強く感じられることに面白みを感じています。</p>
<h2>実際の取り組みとゆかいななかまたちを紹介するぜ</h2>
<p>今年のAdvent Calendarの中で各自が取り組みを紹介しているので、ここでは記事リンクとあわせてメンバー紹介をしていきます。（ドコドコドコ……🥁</p>
<h3>🚀 「一人二役、開発プロセス改善の北風と太陽」Y.Naitoさん</h3>
<p><a href="https://blog.kinto-technologies.com/posts/2025-12-17-releasefirst/">https://blog.kinto-technologies.com/posts/2025-12-17-releasefirst/</a></p>
<ul>
<li>SPI (Software Process Improvement) スペシャリストとしてEO発足以前から社内各所で活動しています</li>
<li>記事にある「リリースファースト」はまさにNaitoさんの真骨頂。スクラムの雰囲気に乗っかってきただけの自分としては、開発プロセスという一連の営みに対する解像度の高さを学ばせていただいております</li>
<li>北風と太陽、モードを使い分けてるけど、なんにせよパワフルさに社内で定評がある。<strong>亀十のどら焼きを布教するプロ</strong></li>
</ul>
<h3>🎨 「EOの特異点、KTCのデザインをデザイン中」emimさん</h3>
<p><a href="https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025/">https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025/</a></p>
<ul>
<li>チーム名称的にエンジニアが増えるかと思いきや、emimさんはデザイナーです。デザインエンジニアとかでもなく純粋にプロダクトデザインが直近の専門</li>
<li>記事では、形骸化しがちな「ユーザーファースト」を再定義する挑戦が綴られています。開発の中でデザインとエンジニアリングのブリッジをしてきた経験を活かし、EOとして内製開発組織の変革に挑戦してくれています</li>
<li>「社内フリーランスで動き回る前提で、しくじると社内無職まっしぐらですよ！（乱暴な意訳）」という珍妙な求人訴求に興味を示してくれた奇特な方。<strong>おいしいコーヒーだいすき</strong></li>
</ul>
<h3>🍔 「バーキン担当アクセシビリティ番長（仮）」辻さん</h3>
<p><a href="https://blog.kinto-technologies.com/posts/2025-12-09-What-I-want-to-achieve-as-blind-person-at-mobile-company/">https://blog.kinto-technologies.com/posts/2025-12-09-What-I-want-to-achieve-as-blind-person-at-mobile-company/</a></p>
<ul>
<li>入社したてホヤホヤ。アクセシビリティエンジニアとして幅広く社内外でご活躍予定</li>
<li>一連の活動を通してアクセシビリティ〜ユーザビリティが直接的に高まる → グループ会社としての使命を本質的に果たせるようになることはもちろん、多様な利用環境に関する気付きを得られるので広くユーザーに対して深い洞察を得られる組織の育成につながると考えています（筆者観点）</li>
<li>東京のオフィス近くにあるバーガーキングで<strong>ワッパーを食べる約束？をしている</strong></li>
</ul>
<h3>👔 「Engineering Office は心の主務」ahomu</h3>
<ul>
<li>じつは7月から人事マネージャーを兼任している。プロ人事の皆さんに支えられています</li>
<li>EOではアウトプットや進め方のレビューだけしてるマネージャー業</li>
<li>EOが仕掛けている組織の変化に備え、私は人事としてチェンジマネジメントに携わっている、というのが正しいフォーメーション説明</li>
</ul>
<h2>さいごに</h2>
<p>KTCのEngineering Officeのご紹介でした。年明けにはSET（Software Engineer in Test）としての専門性を持つメンバーもジョイン予定です。
まさか1年でこんな人が増えるとは思っておらずびっくりですが、求人要件がファジーすぎて難しいので、引き続きリファラル中心で積極的な採用はしていません。</p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/0000127">👉 KINTOテクノロジーズ 採用情報（オープンポジション）はこちら</a></p>
<p>Engineering Office決め打ちでなくても大丈夫なので、ご興味がある方は何らかの手段でコンタクトとっていただければ色々お話させていただきますので、よろしくお願い致します。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ahomu/20251222/2025-12-22-EO.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[WWDC25に現地参加してきました！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-22-wwdc25/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-22-wwdc25/</guid>
            <pubDate>Mon, 22 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[WWDC25期間に参加した各種会議・イベントについてまとめました]]></description>
            <content:encoded><![CDATA[<p>この記事は<a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a>の22日目の記事です🎅🎄</p>
<h1>はじめに</h1>
<p>こんにちは。KINTOテクノロジーズ株式会社（略して、KTC）で iOS, Androidモバイルアプリを開発している、方茂碩（バン・ムゥソク、Bahng Mooseok）です。
これまで「Global KINTO App」、「KINTO かんたん申し込みアプリ」などを担当し、現在は 「KINTO Unlimited」の開発を行っています。</p>
<p>「KINTO かんたん申し込みアプリ」は、新車のサブスクサービスであるKINTOの見積りから審査までができるアプリで、「KINTO Unlimited」は、契約内容の確認、ドライブサポートやクルマのアップグレードの申し込みなどができるアプリです。</p>
<p>今回は、WWDC25に参加してきましたので、テックブログにまとめてみました。</p>
<p>私は2008年からiOSアプリ開発に携わっており、当時はまだ「iOS」ではなく「iPhone OS」と呼ばれていた時代です。
長年にわたって、WWDCは技術情報の収集や最新技術のキャッチアップにおいて最も重要なイベントだと感じてきました。にもかかわらず、今回が初の現地参加となり、とても感慨深い体験となりました。
やはり現地ならではの没入感は、オンライン参加とはまったく違いました。</p>
<h1>Check-in and welcome reception @Apple Infinite Loop</h1>
<p>日曜日の午後、Appleの旧社屋「Apple Infinite Loop」にて、事前チェックインとウェルカムレセプションが行われました。
WWDCのバッジとグッズを受け取り、そのまま夕方までウェルカムパーティが開催されました。</p>
<p><img src="/assets/blog/authors/msbahng/wwdc25/IMG_3412.jpeg" alt="WWDC25の参加バッジ"></p>
<p><em>WWDC25の参加バッジ、KTCからはほとんどのアプリを TOYOTA FINANCIAL SERVICE CORPORATIONの名前でリリースされています</em></p>
<p>WWDCも、この場所も憧れだったので、結構並びますが、WWDC25のモニュメント前での記念撮影は必須です！行列にはなりますが、並ぶ価値ありです。</p>
<p><img src="/assets/blog/authors/msbahng/wwdc25/IMG_3397.jpeg" alt="WWDC25のモニュメント"></p>
<p>前日夜には日本からの参加者が集まる交流会にも参加していたため、その時に出会った方々と話したり、会場内にいるAppleの社員と話したりと、楽しい時間を過ごせました。Appleの偉い方から「あそこが Steve Jobsのオフィスでしたよ」と直接説明を聞けたのは、貴重な経験でした。</p>
<p>そして、Apple Japanの方ともお話する機会があり、日本に戻ってからのアプリ改善に向けた取り組みについて相談することができました。</p>
<h1>Main Event @Apple Park - Keynote, Platforms State of the Union</h1>
<p>WWDC期間中、自分を苦しめたのが時差ボケです。メインイベントの日もちょっと寝坊してしまい、Keynoteの時間にギリギリ到着。
屋根のある良い席も確保できず、Caffe Macsでの朝食も慌ただしくなってしまいました。メインイベントの日は早めに行動するのをおすすめします。</p>
<p><img src="/assets/blog/authors/msbahng/wwdc25/IMG_3450.jpeg" alt="メインイベント"></p>
<p><em>遠い＆日差しが強い！</em></p>
<p>いよいよKeynoteが始まります。
Keynoteの冒頭では、Apple制作の映画F1の予告編がサプライズで上映されました。ちなみに、まだ映画館で公開前でしたが、WWDCの三日目の火曜日の夜、Steve Jobs Theaterで特別上映されました。もちろん席に限りがあるので、申し込みが必要。私はメール確認が遅れ、申し込みに間に合いませんでした。突発的なイベントにも対応できるよう、WWDC期間中は常にメールをチェックすることを強くおすすめします。</p>
<p>Keynoteが終わると、ランチです！</p>
<p><img src="/assets/blog/authors/msbahng/wwdc25/IMG_3463.jpeg" alt="ランチ"></p>
<p>Cupertinoの夏は日差しが非常に強いですが、Apple Parkの内庭でのランチは最高です。
前夜から全て無料で提供される食事、デザートもWWDC現地参加の楽しみの一つです！</p>
<p>午後は「Platforms State of the Union」が開催。開発者向けにより技術的な内容が発表されます。</p>
<p>今回のKeynoteと Platforms State of the Unionの目玉は、Liquid Glass UIと Apple Intelligenceでした。
Liquid Glassは、他の参加者からも好評で、私も非常に洗練された印象を受けました。</p>
<p>Apple Intelligenceでは、新しいFoundation Models Frameworkが登場。Appleアプリ開発において今後、非常に重要な基盤技術になると感じました。</p>
<h1>In-person Labs</h1>
<p>WWDC中でも特に貴重なのが「In-person Labs」です。Appleのエンジニアやデザイナーに直接質問できる機会です。</p>
<p><img src="/assets/blog/authors/msbahng/wwdc25/IMG_3472.jpeg" alt="In-person Labs"></p>
<p>想像以上に多くの方が対応をしていて、あまり待たずに相談することができました。</p>
<p>ただし一つ反省点が。この時間は概念的、抽象的な質問よりは具体的なケースに基づく質問の方が向いていると思いました。Conciergeの方が質問内容に応じて専門スタッフに繋いでくれるのですが、内容が曖昧だと割り振りにも困っているように見えました。</p>
<p>でも、WWDC期間中、Appleの社員と話せる機会は In-person Labsだけではありません。
ウェルカムパーティや、三日目の Developer Activitiesなど、機会は十分にあるので、聞きたいことをたくさん準備すると、より濃い体験になると思います。</p>
<h1>Developer Activities @Apple Developer Center Cupertino</h1>
<p>公式イベントの三日目、火曜日です。
Apple Developer Centerで Developers Activitiesが開催されました。</p>
<p><img src="/assets/blog/authors/msbahng/wwdc25/IMG_3523.jpeg" alt="Developers Activities"></p>
<p>このイベントは事前申請が必要で、WWDCの一週間前ぐらいに案内メールが届きますが、ちょっと遅くなると参加できなくなるか、当日会場の前で空きを待つしかないので、メールが届いたら、即申し込み推奨です。</p>
<p>今回のテーマはLiquid Glass UI。
登壇したAppleのデザイナーやエンジニアたちが、UIの哲学や開発エピソード、さらにはライブコーディングまで披露してくれました。
このセッションはオンライン配信もされない、現地限定コンテンツなので、非常に価値の高い体験でした。</p>
<h1>非公式カンファレンス、イベント - One More Thing Conference, Core Coffee</h1>
<p>WWDC開催中は、Cupertino周辺で多くの非公式Apple系イベントも開催されています。
twostrawsで知られるPaul HudsonさんがWWDCごとにまとめてくれているイベントリストがあるので、要チェックです！</p>
<p><a href="https://github.com/twostraws/wwdc">https://github.com/twostraws/wwdc</a></p>
<p>今回はその中から、「One More Thing Conference」と 「Core Coffee」に参加しました。</p>
<p><img src="/assets/blog/authors/msbahng/wwdc25/IMG_3571.jpeg" alt="One More Thing Conference"></p>
<p>「One More Thing Conference」では、公式発表された新技術をその場で実装・検証しながら、実用的な知見を深められるのが印象的でした。
非公式ながら、世界中の開発者と深く交流できる機会となりました。</p>
<h1>まとめ</h1>
<p>毎年WWDCの内容を追いかけてきた私にとって、今回、初めて現地で参加できたことは、とても意義深い経験となりました。</p>
<p>特にApple純正のFrameworkの理解は、品質・メンテナンス性の高いアプリを作るうえで欠かせないものです。
現地参加を通じて、その重要性をあらためて実感し、新たな刺激とモチベーションを得ることができました。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[組織のAI活用が進まない理由とは？｜組織へのAI実装を成功させる実践手法]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-21-breaking-through-ai-paradox/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-21-breaking-through-ai-paradox/</guid>
            <pubDate>Sun, 21 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[AIを組織に実装する具体的なアプローチを、デザイン思考とシステム思考を組み合わせた人間中心アプローチで、技術先行ではなく本質的な問題解決から始める実践手法を紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の21日目の記事です🎅🎄</p>
<h2>目次</h2>
<p><a href="#%E3%83%A1%E3%83%AA%E3%83%BC%E3%82%AF%E3%83%AA%E3%82%B9%E3%83%9E%E3%82%B92025%E5%B9%B4%E3%81%AEai%E6%B4%BB%E7%94%A8%E6%8E%A8%E9%80%B2%E3%82%92%E6%8C%AF%E3%82%8A%E8%BF%94%E3%81%A3%E3%81%A6">1. メリークリスマス！2025年のAI活用推進を振り返って</a>
<a href="#%E5%80%8B%E4%BA%BA%E6%B4%BB%E7%94%A8%E3%81%AE%E6%88%90%E5%8A%9F%E3%81%A8%E7%B5%84%E7%B9%94%E6%B4%BB%E7%94%A8%E3%81%AE%E8%AA%B2%E9%A1%8C">2. 個人活用の成功と組織活用の課題</a>
　<a href="#%E5%80%8B%E4%BA%BA%E6%B4%BB%E7%94%A8%E6%99%AE%E5%8F%8A%E7%8E%878%E5%89%B2%E3%81%AE%E9%81%94%E6%88%90">2-1. 個人活用普及率8割の達成</a>
　<a href="#%E7%B5%84%E7%B9%94%E7%9A%84%E3%81%AA%E6%B4%BB%E7%94%A8%E3%81%A8%E3%81%AF">2-2. 組織的な活用とは</a>
　<a href="#%E6%A5%AD%E7%95%8C%E5%85%A8%E4%BD%93%E3%81%AE%E8%AA%B2%E9%A1%8C">2-3. 業界全体の課題</a>
<a href="#%E3%82%A4%E3%83%8E%E3%83%99%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E4%BA%8C%E3%81%A4%E3%81%AE%E6%BD%AE%E6%B5%81">3. イノベーションの二つの潮流</a>
　<a href="#ai%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96%E3%81%A8ai%E3%83%89%E3%83%AA%E3%83%96%E3%83%B3">3-1. AIネイティブとAIドリブン</a>
　<a href="#%E3%81%A4%E3%81%BE%E3%81%9A%E3%81%8D%E3%81%AE4%E3%81%A4%E3%81%AE%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3">3-2. つまずきの4つのパターン</a>
<a href="#genai%E3%83%91%E3%83%A9%E3%83%89%E3%83%83%E3%82%AF%E3%82%B9%E3%81%AE%E6%A7%8B%E9%80%A0">4. GenAIパラドックスの構造</a>
　<a href="#%E5%8A%B9%E7%8E%87%E5%90%91%E4%B8%8A%E3%81%A8%E5%8F%8E%E7%9B%8A%E3%81%AE%E4%B9%96%E9%9B%A2">4-1. 効率向上と収益の乖離</a>
　<a href="#%E5%B1%9E%E4%BA%BA%E6%80%A7%E3%81%A8%E3%81%84%E3%81%86%E8%AA%B2%E9%A1%8C">4-2. 属人性という課題</a>
　<a href="#%E8%A6%96%E7%82%B9%E3%81%AE%E8%BB%A2%E6%8F%9B">4-3. 視点の転換</a>
<a href="#%E6%9C%AC%E8%B3%AA%E7%9A%84%E3%81%AA%E5%95%8F%E3%81%84%E3%81%AE%E7%AB%8B%E3%81%A6%E7%9B%B4%E3%81%97">5. 本質的な問いの立て直し</a>
　<a href="#%E6%B1%8E%E7%94%A8ai%E3%83%84%E3%83%BC%E3%83%AB%E3%81%AE%E7%89%B9%E6%80%A7">5-1. 汎用AIツールの特性</a>
　<a href="#%E6%8A%80%E8%A1%93%E5%B0%8E%E5%85%A5%E5%89%8D%E3%81%AE%E8%AA%B2%E9%A1%8C%E6%95%B4%E7%90%86">5-2. 技術導入前の課題整理</a>
　<a href="#%E5%86%8D%E7%8F%BE%E6%80%A7%E3%81%8C%E9%AB%98%E3%81%84%E6%97%A2%E5%AD%98%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E6%B4%BB%E7%94%A8">5-3. 再現性が高い既存フレームワークの活用</a>
　<a href="#%E4%BA%BA%E9%96%93%E4%B8%AD%E5%BF%83%E3%81%AE%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E6%80%9D%E8%80%83%E3%81%AE%E6%B4%BB%E7%94%A8">5-4. 人間中心のシステム思考の活用</a>
　<a href="#%E3%81%A4%E3%81%BE%E3%81%9A%E3%81%8D%E3%81%AE4%E3%81%A4%E3%81%AE%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3%E3%81%AE%E6%AD%A3%E4%BD%93">5-5. つまずきの4つのパターンの正体</a>
　<a href="#%E5%95%8F%E9%A1%8C%E7%99%BA%E6%8E%98%E3%81%AB%E6%9C%89%E7%9B%8A%E3%81%A0%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8">5-6. 問題発掘に有益だったこと</a>
<a href="#%E5%AE%9F%E8%B7%B5%E7%9A%84%E3%81%AA%E8%A7%A3%E6%B1%BA%E3%82%A2%E3%83%97%E3%83%AD%E3%83%BC%E3%83%81">6. 実践的な解決アプローチ</a>
　<a href="#%E4%B8%AD%E9%95%B7%E6%9C%9F%E3%81%A8%E7%9F%AD%E6%9C%9F%E3%81%AE%E4%B8%A1%E7%AB%8B">6-1. 中長期と短期の両立</a>
<a href="#%E3%81%8A%E3%82%8F%E3%82%8A%E3%81%AB%E6%9C%AC%E8%B3%AA%E7%9A%84%E3%81%AA%E4%BE%A1%E5%80%A4%E5%89%B5%E9%80%A0%E3%81%B8">7. おわりに：本質的な価値創造へ</a></p>
<hr>
<h2>メリークリスマス！2025年のAI活用推進を振り返って</h2>
<p>こんにちは！AI FirstグループでAI活用推進を担当しているShiori（<a href="https://x.com/shor_t8q">@shor_t8q</a>）です。企業のDX担当者、AI推進リーダー、そして「個人でのAI活用は進んでいるのに、事業や収益に関して期待した効果が出ていない」という課題を感じているすべての方へ、「組織のAI活用」と業務変革のヒントをお伝えできればと思います。</p>
<h2>個人活用の成功と組織活用の課題</h2>
<h3>個人活用普及率8割の達成</h3>
<p>2025年、私たちは「個人のAI活用」と「組織のAI活用」という両輪でAI活用を推進してきました。
まず社内の現状を共有します。社内AIツールのデータから、従業員のAI活用率は夏の時点で約8割に達しました。多くの社員がAIチャットツールやAIコーディングツールを日常的に活用し、個人の業務効率向上を実現することができました。</p>
<p>しかし、そこである気づきがありました。
「個人の効率は向上しているものの、組織としての成果が見えにくいのでは？」
そこで、組織的なAI活用推進をさらに強化することにしました。実際に取り組みを進める中で分かったのは、「組織的な活用」は「個人的な活用」とは異なる性質を持ち、異なるアプローチが必要だということです。</p>
<h3>組織的な活用とは</h3>
<p>組織的な活用とはどのような状態を指すのでしょうか。私たちはこう定義しました。</p>
<blockquote>
<p><strong>組織的なAI活用</strong>（KINTOテクノロジーズ社内資料より）</p>
<p>複数グループや部門などが連携し、組織横断プロジェクトで、業務・開発プロセス・システムにAIを導入し、事業や収益に対して何らかのインパクトを与えること。</p>
</blockquote>
<p>この定義に照らし合わせたとき、「組織的な活用が、想定以上に進まない」という現実に直面しました。</p>
<h3>業界全体の課題</h3>
<p>社外の動向を調べてみると、McKinseyの<a href="https://www.mckinsey.com/capabilities/quantumblack/our-insights/seizing-the-agentic-ai-advantage">Seizing the agentic AI advantage</a>に興味深いデータが掲載されていました。</p>
<blockquote>
<p><strong>特定のビジネス機能やプロセスに組み込まれた垂直的な活用事例の課題</strong>（McKinseyレポートより）</p>
<p><em>By contrast, vertical use cases—those embedded into specific business functions and processes—have seen limited scaling in most companies despite their higher potential for direct economic impact (Exhibit 2). Fewer than 10 percent of use cases deployed ever make it past the pilot stage, according to McKinsey research.</em></p>
<p><em>「対照的に、特定のビジネス機能やプロセスに組み込まれた垂直的な活用事例は、直接的な経済効果がより高い可能性があるにもかかわらず、ほとんどの企業では限定的なスケーリングしか見られていません。マッキンゼーの調査によると、展開された活用事例のうち、パイロット段階を超えるものは10%未満です。</em></p>
</blockquote>
<p><em>出典: McKinsey &quot;Seizing the agentic AI advantage&quot;</em></p>
<p>つまり、特定のビジネス機能やプロセスに組み込まれた垂直的な領域でAIを活用しようとしている企業の約9割が、パイロット段階（試験運用）を超えることができず、実証実験の段階で終わっている状況です。
これは「パイロット・パーガトリー（実証実験の煉獄）」と呼ばれる現象で、弊社だけの問題ではなく業界全体の傾向だったのです。</p>
<p>業界全体で「組織的なAI活用は容易ではない」という課題が報告されている状況です。私たちは、この課題に向き合うため、まずは社内の現状を分析することから始めました。</p>
<h2>イノベーションの二つの潮流</h2>
<h3>AIネイティブとAIドリブン</h3>
<p><img src="/assets/blog/authors/shiori/2025-12-21/ai_innovation_line.png" alt="AI Innovation Lines"></p>
<p>社内の取り組みを俯瞰してみたとき、大きく分けて2種類のイノベーションラインがあることが分かりました。</p>
<p><strong>AIネイティブ（AI Native）</strong></p>
<ul>
<li>AIがあることを前提として、ゼロからプロダクトや体験が設計されているもの</li>
<li>仕組みの中に自然にAIが組み込まれている</li>
</ul>
<p><strong>AIドリブン（AI Driven）</strong></p>
<ul>
<li>既存のシステムやプロダクトに、後からAIを組み込んでいくもの</li>
<li>既存業務や既存システムの一部をAIで代替・置換する取り組み</li>
</ul>
<p>ここで明らかになったのは、「AIネイティブな取り組みは比較的進んでいるが、AIドリブンは難航している」という傾向でした。
AIネイティブな領域では、社内のアーリーアダプターたちが相互にボトムアップで連携し、新しい価値を生み出してプロダクトやシステムをローンチすることが実現しています。一方、AIドリブン——つまり既存業務や既存システムの置き換え——は、関係者が多く抱えている問題も複雑で進めることが難しい現状があります。</p>
<h3>つまずきの4つのパターン</h3>
<p><img src="/assets/blog/authors/shiori/2025-12-21/trap_pattern.png" alt="4 Traps Pattern"></p>
<p>社内の状況を整理していくと、AI活用で進捗が滞っているケースは、おおむね4つのパターンに分類できることが見えてきました。</p>
<p><strong>1. 技術先行型（Tech First）</strong></p>
<p>「新しい技術が登場したので、まず検証してプロトタイプを作成した」というケース。技術力のあるエンジニアが速やかに作成できるのですが、その後で「このプロトタイプをどこで活用すべきか」と、解決すべき課題を後から探すことになります。</p>
<p><strong>2. トレンド先行型（Trend First）</strong></p>
<p>「AIエージェントを使って〇〇を実現したい」というケース。手段（AIトレンド）が目的化してしまい、誰のどのような課題を解決するのかが明確になっていません。</p>
<p><strong>3. 目的不明瞭型（Lost in Possibility）</strong></p>
<p>「AIを使って〇〇を実現したい。ただ、何をすればよいのか分からない」。意欲はあるものの、入口で立ち止まってしまっているケースです。</p>
<p><strong>4. 優先度判断困難型（Priority Paralysis）</strong></p>
<p>「AIを導入して解決したい課題はある。今の技術であればさまざまな箇所に適用できそうだ。ただ、何から手をつければよいか迷う」。適用すべき箇所の目利きはできているが、どこから着手すべきか判断できず、可能性（適用範囲の幅や深さ）の中で方向性を見いだせないパターンです。</p>
<p>さらに、これらとは別の軸で、「AI環境整備の課題」も存在しました。「AIを活用したいが、方針・ルールの整備がAIに最適化されていないので意思決定ができない」、「利用したいデータが集約されていない」といった、ガバナンス、インフラ、リソースの問題で進捗が滞るケースです。
※「AI環境整備の課題」は範囲が広いので、今回の記事ではこのテーマには言及しません。</p>
<p>上記パターンは、各々解決しなければプロジェクトは前に進みません。私は、組織として「AIプロジェクトの推進モデル」を構築する必要性を感じました。</p>
<h2>GenAIパラドックスの構造</h2>
<h3>効率向上と収益の乖離</h3>
<p>この1年、現場の状況を確認する中で<strong>GenAIパラドックス</strong>と呼ばれる現象を実感してきました。これは、「AIによって個人の業務効率は確実に向上しているのに、組織としての収益や成果には必ずしも直結していない」という状況です。</p>
<p>「GenAIパラドックス」について、McKinseyの<a href="https://www.mckinsey.com/capabilities/quantumblack/our-insights/seizing-the-agentic-ai-advantage">Seizing the agentic AI advantage</a>では、以下のように定義されています。</p>
<blockquote>
<p><strong>GenAIパラドックスとは？</strong>（McKinseyレポートより）</p>
<p><em>「10社中8社近くがGenAIを何らかの形で導入していますが、ほぼ同じ割合の企業が収益に大きな影響はないと報告しています。私たちはこれを『GenAIパラドックス』と呼んでいます。」</em></p>
<p><em>&quot;Nearly eight in ten companies have deployed gen AI in some form, but roughly the same percentage report no material impact on earnings. We call this the &#39;gen AI paradox.&#39;&quot;</em></p>
</blockquote>
<p><em>出典: McKinsey &quot;Seizing the agentic AI advantage&quot;</em></p>
<p>なぜ、このパラドックスが発生するのでしょうか。弊社の分析から、いくつかの要因が浮き彫りになりました。</p>
<ul>
<li><strong>取り組みの断片化</strong>: 個別の検証や単発の取り組みになっており、体系的に繋がっていない</li>
<li><strong>デリバリーモデルの未定義</strong>: 「どのように価値を届けるか」という型が確立されていない</li>
<li><strong>ガバナンスと整備不足</strong>: AIを受け入れるための基盤（ルール、インフラ、データ、プロセス）が整っていない</li>
</ul>
<p>McKinseyの<a href="https://www.mckinsey.com/capabilities/quantumblack/our-insights/seizing-the-agentic-ai-advantage">Seizing the agentic AI advantage</a>でも、同様の内容が言及されています。</p>
<h3>属人性という課題</h3>
<p><img src="/assets/blog/authors/shiori/2025-12-21/real_challenges.png" alt="Real Challenges"></p>
<p>さらに根本的な問題として、<strong>業務の言語化不足</strong>があります。</p>
<p>現在のAI技術、例えばAIエージェントを業務ワークフローに組み込もうとすると、そのワークフロー自体が明確に定義され、言語化されている必要があります。しかし、多くの現場では「ここはAさんの判断で」「ここはBさんの経験則で」といった、人間の判断（属人性）で運用されている部分が非常に多いのです。
「AIを導入したい」と考えても、AIは「暗黙知」を理解することができず、属人的になっている領域には、そのままではAIを適用できません。</p>
<p>まず業務を整理し、定義し直す必要があります。ここに至る組織的認知、具体的なアクションプランをステークホルダーで合意形成し、着実に進めていかなければいけないということが、AI活用の大きなハードルとなっていたのです。</p>
<h3>視点の転換</h3>
<p>そこで、私は
「AIを導入したからといって、イノベーションが加速するわけではない。イノベーションを加速させるために、AIを活用するのだ」という視点の転換が必要なのだと考えるようになりました。</p>
<h2>本質的な問いの立て直し</h2>
<h3>汎用AIツールの特性</h3>
<p><img src="/assets/blog/authors/shiori/2025-12-21/ai_bias.png" alt="AI Bias"></p>
<p>ChatGPT、Gemini、Claudeのような、いわゆる「汎用的なAIチャット」は非常に高性能です。相談すれば、速やかに質の高い解決策を提示してくれます。</p>
<p>しかし、ここに注意すべき点があります。AIは、私たちが投げかけた質問には答えてくれますが、「デフォルトのチャットウィンドウでは、私たちが抱える問題の深掘りまでは行わない」のです。
ただし、これは汎用AIチャットをデフォルト設定のままで使用した場合です。深掘り機能を持つAIエージェントを構築し、そのAIと対話することで、問題を探索する質問を引き出し、課題を整理できることは既に実証済みです。</p>
<p>ユーザーが「これを作りたい」と伝えたとき、AIは素直に作成方法を提案します。しかし本来は、「なぜそれを作りたいのですか？」「もっと本質的な課題は別にありませんか？」といった、コンサルタントのような深掘りこそが必要な場面が多々あります。</p>
<h3>技術導入前の課題整理</h3>
<p>「できる・できない」ではなく「誰に・どのような価値か」</p>
<p>例えば、「社内のお問い合わせの対応が大変なので、お問い合わせをAIで対応できるようにしたい」という相談が現場から寄せられたとします。これに対して、すぐに「RAG（検索拡張生成）を使って社内Wikiを読み込ませたチャットボットを構築しましょう」と提案するのは容易です。しかし、それでは本質的な解決にならないことがあります。</p>
<p>ここで私たちは問い直す必要があります。</p>
<ul>
<li>「そもそも、なぜお問い合わせが発生するのか？」</li>
<li>「マニュアルが分散していて知りたい情報にアクセスできないことが原因ではないのか？」</li>
<li>「あるいは、〇〇についてマニュアルはあるが、従業員が判断しにくい内容になっていることが原因ではないか？」</li>
</ul>
<p>もし後者であれば、AIに回答させる前に、マニュアルの内容を更新するのが適切かもしれません。マニュアル精度が高くないと、AIがハルシネーション（誤った情報）を生成するリスクが高まります。</p>
<h3>再現性が高い既存フレームワークの活用</h3>
<p>「問題の特定」、「課題定義」と「合意形成」を進める上で、私たちがAIプロジェクトにおける課題整理で採用したアプローチは、「デザイン思考」や「システム思考」などの世の中で既存の課題解決フレームワークです。</p>
<p><strong>デザイン思考</strong>（Design Thinking）は、ユーザーへの共感をベースにした人間中心アプローチです。ユーザーニーズを深く理解し、反復的なプロトタイピングを通じて創造的な解決策を生み出します。
一方、<strong>システム思考</strong>（Systems Thinking）は、要素間の相互関係と全体最適を重視し、複雑な組織システムの根本原因を特定します。
デザイン思考とシステム思考を組み合わせることで、個々のステークホルダーの課題（デザイン思考）と、それらが生じている業務や組織全体の構造的問題（システム思考）の両方に目を向けられます。この融合は、関係者の認識を合わせるための有効な手法と成り得るという仮説を持ちました。</p>
<h3>人間中心のシステム思考の活用</h3>
<p><img src="/assets/blog/authors/shiori/2025-12-21/design_and_system_thinking.png" alt="Design and System Thinking"></p>
<p>私たちは複数のプロジェクトにおいて、<strong>デザイン思考</strong>と<strong>システム思考</strong>を組み合わせた「人間中心のシステム思考」というアプローチを導入しました。これは、ユーザーへの共感をベースに関係者と信頼関係を築きながら進める創造的なプロセスとシステム全体を俯瞰し、問題構造を把握する分析的な視点を融合させたものです。</p>
<p>このアプローチにより、以下のことが可能になります：</p>
<ul>
<li><strong>視点の切り替え</strong>: システム全体と個々のステークホルダー、両方の視点を行き来できる</li>
<li><strong>共感の醸成</strong>: 人々の課題だけでなく、システム自体が抱える構造的な問題にも目を向けられる</li>
<li><strong>行動変容の促進</strong>: 根本原因を理解した上で、効果的な介入ポイントを設計できる</li>
</ul>
<p>組織におけるAI活用の課題は、まさに「人間システム」の問題です。複数のステークホルダーが絡み合い、それぞれが異なるニーズや制約を抱えています。人間中心のシステム思考は、この複雑性に対処するための有効な課題解決手段となりました。</p>
<p><strong>デザイン思考</strong>と<strong>システム思考</strong>の違いを知りたい方は、<a href="https://www.ideou.com/blogs/inspiration/differences-between-systems-thinking-and-design-thinking">Systems Thinking vs Design Thinking, What’s the Difference?</a>の記事が参考になります。</p>
<h3>つまずきの4つのパターンの正体</h3>
<p>人間中心のシステム思考を用いた課題解決では、以下のステップで進めることが効果的です。</p>
<p><img src="/assets/blog/authors/shiori/2025-12-21/8steps.png" alt="8 Steps Process"></p>
<ol>
<li><strong>ユーザー・ステークホルダーへの共感（Empathize with Stakeholders）</strong>: 関係者へのヒアリングや観察を通じて、現場の状況を深く理解する</li>
<li><strong>問題の発掘（Discover Problems）</strong>: 顕在化している問題だけでなく、対話を通じて潜在的な問題も引き出す</li>
<li><strong>システム構造の分析（Analyze System Structure）</strong>: システム思考を用いて、問題の真因や要素間の関係性を特定する</li>
<li><strong>課題の定義（Define Issues）</strong>: 取り組むべき課題を整理し、優先順位をつける</li>
<li><strong>解決策の検討（Design Solutions）</strong>: 課題に対する解決アプローチを設計する</li>
<li><strong>技術選定（Select Technology）</strong>: AI、インフラ、ツールなど最適な技術スタックを選定する</li>
<li><strong>プロトタイプ構築（Build Prototype）</strong>: 小さく素早く動くものを作り、仮説を検証する</li>
<li><strong>反復と拡大（Iterate &amp; Scale）</strong>: アジャイル開発プロセスに基づき、フィードバックを取り入れながら改善・拡張を繰り返す</li>
</ol>
<p>つまずきの4つのパターンは、6（技術選定）や7（プロトタイプ構築）からスタートして行き詰まり、1（ユーザー・ステークホルダーへの共感）~5（解決策の検討）を考え始めるというプロセスになっていることがわかりました。</p>
<p><img src="/assets/blog/authors/shiori/2025-12-21/trap_cause.png" alt="4 Traps Pattern"></p>
<p>AIによってプロトタイプを構築しやすくなった反面、従来のプロダクト開発で定義していた1（ユーザー・ステークホルダーへの共感）〜5（解決策の検討）がスキップされる傾向があるため、「システムは構築できたが、誰かの具体的な問題を解決しているわけではない」という状況が見えてきました。</p>
<p>※社内では、6（技術選定）や7（プロトタイプ構築）からスタートし、1（ユーザー・ステークホルダーへの共感）〜5（解決策の検討）を経て、成功しているケースも複数あるので、6（技術選定）や7（プロトタイプ構築）からスタートするプロトタイプドリブンのアプローチも有効だと実証されています。そのため、6（技術選定）や7（プロトタイプ構築）からスタートすることがネガティブな要素というわけではありません。あくまで傾向の話です。</p>
<p>しかし、6（技術選定）や7（プロトタイプ構築）からスタートすることで、「採用した技術や構築したプロトタイプを活かしたい」という潜在的なバイアスが先行し、1（ユーザー・ステークホルダーへの共感）〜5（解決策の検討）のプロセスを、後から中立的に進めることが難しくなることに留意する必要があります。</p>
<h3>問題発掘に有益だったこと</h3>
<p><img src="/assets/blog/authors/shiori/2025-12-21/face_to_face.png" alt="Face to Face Communication"></p>
<p>フレームワークを導入したからといって、すぐに問題解決がスムーズになるわけではありません。まずは、関係者とシステム全体の課題整理を行う必要がありました。最初にワークショップを行い、ステークホルダーが抱える問題をデジタルホワイトボードで整理しようと試みたのですが、想定以上に難航しました。
原因を分析すると、関係するステークホルダーが多く、問題を引き起こしている要素が複数あるような複雑性が高い問題は、言語化のハードルが高いため、ホワイトボード上に網羅的に問題を書き出すことが難しいということがわかりました。</p>
<p>そこで、ホワイトボード形式ではなく、2on2や1on2でヒアリングを実施し、対話を通して潜在課題まで発掘できるようなコミュニケーションアプローチを取りました。
結果として、グループや部署を横断するような業務改革を行うときは、最初は対話を通じて信頼関係を築くことが重要であり、次に対話を通して問題発掘を行い、関係者の問題解像度が上がってきた時点で、適宜ワークショップを実施して課題を整理することが効果的だということがわかりました。</p>
<h2>実践的な解決アプローチ</h2>
<h3>中長期と短期の両立</h3>
<p><img src="/assets/blog/authors/shiori/2025-12-21/strategy.png" alt="Strategy"></p>
<p>とはいえ、「業務フローや課題を全て整理してからAIを導入しましょう」と言っていたら、プロジェクトの推進が遅れてしまいます。そこで私たちは、「中長期」と「短期」を分けて、並行して進める戦略を推奨しています。</p>
<table>
<thead>
<tr>
<th>アプローチ</th>
<th>目的</th>
<th>アクション</th>
</tr>
</thead>
<tbody><tr>
<td>中長期（Slow）</td>
<td>本質的な課題解決、方針策定</td>
<td>組織全体の課題整理、ワークフローの再定義、関係者との合意形成</td>
</tr>
<tr>
<td>短期（Fast）</td>
<td>スピード感、現場の改善</td>
<td>目の前の明確な課題に対して、AI技術を適用し、プロトタイプを作成し、改善を繰り返す</td>
</tr>
</tbody></table>
<p>全体の方針を策定しつつ（中長期）、現場では迅速に改善を回していく（短期）。この両軸で進めることで、スピード感を維持しながら、点と点を線に繋げていくことができます。</p>
<h2>おわりに：本質的な価値創造へ</h2>
<p>最後に、2025年の1年で得た最も重要な学びを共有します。</p>
<p>「AIを活用すること自体を、目的化してはいけない。」</p>
<p>「AIで何ができるか」よりも、「私たちが本当に解決すべき問題は何か」「それを解決したとき、誰にどのような価値があるのか」——この原点に立ち返ること。そして、その価値を最大化するために、AIの<strong>人間の能力を拡張する力</strong>や<strong>処理速度を向上させる力</strong>や<strong>数や量を出す力</strong>をどう活用するかを考えることが重要だと気づきました。</p>
<p>AI導入は、単なるツールの活用ではありません。それは、私たちが自分たちの仕事のやり方、ひいては<strong>本質的な価値</strong>を見つめ直す過程でもあります。組織的AI活用のハードルが「自分たちの業務の未定義さ」や「目的の曖昧さ」や「問題の真因を見極める力」にあると認識できれば、乗り越える方法は必ず見つかります。特にAI活用の問題は技術ではなく、業務、人と組織、マインド、仕組みやルールにあるケースがほとんどです。</p>
<p>今回の考察が、同じ課題に取り組む皆様の参考になれば幸いです。
2025年は技術革新の激動の中、皆様大変お疲れ様でした！</p>
<p>2026年もさらに未知な技術や概念が現れるかもしれませんが、共に頑張りましょう！</p>
<p>Made with Drive♥️</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/shiori/2025-12-21/cover_breaking-through-ai-paradox.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[発信するということ]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-21-learning-in-public/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-21-learning-in-public/</guid>
            <pubDate>Sun, 21 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[技術者として、技術者集団と関わる社員として、「発信すること」について考えたことを綴りました。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> と<a href="https://qiita.com/advent-calendar/2025/tech_pr">技術広報 Advent Calendar 2025</a>の21日目の記事です🎅🎄</p>
<h1>何の記事？</h1>
<p>本記事は、私が技術広報として働く中で「発信すること」について発見したこと、感じたこと、ある考えに至ったことをまとめたものです。
端的に結論を書くと「発信しよう！」ということなのですが、そこに思い至った経緯なども合わせて読んでいただければ嬉しいです。</p>
<h1>想定読者</h1>
<ul>
<li>エンジニアの（特に発信が苦手、嫌い、やったことない、という）方</li>
<li>発信を呼び掛けてる技術広報の方</li>
<li>テック企業に勤めている非エンジニアの方</li>
<li>SNSや社内ブログで発信をめちゃくちゃやっている方（レビュアーとして意見もらえたら嬉しいです）</li>
</ul>
<h1>簡単に自己紹介</h1>
<p>はじめまして。Bambooこと竹中（<a href="https://x.com/shell_in_bamboo">@shell_in_bamboo</a>）です。
7月にKINTOテクノロジーズに入社し、Fukuoka Tech Labの立ち上げと技術広報グループのマネージャーを兼任しています。</p>
<p>新卒で12年ほど独立系のSIerでエンジニアやPMを経験し、前職では福岡の金融機関の内製開発組織でスクラムマスターをやっていました。スクラムマスターってなにするの？とよく聞かれるのですが、「組織やチームのことを考えてる人」と最近では答えてます。あんまりよくわからねーですね。笑</p>
<h1>マインドチェンジ</h1>
<p>さて、そんな私がKINTOテクノロジーズに入社して技術広報を担うことになったのですが、それまでは発信することに積極的ではありませんでした。</p>
<h2>ちょっと前までの考え方</h2>
<p>自己紹介で書いたとおり、前職までは組織やチームのことを考えて働いていたので「外に発信してる余裕なんてない！」という感じでした。イベントやセミナーで情報を仕入れることはあっても、自分から情報発信はほとんど行っていませんでした。
そういう役割をやっている人もいたし、向いている人がやるんだろうな～がんばれ～ありがとう～という考え方でした。</p>
<h2>考え方がかわったタイミング</h2>
<p>ハッ！
・・・としたタイミングはなかったです。どちらかというと、技術広報って何だろう？どうあるべきだろう？と調べたり考える中で徐々に変わっていった感覚です。</p>
<p>しかし、エンジニア時代の経験や、スクラムマスターとしての考え方が「発信すること」に確かにつながった感覚があります。</p>
<h2>で、どう変わったの？</h2>
<p>「間違ってた！発信はやれたらやるものでもなく、やれる人がやるものでもなく、自分も含めみんながやった方がいい！」
です。</p>
<p>暑苦しいですね。一気に離脱者が増えそうです（技術広報としてビュー数を意識する私偉い）。</p>
<h2>なぜそう思ったのか？</h2>
<p>1つ目は、エンジニア時代にたくさんの技術記事やブログに助けられた経験を思い出したからです。
何も技術がわからない新人時代、ある程度開発ができるようになってきたとき、PMとしてプロジェクトを管理していたとき・・・どんなときでも検索して出てくる先達や同士の情報に救われていました。これは、「発信してくれる」人がいなければ得られないものでした。</p>
<p>それにも関わらず、私自身は発信をほとんどせず、恩を返すこともできていなかったわけです。</p>
<p>2つ目は、スクラムマスターとして自分がアジャイルな行動をできていないことに気づいたからです。
簡潔に言うと「早くリリースして早く検証する」という考え方があるのですが、私は「早く」どころかリリース自体をしていなかったわけです。自分で良いと思ったものも、逆に良くないと避けてるものも、外に出してみないと正しいのかどうかわかりません。</p>
<p>私が独りよがりで信じたものをチームや組織の誰が受け入れるでしょうか。間違っていても、失敗しても、発信をして、フィードバックをもらうべきだったのです。</p>
<h1>Learning in Public</h1>
<p>このブログを書くにあたり調べていると、&quot;Learning in Public&quot; という言葉が海外にはあることを知りました。
自分の学びの途中経過を公開しながら成長していく姿勢・文化のことを言うらしいです。</p>
<p>Learning in Publicの効果としては、以下のようなものが挙げられています。</p>
<ul>
<li>学びの定着が圧倒的に早くなる。<ul>
<li>人に伝える、教えることは理解をより深めることにつながります。説明する力もつきますね。</li>
</ul>
</li>
<li>同じことで悩む誰かの助けになる。<ul>
<li>失敗は悪いことではありません。誰かが失敗をシェアすることで同じ轍を踏む同士を救えます。エンジニアリングの現場では「自分の環境だけで起きているわけではない」という情報は重要なヒントにもなり得ます。</li>
</ul>
</li>
<li>フィードバックがもらえる。<ul>
<li>「自分は未熟だから」と発信を戸惑いますよね。けれど、未熟だからこそ発信をしてフィードバックを得て成長するチャンスを掴む、と前向きにとらえることができます。</li>
</ul>
</li>
</ul>
<p>他にもありますが、いくつかピックアップしました。この考え方は、まさに私が技術広報として今考えに至ったものでした。</p>
<h1>アクションプラン</h1>
<p>格好つけようとしてこんな見出し付けたらAI感出ますね。。安心してください、(手で)書いてますよ。笑</p>
<p>さてさて、どれだけマインドが変わっても、具体的な行動に移すのは難しいものです。かくいう私も、この投稿が今の会社に入って初投稿です。上司には秘密ですよ。（バレてる）</p>
<p>「いつだったらブログ書けるかな？？」
「仕事が落ち着きそうなときにイベント登壇しようかな・・・」</p>
<p>こんな感じで時期を見計らっていてはいつまで経っても先に進めません。目の前の仕事、割り込みの依頼などなどは、なかなか途切れることはないですからね(´・ω・｀)</p>
<p>では、どうすればいいんでしょう。</p>
<h2>発信する日を勢いで適当に決める</h2>
<p>勢い？適当？そんなことでいいのか！
と、思われたでしょうか？</p>
<p>いいんです。
そんな真面目なあなたは、きっと決めた期限までに完成させるでしょう。笑</p>
<p>実はこれは「仕事の量は、完成のために与えられた時間を全て満たすまで膨張する」というパーキンソンの第一法則というものを逆手に取っています。</p>
<p>期限を先に決めることで、人はその期限に向かって頑張るわけですね。プロジェクトで先にリリース日を決めるのもこんな効果があったりします。</p>
<p>日付は、自分の中だけで決めるのではなく、ある程度強制力を持たせた方が効果的です。アドカレやイベントに登録してしまうのがわかりやすいと思います。</p>
<p>できるかどうかはさておき、まず、記念すべき発信の日を決めてみてください！</p>
<h2>発信するペースを決める</h2>
<p>2つめの方法は、ペースを決めることです。
ペースを維持することで人はそれに慣れてきます。</p>
<p>余談ですが、スクラムのスプリントも、一定のペースを保つことが推奨<a href="%E3%82%B9%E3%82%AF%E3%83%A9%E3%83%A0%E3%82%AC%E3%82%A4%E3%83%892020%E3%81%A7%E3%81%AF%E3%80%8C%E4%B8%80%E8%B2%AB%E6%80%A7%E3%82%92%E4%BF%9D%E3%81%A4%E3%81%9F%E3%82%81%E3%80%8D%E3%81%A8%E8%A8%98%E8%BC%89%E3%81%95%E3%82%8C%E3%81%A6%E3%81%84%E3%82%8B%E3%81%AE%E3%81%BF%E3%80%82%E3%82%B9%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88%E6%9C%9F%E9%96%93%E3%82%92%E5%A4%89%E6%9B%B4%E3%81%97%E3%81%A6%E3%81%AF%E3%81%84%E3%81%91%E3%81%AA%E3%81%84%E3%82%8F%E3%81%91%E3%81%A7%E3%81%AF%E3%81%AA%E3%81%84%E3%80%82">^1</a>されています。</p>
<p>1ヶ月に1本ブログ書く！
とかだと、月初になったり月末になったりペースが狂うので、日単位ぐらいで固定化したほうがいいかなと思います。</p>
<h1>おわりに</h1>
<p>ここまでお付き合いいただきありがとうございました。
こんなことを書いておきながら、まだまだ筆不精な面はありますし、億劫な気持ちも残ってはいます。</p>
<p>けれど、せっかくだから自分をアップデートしようと前向きな気持ちの方が大きいです。何よりも、少しでも世の中に還元できるようになれたら嬉しいというワクワク感があります。</p>
<p>次の記事が1年後とかにならないように頑張ります。笑
ではでは～</p>
<hr>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[TipKit外部から表示状態をコントロールする]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-21_tipkit/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-21_tipkit/</guid>
            <pubDate>Sun, 21 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[TipKit外部から表示状態をコントロールする]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の21日目の記事です🎅🎄</p>
<hr>
<p>KINTOテクノロジーズで my route for iOS を開発しているRyommです。
最近、my routeアプリ内にTipKitを導入しました。その際TipKitで外部から表示状態をコントロールする方法が少し癖があったので、紹介します。</p>
<p><img src="/assets/blog/authors/ryomm/2025-12-21/01.png" alt="実際のTipKit使用箇所">
<em>実際のTipKit使用箇所</em></p>
<h2>背景</h2>
<p>TipKitとは、iOS17以降で使えるようになった、アプリの機能を見つけるのに役立つヒントを出すためのフレームワークです。
<a href="https://developer.apple.com/documentation/tipkit/">https://developer.apple.com/documentation/tipkit/</a></p>
<p>TipKitを使うと、手軽に吹き出しのポップアップなどを出せたり、TipKit内で表示状態のコントロールを行えたりします。
しかし、TipKit内で全てが完結するということは、Viewとロジックが密接に絡み合ってしまうということでもあります。これでは既存のアーキテクチャと齟齬が生じてしまいます。
また、my routeでは以前からTipKitを使わずに似たような表示コントロールを行なっていたため、既存のロジックをTipKitに持ってこようとすると一度全てのユーザーの表示状況がリセットされてしまう問題もありました。
そこで、UserDefaultなどに保存してある既存の状態を維持したまま、どうにかTipKitに外部から表示状態を差し込みたいと考えました。</p>
<h3>TipKitの基本の使い方</h3>
<p>TipKitは基本的に、以下のように書いて使います。TipViewStyleを使ってスタイルをカスタマイズすることもできます。</p>
<pre><code class="language-swift">import TipKit

struct FavoriteLandmarkTip: Tip {
    // 表示文言
    var title: Text { Text(&quot;Save as a Favorite&quot;) }

    // 表示状態の条件を制御するマクロ
    @Parameter static var userIsLoggedIn: Bool = false

    var rules: [Rule] {
        #Rule(Self.$userIsLoggedIn) {
            // ログイン状態ならTipを表示
            $0 == true
        }
    }
}
</code></pre>
<p>TipKit内にユーザーアクションを定義して追跡することもできます。</p>
<pre><code class="language-swift">struct FavoriteLandmarkTip: Tip {
    // 追跡するユーザーアクションを一意のIDを持つイベントとして定義
    static let didViewLandmark: Event = Event(id: &quot;didViewLandmark&quot;)

    var rules: [Rule] {
        #Rule(Self.didViewLandmark) {
            // イベントが3回以上発生したらTipを表示
            $0.donations.count &gt; 3
        }
    }
}
</code></pre>
<p>このように、TipKit側で独自に表示状態の条件を管理しています。
しかしこのままでは上述の通りに問題があります。</p>
<h2>TipKitに外部から表示状態を差し込めるようにする</h2>
<p>@Parameter マクロを利用して、表示状態を差し込めるようにします。
@Parameterでラップされた値が変更されると、依存するTipルールが再評価されます。
そこで、外部の値を @Parameter を経由して変更してあげることで、TipKitの状態をコントロールすることができます。</p>
<p><a href="https://developer.apple.com/documentation/tipkit/tips/parameter">https://developer.apple.com/documentation/tipkit/tips/parameter</a></p>
<p>まず、TipKit自体は以下のように定義します。ここでは呼び出し側のViewに定義してある @Parameter をみてTipの表示有無を判断しています。</p>
<pre><code class="language-swift">import TipKit
struct MemoTip: Tip {
    var title: Text {
        Text(&quot;気に入った記事のURLはこちらからおでかけメモに追加ができます&quot;)
    }

    var rules: [Rule] {
        #Rule(ParentView.$isPresentingMemoTip) {
            $0 == true
        }
    }
}
</code></pre>
<p>外部から値を受け取るためのパラメータとは別に、TipKit用に @Paramter にラップしたパラメータを用意します。
<code>onAppear</code>と<code>onChange</code>によって2つのパラメータが同期するようにしています。</p>
<pre><code class="language-swift">struct ParentView: View {
    @Binding var showTip: Bool // 外部から値を受け取るパラメータ。本当はもっと構造体になってたりObservedObjectだったりする。

    @Parameter static var isPresentingMemoTip: Bool = false // TipKit用に外部の値をラップするパラメータ
    var memoTip = MemoTip()

    var body: some View {
        SomeView()
        .safeAreaInset(edge: .bottom) {
            memoButton
              .popoverTip(memoTip, arrowEdge: .bottom) // チップを出す場所
              .onAppear {
                  // Viewがつくられた最初はonChangeが動かないので、初期値を挿入
                  ParentView.isPresentingMemoTip = showTip
              }
              .onChange(of: showTip) { _, newValue in
                  // 外部から変更があったら、@Parameterマクロに値を挿入
                  ParentView.isPresentingMemoTip = newValue
              }
        }
    }
}
</code></pre>
<p>こうすることで、ロジックをViewから切り離せるようになりました。
ViewModelなどで表示状態の条件のロジックを書いて、その状態をshowTipに渡してあげればTipKit側の状態も更新されます。いい感じです。</p>
<h2>まとめ</h2>
<p>TipKitの表示状態を外部からコントロールできるようにする方法を紹介しました。
TipKitはiOS17から利用できるので、これから利用するケースも徐々に増えていくと思います。
完全に新規の機能として作成するのであればTipKitで完結させる方が良いとは思いますが、既存のものを移行する場合は困るシーンもあるので、この記事が役立つと良いなと思います。
それでは、メリークリスマス🎄</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ryomm/2025-12-21/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AIエージェントでSOC監視業務を半自動化した話 〜AI活用の理想と現実〜]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-20-ai-agent-soc-monitoring-automation/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-20-ai-agent-soc-monitoring-automation/</guid>
            <pubDate>Sat, 20 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[AI エージェントと Splunk MCP を活用して SOC 監視業務を半自動化し、作業時間を約半分に短縮した取り組みを紹介。試行錯誤の中で学んだ「手順書駆動プロンプト開発」の重要性や、効率化以外の副次的効果についても共有する。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の20日目の記事です🎅🎄</p>
<h2><strong>1. はじめに</strong></h2>
<p>こんにちは！ セキュリティ・プライバシー部でサイバーセキュリティに関する業務を担当している、<a href="https://blog.kinto-technologies.com/posts/2025-02-26-newcomers-introduction-oct-nov/#%E3%81%9F%E3%81%AA%E3%81%A1%E3%82%85%E3%83%BC">たなちゅー</a>です！</p>
<p>今回は、AI エージェントを活用して SOC 監視業務を半自動化し、<strong>作業時間を約半分に短縮</strong>した取り組みについてお話しします。</p>
<p>「AI エージェントを使えばすぐできそう！」と思って始めましたが、実際に取り組みを進めてみると試行錯誤の連続で、<strong>想定外の落とし穴</strong>や<strong>失敗</strong>がたくさんありました。この記事では、うまくいったことだけでなく、<strong>失敗からの学び</strong>も含めて共有したいと思います。</p>
<h2><strong>2. 背景：SOC 業務の課題</strong></h2>
<h3><strong>SOCとは？</strong></h3>
<p>SOC（Security Operation Center）は、組織の IT 環境を監視し、サイバー攻撃や不正アクセスなどのセキュリティ脅威を検知・分析・対処する役割を担います。</p>
<p>主な業務内容は以下の通りです。</p>
<ul>
<li><strong>ログ・アラート監視</strong>：システムのログやアラートを監視し、異常を検知</li>
<li><strong>脅威分析</strong>：検知したアラートが本当の脅威かどうかを分析・判断</li>
<li><strong>インシデント対応</strong>：セキュリティインシデント発生時の初動対応や調査</li>
<li><strong>レポーティング</strong>：監視結果や対応状況を関係者に報告</li>
</ul>
<p>いわば、組織のサイバーセキュリティにおける「最前線」です。</p>
<h3><strong>監視業務の課題</strong></h3>
<p>ログ・アラート監視の業務には、リアルタイムのアラート監視と定期的なログ監視・分析があります。今回、半自動化の対象としたのは後者の定期的なログ監視です。</p>
<p>定期的なログ監視では、SIEM（Splunk Cloud）を使い、以下の作業を手動で行っていました。</p>
<ul>
<li>ダッシュボード上のイベント内容を確認</li>
<li>深掘り調査のための SPL クエリを実行</li>
<li>セキュリティインシデントか否かを判断</li>
<li>調査結果のレポートを作成</li>
</ul>
<p>この手動運用は、1回あたり2〜3時間の作業時間がかかり、大きな業務負担になっていました。また、ログ解析スキルや SPL の知識が必要なため、特定の担当者に知識やノウハウが集中し、属人化が進んでいるという課題もありました。</p>
<p>これらの課題を解決するため、AI エージェント（OpenAI Codex IDE）と <a href="https://www.splunk.com/ja_jp/blog/artificial-intelligence/unlock-the-power-of-splunk-cloud-platform-with-the-mcp-server.html">Splunk MCP</a> を活用した半自動化に取り組みました。</p>
<h3><strong>使用したツール</strong></h3>
<ul>
<li><strong>MCP クライアント</strong><ul>
<li>OpenAI Codex IDE：OpenAI の AI エージェント「Codex」の VSCode 拡張機能。MCP サーバーと連携し、プロンプトに基づいた処理を実行</li>
<li>モデルは「GPT-5.1-Codex-Max」、推論は「High」に設定</li>
</ul>
</li>
<li><strong>MCP サーバー</strong><ul>
<li>Splunk MCP：Splunk Cloud へのログ検索を実行</li>
</ul>
</li>
<li><strong>その他</strong><ul>
<li>Splunk Cloud：システムのログ集約・分析基盤（SIEM）</li>
<li>Github：メンバー間でのプロンプト共有基盤</li>
</ul>
</li>
</ul>
<h2><strong>3. 実現したこと</strong></h2>
<p>結論から言うと、この取り組みにより監視業務の作業時間を2〜3時間から1〜1.5時間へ、約半分に短縮できました。ここからは、Before/After の運用フローを比較しながら、具体的に何が変わったのかを紹介します。</p>
<h3><strong>Before：半自動化前の運用フロー</strong></h3>
<p>半自動化以前は、以下の流れで定期的な監視業務を手動で実施しており、1回あたり2〜3時間を要していました。特にステップ1「一次調査」とステップ2「深掘り調査の要否判断」はログ解析スキルや SPL の知識が必要なため、属人化が課題となっていました。</p>
<ol>
<li><strong>一次調査を実施</strong><ul>
<li>ダッシュボードに表示された各イベントごとに、Splunk Cloud を使用して以下のような一次調査を実施<ul>
<li>対象イベント前後のタイムラインを確認</li>
<li>対象イベントの調査の軸となる情報を基に異常性の有無を確認</li>
</ul>
</li>
<li>一次調査結果を各イベントごとに整理</li>
</ul>
</li>
<li><strong>深掘り調査の要否判断</strong><ul>
<li>整理した情報を基に、深掘り調査の必要性を判断</li>
</ul>
</li>
<li><strong>深掘り調査の実施</strong><ul>
<li>ヒアリング</li>
<li>SIEM 外の情報収集（チャット内容、社内ナレッジなど）</li>
</ul>
</li>
<li><strong>セキュリティインシデントか否かの判断</strong><ul>
<li>深掘り調査の結果を基に、セキュリティインシデントかどうかを判断</li>
</ul>
</li>
<li><strong>セキュリティインシデント対応</strong><ul>
<li>関係者と連携し、セキュリティインシデント対応を実施</li>
</ul>
</li>
</ol>
<h3><strong>After：半自動化後の運用フロー</strong></h3>
<p>AI エージェントがステップ1・2を担当し、人間は品質チェックと最終判断に集中する形としました。これにより作業時間は約半分（1〜1.5時間）に短縮。さらに、属人化していた監視業務の標準化も進み、チームとして運用できる体制が整いました。</p>
<ol>
<li><strong>一次調査を実施（AI エージェント）</strong><ul>
<li>Splunk MCP でダッシュボード相当の検索を実行し、各イベントごとに、以下のような一次調査を実施<ul>
<li>対象イベント前後のタイムラインを確認</li>
<li>対象イベントの調査の軸となる情報を基に異常性の有無を確認</li>
</ul>
</li>
<li>一次調査結果を各イベントごとに整理</li>
</ul>
</li>
<li><strong>深掘り調査の一次要否判断（AI エージェント）</strong><ul>
<li>整理した情報を基に、深掘り調査の必要性を一次判断</li>
<li>一次調査結果と判断内容を整理したレポートを作成</li>
</ul>
</li>
<li><strong>レポートの品質チェック・深掘り要否判断（人間）</strong><ul>
<li>AI エージェントが作成したレポートの品質を人間がチェック</li>
<li>品質チェックと合わせて、深掘り調査の必要性を人間が判断</li>
</ul>
</li>
<li><strong>深掘り調査の実施（人間）</strong><ul>
<li>ヒアリング</li>
<li>SIEM 外の情報収集（チャット内容、社内ナレッジなど）</li>
</ul>
</li>
<li><strong>セキュリティインシデントか否かの判断（人間）</strong><ul>
<li>深掘り調査の結果を基に、セキュリティインシデントかどうかを判断</li>
</ul>
</li>
<li><strong>セキュリティインシデント対応（人間）</strong><ul>
<li>関係者と連携し、セキュリティインシデント対応を実施</li>
</ul>
</li>
</ol>
<p>Before/After の運用フローを図で比較すると、このような形です。</p>
<p><img src="/assets/blog/authors/tanachu/2025-12-20-ai-agent-soc-monitoring-automation/01_before_after_soc_operation.png" alt="01_before_after_soc_operation.png"> <em>Before/After の運用フローの比較図</em></p>
<h3><strong>具体例：半自動化後の運用フロー</strong></h3>
<p>では、実際にどのように監視業務を進めているのか、具体的な流れを紹介します。前述の6ステップを、実務の観点から5つの作業に整理しています。</p>
<ol>
<li><strong>監視用プロンプトを準備</strong>：GitHub リポジトリから最新の監視用プロンプト（md ファイル）を取得</li>
</ol>
<p><img src="/assets/blog/authors/tanachu/2025-12-20-ai-agent-soc-monitoring-automation/02_cloned-soc_monitoring-prompt.png" alt="02_cloned-soc_monitoring-prompt.png"> <em>クローンした監視用プロンプト</em></p>
<ol start="2">
<li><strong>Codex で一次調査（深掘り調査含む）を実行</strong>：プロンプトファイルを指定して Codex に処理を実行<ul>
<li>指示例：xxxxx/prompts/yyyyy.md のタスクを実行してください</li>
<li>処理時間：1プロンプトあたり10〜20分程度</li>
</ul>
</li>
</ol>
<p><img src="/assets/blog/authors/tanachu/2025-12-20-ai-agent-soc-monitoring-automation/03_codex-inst.png" alt="03_codex-inst.png"> <em>Codexへの指示</em></p>
<ol start="3">
<li><strong>レポートの品質チェック・深掘り要否を判断</strong>：AI エージェントが作成したレポートを確認し、深掘り調査の必要性を判断<ul>
<li>ログ件数や日時、調査内容の妥当性をチェック</li>
<li>品質チェック用ダッシュボードを作成し、チェック作業を効率化</li>
</ul>
</li>
</ol>
<p><img src="/assets/blog/authors/tanachu/2025-12-20-ai-agent-soc-monitoring-automation/04_ai-agent-monitoring-report.png" alt="04_ai-agent-monitoring-report.png"> <em>AI エージェントによる監視レポート</em></p>
<p><img src="/assets/blog/authors/tanachu/2025-12-20-ai-agent-soc-monitoring-automation/05_quality-check-dashboard.png" alt="05_quality-check-dashboard.png"> <em>品質チェック用ダッシュボード</em></p>
<ol start="4">
<li><p><strong>深掘り調査を実施</strong>：必要に応じて追加調査を行い、セキュリティインシデントか否かを判断</p>
<ul>
<li>Splunk Cloud で手動検索</li>
<li>Codex + Splunk MCP を活用した追加調査</li>
</ul>
</li>
<li><p><strong>セキュリティインシデント対応</strong>：インシデントと判断した場合、関係者と連携して対応を実施</p>
</li>
</ol>
<h2><strong>4. 学んだこと</strong></h2>
<p>...と、ここまでは「うまくいった話」です。
ここからは、半自動化にたどり着くまでに経験した失敗や学びを共有します。</p>
<h3><strong>【学び１】すべては「手順の明確化」から始まる</strong></h3>
<p>最初に試したのは、Codex やその他の生成 AI とチャットで対話しながらプロンプトを作るアプローチでした。</p>
<p>「SOC の監視を自動化するプロンプトを作りたいんだけど...」
「こんな感じで...」
「うーん、もうちょっとこうして...」</p>
<p>このようなやり取りを何度も重ねて作成した初期のプロンプトは、以下のような構成でした。</p>
<pre><code class="language-md">## 1. メタデータ
- 出力先、必要ツール、タイムゾーン

## 2. ロール/前提
- SOC 調査アナリストとしての役割定義
- 前提

## 3. 共通解析手順（全 SPL 共通）
1. 調査対象イベントの特定（主指標の定義）
2. 深掘り（タイムレンジ拡張、ピボット観点）
3. エラー/事象の分類
4. 30日観測
5. 優先度判定
6. 不明点の明確化
7. 調査ログ（実行した SPL クエリ一覧）

## 4. 優先度判定基準（全SPL共通）
- 要（即時対応）/ 観察 / 不要 の3段階と具体的条件
- **要（即時対応）**
  - ベースライン比 ≥ 2.0 または P95超過、かつ分母 ≥ 30
  - 条件1 → 条件2：60分以内に失敗 ≥ 2
  - 条件3：xxx_count ≥ 2 かつ yyy_count ≥ 3
  ...（以下、複雑な条件が続く）

## 5. 調査結果整理
- 時間帯別サマリ表のヘッダー定義
- 主指標（SPL 別の記載ルール）

## 6. 調査用 SPL 一覧
- 調査観点①
- 調査観点②
...（以下、いくつかの調査観点が続く）

## 7. 出力フォーマット（必須）
- 調査概要
- 時間帯別サマリ表
- 調査ログ（実行した SPL クエリ一覧）
</code></pre>
<p>一見すると形になっているように見えますが、実際に運用してみると以下の問題が発生しました。</p>
<ul>
<li><strong>基準が曖昧</strong>：「ベースライン」の算出方法が不明確で、P95の計算に必要なデータ量も現実的ではなかった</li>
<li><strong>条件が複雑すぎる</strong>：AI エージェントが正しく解釈できず、人間でさえ理解が難しいケースがあった。結果として、一次調査レポートの品質にブレが生じた</li>
<li><strong>横展開できない</strong>：プロンプトが特定システムに過度に最適化されており、別システムへ展開する際はゼロから作り直しが必要だった</li>
</ul>
<p>根本原因は明確でした。SOC 監視業務において「何を」「どういう基準で」判断しているのか、どのような出力を期待しているのか。これらを詳細に整理・文書化できていなかったのです。</p>
<p><strong>解決策：手順書駆動プロンプト開発</strong></p>
<p>そこでアプローチを変えました。やったことはとてもシンプルです。</p>
<ol>
<li>まず「手順書」を作成する（人間が読んでも迷わず実行できる調査手順・判断基準を明文化）</li>
<li>その手順書を生成 AI に渡して「この手順書に沿って SOC 監視用のプロンプトを作って」と依頼する</li>
</ol>
<p>すなわち、バイブコーディング感覚でプロンプトを作成するのではなく、仕様書駆動開発の考え方に倣い、期待値を先に整理してからプロンプトを作成してもらうアプローチです。</p>
<p><img src="/assets/blog/authors/tanachu/2025-12-20-ai-agent-soc-monitoring-automation/06_investigation-guide.png" alt="06_investigation-guide.png"> <em>調査観点などを記載した手順書</em></p>
<p>効果は劇的でした。</p>
<p>最初のアプローチでは、1〜2週間かけて試行錯誤を繰り返しても納得のいくプロンプトが作れませんでした。しかし、手順書駆動プロンプト開発に切り替えたところ、手順書作成を含めて約2日間で期待通りの一次調査レポートを出力できるプロンプトが完成しました。</p>
<p>さらに、同じ要領で他システム向けのプロンプトも作成できるため、レポートフォーマットを統一しながら横展開も容易になりました。</p>
<p>以下は、このアプローチで作成したプロンプトの構成です。</p>
<pre><code class="language-md">## 1. メタデータ
- 出力先、必要ツール、タイムゾーン

## 2. 役割定義
- SOC 調査アナリストとしての役割

## 3. 前提条件
- 監視対象期間
- 既知情報（静観対象）
- 無害化ルール

## 4. 監視項目
各監視項目ごとに以下を記載：
- 監視目的
- SPL クエリ
   - サーチマクロを活用することで、トークン節約、ブレ軽減
- 判断基準
   - 「調査必須：〇〇期間に〇〇件以上のイベント」など明確な基準を記載
- 深掘り手順
   - 深掘り時の観点や使用する SPL を記載
- 出力要件
   - 出力時の必要情報を明記

## 5. 出力フォーマット
各監視項目ごとに具体的なフォーマットを記載：
- 監視概要・サマリ
- 詳細結果
   - 表形式で出力（項目名や値の記述フォーマットなども明記）
- 総合所見・推奨対応
- 調査ログ
   - 実際に使用した SPL と検索結果サマリ等を全て調査履歴として記録

## 6. 注意事項
- 必須ルールを箇条書き

## 7. 実行手順
- 実行手順をいくつかのステップに分けて記載
</code></pre>
<blockquote>
<p><strong>学び</strong>
急がば回れ。まず業務内容を整理・文書化する。それを基にプロンプトを作成することで、AI エージェントとの認識のズレがなくなり、期待通りの出力が得られる。</p>
</blockquote>
<h3><strong>【学び２】「できること」と「やるべきこと」は違う</strong></h3>
<p>これが一番の失敗談かもしれません。</p>
<p>AI エージェントと Splunk MCP を組み合わせると、「過度に複雑な調査」が気軽にできてしまいます。「せっかくだから、この観点も追加しよう」「もっと深掘りできるな…」と、気づけば監視対象の観点を広げすぎて、本来注力すべきポイントがぼやけていました。</p>
<p>さらに厄介なのは、「やっている感」が出てしまうことです。AI エージェントが大量の分析をしてくれるので、なんとなく仕事をした気になります。しかし実際には、生成されたレポートのレビューに時間がかかり、本末転倒な状態に陥っていました。</p>
<p>あくまで「半自動化」であり、最終判断は人間が行います。人間がレビューできる範囲で、本来注力すべき観点に絞ること。この意識が大事だと学びました。</p>
<blockquote>
<p><strong>学び</strong>
AI エージェントができることを最大限やるのではなく、人間のレビューも含めた全体を俯瞰し、「監視の意図」に立ち返る。「やっている感」に惑わされない。</p>
</blockquote>
<h3><strong>【学び３】業務の標準化効果は想定以上だった！</strong></h3>
<p>当初は「作業時間の短縮」を主な目的としていましたが、「業務の標準化」という副次的効果が想定以上でした。</p>
<p>詳細な手順書を作成したことで、判断基準が明文化されたことに加えて、ログ解析スキルや SPL の知識を要する部分を AI エージェントが担当することで、専門知識の有無に関わらず、誰が実施しても一定の品質で監視業務が行えるようになりました。</p>
<p>結果として、引き継ぎがしやすくなり、チーム全体の対応力向上にもつながっています。</p>
<blockquote>
<p><strong>学び</strong>
AI エージェント活用は効率化だけでなく、業務の標準化・属人化の解消にも大きな効果がある。</p>
</blockquote>
<h2><strong>5. まだ課題として残っていること</strong></h2>
<p>半自動化を実現したものの、正直に言うとまだ改善の余地があります。</p>
<ol>
<li><p><strong>品質チェックに一定時間がかかる</strong>
AI エージェントの出力を鵜呑みにはできないため、人間によるレビューは必須です。ログの日時や件数、判断内容の妥当性チェックには、依然として一定の時間を要します。</p>
</li>
<li><p><strong>ナレッジの蓄積・反映の仕組み</strong>
日々の調査で得られる「このパターンは実は〇〇だった」といった細かなナレッジを、プロンプトへフィードバックする仕組みが整っていません。現在、調査履歴を参照する形で解消できないか検証中です。</p>
</li>
<li><p><strong>手順書やプロンプト、監視観点のメンテナンス</strong>
新しい脅威パターンや監視から得られた知見に合わせて、手順書・プロンプト・監視観点（SPL）を更新する必要があります。現状、これらについては属人的で、メンテナンスプロセスの標準化が今後の課題です。</p>
</li>
</ol>
<h2><strong>6. まとめ</strong></h2>
<p>今回の取り組みを通じて学んだ3つのことを振り返ります。</p>
<ol>
<li><p><strong>すべては「手順の明確化」から始まる</strong>
急がば回れ。まず業務内容を整理・文書化する。これを基にプロンプトを作成することで、AI エージェントとの認識のズレがなくなり、期待通りの出力が得られる。</p>
</li>
<li><p><strong>「できること」と「やるべきこと」は違う</strong>
AI エージェントができることを最大限やるのではなく、人間のレビューも含めた全体を俯瞰し、「監視の意図」に立ち返る。「やっている感」に惑わされない。</p>
</li>
<li><p><strong>業務の標準化効果は想定以上</strong>
効率化だけでなく、業務の標準化・属人化の解消という効果も大きい。</p>
</li>
</ol>
<p>「AI エージェントで業務効率化」というと華やかに聞こえますが、実際には地道な言語化作業と試行錯誤の連続でした。</p>
<p>この取り組みを通じて感じたのは、「業務効率化」ではなく「業務の棚卸しと標準化」と捉えること、そして完全自動化ではなく「半自動化」という現実的なゴールを設定することの大切さです。</p>
<p>この記事が、AI エージェントの業務活用を検討している方の参考になれば幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/tanachu/2025-12-20-ai-agent-soc-monitoring-automation/00_coverImage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AWS DevOps Agent とNew Relicを連携して障害調査の自動化がどこまでできるか検証]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-20-aws-devopsagent-newrelic/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-20-aws-devopsagent-newrelic/</guid>
            <pubDate>Sat, 20 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[AWS DevOps Agent とNew Relicを連携して障害調査の自動化がどこまでできるか検証]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>この記事は <a href="https://qiita.com/advent-calendar/2025/newrelic">New Relic Advent Calendar 2025</a> 20日目の記事です。</p>
<p>こんにちは、KINTOテクノロジーズ(以下KTC)SREチームの手﨑です。
本記事では、先日のAWS re:Invent 2025で発表されたAWS DevOps Agentと、普段KTCで利用しているNew Relicを連携させて、障害調査をどこまで自動化できるのか検証した結果をご紹介します。</p>
<h2>注意事項</h2>
<ul>
<li>AWS DevOps Agentは本記事執筆時点(2025年12月)ではプレビュー版です。</li>
<li>GA（一般提供）までに仕様が変更される可能性があります。</li>
</ul>
<h1>AWS DevOps Agent とNew Relicの連携</h1>
<h2>AWS DevOps Agent とは</h2>
<p>AWS DevOps Agentは、インシデントを解決、予防し、信頼性とパフォーマンスを継続的に向上させるフロンティアエージェントです。詳細は以下の公式ページをご覧ください。</p>
<p><a href="https://aws.amazon.com/jp/devops-agent/">AWS DevOps Agent</a></p>
<p>特徴的な点はCapabilitiesという設定で、DevOps Agentが扱える情報(能力)を拡張できることです。
今回もこのCapabilitiesを利用してNew Relic MCP経由で障害調査を行います。
他にもCapabilitiesには以下のような設定が可能です。</p>
<ul>
<li>Cloud<ul>
<li>他のAWSアカウントのリソースへのアクセス</li>
</ul>
</li>
<li>Telemetry<ul>
<li>オブザーバビリティSaaSのテレメトリーデータへのアクセス</li>
</ul>
</li>
<li>Pipeline<ul>
<li>ソースコードが管理されているリポジトリやデプロイパイプラインの情報へのアクセス</li>
</ul>
</li>
<li>Communications<ul>
<li>Slackなどのコミュニケーションツールとの連携</li>
</ul>
</li>
<li>MCP Server<ul>
<li>MCPサーバーを使った情報の収集</li>
</ul>
</li>
<li>Webhook<ul>
<li>Webhook経由でのサードパーティ製アプリケーションやサービスへのアクセス</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/tesaki/2025-12-20/architecture.png" alt="アーキテクチャ図"></p>
<h2>New Relic との連携方法</h2>
<h3>AWS DevOps Agentの初期設定</h3>
<ul>
<li>AWS DevOps Agentの初期設定はエージェントスペースの作成から始まり、Roleの作成などが必要ですが、ここではNew Relicとの連携設定の箇所に絞って記載します。</li>
<li>以下のページにあるようにTelemetryの設定にNew Relicを追加することでNew RelicからAWS DevOps Agentを実行するためのwebhook URLを取得できます。<ul>
<li><a href="https://docs.aws.amazon.com/ja_jp/devopsagent/latest/userguide/configuring-capabilities-for-aws-devops-agent-connecting-telemetry-sources-connecting-new-relic.html">Connecting New Relic</a></li>
</ul>
</li>
<li>この設定により、New Relic remote MCP も利用されるようになります。</li>
</ul>
<h4>Telemetryの設定</h4>
<ol>
<li>AWS DevOps AgentのSettingsから「Telemetry」-「New Relic」を選択します。</li>
<li>New RelicのaccountIDとAPIキーを入力し、Regionを選択します。</li>
<li>webhook URLとsecret keyが発行されるため、保存しておきます。</li>
</ol>
<p>  <img src="/assets/blog/authors/tesaki/2025-12-20/telemetry.png" alt="Telemetry画面"></p>
<ol start="4">
<li>エージェントスペースを選択し、「Capabilities」タブから「Telemetry Sources」に「New Relic Server」
  を追加します。</li>
</ol>
<p>  <img src="/assets/blog/authors/tesaki/2025-12-20/telemetry-sources.png" alt="Telemetry Sources画面"></p>
<h4>New Relicのアラートの設定</h4>
<ul>
<li><p>New RelicのアラートからAWS DevOps Agentをwebhookで起動するために、New RelicのDestinationsとWorkflowの設定を行います。</p>
</li>
<li><p>Destinationsの設定</p>
<ol>
<li>New Relicの「Alerts」から「Destinations」を選択し、「Add a destination」から「Webhook」を選択します。</li>
<li>「Name」に任意の名前を入力し、以下のとおり設定値を入力します。<pre><code>Name: New Relic Webhook
Endpoint URL: https://event-ai.us-east-1.api.aws/webhook/generic/******  //Telemetryの設定の発行画面で発行されたURL
Authorization: Bearer
Authorization Value: ******  //Telemetryの設定の発行画面で発行されたsecret key
</code></pre>
</li>
<li>設定を保存します。</li>
</ol>
</li>
<li><p>Workflowの設定</p>
<ol>
<li>New Relicの「Alerts」から「Workflows」を選択し、「Add a workflow」から新しいWorkflowを作成します。(もしくは既存のWorkflowに対して設定を追加します)</li>
<li>NotifyのAdd channelからWebhookを選択し、「Destinationsの設定」で追加したDestinationを選択します。</li>
<li>PayloadのTemplateに以下の内容を入力します。<pre><code>{
  &quot;eventType&quot;: &quot;incident&quot;,
  &quot;incidentId&quot;: {{ json incidentIds.[0] }},
  &quot;action&quot;: &quot;created&quot;,
  &quot;priority&quot;: {{ json priority }},
  &quot;title&quot;: {{ json annotations.title.[0] }},
  &quot;description&quot;: {{ json state }},
  &quot;service&quot;:  {{json entitiesData.names.[0]}},
  &quot;timestamp&quot;: {{#timezone activatedAt &#39;Asia/Tokyo&#39;}}{{/timezone}}
}
</code></pre>
</li>
</ol>
</li>
</ul>
<p>以上でAWS DevOps AgentとNew Relicの連携設定は完了です。</p>
<h2>実際に試した結果</h2>
<p>実際にNew RelicのアラートからAWS DevOps Agentをwebhookで起動してみます。</p>
<h3>連携の流れ</h3>
<ul>
<li><p>実際に連携を試した際の流れは以下の通りです。</p>
<ol>
<li><p><strong>New RelicのアラートからAWS DevOps Agentへwebhookする</strong></p>
<ul>
<li>New Relicでアラートが発生すると、AWS DevOps Agent に webhook が送信されます</li>
</ul>
</li>
<li><p><strong>AWS DevOps AgentからNew Relicのテレメトリを取得して分析する</strong></p>
<ul>
<li>AWS DevOps Agentは自律的にNew RelicのMCP経由でテレメトリデータの取得を繰り返し、分析を行います</li>
</ul>
</li>
<li><p><strong>同時にAWSリソース内も調査する</strong></p>
<ul>
<li>New Relicのテレメトリ分析と並行して、AWSリソースの状態も調査します</li>
</ul>
</li>
<li><p><strong>Root Causeレポートが生成される</strong></p>
<ul>
<li>分析結果を基に、根本原因（Root Cause）レポートが自動生成されます</li>
</ul>
</li>
<li><p><strong>修正案の計画を指示できる</strong></p>
<ul>
<li>生成されたレポートを基に、修正案の計画を指示することができます</li>
</ul>
</li>
</ol>
</li>
</ul>
<h3>調査結果</h3>
<ul>
<li>調査状況をweb appから確認すると以下のような結果となります(表示しているものはサンプルのアプリケーションです)<ul>
<li>New Relic MCPを利用してアラートに関する情報を取得しています。
<img src="/assets/blog/authors/tesaki/2025-12-20/nrql_sample.png" alt="NRQL Sample"></li>
<li>今回のアラートはサンプルアプリケーションのため必ずエラーが発生する作りであることを Root Cause として結論付けられています。
<img src="/assets/blog/authors/tesaki/2025-12-20/rootcause.png" alt="Root Cause"></li>
<li>追加の調査として直近のデプロイとアラートの発生に関連性があるか確認してもらいました。<ul>
<li>AWS側の調査により、Fargate Spotのプロビジョニング遅延によって起動が遅れたことと、直近でデプロイが実施されていないことが報告されました。
<img src="/assets/blog/authors/tesaki/2025-12-20/deploy.png" alt="Deploy"></li>
</ul>
</li>
</ul>
</li>
</ul>
<h3>結果</h3>
<ul>
<li>New RelicのアラートをトリガーにAWS DevOps Agentをwebhookで起動し、MCP経由で取得したNew RelicのテレメトリとAWSリソース情報を組み合わせて自動調査させるところまで確認できました。</li>
<li>AWS DevOps Agentのweb appは対話形式で追加の調査を行うことができ、AWSリソースの直接参照とNew Relicのテレメトリ分析の両方を自律的に実施してくれるため、他の連携先が増えても柔軟に対応することができそうです。</li>
</ul>
<h2>まとめ</h2>
<p>本記事では、AWS DevOps AgentとNew Relicを連携させて障害調査の自動化を試しました。</p>
<p>New RelicのアラートをトリガーにAWS DevOps Agentをwebhookで起動し、MCP経由で取得したNew RelicのテレメトリとAWSリソース情報を組み合わせて分析することで、自律的にRoot Causeレポートを生成できることを確認しました。プレビュー段階ではありますが、障害発生時の初期調査を自動化できる点は非常に価値があると感じています。</p>
<p>また、今回は試していませんが、将来のインシデントを予防するPrevention機能を利用すれば、New Relic MCP経由でAPMの情報を参照してパフォーマンスチューニングのような改善サイクルにも活用できると考えています。</p>
<p>引き続きGA版の様子も見ながら検証していきたいと思います。</p>
<p>以上です。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/tesaki/2025-12-20/new_relic_logo_vertical.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[初参加でも迷わない！AWS re:Invent 2025の歩き方まとめ]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-20-aws-reinvent-2025-first-timer-guide/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-20-aws-reinvent-2025-first-timer-guide/</guid>
            <pubDate>Sat, 20 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[ラスベガスで開催されたAWS re:Inventに初参加しました。5日間過ごした上での学びを記録して、今後参加される方への参考になればと思います。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の20日目の記事です🎅🎄</p>
<h1>はじめに</h1>
<p>ラスベガスで開催された AWS re:Invent 2025に弊社から6名が参加してきました。
世界中からエンジニアが集まり、AWSの新サービスが発表されたり、ワークショップを通じて使い方を学べたりする、年に一度の巨大イベントです。</p>
<ul>
<li>期間：12/1(月)〜12/5(金)</li>
<li>参加者数：現地約6万人（うち日本からの参加者は約1,900人）／オンライン約200万人</li>
<li>セッション数：3,044
※これらの数値は、re:Invent 2025中の公式セッション「Japan Wrap-up Session &amp; Werner&#39;s Keynote (GBL105)」（現地時間12/4 14:45-17:30）で発表された公式情報です。</li>
</ul>
<p>2025年6月に日本で行われたAWS Summit Japan 2025のセッション数が160以上とのことなので、規模の違いが容易にわかります。</p>
<p>この記事では現地に行くまで知らなかったことや、参加して本当に役立ったTipsをまとめています。
来年以降、参加を検討している方の参考になれば幸いです。</p>
<h1>準備（持っていってよかったもの）</h1>
<ul>
<li>録音機器</li>
</ul>
<p>セッション中にメモを取る必要がほとんどなくなります。
私は<a href="https://jp.plaud.ai/products/notepin">Plaud NotePin</a>をレンタルしました。
録音し、AI要約して整理してくれるので、目の前の内容に集中できます。</p>
<p>なお録音することが不安な場合は、あらかじめ講師やスタッフに確認すると良いです。
私が確認した際には「もちろん！」という回答をもらえました。</p>
<ul>
<li>ケトル</li>
</ul>
<p>いきなりケトル？と思いますが、ラスベガスのホテルでは置いていないところが多い（らしい）です。
朝一番のキーノートに行く方はカンファレンスのご飯を食べていては間に合わない場合もあるので、用意しておくと良いでしょう。
また時差ボケにより、夜活動してしまう人にも便利です。</p>
<h1>各セッションTypeの違い</h1>
<p>セッションが多すぎてどれを選んでいいか分からない方へ、まずはセッションTypeの違いを意識すると良いです。</p>
<h2>Breakout Session</h2>
<p>最もオーソドックスな講義形式。新サービスの解説やベストプラクティスなど幅広いです。
人気なセッションはタイトルに[OVERFLOW]と書かれた中継形式のものがありますので、拠点移動が間に合わない場合などはそれに参加するのも手です。</p>
<p>ただし座席間が狭いためPCを広げるのは至難の業でした。どれぐらい狭いかというと、日本の電車の座席より狭く、腕組みしようものなら横の人と当たるレベルでした。</p>
<p>後日YouTubeで配信されるので、優先度が高くなければそれを待つのもありです。</p>
<h2>Builder’s Session</h2>
<p>講師1人に対して10人程度が参加するハンズオン形式のセッション。事前説明後にハンズオン手順が配られるので、それに従って進めます。
講師との距離が近いので、気になる機能を深掘りしたい方は参加すると良いでしょう。</p>
<p>PC（laptop）が必須です。ハンズオンの手順を見ながら進められるよう、PCとは別に小さなモニターがあると便利です。
自分は過去の参加レポートを見て、iPadを持参して画面拡張できるようにしていました。</p>
<p>日本からの参加者以外ではあまりやっている人はいなかったですが、気にせず広げて問題なかったです。</p>
<p>![sub_screen](/assets/blog/authors/yuji_morimoto/re_invent_2025/sub_screen.jpg =500x)
<em>講師の方も「それいいね！」と思わず感心してました</em></p>
<h2>Workshop</h2>
<p>こちらもPCが必須です。</p>
<p>数時間かけて行われるハンズオン形式のセッション。Builder’s Sessionと比べて大規模な部屋に数人の講師がいます。
こちらも講師を捕まえれば質問はできますが、参加者が多く、順番が回ってくるまで時間がかかる場合もあります。</p>
<h2>Gameday</h2>
<p>こちらもPCが必須です。</p>
<p>テーブルに集まったメンバーで課題を解決することで順位を競うセッションです。
まず講師から事前説明があります。その後、チームメンバーと役割分担しながらゴールを目指します。</p>
<p>自分以外のメンバーと合わず、居心地が悪いと感じたら、席を移動してしまっても問題なさそうです。
実際にそのようにしている方はいました。</p>
<p>他の方の参加レポートでは事前知識不要という方もいますが、私が参加した会ではある程度のインフラ知識が必要でした…</p>
<h2>Chalk Talk</h2>
<p>少人数の聴衆を対象とした対話形式のセッションです。講師による短い講義と、その後の参加者とのQ&amp;Aで構成されます。実世界のアーキテクチャの課題を中心とした技術的な議論をします。</p>
<p>他のセッションと比べると講師に質問できる時間が多く、また記録にも残らないので、まさに現地に行って参加する価値のあるコンテンツとなっています。
英語に自信がある方はぜひ参加して発言してみてください。</p>
<h2>Code Talk</h2>
<p>講師が実装しながらサービスについて解説をするセッションです。</p>
<h2>Lightning Talk</h2>
<p>20分程度の短時間セッションです。
セッションとセッションの隙間時間で聴くことができます。</p>
<h1>回り方のTips</h1>
<h2>会場が異なるセッション移動は1時間見込む</h2>
<p>会場が異なるセッションの場合は1時間を見込んでおくと安全です。</p>
<p>ちなみにre:Inventはラスベガスの複数のホテル・コンベンションセンターで同時に開催されました。
北から以下の通りの並びになっており、1つ1つの会場が大きいです。</p>
<ul>
<li>Wynn</li>
<li>Venetian</li>
<li>Caesars Forum</li>
<li>MGM Grand</li>
<li>Mandalay Bay</li>
</ul>
<p>参加したいセッションが開催されている会場に時間までに向かう必要があります。</p>
<h3>1.会場間の移動</h3>
<p>まず移動に時間がかかります。移動手段としては以下のものがあります。</p>
<ul>
<li>徒歩</li>
<li>モノレール</li>
<li>シャトルバス</li>
<li>タクシー/ライドシェア</li>
</ul>
<p>隣り合った会場のWynn↔︎Venetianであっても、徒歩15分程度かかりました。</p>
<p>また各会場間はシャトルバスが走っており、会場出てすぐそばに発着するのでアクセスが良くおすすめですが、運行間隔が15〜20分程度空くこともあります。</p>
<p>ライドシェアの場合は呼べる場所が制限されている場合があり、目の前を乗車場所に指定できないことがあります。</p>
<p>![shuttle_bus](/assets/blog/authors/yuji_morimoto/re_invent_2025/shuttle_bus.jpg =500x)
<em>会場前にあるシャトルバス 各会場ごとに分かれている</em></p>
<h3>2.荷物検査</h3>
<p>会場に到着しても荷物検査があるので、混雑具合によっては時間を要します。</p>
<p>PCやタブレットを取り出してセキュリティを通過する必要があります。
呼び止められてしまったら、入念に荷物をチェックされます。</p>
<h3>3.予約していてもセッション開始10分前には会場へ</h3>
<p>re:Inventには「Walk-up」という当日枠があります。
事前予約が埋まってしまっても入れる可能性があるこの枠ですが、セッション開始10分前に予約した人を対象とした受付を一旦締め切り、当日枠の入場を開始してしまいます。
これにより、事前予約していても入れなくなることがあるので注意が必要です。</p>
<p>![walk_up](/assets/blog/authors/yuji_morimoto/re_invent_2025/walk_up.jpg =500x)
<em>並ぶ列が予約枠と当日枠で分かれている</em></p>
<h2>1日1つの会場にこもってみる</h2>
<p>移動が大変であれば、特定の拠点に1日こもってセッションを梯子するのもアリです。
各会場には朝食・昼食・軽食（コーヒー、紅茶やお菓子など）が用意されているので、食事面も心配ありません。</p>
<p>次のセッションまで時間がある場合は前のセッションの内容を見直すのがおすすめです。
会場の廊下には床に座ってPCを開いている人がたくさんいました。</p>
<p>![people_seated](/assets/blog/authors/yuji_morimoto/re_invent_2025/people_seated.png =500x)
<em>奥の窓際に人が座っており、各々の作業をしている</em></p>
<h2>新機能について紹介する[NEW LAUNCH]が突如出てくる</h2>
<p>re:Invent開催前や期間中、またKeynoteで発表された新機能は、セッションタイトルの頭に[NEW LAUNCH]と記載されているものが突然出てくる場合があります。
気になる新機能の場合はセッション一覧をときどき探してみて、新しく追加されていないか見ると良いでしょう。</p>
<h2>SNSで情報を探る</h2>
<p>上記のような新機能のセッション情報や、突然始まるSWAG（ノベルティ）配布情報など、SNSで拡散されていることがあります。
気になる新機能のセッションが実は2時間前に終了していたということもあったので、逐一チェックすることをおすすめします。</p>
<h2>5日間もあるので頑張りすぎない</h2>
<p>セッションを詰め込みすぎると体力を消耗します。
日本からの移動の疲れ、日々の移動の疲れなどが知らない間に蓄積していきます。
楽しむにも体力がいるので、無理せずに行動しましょう。</p>
<p>疲れが溜まった時には認定者ラウンジに行くのもおすすめです。
テーブル席やソファ席などがあり、軽食も用意されています。
会場の廊下で仮眠を取るのは心配ですが、このラウンジでは何人か仮眠している人がいました。</p>
<p>![certificated_lounge](/assets/blog/authors/yuji_morimoto/re_invent_2025/certificated_lounge.jpg =500x)
<em>Venetianにある認定者ラウンジ</em></p>
<h2>イベントを楽しむ</h2>
<p>AWS公式が企画しているイベントが毎日何かしらあります。
セッションがない朝や夜の時間で開催されているので、息抜き（兼ネットワーキング）に参加してみましょう。
私は4つ参加してみました。</p>
<ul>
<li>APJ Kick Off Party
APJ（アジア太平洋・日本）の参加者向けのイベント</li>
<li>ビンゴ大会</li>
<li>5k race
チャリティーを目的とした5kのランニング</li>
<li>re:Play
4日目の夜に開催される、セッションに頑張って参加した参加者へのご褒美イベント</li>
</ul>
<p>![5k_race](/assets/blog/authors/yuji_morimoto/re_invent_2025/5k_race.jpg =500x)
<em>朝6時に会場に集まった参加者</em></p>
<h2>コミュニケーションを全力で楽しむ</h2>
<p>re:Inventに参加して強く感じたのは、現地で得られる価値の多くはコミュニケーションから生まれていたということでした。</p>
<p>英語に自信があるわけではありませんでしたが、AWSという共通言語があることで、思っていた以上に会話はできました。セッションの感想を一言伝えたり、ふと感じた疑問を質問したりすると、新しい視点や気づきを得られる場面が多かったです。</p>
<p>日本人同士であっても、参加しているセッションや注目しているトピックはそれぞれ異なり、会話を通じて自分1人では拾えなかった情報を得ることができました。
（お酒の場でも名刺を交換するところにも日本人らしさを感じ、嬉しくなりました。）</p>
<p>現地の熱量を最も強く感じられたのも、人と直接コミュニケーションを取っている瞬間でした。
Keynote後のざわつきや、セッション終了後の高揚感は、その場で誰かと共有することでより鮮明に感じられました。</p>
<p>旅の恥はかき捨てという言葉通り、うまく話せなかったとしてもその場限り、それならと遠慮せずにどんどん話しかけて、現地で感じる熱量を最大化するのは良かったと思います。</p>
<p>実際にその体験は、上手く行かなかったこと含め、日本に戻ってからの学習意欲やモチベーションとして確実に自分の中に残りました。</p>
<h1>まとめ</h1>
<p>re:Inventは「柔軟さ」がカギだと感じました。</p>
<p>参加前は3,000を超えるセッションの中から何を選べばいいか分からず、参加したいセッションは予約開始と同時に埋まってしまう。満足にセッションに参加できないまま終わってしまうのではないか、そんな不安を抱えながらラスベガスに向かいました。</p>
<p>しかし、それは杞憂でした。</p>
<p>Walk-upと呼ばれる当日枠を活用すれば予約なしでも参加できますし、セッションによっては開始後も空席があれば入場できました。「どうせ無理だろう」と諦めず、果敢に挑戦してみることの大切さを学びました。</p>
<p>Gamedayのような参加者同士でコミュニケーションを取る場も、最初は英語力への不安がありましたが、ギリギリ会話ができることを実感できたので参加してよかったです。もちろん、「言いたかったことはそうじゃなかった…」と歯がゆい思いをすることもありましたが、それが「もっと英語を学ぼう」という具体的なモチベーションに変わりました。</p>
<p>5日間、業務を離れて学びに集中できる環境は貴重でした。技術を深掘りし、気になることがあればその場で質問できる、こんな贅沢な場はそうそうありません。そして、これだけ多くのAWSファンが世界中から集まり、熱量を注いでいる光景を目の当たりにできたことも大きな収穫でした。</p>
<p>これから参加される方も、計画通りにいかないことを恐れず、その場その場で新しい選択肢を見つけてみてください。
（2026年は11/30〜12/4と決定しているみたいです。）</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yuji_morimoto/re_invent_2025/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ（Part 2）：実装ガイド]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-20-cmp-swiftui-hybrid-part2/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-20-cmp-swiftui-hybrid-part2/</guid>
            <pubDate>Sat, 20 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[DroidKaigi 2025で発表した「Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ」の内容をまとめました。Part 2では、プロジェクト構造、ナビゲーション統合、Koin DIなど具体的な実装方法を紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の20日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>こんにちは、KINTOテクノロジーズ Mobile KMPチームです。</p>
<p>本記事は「Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ」シリーズのPart 2です。Part 1ではハイブリッド開発を選んだ背景とアーキテクチャ概要を紹介しました。今回は具体的な実装方法を詳しく解説します。</p>
<blockquote>
<p><strong>本シリーズの構成</strong></p>
<ol>
<li>Part 1：なぜハイブリッドなのか</li>
<li><strong>Part 2：実装ガイド</strong> ← 現在の記事</li>
<li>Part 3：SwiftUI連携と技術Tips（近日公開）</li>
</ol>
</blockquote>
<h2>実装事例</h2>
<h3>プロジェクト構造</h3>
<p><strong>プロジェクトのディレクトリ構成</strong></p>
<pre><code>my-cmp-app/
├── composeApp/
│   ├── src/
│   │   ├── commonMain/           // CMP Shared code
│   │   ├── androidMain/          // Android-specific
│   │   └── iosMain/              // iOS-specific
├── iosApp/
│   ├── Views/                    // SwiftUI views
│   ├── Screens/                  // Native screens
│   └── ComposeViewContainer.swift
└── shared/                       // KMP domain layer
    ├── viewmodels/
    └── models/
</code></pre>
<p>まず<strong>composeApp</strong>です。<code>commonMain</code>にはCMPの共通コード、<code>androidMain</code>/<code>iosMain</code>には各プラットフォーム固有の実装を入れています。</p>
<p>次に<strong>iosApp</strong>。SwiftUIのViewやネイティブ画面が含まれています。特に<code>ComposeViewContainer.swift</code>ファイルでCMPコンポーネントをiOS側に組み込んでいます。</p>
<p>最後に<strong>shared</strong>。KMPのドメイン層で、viewmodelsやmodelsを共通化しています。</p>
<p>このように、ディレクトリレベルでも<strong>共通化できる部分はまとめ、ネイティブ依存部分は分離</strong>しています。</p>
<h3>ハイブリッドUIの実例</h3>
<p>実際に作成したPoCアプリの画面構成を紹介します。</p>
<p>MainScreenでは、ボトムナビゲーションに4つのタブを配置しています。</p>
<table>
<thead>
<tr>
<th>タブ</th>
<th>UI実装</th>
</tr>
</thead>
<tbody><tr>
<td>Application</td>
<td>CMP</td>
</tr>
<tr>
<td>Lineup</td>
<td>CMP</td>
</tr>
<tr>
<td>HelpCenter</td>
<td>CMP</td>
</tr>
<tr>
<td>Settings</td>
<td>SwiftUI on iOS</td>
</tr>
</tbody></table>
<ul>
<li><strong>Application、Lineup、HelpCenter</strong>はCMPで共通UIを実装</li>
<li><strong>Settings</strong>だけはネイティブ（iOSではSwiftUI）で作っています</li>
</ul>
<p>このように、共通UIとネイティブUIを組み合わせても、タブ切り替えはシームレスに行えます。</p>
<p><strong>iOS固有のUIが活きるSettings Tab</strong></p>
<p><img src="/assets/blog/authors/yenahwang/ios_tab.png" alt="Settings Tab 画面（iOS）"></p>
<h2>ネイティブからCMPベースのハイブリッドナビゲーションへの移行</h2>
<p>このような構成では、ネイティブ中心のナビゲーションからCMPベースのハイブリッドナビゲーションへ移行する必要がありました。どのように移行したのか、具体的な方法をご紹介します。</p>
<ul>
<li>既存のナビゲーションスタックをリファクタリングし、ネイティブとCMP間でルーティング</li>
<li>プラットフォーム固有のナビゲーションロジックを共有インターフェースの背後に抽象化</li>
</ul>
<p><img src="/assets/blog/authors/yenahwang/p-nav-migration.png" alt="ハイブリッドナビゲーション移行"></p>
<h3>ルートベースのナビゲーション戦略</h3>
<p>先ほど紹介したPoCアプリのタブ構造を振り返ると、1〜3番目のタブはCMP、4番目はSwiftUIで実装しています。</p>
<p><strong>PoCアプリのタブ構造</strong></p>
<p><img src="/assets/blog/authors/yenahwang/poc_app.png" alt="PoCアプリのタブ構造"></p>
<p>このような構成でルーティングを実現するために、各画面のルートをstring constantで定義しておきます。</p>
<pre><code class="language-kotlin">// Routes.kt - Navigation routes defined in shared code
object Routes {
    const val LOGIN = &quot;login&quot;
    const val SCREEN_A = &quot;screen_a&quot;
    const val SCREEN_B = &quot;screen_b&quot;
    const val SCREEN_C = &quot;screen_c&quot;
    const val SCREEN_D = &quot;screen_d&quot;
}
</code></pre>
<p>プラットフォームごとに実装を分けて、AndroidではCompose Navigation、iOSではSwiftUI NavigationでComposeをつなぐ<strong>Bridge</strong>をします。</p>
<h3>Kotlinナビゲーション実装</h3>
<p>こちらは<strong>ComposeViewController.kt</strong>の例です。パラメータとして<code>initialRoute</code>を受け取り、そのルートに応じてwhen分岐で<strong>遷移先を切り替え</strong>ています。</p>
<pre><code class="language-kotlin">// ComposeViewController.kt
fun createComposeViewController(
    initialRoute: String,
    onCallback: (Boolean) -&gt; Unit = {}
): UIViewController {
    return ComposeUIViewController {
        MyAppTheme {
            when (initialRoute) {
                &quot;login&quot; -&gt; LoginNavigation(onLoginSuccess = onCallback)
                &quot;screen_a&quot; -&gt; ScreenANavigation()
                &quot;screen_b&quot; -&gt; ScreenBNavigation(iosCallback = onCallback)
                &quot;screen_c&quot; -&gt; ScreenCNavigation()
                &quot;screen_d&quot; -&gt; ScreenDNavigation()
                else -&gt; FallbackNavigation() // default fallback
            }
        }
    }
}
</code></pre>
<p>例えば、&quot;login&quot;なら<code>LoginNavigation</code>へ、&quot;screen_a&quot;や&quot;screen_b&quot;なら、それぞれの画面のNavigationを呼び出します。このように、ルートを定義しておけば、プラットフォーム固有のナビゲーション処理と簡単に接続できます。</p>
<h3>iOS側でCompose Navigationを統合する仕組み</h3>
<p>まず<strong>ComposeViewContainer</strong>を定義して、<code>UIViewControllerRepresentable</code>を実装します。これによってSwiftUIの中に<strong>Compose UIを埋め込む</strong>ことができます。</p>
<pre><code class="language-swift">// ComposeViewContainer.swift
struct ComposeViewContainer: UIViewControllerRepresentable {
    let initialRoute: String
    let onCallback: (Bool) -&gt; Void

    func makeUIViewController(context: Context) -&gt; UIViewController {
        // Wrap the Swift callback into the KotlinBoolean signature
        let kotlinCallback: (KotlinBoolean) -&gt; Void = { kotlinBool in
            onCallback(kotlinBool.boolValue)
        }
        // Create the Compose UIViewController, passing the wrapped callback
        return ComposeViewControllerKt.createComposeViewController(
            initialRoute: initialRoute,
            onCallback: kotlinCallback
        )
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {
        // No dynamic updates needed here
    }
}
</code></pre>
<p>次に、<code>makeUIViewController</code>の中でKotlin側の<strong>createComposeViewController</strong>を呼び出します。</p>
<h3>iOS Navigationの実践</h3>
<p>こちらは、統合コードを使ったiOS Navigation codeです。<code>TabView</code>の中に4つのタブを定義しています。1〜3番目のタブは<strong>CMP画面</strong>で、<code>NavigationView</code>の中に<strong>ComposeViewContainer</strong>を埋め込み、初期ルート&quot;screen_a&quot;を指定しています。</p>
<pre><code class="language-swift">// MainScreen.swift
struct MainScreen: View {
    var body: some View {
        TabView {
            // Tab 1~3: CMP screen
            NavigationView {
                ComposeViewContainer(initialRoute: &quot;screen_a&quot;, onCallback: { _ in })
            }.tabItem {
                Label(&quot;Application&quot;, systemImage: &quot;doc.plaintext&quot;)
            }
            ...
            // Tab 4: SwiftUI screen
            NavigationView {
                SettingsScreen()
            }.tabItem {
                Label(&quot;Settings&quot;, systemImage: &quot;gearshape.fill&quot;)
            }
        }
    }
}
</code></pre>
<p>4番目のタブはSwiftUIネイティブの<code>SettingsScreen()</code>を使用しています。このように、CMPとネイティブ画面を同じナビゲーション構造内で自然に共存させることができます。</p>
<h3>Koinによるモジュール化</h3>
<p>ここまでナビゲーション統合を見てきましたが、実際のアーキテクチャはCMP + MVVMでどんどん複雑になります。そこで、Multiplatform向けDIの<strong>Koin</strong>を導入しました。結果、構造がシンプルになり、テストや保守もしやすくなりました。</p>
<p><img src="/assets/blog/authors/yenahwang/koin.png" alt="Koin"></p>
<p><strong>Koinの利点：</strong></p>
<ul>
<li><strong>依存関係をきれいに管理</strong>できる</li>
<li>同じ定義を使って<strong>複数プラットフォームで再利用</strong>できる</li>
<li><strong>モジュール単位で分離</strong>でき、構造がシンプルになる</li>
<li><strong>テストのしやすさ</strong>が向上</li>
</ul>
<pre><code class="language-kotlin">// commonMain/di/AppModule.kt
val appModule = module {
    single { NetworkClient() }
    single { AuthRepository(get()) }
    viewModel { HomeViewModel(get()) }
    viewModel { ProductViewModel(get()) }
    factory&lt;Validator&gt; {
        ValidatorImpl(get(), get())
    }
}
</code></pre>
<p><code>single</code>や<code>viewModel</code>をシンプルに書くだけで、依存関係を自動的に解決できます。</p>
<h3>プラットフォーム固有のDI</h3>
<p>Koinでは、iOSとAndroidそれぞれに<strong>専用のモジュール</strong>を定義できます。</p>
<pre><code class="language-kotlin">// iosMain/di/PlatformModule.ios.kt
val iosModule = module {
    single&lt;HttpClientEngine&gt; { Darwin.create {} }
    single&lt;DataStore&lt;Preferences&gt;&gt; { createDataStore() }
    single { IOSLocationService() as LocationService }
    single { AppStoreConnectTracker() as AnalyticsTracker }
}

// androidMain/di/PlatformModule.android.kt
val androidModule = module {
    single&lt;HttpClientEngine&gt; { OkHttp.create {} }
    single&lt;DataStore&lt;Preferences&gt;&gt; { createDataStore(androidContext()) }
    single { AndroidLocationService() as LocationService }
    single { FirebaseAnalyticsTracker() as AnalyticsTracker }
}
</code></pre>
<p>iOS側では<code>iosModule</code>を定義し、<code>Darwin.create()</code>を使った<code>HttpClient</code>を登録しています。一方Android側では<code>androidModule</code>を定義し、<code>OkHttp.create()</code>を使った<code>HttpClient</code>を登録しています。</p>
<p>このように、<strong>プラットフォーム固有の実装はモジュールごとに分離</strong>しながら、共通のインターフェースを通して利用できるようにしています。</p>
<h3>Koinの初期化</h3>
<p>ここではKoinの初期化方法です。まず共通コード側に<code>initKoin</code>関数を用意しておきます。</p>
<pre><code class="language-kotlin">// commonMain/di/KoinSetup.kt
fun initKoin(platformModule: Module) = startKoin {
    modules(appModule, networkModule, platformModule, ...)
}

// Android Application class
class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        initKoin(androidModule)
    }
}
</code></pre>
<p>Androidでは<code>Application</code>クラスの<code>onCreate</code>から<code>androidModule</code>を渡して初期化します。</p>
<pre><code class="language-swift">// iOS AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions...) {
    KoinKt.doInitKoin(platformModule: IOSPlatformModuleKt.iosModule)
    return true
}
</code></pre>
<p>iOSでは<code>AppDelegate</code>から<code>iosModule</code>を渡すだけです。このようにして、<strong>両方のプラットフォームで同じ初期化処理を共通化</strong>できます。</p>
<h3>Koinでの動的モジュールロード</h3>
<p>次に、Koinでの<strong>動的モジュールロード</strong>です。</p>
<pre><code class="language-kotlin">// Feature module definition
val featureModule = module {
    viewModel { FeatureViewModel(get(), get()) }
    factory { FeatureValidator() }
    single { FeatureRepository(get()) }
}

// Register dynamically after the Koin&#39;s initialization
loadKoinModules(featureModule)
</code></pre>
<p>まず<code>featureModule</code>を定義して、ViewModel・Validator・Repositoryを登録します。その後、<code>loadKoinModules</code>に<code>featureModule</code>を渡してロードします。</p>
<p><strong>Koin初期化後にモジュールを動的にロード</strong>できます。これにより、大規模アプリでも<strong>必要な機能だけを後から読み込む構成</strong>が可能になります。これがKoinの強みです。</p>
<h2>開発効率化事例：Validator統合</h2>
<p><img src="/assets/blog/authors/yenahwang/validation.png" alt="Validator統合"></p>
<h3>問題状況</h3>
<p>メール、電話番号、郵便番号、パスワード強度、日付形式など、様々な検証ロジックが必要でした。</p>
<p><strong>従来方式で実装した場合：</strong></p>
<ul>
<li>Android：24個のValidator（Kotlin）</li>
<li>iOS：24個のValidator（Swift）</li>
<li>合計48個の実装</li>
<li>合計48個のValidationResult</li>
<li>合計48セットのユニットテスト</li>
</ul>
<h3>KMPソリューション</h3>
<p>すべての検証ロジックを共有KMPモジュールに移動しました。</p>
<ul>
<li>Kotlinで各Validatorを一度だけ実装</li>
<li>共通関数/クラスとして公開</li>
<li>CMP画面、Android/iOSネイティブ画面すべてから呼び出し可能</li>
</ul>
<p><img src="/assets/blog/authors/yenahwang/p-validator-kmp.png" alt="Validator統合によるコード削減"></p>
<p><strong>結果：</strong></p>
<ul>
<li>実装量50%削減</li>
<li>テストコード50%削減</li>
<li>プラットフォーム間検証ロジックの一貫性保証</li>
<li>バグ修正も一箇所で完了（両OS同時に修正される）</li>
</ul>
<h2>次回予告</h2>
<p>Part 2では、プロジェクト構造、ナビゲーション統合、Koin DI、Validator統合など具体的な実装方法を紹介しました。</p>
<p><strong>Part 3：SwiftUI連携と技術Tips</strong>では、SwiftUIとCMPの相互埋め込み、CMP vs Flutter比較、実践で直面した落とし穴、導入戦略などを詳しく解説します。お楽しみに！</p>
<hr>
<p>こちらは「Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ」シリーズのPart 2です。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yenahwang/coverImage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Figma MCPとClaude CodeでAndroidのUI構築を高速化]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-19-claude-code-figma-mcp/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-19-claude-code-figma-mcp/</guid>
            <pubDate>Fri, 19 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Figma MCPとClaude CodeでAndroidのUI構築を高速化]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の19日目の記事です🎅🎄</p>
<hr>
<p>KINTOテクノロジーズで Unlimited（Android）アプリの開発を担当している kikumido と申します。</p>
<p>これまで Android の UI 実装では、Figma のデザインを見ながら色・タイポグラフィー・レイアウト・アセットを手作業で Compose へ落とし込む必要があり、どうしても時間と労力がかかっていました。</p>
<p>しかし、Figma が提供する <strong>MCP（Model Context Protocol）</strong> と、Anthropic の <strong>Claude Code</strong> を組み合わせることで、このワークフローが大きく変わります。<br>Figma MCP を通じて AI がデザインデータを直接読み取り、Claude Code が <strong>Compose コードを自動生成</strong>してくれるため、これまで手作業で行っていた工程が一気に効率化されます。</p>
<p>本記事では、この「Figma MCP × Claude Code」による UI 実装高速化の流れを、設定方法からプロンプト例、生成結果までまとめて紹介します。</p>
<hr>
<h2>目次</h2>
<ol>
<li><a href="#1-figma-mcp-%E3%82%92%E6%9C%89%E5%8A%B9%E3%81%AB%E3%81%99%E3%82%8B">Figma MCP を有効にする</a>  </li>
<li><a href="#2-mcp-%E3%82%AF%E3%83%A9%E3%82%A4%E3%82%A2%E3%83%B3%E3%83%88%E3%82%92%E5%B0%8E%E5%85%A5%E3%81%99%E3%82%8B">MCP クライアントを導入する</a>  </li>
<li><a href="#3-api-tokenpat-%E3%82%92%E8%A8%AD%E5%AE%9A%E3%81%99%E3%82%8B">API Token（PAT）を設定する</a>  </li>
<li><a href="#4-figma-%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB-url-%E3%82%92%E6%8C%87%E5%AE%9A%E3%81%99%E3%82%8B">Figma のファイル URL を指定する</a>  </li>
<li><a href="#5-claude-code-%E3%81%AB%E5%AE%9F%E8%A3%85%E3%82%92%E4%BE%9D%E9%A0%BC%E3%81%99%E3%82%8B">Claude Code に実装を依頼する</a>  </li>
<li><a href="#6-%E5%AE%9F%E8%A1%8C%E7%B5%90%E6%9E%9C">実行結果</a>  </li>
<li><a href="#7-%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></li>
</ol>
<hr>
<h2>1. Figma MCP を有効にする</h2>
<p>デスクトップ版 Figma の <strong>MCP サーバー機能をオン</strong>にするだけで準備は完了です。<br>公式ヘルプに非常にわかりやすい手順があります。</p>
<p>🔗 <a href="https://help.figma.com/hc/ja/articles/32132100833559-Figma-MCP%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AE%E3%82%AC%E3%82%A4%E3%83%89">https://help.figma.com/hc/ja/articles/32132100833559-Figma-MCP%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%AE%E3%82%AC%E3%82%A4%E3%83%89</a></p>
<hr>
<h2>2. MCP クライアントを導入する</h2>
<p>本記事では、チームで使用している <strong>Claude Code</strong> を MCP クライアントとして設定します。<br>Claude Code は Figma MCP に公式対応しており、設定も非常に簡単です。</p>
<p>🔗 <a href="https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/#claude-code">https://developers.figma.com/docs/figma-mcp-server/remote-server-installation/#claude-code</a></p>
<hr>
<h2>3. API Token（PAT）を設定する</h2>
<p>Figma の API を利用するためには <strong>Personal Access Tokens（PAT）</strong> が必要です。<br>セキュリティを考慮し、Claude Codeに直接PATを記載するのではなく<strong>環境変数に保存して使用する方法が安全</strong>です。</p>
<h3>3.1 環境変数が安全とされる理由</h3>
<ol>
<li>誤ってAIに送信されない</li>
<li>Git に誤コミットされるリスクを防げる</li>
<li>PC（OS）のユーザー権限で保護される</li>
</ol>
<h3>3.2 PAT の取得方法</h3>
<ol>
<li>Figma にログイン  </li>
<li>右上の自分のアイコン → <strong>Settings</strong>  </li>
<li>左メニューの <strong>Personal Access Tokens</strong>  </li>
<li><strong>Generate new token</strong>  </li>
<li>名前を入力して生成 → 表示されたトークンをコピー（再表示不可）</li>
</ol>
<h3>3.3 環境変数への設定（例：macOS / Linux）</h3>
<pre><code class="language-bash">echo &#39;export FIGMA_TOKEN=&quot;あなたのPAT&quot;&#39; &gt;&gt; ~/.zshrc
source ~/.zshrc
</code></pre>
<p>PAT の詳細：
🔗 <a href="https://www.figma.com/developers/api#access-tokens">https://www.figma.com/developers/api#access-tokens</a></p>
<h2>4. Figma のファイル URL を指定する</h2>
<p>Claude Code に Figma の URL を渡すだけで、<strong>ノード階層・レイアウト・色・フォント・画像アセット</strong> を自動で読み込みます。</p>
<h3>4.1 正しい Figma ファイル URL の取得方法</h3>
<ol>
<li>対象の <strong>フレーム / コンポーネント / レイヤー</strong> を選択  </li>
<li>右クリック  </li>
<li><strong>Copy → Copy link（リンクをコピー）</strong></li>
</ol>
<p>→ <code>?node-id=xxx</code> を含む正しい URL がコピーされます。</p>
<h3>4.2 環境変数への設定（例：macOS / Linux）</h3>
<pre><code class="language-bash">echo &#39;export FIGMA_FILE_KEY=&quot;コピーしたリンク&quot;&#39; &gt;&gt; ~/.zshrc
source ~/.zshrc
</code></pre>
<hr>
<h2>5. Claude Code に実装を依頼する</h2>
<p>以下は、Figma のデザインを Jetpack Compose に落とし込むためのプロンプト例です。
ご自分のプロジェクトに合わせて適宜調整してください。</p>
<pre><code>目的：
Figma MCP を使い、Figma のデザインデータを直接参照して Jetpack Compose の UI コードを生成してください。

前提：
- FIGMA_TOKEN（環境変数）
- FIGMA_FILE_KEY（環境変数）

■ デザイン情報
- Figma に存在する色・Typography のみ使用し、未定義の値は追加しない
- 以下のファイルに分割して実装
  - Color.kt
  - Type.kt
  - Theme.kt
- 上記テーマを使って Screen.kt を実装
- @Preview を追加
- 不明点は推測せず、Figma MCP が取得したデータを使用する

■ 画像アセットの扱い
- SVG の場合：
  - Figma MCP から取得したファイルを基に、正確に Android VectorDrawable（.xml）へ変換して配置
- PNG の場合：
  - Figma の Export 設定に従う  
  - Export 設定がなければ以下 5 種類の密度で PNG を生成して配置  
    - mdpi / hdpi / xhdpi / xxhdpi / xxxhdpi
- アセット名は Figma 名を Android の命名規則（lower_snake_case）へ変換して作成する
</code></pre>
<hr>
<h2>6. 実行結果</h2>
<p>Claude Code は、指定された Figma ノードから <strong>色・Typography・レイアウト・画像アセット</strong> を取得し、<br><code>Color.kt</code> / <code>Type.kt</code> / <code>Theme.kt</code> / <code>Screen.kt</code> / <code>Preview</code> を自動生成します。</p>
<p>これにより、手作業で行っていた  </p>
<ul>
<li>色のコピペ  </li>
<li>フォント設定  </li>
<li>画像書き出し  </li>
<li>レイアウト再現</li>
</ul>
<p>といった作業が大幅に自動化され、<strong>UI 実装の速度と正確性が飛躍的に向上します。</strong></p>
<p>それでは、Claude Code にプロンプトを渡した結果を確認してみましょう。</p>
<h3>6.1 UIを確認する</h3>
<p>左が Figma 、右が Claude Code が実装した Compose のPreviewです。
良い精度で再現できていると思います。
※「×」はSVGからVectorDrawableに変換された画像、アバターはPNG画像です。</p>
<table>
<thead>
<tr>
<th>Figma</th>
<th>ComposeのPreview</th>
</tr>
</thead>
<tbody><tr>
<td>![Figma](/assets/blog/authors/kikumido/figma.png =200x)</td>
<td>![Compose](/assets/blog/authors/kikumido/preview.png =200x)</td>
</tr>
</tbody></table>
<p>コードも確認してみましょう。</p>
<h3>6.2 コードを確認する</h3>
<p>画像は以下のように実装されていました。</p>
<pre><code class="language-kotlin">Image(
    painter = painterResource(id = R.drawable.ic_avatar_large),
    contentDescription = &quot;メインアバター&quot;,
    modifier = Modifier
        .size(106.dp)
        .clip(CircleShape),
    contentScale = ContentScale.Crop
)
</code></pre>
<p>Textは以下のように実装されていました。
styleや色も Figma で指定されたtypography、colorが適切に指定されています。</p>
<pre><code class="language-kotlin">// ドライバー名
Text(
    text = driverName,
    style = KintoTheme.typography.labelLarge,
    color = KintoTheme.colors.textPrimary
)
</code></pre>
<p>入力欄の幅が Figma は固定値なのに対し、Compose は<code>fillMaxWidth()</code>を使用し横幅いっぱいに広がるようになっていました。
こちらはFigmaを正とするのであれば実装を修正するべきですが、横幅の広い端末を考慮しデザイナーと相談しても良いかもしれません。
入力欄の間の余白が14dpであることも Figma 通りに再現できています。
同じ見た目の入力欄が一つの<code>InputField</code>として実装されている点も注目です。
さらに、選択状態に合わせてボーダーの色が変わるようにすでに実装されてもいます。</p>
<pre><code class="language-kotlin">var selectedField by remember { mutableStateOf&lt;SelectedField&gt;(SelectedField.BirthYear) }
// 入力フィールド
Row(
    modifier = Modifier.fillMaxWidth(),
    horizontalArrangement = Arrangement.spacedBy(14.dp)
) {
    // 生年月
    InputField(
        label = &quot;生年月&quot;,
        value = birthYear,
        isSelected = selectedField == SelectedField.BirthYear,
        onClick = { selectedField = SelectedField.BirthYear },
        modifier = Modifier.weight(1f)
    )

    // 普段の起床時刻
    InputField(
        label = &quot;普段の起床時刻&quot;,
        value = wakeUpTime,
        isSelected = selectedField == SelectedField.WakeUpTime,
        onClick = { selectedField = SelectedField.WakeUpTime },
        modifier = Modifier.weight(1f)
    )
}
</code></pre>
<p><code>InputField</code>は Figma の仕様どおりに実装されており、onClick などの挙動もパラメータで柔軟に渡せる設計になっています。
また、入力ボックス内のテキストに typography が設定されていない点も、Figma の定義を忠実に反映した結果です。</p>
<p>なお、プロンプトで「未定義の値は追加しない」と指定しない場合、Claude Code が推測で適切そうな typography を補完してしまいます。
しかし、望ましいアプローチはアプリ側で補正することではなく、Figma のデザイン側を修正し、正しい typography を設定することだと考えています。
その方が、UI の一貫性やメンテナンス性が大きく向上するためです。</p>
<pre><code class="language-kotlin">@Composable
private fun InputField(
    label: String,
    value: String,
    isSelected: Boolean,
    onClick: () -&gt; Unit,
    modifier: Modifier = Modifier
) {
    Column(modifier = modifier) {
        // ラベル
        Text(
            text = label,
            style = KintoTheme.typography.labelMedium,
            color = KintoTheme.colors.textPrimary
        )

        Spacer(modifier = Modifier.height(8.dp))

        // 入力ボックス
        Box(
            modifier = Modifier
                .fillMaxWidth()
                .height(53.dp)
                .border(
                    width = if (isSelected) 2.dp else 1.dp,
                    color = if (isSelected) KintoTheme.colors.borderFocused else KintoTheme.colors.borderDefault,
                    shape = RoundedCornerShape(8.dp)
                )
                .clickable { onClick() }
                .padding(horizontal = 16.dp),
            contentAlignment = Alignment.CenterStart
        ) {
            Text(
                text = value,
                style = TextStyle(
                    fontFamily = FontFamily.SansSerif,
                    fontWeight = FontWeight.Normal,
                    fontSize = 16.sp,
                    lineHeight = 21.sp
                ),
                color = KintoTheme.colors.textPrimary
            )
        }
    }
}
</code></pre>
<p>先述のプロンプトでは文字を<code>strings.xml</code>に定義するように指示をしていないので、コード上にそのまま記載されています。
先のプロンプトを修正したり、追加で指示を出せば<code>strings.xml</code>に定義した上で使用することもしてくれます。</p>
<hr>
<h2>7. まとめ</h2>
<p>まとめとして強調したいのは、<strong>実装の精度は Figma 側のデザインシステムが適切に整備されているかに大きく左右される</strong>という点です。<br>Figma のスタイルが未整理だったり、コンポーネント化が不十分な場合、AI が取得する情報も不完全になり、結果的にコードの手直しが必要になってしまいます。</p>
<p>逆に、デザイナーとエンジニアが協力してデザインシステムをしっかり構築しておけば、<br>UI 実装の速度・正確性は飛躍的に向上し、保守性も格段に高まります。<br>そのためにも、<strong>日頃から密に連携し、同じ前提でデザイン・実装を行うことが不可欠</strong>です。</p>
<p>さらに、Compose Multiplatform と組み合わせて Figma MCP を活用すれば、<br>プラットフォームをまたいだ UI 実装の高速化にもつながり、よりスピーディに高品質なアプリ開発が可能になります。</p>
<p>質の高いアプリを素早く届けるためにも、<br>エンジニアとして継続的に知識をアップデートし、より良いワークフローを探求していくことが重要だと感じています。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/kikumido/header.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ（Part 1）：なぜハイブリッドなのか]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-19-cmp-swiftui-hybrid-part1/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-19-cmp-swiftui-hybrid-part1/</guid>
            <pubDate>Fri, 19 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[DroidKaigi 2025で発表した「Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ」の内容をまとめました。Part 1では、KMP+CMPを活用したハイブリッド開発を選んだ背景とアーキテクチャ概要を紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の19日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>こんにちは、KINTOテクノロジーズ Mobile Assistanceマネージャー&amp;KMPチームリードのYena Hwangです。</p>
<p>2025年9月にDroidKaigi 2025で「<strong>Compose MultiplatformとSwiftUIで作るハイブリッドモバイルアプリ</strong>」というテーマで登壇する機会をいただきました。本記事では、発表内容をもとに、私たちKMPチームがハイブリッド開発を導入した背景と実際の経験を共有したいと思います。</p>
<p><a href="https://2025.droidkaigi.jp/timetable/945018/">DroidKaigi 2025セッションページ</a>から発表動画をご覧いただけます。また、<a href="https://speakerdeck.com/yena/kmpteam-yenahwang-droidkaigi2025-compose-multiplatformtoswiftuitezuo-ruhaihuritutomohairuahuri">発表スライド</a>も公開していますので、ぜひご参照ください。</p>
<blockquote>
<p><strong>本シリーズの構成</strong></p>
<ol>
<li><strong>Part 1：なぜハイブリッドなのか</strong> ← 現在の記事</li>
<li>Part 2：実装ガイド（近日公開）</li>
<li>Part 3：SwiftUI連携と技術Tips（近日公開）</li>
</ol>
</blockquote>
<h2>私たちのチーム紹介</h2>
<p>Mobile KMPチームは元々<strong>Androidエンジニアのみで構成されたチーム</strong>でした。しかし、Kotlin Multiplatform（以下KMP）とCompose Multiplatform（以下CMP）を導入することで、<strong>iOSアプリの開発まで担当できるチームへと進化</strong>しました。</p>
<p>これこそがハイブリッドアーキテクチャの大きなメリットの一つです。AndroidエンジニアがKotlinの知識をベースにiOS開発まで担当できるようになり、逆にiOSエンジニアもKotlinを習得することで共有コードの開発に参加できます。各OS専門のエンジニアを別々に確保するより、はるかに効率的なチーム運営が可能です。</p>
<p><strong>チーム構成（3名）：</strong></p>
<table>
<thead>
<tr>
<th>メンバー</th>
<th>スキル拡張</th>
</tr>
</thead>
<tbody><tr>
<td>Yao Xie (<a href="https://blog.kinto-technologies.com/authors/a7e4b35f-7884-5df4-8110-d75ae43bff21/">Tech blog</a>)</td>
<td>Android → KMP, CMP, iOS</td>
</tr>
<tr>
<td>Yonghui Chen (<a href="https://blog.kinto-technologies.com/authors/067b4ddf-6db7-54af-af11-eff39288c89f/">Tech blog</a>)</td>
<td>iOS, Android → KMP, CMP</td>
</tr>
<tr>
<td>Garamoi Choi (<a href="https://blog.kinto-technologies.com/authors/8ff703cc-0401-5075-b94e-ff4b91ead205/">Tech blog</a>)</td>
<td>API, Android → KMP, CMP, iOS</td>
</tr>
</tbody></table>
<p>この<strong>3名のチーム</strong>で、Androidアプリの開発に加え、iOSチームに提供する共有ライブラリ/SDKの開発も担当しています。KMP/CMPで作成したロジックやUIコンポーネントをライブラリ形態でiOS側に提供することで、iOSチームは共有モジュールを統合するだけで同じ機能を実現できます。</p>
<p>現在、私たちのチームはAndroid/iOSアプリで共通利用できるSDKの開発を担当しています。</p>
<h2>なぜハイブリッド開発を選んだのか</h2>
<h3>現実的な制約条件</h3>
<p>昨年（2024年）に、既存のコードベース（Jetpack Compose + SwiftUI）を活用して新しいプロダクトを開始することになりました。プロトタイプ段階から求められた条件は明確でした：</p>
<ul>
<li><strong>High Speed</strong>：非常に短い期間</li>
<li><strong>Low Cost</strong>：少人数の開発チーム</li>
<li><strong>High Quality</strong>：ネイティブアプリレベルのUIパフォーマンス</li>
</ul>
<p>しかし現実では、この3つを同時に達成することは「不可能な三角形」と呼ばれるほど難しいことです。通常は2つを選ぶと、残り1つを諦めなければなりません。</p>
<p><img src="/assets/blog/authors/yenahwang/p14-15.png" alt="不可能な三角形"></p>
<h3>KMP + CMPという解答</h3>
<p>すでにKMPでビジネスロジックを共有していた私たちにとって、2024年にCMPのiOSサポートがBetaになったことは、UIレイヤーまで共有する挑戦を始める絶好のタイミングでした。ロジックだけでなくUIまで共有することで、この「不可能な三角形」を破れると考えました。</p>
<p><strong>このアプローチが約束すること：</strong></p>
<ul>
<li>少人数チームでも迅速なプロトタイプリリース</li>
<li>ネイティブレベルのパフォーマンス</li>
<li>既存のJetpack Composeコンポーネント再利用</li>
<li>ロジックとUIコード共有による効率最大化</li>
<li>タイトなデッドライン達成</li>
</ul>
<p>AndroidとiOSを別々のチームで開発していたら実現できなかった、不可能な三角形を破る実用的な方法でした。</p>
<h2>KMPとCMPとは</h2>
<p>本記事では以下の略称を使用します：</p>
<ul>
<li><strong>KMP（Kotlin Multiplatform）</strong>：ビジネスロジックをAndroid/iOS/Desktop/Web間で共有</li>
<li><strong>CMP（Compose Multiplatform）</strong>：Jetpack ComposeベースのUIをプラットフォーム間で共有</li>
</ul>
<p><img src="/assets/blog/authors/yenahwang/cmp.png" alt="KMPとCMPのアーキテクチャ"></p>
<p>KMPでロジック層を、CMPでUI層を共有することで、効率的なクロスプラットフォーム開発が可能になります。</p>
<h2>ハイブリッドアーキテクチャとは</h2>
<p><img src="/assets/blog/authors/yenahwang/p21.png" alt="ハイブリッドアーキテクチャ全体像"></p>
<h3>レイヤー別技術選択基準</h3>
<p>私たちが確立した実用的なガイドラインです：</p>
<table>
<thead>
<tr>
<th>レイヤー</th>
<th>技術</th>
<th>適した領域</th>
</tr>
</thead>
<tbody><tr>
<td>KMP</td>
<td>Kotlin Shared</td>
<td>ビジネスロジック、APIサービス、データ管理</td>
</tr>
<tr>
<td>CMP</td>
<td>Compose Multiplatform</td>
<td>リスト/カード/フォーム画面、データ中心の詳細画面</td>
</tr>
<tr>
<td>Native</td>
<td>SwiftUI/UIKit</td>
<td>ナビゲーション/ジェスチャー、Map/Camera/Wallet、プラットフォーム固有スタイリング</td>
</tr>
</tbody></table>
<h3>なぜ100% CMPではなくハイブリッドなのか</h3>
<p>もちろん、100% CMPで構築することも可能です。しかし、私たちの場合は以下の理由でハイブリッドを選択しました。</p>
<p><strong>Best of Both Worlds：</strong></p>
<ul>
<li>プラットフォーム別UI/UXガイドライン準拠が可能</li>
<li>ネイティブコンポーネント統合（Maps、Camera）</li>
<li>段階的マイグレーションパス確保</li>
<li>チームの既存専門性活用</li>
</ul>
<p><strong>適したユースケース：</strong></p>
<ul>
<li>既存ネイティブアプリがある場合</li>
<li>プラットフォーム別デザイン要件がある場合</li>
<li>複雑なネイティブ統合が必要な場合</li>
</ul>
<p>以下は、私たちがPoCアプリで実際に使用したハイブリッドアーキテクチャです。</p>
<p><img src="/assets/blog/authors/yenahwang/p22.png" alt="ハイブリッドアーキテクチャ"></p>
<h2>End-to-End Blueprint</h2>
<p>ハイブリッドアーキテクチャを実現するための全体像です。</p>
<table>
<thead>
<tr>
<th>要素</th>
<th>説明</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Module layout</strong></td>
<td>共有コードとプラットフォーム固有コードを明確に分離</td>
</tr>
<tr>
<td><strong>Shared ViewModel</strong></td>
<td>UIからロジックを切り離すための共通契約</td>
</tr>
<tr>
<td><strong>Bidirectional UI interop</strong></td>
<td>CMPビューをネイティブ画面に埋め込む<br>ネイティブコンポーネントをCMP画面に埋め込む</td>
</tr>
<tr>
<td><strong>Navigation &amp; State</strong></td>
<td>プラットフォーム間で動作するナビゲーションと状態管理戦略</td>
</tr>
</tbody></table>
<h2>次回予告</h2>
<p>Part 1では、ハイブリッド開発を選んだ背景とアーキテクチャの全体像を紹介しました。</p>
<p><strong>Part 2：実装ガイド</strong>では、実際のプロジェクト構造、ナビゲーション統合、Koin DIの設定など、具体的な実装方法を詳しく解説します。お楽しみに！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yenahwang/coverImage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[アクセシビリティカンファレンス福岡2025でKTCは「おやつスポンサー」をつとめました]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-18-fukuoka_a11yconf2025/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-18-fukuoka_a11yconf2025/</guid>
            <pubDate>Thu, 18 Dec 2025 10:30:00 GMT</pubDate>
            <description><![CDATA[2025年12月6日に福岡で開催されたアクセシビリティカンファレンス福岡で、KINTOテクノロジーズはおやつスポンサーとして、クッキーとコーヒーとチロルチョコの提供をおこないました]]></description>
            <content:encoded><![CDATA[<p>こんにちは、Engineering Office……もとい、技術広報グループのemimです。</p>
<p>主業務はEngineering Officeのデザイナーなのですが、社外交流や社内の勉強会、さらに今回のようなイベント出展にて技術広報メンバーの手を煩わせる機会が多くなりそうだな……という試算から、少し前から技術広報チームの一員としても活動しています。</p>
<p>この記事は、<a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a>の18日目の記事として執筆しています。</p>
<p>今回は、首題のとおり、アクセシビリティカンファレンス福岡2025にKINTOテクノロジーズ株式会社（以下KTC）が「おやつスポンサー」として協賛したので、そのレポートを行います。</p>
<h1>アクセシビリティカンファレンス福岡とは</h1>
<p><a href="https://fukuoka.a11yconf.net/">https://fukuoka.a11yconf.net/</a></p>
<p>アクセシビリティカンファレンス福岡は、2023年より毎年福岡で開催されている「アクセシビリティ」をテーマとしたカンファレンスです。今年で3回目の開催です。私は過去2回も現地で参加しています。</p>
<p>福岡から始まった地方カンファレンスですが、これまで賛同者による派生版として、名古屋（愛知）、石川、千葉など他地方でも開催されています。「アクセシビリティカンファレンス」は表記も発話も長いので、いずれの場合でも略して「アッカン」と表されます。</p>
<p>さて、アクセシビリティとは、誰もがどんな状況にあっても容易に利用・参加できる状態や仕組み、及びその状態（利用可能性）のことを指す言葉です。物理的な環境や情報、サービスなど、さまざまな面で誰もが平等にアクセスできることを意味し、特にソフトウェア分野では最低限の品質の閾値として捉えても齟齬はないでしょう。</p>
<p>そのため、アクセシビリティへの興味関心の高いあらゆる職種の方は、総じて、高いスキルと感度を持ち合わせていると個人的に考えています。</p>
<p>話をKTCに戻すと、今年はたまたま当社の<a href="https://blog.kinto-technologies.com/posts/2025-12-09-KINTO%E3%83%86%E3%82%AF%E3%83%8E%E3%83%AD%E3%82%B8%E3%83%BC%E3%82%BA-%E7%A6%8F%E5%B2%A1%E3%82%AA%E3%83%95%E3%82%A3%E3%82%B9%E3%83%84%E3%82%A2%E3%83%BC/">福岡オフィスの開所が決まった</a>年でした。そこで、そんなに意識の高い方たちの集まるカンファレンスが、タイミングよく福岡で開催されるならば！と採用とKTCの感度の高さアピールを目的に、なんらかスポンサーができないか、と考えたのが始まりです。</p>
<p>そうは言っても、KTCでの具体的なアクセシビリティに関する取り組みはまだほとんどありません。</p>
<p>過去2回の参加経験から、</p>
<ul>
<li>会場参加者の印象に残りやすい</li>
<li>会場で一定の交流ができる</li>
<li>展示物や具体の成果アピールが必ずしも必要ではない</li>
</ul>
<p>という3点をカバーできる「おやつスポンサー」がKTCには適していると考え、前のめり気味で応募しました。</p>
<h2>おやつスポンサーの内容</h2>
<p>おやつ……！</p>
<p>参加されていない方には何も伝わらないですよね。かくいう当社でも、このゆるふわなスポンサー区分名について「これでは（威厳＝期待値が薄れて）申請が通らないかもしれない」という懸念が上がり、内部的な申請の際に横文字な名称でカモフラージュされたとかなんとか……
（個人的には、これがとてもアッカンらしいユーモラスな名称でいいな、と感じています。）</p>
<p>具体的なおやつスポンサーの役割は、クッキーとコーヒー及びアッカン公式チロルチョコを食べ放題／飲み放題の形で提供する、ブースへのスポンサーです。ブースで担当者がクッキーやコーヒーを提供するついでに、自社の事業内容などをアピールする機会を得られます。</p>
<p>この軽食サービスは、カンファレンスの最初の開催の2023年当初から提供されています。</p>
<p>他のカンファレンスに比べ、アッカンでは色々な障害がある人でも「ここにいる。^[「ここにいる。」とは、アクセシビリティカンファレンス福岡2023のテーマでした。]」ことが当たり前の世界です。長時間同じ姿勢がしんどいような方や、ずっと同じ所に居続けることに不安のある方もいるかもしれません。会場内には託児所などもあるので、子供の声も聞こえていました。そんな方々でもカンファレンスを楽しめるように、各セッションの時間が短めだったり、休憩時間が長めだったりという工夫がなされています。</p>
<p>そこにマッチするのが、コーヒー^[中にはコーヒーが苦手な方もいらっしゃったようで、提供はコーヒーだけなので少し窮屈な思いをさせてしまったようです。]とクッキーです。甘いものと苦いもので、疲れた脳と心を癒やしてくれます。会場のみの提供となってしまいますが、参加者が長丁場のカンファレンスで疲れないように配慮されたサービスです。</p>
<p><img src="/assets/blog/authors/emim/2025-12-23-fukuoka_a11yconf/2025-12-23-fukuoka_a11yconf_01.png" alt="3枚の写真のコラージュ：左上には、アッカン福岡オリジナルのチロルチョコの上に乗るKTCメンバーのSNSアイコンアクスタと、端にくもびぃ／左下には、2種のくもびぃのプリントされたクッキーの写真／右半分には、ブーススタッフを務めるKTC竹中と辻の横にコーヒーサーバー2つとKINTOテクノロジーズのロゴの入った紙コップの束。コーヒーサーバー前には「RECさんの！！コーヒー」と手書きで書かれている。"></p>
<p>昨年まではアイシングクッキーが配られていましたが、今年はシンプルなクッキーに希望の図柄のプリントされたクッキーに変更となっていました。</p>
<p>後日、隣席の同僚が「なんだ〜、アイシングの方がいいじゃん……って最初思ったけど高級クッキーじゃん！！」と小躍りしたクッキーです。</p>
<p>コーヒーは、福岡で有名な<a href="https://rec-coffee.com/">REC COFFEE</a>とスターバックスのものがサーバーで用意されました。</p>
<h2>カンファレンスの様子（と前夜祭の様子）</h2>
<h3>前夜祭について</h3>
<p>カンファレンスの前日夜には、LINEヤフーさんの博多オフィスにて、<a href="https://lycorptech-fukuoka.connpass.com/event/375200/">アクセシビリティカンファレンス福岡 前夜祭</a>が開催されていました。</p>
<p>私は別件の予定があり不参加だったのですが、当社からは<a href="https://blog.kinto-technologies.com/posts/2025-12-09-What-I-want-to-achieve-as-blind-person-at-mobile-company/">11月からジョインした辻</a>がLTで登壇し、スクリーンリーダーの<a href="https://www.nvda.jp/index.html">NVDA</a>を用いてKindleの書籍を読む方法（「『聴く』読書から『読む』読書へ」という副題が添えられています）が紹介された他、福岡オフィスのメンバーなどが聴講者として参加をしました。辻については、転職の記事が公開されていない時期だったこともあり、会場でざわめきを作ったと聞いています。</p>
<p>さらに後から聞いた感想では、自称アクセシビリティ初心者な人たちでも「自分にもできるかもしれない」となれるような、代替テキストについてなどの話題提供が心に残ったそうです。当社の参加メンバーから、当日福岡にいなかったチームメンバーにも「ぜひ聞いてほしかった」との声を聞きました。</p>
<h3>カンファレンス当日について</h3>
<p>カンファレンス当日は、開場より少し早めに会場入りをしてブース設営などを行いました。我々は他のカンファレンスなどでも利用する、KINTOのキャラクターである「<a href="https://corp.kinto-jp.com/mascot/">くもびぃ</a>」とともにブースに立ちました。</p>
<p>ちなみに、アッカンは他のカンファレンスやセミナーに比べて、運営メンバーにも参加者にも女性が多い印象です。ブースに来てくれる方々も、すれ違う方々も、開場前のアッカン福岡実行委員会やボランティアスタッフなど、とにかく多くの方から「かわいい〜」という声を聞けたのが嬉しかったです。</p>
<p>スポンサーブースエリアは本会場と別ながらも、大きなスクリーンとクリアな音声（スピーカー）で、カンファレンス本編の内容がサテライト放映されていました。スポンサーブースにずっといたとしても、講演内容が楽しめるようになっています。</p>
<p>KTCブースでは、前日にLTを行った辻も端にずっと居て、普段利用しているスクリーンリーダーや点字ディスプレイを利用し、デモの披露など行っていました。途中、カンファレンスの手話通訳の方が「視覚障害者がどのように情報を受け取られているのか」と話しを聞きにきてくれたり、聴覚障害の方も点字ディスプレイの様子を見に来てくれたりしたそうです。</p>
<p><img src="/assets/blog/authors/emim/2025-12-23-fukuoka_a11yconf/2025-12-23-fukuoka_a11yconf_02.png" alt="3枚の写真のコラージュ：左上に前夜祭での辻の登壇スライドのキャプチャー（登壇時利用した書籍『誰のためのアクセシビリティ？』についての詳細／左下に、各スポンサーのポスター／右に、KINTOのキャラクターくもびぃを頭と胸に付けた辻）"></p>
<p>他のイベントではスポンサーブースに居ると、なかなかカンファレンス本編を楽しめなかったりするのですが、その辺りもみんなで楽しめるように計算されているように感じます。「お客さんと楽しむ」だけではないことが幸いし、スポンサーブースにいただけでも学びの多かったKTCスタッフは「せっかくなら、アクセシビリティに興味のあるメンバーも参加できたらよかった…次回はぜひ！」と話していました。</p>
<p>また、現地ではカンファレンスの後に懇親会も開催されました。KTC福岡のスタッフ陣が、熱冷めやらぬまま福岡の各社の方々と情報交換を行い「引き続き勉強会などを開催しましょう！」と意気投合したと聞いています。大変楽しみです。</p>
<p>この記事では本編の様子に全然触れられていないのですが、前夜祭と当日のXの様子が公式にまとめられています。当日Xでは、ハッシュタグ付きポストの多さからか度々「本日のニュース」に取り上げられたりしていました。少しでも盛り上がりを感じたい方は、以下のまとめをご覧ください。</p>
<p><a href="https://posfie.com/@FukuokaA11yconf/p/OD58dzG">https://posfie.com/@FukuokaA11yconf/p/OD58dzG</a></p>
<p>後日アーカイブも公開されるそうです。</p>
<p><a href="https://x.com/FukuokaA11yconf/status/1997858020148330885?s=20">https://x.com/FukuokaA11yconf/status/1997858020148330885?s=20</a></p>
<p>アクセシビリティカンファレンス福岡実行員会の皆様及び、ご登壇の皆様、のみならず各スポンサーブースの方々、オフライン／オンラインの各参加者の皆様、お疲れ様でした。そして交流くださって、ありがとうございます。また来年も（開催が予告された）アッカン福岡に参加できることを楽しみにしています。</p>
<h2>おまけレポート：ブース出展に伴う制作物</h2>
<p>今回、ブースでの配布物にくもびぃの紙クリップを用意しました。過去、別の機会でも配布していたノベルティです。せっかくなので触って形のわかってもらえるもの、そして使えそうなもの、かつお菓子といっしょにもらっても困らないサイズのもの、ということで選びました。この「触ってわかる」という点は好評でした。</p>
<p>さらに、スタッフとして参加していないのに「これがあると『今日会場に来ているのはこの人』って説明ができるんです！」と参加スタッフのアクスタ（アクリルスタンド）を<a href="https://blog.kinto-technologies.com/authors/acf3af63-9aa2-58be-b785-971d0a0d2a75/">ゆかちさん</a>が作ってくれました。まさかのアクスタデビューです。</p>
<p>土台の部分にはNFCタグが組み込まれており、個々人のSNSに繋がっていたりします。これが（意外にも？）好評で、他社の広報チームでも「真似したい」という意見をもらいました。</p>
<p>ちなみに辻に「視覚障害の場合は、名刺をもらうよりもNFCの方が便利ですか？」と事前に確認をしたところ、「会場（うるさい所）で忙しくNFCで操作するよりも、家に帰ってから名刺を改めて確認できる方がいいように思います」との意見をもらいました。なるほど。</p>
<p><img src="/assets/blog/authors/emim/2025-12-23-fukuoka_a11yconf/2025-12-23-fukuoka_a11yconf_03.png" alt="2枚の写真のコラージュ：左に、アッカン当日現場でブースに立っていた新田、佐藤、辻、竹中、守谷のアクスタを撮った写真／右に、くもびぃの紙クリップの接写。くもびぃの形に型抜きされたクリップ。"></p>
<p>今回ブースにいたメンバーは、SNS上でアイコンの方が知られている人も多かったです。そのため「この人だよ」と紹介をするのにもアクスタが役立った、というポイントも補足しておきます。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/emim/2025-12-23-fukuoka_a11yconf/2025-12-23-fukuoka_a11yconf_title.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[TURTLEデザイン分科会：コミュニティの力でデザインを進化させる]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-18-turtle-design-subcommittee/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-18-turtle-design-subcommittee/</guid>
            <pubDate>Thu, 18 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[トヨタグループ内コミュニティ「TURTLE」にて、2025年4月にデザイン分科会を設立しました。アンバサダーとして分科会の勉強会企画・運営に携わったので、振り返りと今後の意気込みをまとめます。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の18日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>KINTOテクノロジーズのクリエイティブグループ所属の福田です。<a href="https://www.kinto-technologies.com/company/osakatechlab/">Osaka Tech Lab</a>に所属しています。
トヨタグループ内コミュニティ「TURTLE」にて、2025年4月にデザイン分科会を設立しました。<br>アンバサダーとして分科会の勉強会企画・運営に携わったので、振り返りと今後の意気込みをまとめます。</p>
<h3>TURTLEとは</h3>
<p>TURTLE（Toyota cloud UseR TechnicaL alliancE）は、トヨタグループのクラウド技術に関するエンジニアが集まり、知見を共有するために設立されたコミュニティです。<br>「社外コミュニティでは話せないことが多く、参加しづらい」「社内にインプット・アウトプットの場がない」「同じものを複数社で開発しているのはもったいない」<br>こうした現場の声を背景に、トヨタグループとしてエンジニアのコミュニティをみんなで作ろうという想いから誕生しました。</p>
<h3>なぜデザイン分科会なのか</h3>
<p>近年、ビジネスにおけるデザインの重要性は急速に高まっています。<br>デザインは単なる見た目の美しさを超え、ユーザー体験やブランド構築、サービスの使いやすさにまで影響を及ぼし、企業活動の前線に立つ存在となっています。</p>
<p>しかし、私が所属するクリエイティブグループでは、トヨタグループ内でデザインに関する知見やノウハウを共有できる場がほとんどありませんでした。<br>特に、デザインに関連する事例や成果物には社内専用の情報が多く、社外コミュニティでは安心して話すことが難しい状況もありました。</p>
<p>こうした背景から、Osaka Tech Labに所属し、TURTLEの他分科会で企画・運営を担っているアンバサダーに相談しました。<br>その結果、グループ横断でデザイン領域の知見を共有できる場の必要性を再認識し、TURTLE事務局に相談した後、2025年4月に『デザイン分科会』が正式承認されました。  </p>
<h2>分科会設立までの流れ</h2>
<ul>
<li><strong>2025年2月</strong><br>TURTLE総会で設立準備中の案内<br>デザイン分科会の勉強会企画・運営を共に担うアンバサダー募集開始</li>
<li><strong>2025年3月</strong><br>デザイン分科会アンバサダーのキックオフMTG開催<br>分科会設立申請</li>
<li><strong>2025年4月</strong><br>デザイン分科会設立が事務局にて正式承認</li>
</ul>
<p><img src="/assets/blog/authors/harunori_fukuda/20251218/main-turtle-202502.jpg" alt="TURTLE総会でのデザイン分科会開設準備発表時の様子、左端に登壇を行う福田が写り込む">
2025年2月のTURTLE総会での案内時の写真です。この開設準備発表がきっかけでトヨタグループ他社のアンバサダーの仲間を集めることができました。</p>
<h2>活動実績（2025年4月～9月）</h2>
<p>設立当初、デザイン分科会には2社から6名のアンバサダーが参加し、隔週で定例会を開きながら情報共有と関係構築に努めてきました。  </p>
<h3>勉強会開催</h3>
<p>半年間でオンライン勉強会を2回実施し、質疑応答や投票など参加型コンテンツを簡単に作成できるインタラクションツール「<a href="https://www.slido.com/jp">slido</a>」や、チームでのアイデア出しや共同作業をスムーズに行えるオンラインホワイトボードツール「<a href="https://www.figma.com/ja-jp/figjam/">FigJam</a>」を活用することで、オンラインでも双方向のコミュニケーションを重視した勉強会を実現しています。</p>
<ul>
<li>第1回：「コーポレートサイトのリニューアル」<br>コンセプトの重要性やデザインプロセスを共有</li>
<li>第2回：「デザインシステム導入とFigma運用の最適化」<br>実践的なツール活用法を紹介</li>
</ul>
<h2>まとめ</h2>
<p>2025年12月時点で、デザイン分科会の参加企業数は30社、参加者数は179名に達しています。<br>明日（2025年12月19日）には第3回勉強会「UX入門勉強会」の開催も予定しています。</p>
<p>現在、アンバサダーの構成は2社7名による運営です。<br>今後もグループ全体でデザインに関する知見やノウハウを共有し、新たなグループ会社からのアンバサダーの仲間集めにも取り組み、学びの多い場を目指します。</p>
<p>デザイン分科会は「デザインをビジネスの前線に」というビジョンのもと、トヨタグループ全体でのデザイナー同士の知見共有を加速させ、より良いユーザー体験の創出を目指して活動を続けていきます。</p>
<p>最後まで読んでいただき、ありがとうございました！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/harunori_fukuda/20251218/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Lambda×Strands AgentsでGA4への問い合わせ画面を構築した話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-17-lambda_strands-agents_ga4/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-17-lambda_strands-agents_ga4/</guid>
            <pubDate>Wed, 17 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Lambda上でStrands Agentsを稼働させ、GA4に問い合わせるWeb画面を構築した事例]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の17日目の記事です🎅🎄</p>
<h2>1.はじめに</h2>
<p>こんにちは！   </p>
<p>KINTOテクノロジーズのデジタル戦略部DataOpsG所属の上平です。<br>普段は社内のデータ分析基盤と「cirro」というAIを活用した社内アプリの開発・保守・運用を担当しています。   </p>
<p>以前、下記の記事で、Strands Agentsの導入前の検証事例をご紹介させていただきました。<br><a href="https://blog.kinto-technologies.com/posts/2025-07-18-try-strands-agent/">https://blog.kinto-technologies.com/posts/2025-07-18-try-strands-agent/</a></p>
<p>当時より発展し、現在「cirro」はAmazon Bedrock × Strands Agentsという構成になっています。<br>本記事では、「cirro」にStrands Agentsを利用し、GA4への問い合わせ画面を構築した事例を紹介します。</p>
<h2>2.本記事の対象者</h2>
<p>本記事は、Amazon BedrockをConverse APIやInvoke Model経由で利用した経験があり、<br>Strands Agents等エージェント開発SDKへの切り替えを迷っている方が対象となります。</p>
<h2>3.GA4への問い合わせ画面構築の背景、概要</h2>
<h3>3.1.背景</h3>
<p>GA4 (Google Analytics 4) は、Webサイトやアプリに <strong>「誰が」「どこから来て」「何をしたか」</strong> という、<br>訪問者のアクセス状況を分析できる非常に便利なサービスです。<br>しかし、その分析できる項目の種類があまりにも多いため、<br>初めて使う方にとっては、どこを見たらいいのか、どうデータを読み解けばいいのか、かなり難しいです。   </p>
<p>GA4のデータは、主に次の2種類の要素で構成されています。</p>
<ul>
<li>ディメンション：データを分類したり切り口としたりする「属性」を示す項目です。   <ul>
<li>「どこから」アクセスしたか（例：Google検索、Twitterなど）</li>
<li>「どの国・地域」から来たか</li>
<li>「どのページ」を見たか</li>
<li>「どんなデバイス」を使ったか（例：スマートフォン、PC）</li>
</ul>
</li>
<li>メトリクス：具体的な量や頻度を表す「数値」を示す項目です。   <ul>
<li>Webサイトへのアクセス数（セッション数）</li>
<li>特定のページが表示された回数（表示回数）</li>
<li>ユーザーがサイトに滞在した時間</li>
<li>サイトを訪れたユーザーの数</li>
</ul>
</li>
</ul>
<p>これらのディメンションやメトリクスの項目が200項目・300項目の規模で存在するため、<br>慣れていないと欲しい情報にたどり着くこと自体が課題です。</p>
<p><a href="https://support.google.com/analytics/table/13948007?hl=ja">Google公開資料：アナリティクスのディメンションと指標</a></p>
<p>この課題について、例えば「今週のアクティブユーザ数が知りたい」「アクセス数が多いページは？」等、<br>自然言語で問い合わせできるようになれば、サイトやアプリの管理者が簡単に傾向を見ることができ<br>改善に役立つのでは…と構築に着手しました。</p>
<h3>3.2.概要</h3>
<p>GA4への問い合わせ画面は、よくあるシンプルなチャット画面です。</p>
<p><img src="/assets/blog/authors/kamihira/202512-lambda_strands-agents_ga4/cirro_disp_image.png" alt="cirroの画面イメージ"></p>
<p>以下の流れでユーザの問い合わせに対し、GA4の内容を応答します。</p>
<ol>
<li>ユーザの問い合わせを入力</li>
<li>AIがGA4のAPIから現在有効なディメンションとメトリクスの一覧を取得</li>
<li>AIが適したディメンションとメトリクスを選択し、GA4へ問い合わせる</li>
<li>GA4の結果をAI側で表示用の形式に変換し、回答を出力</li>
</ol>
<p>シンプルですが、ユーザはディメンションやメトリクスを把握せずともAIを介してGA4のデータにアクセスできます。
初心者には単純にデータアクセスの補助として、<br>ディメンションやメトリクスを全て把握していない中級者の方には、<br>どの項目で必要なデータが取得できそうかの当たりをつけることに役立ちます。</p>
<h2>4.要素技術・アーキテクチャの紹介</h2>
<p>ここからは、「GA4への問い合わせ画面」を構築時に使用した要素技術やアーキテクチャをご紹介していきます。</p>
<h3>4.1.Strands Agentsとは？</h3>
<p>2025年5月16日にAWS Open Source Blogで公開されたオープンソースのAIエージェントSDKです。<br>以下は、AWSのAmazon Web Services ブログで公開されている図です。</p>
<p><img src="/assets/blog/authors/kamihira/202508-try-strands-agents/AWS-agentic-loop.png" alt="AWSブログからの引用図"></p>
<p>図のように、ツールを備えたAIを実装するには、Agentic Loopと呼ばれるループ処理が必要です。<br>この処理では、AIの応答がユーザーへの回答なのか、ツールを使ってさらに処理を進めるべきかを判断します。   </p>
<p>Strands Agentsを使えば、このループ処理を開発者が自前で実装することなく、AIエージェントを構築できます。</p>
<p><a href="https://aws.amazon.com/jp/blogs/news/introducing-strands-agents-an-open-source-ai-agents-sdk/">参考、図の出典：Strands Agents – オープンソース AI エージェント SDK の紹介</a> </p>
<h3>4.2.cirroってどんなサービス？</h3>
<p>私の所属するDataOpsGでは下記ミッションを目的に活動しています。   </p>
<p>「全社のデータを分析用に整備、AI-Readyなデータ分析基盤を提供することでデータドリブンな意思決定を支援する」   </p>
<p>AIは、非エンジニアでも容易にデータにアクセスするための入り口として注目しており、<br>cirroはDataOpsGで独自に開発・運用している生成AIの活用基盤です。<br>UI等可能な限り共通化し、チャットや問い合わせ画面のような画面構成であれば、<br><strong>プロンプトと参照データの追加のみで</strong>金太郎飴的に量産できる構成となっています。</p>
<p><img src="/assets/blog/authors/kamihira/202512-lambda_strands-agents_ga4/cirro_system_architecture.png" alt="cirroの構成図"></p>
<p>図のように、①UI／コンピューティング機能と、②システムプロンプトや参照データを分離し、<br>②を切り替えることで1つのシステムで複数のAIの機能を実現しています。<br>また、URLパラメータで使用するツールを指定することができ、ツールを持ったAIエージェントとして稼働することも可能です。   </p>
<p>今回はこの機能を利用し、cirroでGA4への問い合わせ画面を構築しました。</p>
<h3>4.3.GA4の問い合わせ用のツール</h3>
<p>Googleが公開する2つのAPIをStrands Agentsが使用するツールとしてラップし構築しました。<br>cirroはツールを使用して、「利用できるディメンションとメトリクスの取得」と「ユーザの質問に合ったデータをGA4から取得」を実現しています。</p>
<h4>4.3.1.getMetadata</h4>
<p>GA4で使用できるメトリクスとディメンションを取得するAPI<br><a href="https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/getMetadata?hl=ja">getMetadata詳細</a></p>
<h4>4.3.2.runReport</h4>
<p>指定したプロパティID、メトリクス、ディメンション等から、合致するデータを取得するAPI<br><a href="https://developers.google.com/analytics/devguides/reporting/data/v1/rest/v1beta/properties/runReport?hl=ja">runReport詳細</a></p>
<h2>5.実装時のポイント</h2>
<p>本記事では、MCPを使用せずツールの形で機能追加を実現しました。
ここではMCPを使用しなかった理由や、ツールでの実装を採用したメリットをご紹介します。</p>
<h3>5.1.MCPを使用しなかった理由</h3>
<p>cirroはLambdaがコンピューティングエンジンです。<br>そもそもがツールでできることについて、より工数をかけてMCPサーバを立てることにメリットを見いだせなかった背景もありますが、<br>Lambda主体とすることで以下の課題があり、確実に機能提供するためツールでの実装を採用しました。   </p>
<ul>
<li>Lambdaでの起動方法や、起動時のオーバヘッドの課題   <ul>
<li>Googleが公開するMCPサーバはローカル稼働前提になっている</li>
<li>別途ECS等を使用し自前でリモートサーバとして用意する方法もあるが、Lambda主体の維持管理が容易なcirroのコンセプトと異なる。</li>
</ul>
</li>
<li>Lambdaの特性上、うまくMCPサーバと通信できるかの課題 <ul>
<li>MCPサーバとAIの通信は、調べる限り別プロセスを立ち上げ、標準出力を介してやり取りしている。
Lambda上でMCPサーバと通信できるか調査期間では確証が持てなかった。</li>
</ul>
</li>
</ul>
<h3>5.2.ツールでの実装を採用したメリット</h3>
<p>ツールとして実装することによるメリットもありました。</p>
<h4>5.2.1.クレンジング処理の追加が容易</h4>
<p>API「getMetadata」は大量のディメンションやメトリクスの内容を応答します。<br>おそらくすべてのディメンションやメトリクスが必要なケースは少ないのではないでしょうか？<br>私の環境でも、使用していなかったり、想定するユーザ（GA4初心者）にとって不要と思われる項目を、
ツール内で除去しています。<br>AIに与える情報を削減することで、精度の向上や応答速度、トークン消費量を抑える工夫をしました。</p>
<h4>5.2.2.カスタムが容易</h4>
<p>ローカルでも稼働するツールとすることで、実装と確認を容易にしました。<br>また各クライアントで資源を共有するMCPサーバでなく、ツールとして独立するため、<br>案件ごとの個別の変更を他に影響なく実施できます。<br>修正前後の比較も、新たなツールと旧ツールそれぞれ共存させ、確認することが容易です。</p>
<h4>5.2.3.親子構成のマルチエージェントが構築しやすい</h4>
<p>ツールはただの関数です。当然関数内でBedrock等AIのサービスを呼び出すことができます。<br>AIを呼び出すツールを用意すれば、メインのAIとツール内の子AIが存在するマルチエージェントの構成が組めます。   </p>
<p>本記事では採用しませんでしたが、例えば自由記述の文面の前処理や、AIの出力内容のチェック等、<br>ロジックベースの処理では難しい内容を子エージェント側で処理させるようなこともできそうだ…と考えています。   </p>
<p>MCPが注目される昨今ですが、ツールにはツールのメリットがあり小規模で個別開発が発生するようなケースの場合、
まだまだツールでの実現の選択肢は残るだろうな...と個人的には思っています。</p>
<h2>6.おわりに</h2>
<p>今回は、デジタル戦略部で展開しているAI活用システム「cirro」を活用し、
GA4への問い合わせ画面を構築した事例を紹介させていただきました。</p>
<p><strong>「AIにツールを持たせる」というと難しく感じるかもしれませんが、Strands Agentsを使えば驚くほどシンプルです。</strong><br>元々「Converse API」を使用したただのチャットボットだったcirroが、
「Strands Agents」に切り替えるだけで、ツールを使ってAPIを呼び出すエージェントに進化しました。   </p>
<p>AIエージェント開発に興味はあるけれど、何から始めればいいか分からない...という方は、
まずStrands Agentsで簡単なツールを1つ持たせてみることから始めてみてはいかがでしょうか？<br>きっと、AIができることの幅が一気に広がる体験ができると思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[注力テーマ：リリースファーストの現在地]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-17-releasefirst/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-17-releasefirst/</guid>
            <pubDate>Wed, 17 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[本記事は、KINTOテクノロジーズの2025年注力テーマの1つ「リリースファースト」について、Engineering Officeの視点から1年間の取り組みと変化を振り返ります。]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<p>この記事は<a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の17日目の記事です🎅🎄</p>
<p>Engineering OfficeのNaitoです。KINTOテクノロジーズ（以下、KTC）には4つの2025年注力テーマ（インテンシティ、AIファースト、ユーザーファースト、リリースファースト）があります。
<a href="https://blog.kinto-technologies.com/posts/2025-09-05-FindyTeamAward/">以前のブログ</a>で2025年注力テーマの1つ、リリースファーストについてお伝えしました。
今回はリリースファーストにまつわるこの1年の取り組みや変化について紹介します。
最初に認識合わせをしておくと、KTCではリリースファーストは、「アイディア創出からリリースまでの期間を短くし、ユーザー・顧客に早く価値を提供する」としています。</p>
<h2>2025年1月〜3月</h2>
<p>Engineering Officeが立ち上がりました。
メンバーは<a href="https://blog.kinto-technologies.com/authors/6faabe6f-aeec-50b8-a109-25ba1dde5bb4/">ahomu</a>と私（当時入社3ヶ月）の2人です。
Findy Team+の導入・活用支援という切り口で開発チームとの対話を通し、各チームの状況やメンバーのことを知り始めた状態で、その結果わかったことは以下のようなことでした。</p>
<ul>
<li>自分たちの現状を定量的に把握できているのは一部のチームにとどまっている</li>
<li>Findy Team+を導入したが、うまく計測・可視化できないチームもある</li>
<li>Findy Team+の1機能である、プロセスタイム分析（JiraをINPUTとして各工程ごとのリードタイム）の計測・可視化ができるチームはゼロ</li>
<li>開発パフォーマンスの計測・可視化に取り組んでいるチームにおいても一部の人で計測データを見て試行錯誤しながら、改善活動に取り組んでいる。チーム全体には広がっていない</li>
<li>計測・可視化に関心があっても先行しているチームがどんな状況なのかはわからず、情報収集ができない</li>
</ul>
<h2>現在（2025年末）</h2>
<p>Engineering Officeはなんと4名に増えました。それぞれの専門領域を最大限発揮しつつ、コラボレーションしてチームで活動しています。
「一緒にやろう」と複数チームから声をかけていただき（ありがたい！）、私たちはこの1年間で約20チームに関わらせていただきました。
各チームは、</p>
<ul>
<li>注力テーマに紐づいた目標を設定し、取り組んでいる</li>
<li>リリースファーストの第一歩は自分たちの現状を定量的に定性的に把握することからという意識が全社に広がっており、社内の主なプロダクト、開発チームはFindy Team+を導入して開発パフォーマンスの計測を行っている</li>
<li>Findy Team+ではFour Keysについては全チームが計測可能な状態になっている</li>
<li>Findy Team+を見て話し合うことがチーム全体に広がっているところも増え、改善事例が少しずつ共有されている</li>
<li>Findy Team+の1機能である、プロセスタイム分析（JiraをINPUTとして各工程ごとのリードタイム）は3プロダクトが計測できている</li>
<li>チームを超えたリリースファーストのタスクフォースが発足し、業務のかたわら、プロダクト横断でリリースファーストを実現するための仕組みづくりを実施中</li>
<li>ある部門では部門全体で役割分担や開発プロセスを見直し、役割間・チーム間のコラボレーションを強化中</li>
</ul>
<p><img src="/assets/blog/authors/naito/2025-12-22ReleaseFirst/FindyTeam_before_after.png" alt="サイクルタイム平均値の値が大幅に下がっているが、中でもオープンからレビューまでの平均時間が31.8hから9.3hに、レビューからアプルーブまでの平均時間が68.3hから19.8hに短縮している様子のキャプチャー"><em>チームのレビュープロセスを見直した結果、レビュー時間が向上した事例</em></p>
<p>この変化がどうやって起こったのか？正直、各チームの頑張りの賜物なわけですが。Engineering Office（横軸組織）から見た目線でお伝えしていきたいとおもいます。</p>
<h2>注力テーマを理解する</h2>
<p>各プロダクトにおいては注力テーマの意味はなんとなくわかるけど具体的にどういうこと？というのが当時の状況でした。
そのため、最初の2−3ヶ月は推進メンバー全員で注力テーマについて社内に浸透させるところから始まりました。
まずは社内で言葉の認識合わせを行い、「なぜこれがいまKTCにとって大事なのか」また「リリース期間だけ短くなればよいということではなく、ユーザー・顧客に向き合い、価値を提供することに意味がある」「ユーザーファーストをないがしろにしてリリースを優先しても技術的負債になってしまう」ということを伝える活動を続けました。</p>
<h2>プロダクトやチームごとに自分たちにとってのリリースファーストを考える</h2>
<p>注力テーマについて各人が理解すると、次はプロダクトごと、チームごとに自分たちは注力テーマを実現するために何をしたらいいのか？自分たちの現状ってどんな感じなのか？ということが話し合われました。
各方面で注力テーマについて考えた結果、私のところにも以下のような声が届いてきました。</p>
<ul>
<li>リリースが早くなったかどうかBefore/Afterをわかるようにしたい</li>
<li>AIを活用して実装スピードあげたい</li>
<li>リリースまでの流れってどうなっているのか</li>
<li>統合テストに時間かかっている</li>
<li>ユーザーが遠くて価値が届いているのかわからない</li>
<li>設計・実装より前のフェーズにボトルネックがあるように思うが計測できていない</li>
<li>自チームだけでは前に進めるのは難しい。関係者で集まって話し合いたい</li>
</ul>
<p>こういった課題感共有や話し合いを経て、プロダクトやチームで注力テーマを実現するための自分たちのチームの目標を定めていきました。</p>
<h2>取り組み</h2>
<p>以下はリリースファーストに関する取り組みの一部です。複数のチームで同様の取り組みをしているケースもあります。</p>
<ul>
<li>なぜ開発パフォーマンスを計測するのかとFindy Team+導入の説明会</li>
<li>GitHubのブランチ戦略・運用ルール変更</li>
<li>PRレビュープロセスの改善</li>
<li>Findy Team+を見合う会</li>
<li>チームのケイパビリティの可視化</li>
<li>Jiraを使った工程ごとのリードタイムの計測（そのためにJiraの運用ルール見直し）</li>
<li>全社横断でのAI利活用状況の計測・可視化</li>
<li>Value Streamの可視化</li>
<li>部門全体でのValue Streamの改善</li>
<li>アジャイルトレーニングの実施</li>
<li>ユーザー視点で要求を整理し、PRDを作成するワークの実施</li>
<li>プロダクト毎のテスト環境構築</li>
<li>インシデント対応プロセス改善</li>
<li>テストデータ作成方法の見直し</li>
<li>Techラウンドテーブル（チーム間の情報共有）</li>
<li>トヨタ生産方式勉強会入門編</li>
</ul>
<p>ここに挙げたのは私が関わった取り組みだけですので、社内には他にもたくさんの取り組みがあります。</p>
<p><img src="/assets/blog/authors/naito/2025-12-22ReleaseFirst/vsm.jpg" alt="Value Stream Mappingの改善事例：ムダな時間ムリ・ムラの可視化を行った"><em>ボトルネックを特定し、Value Streamを改善した事例</em></p>
<p><img src="/assets/blog/authors/naito/2025-12-22ReleaseFirst/TechRoundTable_Osaka.jpeg" alt="Techラウンドテーブル開催の様子：複数の男女が付箋一覧を囲み意見を交わしている写真"><em>Techラウンドテーブル @Osaka Tech Lab</em></p>
<h2>まとめ</h2>
<p>上記のような取り組みを1年間繰り返し続けてきた結果が冒頭のような変化に繋がっています。
当初のリリースファーストの目標を実現しているチームもまだ途中のチームもいますが、どのチームも自分たちの行動で起きた変化は実感していることとおもいます。</p>
<p>社内の風土としてボトムアップで様々なことが進むことが多いです。そのため、今回の注力テーマの各取り組みにおいてもまずは自分のチームの範囲でやってみようという形で始まることが大半でした。</p>
<p>一方で、KTCのプロダクトは複数のチームで構成されていることが多いです。リリースファーストやユーザーファーストを実現するためには、プロダクト全体を俯瞰する視点が欠かせません。
自チームが整うと、自然とメンバーの視野も広がりより広い範囲に目を向けるようになります。そのため、2025年後半にはチームを超えた「リリースファースト」の取り組みが増えてきました。
チームを超えると部門が異なる人や他の役割の人たちと課題を共有し、目線合わせをしていく必要があるのですが、専門領域や役割の違いにより見えているもの感じているものが異なる場合も多く、ゴール設定や進め方で躓くことも多いです。</p>
<p>このような時にはビジネス・技術・プロセスを統合して見えているマネージャーのアドバイスや方向性づけ、判断というのがとても大事になります。
それによりチームを超えた様々な関係者がまとまり、取り組みが加速するというの目の当たりにしています。</p>
<p>今年の変化が成果となって表れるのが来年です。来年、リリースファースト・ユーザーファーストの活動を加速させ、より多くの成果に結びつけていくためにはマネージャーの関与は不可欠です。
本当の意味でリリースファーストを実現するプロダクトがどれだけ増えるのか今からとても楽しみです。
リリースファースト、ユーザーファーストは一年で終わる話ではありません。プロダクト開発に携わる以上は継続的に取り組んでいくテーマです。</p>
<p>Engineering Officeは来年も引き続き各チームとともにリリースファースト、ユーザーファーストに取り組んでいきます。</p>
<h2>お知らせ</h2>
<p><a href="https://2026.scrumgatheringtokyo.org/index.html">Regional Scrum Gathering Tokyo 2026</a>のDay1（1/7）に出ます。具体の取り組みのいくかについて話す予定です。みなさんよかったら見に来てくださいね。</p>
<p><a href="https://confengine.com/conferences/regional-scrum-gathering-tokyo-2026/proposal/23544">「リリースファースト」の実感を届けるには〜停滞するチームに変化を起こすアプローチ〜</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Lake Formationのハイブリッドアクセスモードとタグベースアクセス制御を検証]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-16-lakeformation-hybrid-access-mode/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-16-lakeformation-hybrid-access-mode/</guid>
            <pubDate>Tue, 16 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Lake Formationのハイブリッドアクセスモードとタグベースアクセス制御について、サービスの仕様を調査し、設定方法と注意点を記す]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>KINTOテクノロジーズのデジタル戦略部DataOpsG所属の西です。<br>普段は社内のデータ分析基盤の開発・保守・運用を担当しています。</p>
<p>データガバナンスの一環としてのデータ分析基盤へのアクセス制御のため、AWS Lake Formation、その機能であるハイブリッドアクセスモードとLFタグを扱う機会がありました。</p>
<p>はじめてLake Formationを触ると、その仕様や用語を理解するだけでも大変だと思います。（自分は、なかなか苦労しました。）</p>
<p>そのため本記事ではLake Formationの主要な概念や設定方法をご紹介します。</p>
<ul>
<li>Data lake locations、Data permissions、Data locations、IAMAllowedPrincipalsについて</li>
<li>ハイブリッドアクセスモードについて</li>
<li>LFタグによるアクセス制御について</li>
<li>ハイブリッドアクセスモード、LFタグによるアクセス制御の設定方法</li>
</ul>
<p>:::message
なお、本記事はGlue Data Catalog（以下、Glueデータカタログ）上のリソースに対するLake Formationのアクセス制御を前提として書かれています。
:::</p>
<p>またLake Formationを触って気づいた注意点として、下記を本稿後半の<a href="#%E6%B3%A8%E6%84%8F%E7%82%B9">注意点</a>に記します</p>
<ul>
<li>ハイブリッドアクセスモード運用における新規作成IAMロールの関係について </li>
<li>ハイブリッドアクセスモードとCloudFormation</li>
<li>Amazon Athenaビューへのアクセス制御</li>
</ul>
<h1>AWS Lake Formation</h1>
<p>データ分析基盤のデータカタログにGlueデータカタログを採用している場合、IAMポリシーやS3バケットポリシーよりもさらにきめ細かいアクセス制御を実施するには Lake Formationの導入検討が必要になると思います。</p>
<p>Lake Formation自体の詳細はAWSから出ている下記ドキュメントをあたってください。</p>
<ul>
<li><a href="https://docs.aws.amazon.com/ja_jp/lake-formation/latest/dg/what-is-lake-formation.html">デベロッパーガイド</a></li>
<li><a href="https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AWS-Lake-Formation_1010_v1.pdf">AWS Black Belt</a> ← 個人的に、Lake Formationの概要を掴むのにわかりやすい資料でした</li>
</ul>
<h2>Data lake locations</h2>
<p>Lake Formationでアクセス制御する対象を登録する機能 </p>
<ul>
<li>対象：S3パス</li>
<li>説明：ここに登録されたS3パス配下をLake Formationによるアクセス制御対象として登録する</li>
<li>例：Glueデータカタログ上のデータベースをアクセス制御したい場合、データベースのS3ロケーションはData lake locationsに登録されたS3パス配下である必要がある</li>
<li>注意点：ここにS3パスを登録するだけではLake Formationによるアクセス制御は実施されない</li>
</ul>
<h2>Data locations</h2>
<ul>
<li>対象：プリンシパル<a href="https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_elements_principal.html">^1</a> とS3パス</li>
<li>説明：Glueデータカタログのデータベースやテーブルへの作成や変更の権限を付与する。ここで行う設定はデータロケーション（S3リソース）へのメタデータをプリンシパルに作成、変更する権限</li>
</ul>
<h2>Data permissions</h2>
<ul>
<li>対象：プリンシパル、Glueデータカタログのデータベースやテーブル</li>
<li>説明：下記のアクセス権限をプリンシパルに付与する<ul>
<li>データベースに対して Create Table、Alter、Update、Drop、Describe</li>
<li>テーブルに対して Select、Insert、Delete、Describe、Alter、Drop</li>
</ul>
</li>
<li>注意点：<ul>
<li>Data lake locationsの設定を事前におこなっておく</li>
<li>メタデータを作成、変更する権限（Create Table/Alterなど）をData locationsで事前に設定しておく</li>
</ul>
</li>
</ul>
<p><a href="https://pages.awscloud.com/rs/112-TZM-766/images/AWS-Black-Belt_2023_AWS-Lake-Formation_1010_v1.pdf">AWS Black Belt</a> のP19,P20 がわかりやすいです。</p>
<h2>IAMAllowedPrincipals</h2>
<p>IAMAllowedPrincipals について
<a href="https://docs.aws.amazon.com/ja_jp/lake-formation/latest/dg/lf-permissions-reference.html">Lake Formation 許可のリファレンス</a> に下記の通り説明があります。(2025/11)</p>
<blockquote>
<p>「Lake Formation は、データカタログ内のすべてのデータベースとテーブルに対する Super アクセス許可を、デフォルトで IAMAllowedPrincipals というグループに設定します。このグループアクセス許可がデータベースまたはテーブルに存在する場合、アカウント内のすべてのプリンシパルが、 AWS Glueの IAM プリンシパルポリシーを介してリソースにアクセスできるようになります。」</p>
</blockquote>
<p>IAMAllowedPrincipalsにSuper権限があるため、Lake Formationのデフォルト設定時では、GlueデータカタログリソースにIAMポリシーでアクセスが可能です。</p>
<p>Glueデータカタログのデータベースやテーブルのアクセス制御について、IAMポリシーでのアクセス制御からLake Formationでのアクセス制御に移行するには</p>
<ul>
<li>IAMAllowedPrincipalsの設定の排除が必要</li>
<li>それぞれのIAMプリンシパルに対して、Lake Formationでアクセス制御（メタデータ、データロケーションへの制御）を設定する</li>
</ul>
<p>上記を実施する場合、既存IAMプリンシパルの棚卸が必要になります。
この棚卸に漏れがあると、GlueデータカタログのリソースにアクセスできないIAMプリンシパルが発生することに注意が必要です。</p>
<p>また、Lake Formationのアクセス制御を解除し元のIAMによるアクセス制御に戻す際は、手順の逆を実施することになります。</p>
<ul>
<li>こちらにその手順とスクリプトが公開されています <a href="https://github.com/aws-samples/aws-glue-samples/tree/master/utilities/use_only_IAM_access_controls">Using only IAM access controls
</a></li>
</ul>
<h1>ハイブリッドアクセスモード</h1>
<p>これまでの方式では、Lake Formationでアクセス制御を実施するには、IAMAllowedPrincipalsの設定を排除しLake Formationを設定する必要があります。（この方式はLake Formationモードと呼ばれます）
一方、ハイブリッドアクセスモードの場合はIAMAllowedPrincipalsが付与されていてもLake Formationのアクセス制御が優先されるため、IAMAllowedPrincipalsの設定を排除せずにLake Formationのアクセス制御を有効にできます。
つまり、IAMAllowedPrincipalsが付与されていてもLake Formationによる制御を強制的に⾏わせる機能です。そのため、上述のIAMプリンシパルの棚卸を行わずにLake Formationによるアクセス制御を有効にできます。</p>
<p>ハイブリッドアクセスモードの場合、下記が可能になります。</p>
<ul>
<li>選択したリソースとプリンシパルについてLake Formationのアクセス制御を有効化（ハイブリッドアクセスモードのオプトイン）</li>
<li>Lake Formationアクセス制御を解除するには、ハイブリッドアクセスモードのオプトインを解除する</li>
</ul>
<h1>LFタグ</h1>
<p>Glueデータカタログのリソース（データベース、テーブル、カラム）にタグを割り当て、そのタグに基づいてプリンシパルにアクセス権限を付与するための機能です。</p>
<ul>
<li>LFタグを定義（例： Key=Confidential, Value=True/False）</li>
<li>リソースにLFタグを付与する</li>
<li>プリンシパルに、LFタグに対するアクセス権を付与する</li>
</ul>
<p>LFタグを使うと、リソースとプリンシパル間の権限関係を疎結合に保つことができます。
LFタグを使わない場合、リソースとプリンシパル間で直接アクセス権を設定する必要があります。この場合、リソースやプリンシパルが増えると、アクセス権の管理が煩雑になります。</p>
<h1>設定方法</h1>
<p>以下、Lake FormationのハイブリッドアクセスモードとLFタグ方式によるアクセス制御の手順を記します。</p>
<h2>前提条件</h2>
<p>今回は、下記のペルソナのもとアクセス制御を実施したいと思います。</p>
<h3>ペルソナとLFタグ</h3>
<table>
<thead>
<tr>
<th>ペルソナ</th>
<th>役割</th>
<th>対象IAMロール名</th>
<th>対象LFタグ</th>
<th>許可する権限</th>
</tr>
</thead>
<tbody><tr>
<td>データレイク管理者</td>
<td>Lake Formationの操作権</td>
<td>lf-test-001</td>
<td>Confidential = True, False</td>
<td>Super</td>
</tr>
<tr>
<td>データユーザー</td>
<td>許可されたテーブルにアクセスできる</td>
<td>lf-test-001-user</td>
<td>Confidential = False</td>
<td>Select、Describe</td>
</tr>
</tbody></table>
<h2>手順</h2>
<p>Lake Formationでアクセス制御を実施するには、大まかには、下記を実施します。</p>
<ol>
<li>データレイク管理者に登録する</li>
<li>Glueデータカタログのメタデータへのアクセス制御のため、S3パスをData lake locationsに登録し、そのS3パスをLake Formationで制御可能状態にする</li>
<li>Glueデータカタログのメタデータの作成の制御として、Data locationsの設定。これでテーブルのCreateやAlter権限をLake Formationで付与</li>
<li>LFタグの設定</li>
</ol>
<h3>Data lake administratorsの設定</h3>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/data_lake_administrator.png" alt="data_lake_administrator"></p>
<ul>
<li>Lake Formationの操作設定権をIAMロールに付与する</li>
<li>Data lake administratorとして今回はlf-test-001というIAMロールをセットする</li>
</ul>
<h3>Data lake locationsの設定</h3>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/data_lake_location.png" alt="data_lake_location"></p>
<ul>
<li>GlueデータカタログのデータベースのS3ロケーションを設定する</li>
<li>Hybrid access modeを選択する</li>
</ul>
<h3>Data locationsの設定</h3>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/data_locations_2.png" alt="data_locatios_grantable_off"></p>
<ul>
<li><p>Data locationsの設定として、ハイブリッドアクセスモードでアクセス制御をしたいプリンシパルと制御対象のS3プレフィックスを登録する</p>
</li>
<li><p>この制御対象のS3プレフィックスはGlueデータカタログのデータベースのうち、ハイブリッドアクセスモードでアクセス制御したいデータベースのS3ロケーションを設定している</p>
</li>
</ul>
<h3>LFタグを作成</h3>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/add_lf-tag.png" alt="Add LF-Tag"></p>
<ul>
<li>キーはConfidential、バリューはTrue、Falseを作成する</li>
</ul>
<h3>LFタグへのアクセス権を設定</h3>
<p>「Permissions」の「Data permissions」からアクセス権を設定します。</p>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/lf-tag-lf-test-user-1.png" alt="lf-tag-lf-test-user-1"></p>
<ul>
<li>Lake Formationで権限を渡したいプリンシパルを選択する</li>
<li>Lake Formationで管理したいLFタグを選択する</li>
<li>今回は、lf-test-001-userというIAMロールに対して、LFタグのキーがConfidentialでバリューがFalseにアクセス権を付与する</li>
</ul>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/lf-tag-lf-test-user-2.png" alt="lf-tag-lf-test-user-2"></p>
<ul>
<li>Lake Formationで許可したいアクセス権を選択する</li>
<li>lf-test-001-userはキーがConfidential、バリューがFalseのリソースに対して、データベースの場合Describeを付与、テーブルの場合Select、Describeを付与する</li>
</ul>
<h3>LFタグを管理対象リソースに設定する</h3>
<p>今回はconfidential というデータベースのtable_001、table_002 というテーブルに下表のようにLFタグを付与します。</p>
<table>
<thead>
<tr>
<th>データベース名</th>
<th>テーブル名</th>
<th>LFタグのキー名</th>
<th>LFタグのバリュー名</th>
</tr>
</thead>
<tbody><tr>
<td>confidential</td>
<td>table_001</td>
<td>Confidential</td>
<td>True</td>
</tr>
<tr>
<td>confidential</td>
<td>table_002</td>
<td>Confidential</td>
<td>False</td>
</tr>
</tbody></table>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/lf-tag_table_001.png" alt="lf-tag_table"></p>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/lf-tag_table_002.png" alt="lf-tag_table_002"></p>
<ul>
<li>table_001にはキー=Confidentialのバリュー=True、table_002にはキー=Confidentialのバリュー=Falseを設定する</li>
</ul>
<h3>ハイブリッドアクセスモードを有効化する</h3>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/hybrid_access_mode.png" alt="hybrid_access_mode"></p>
<ul>
<li>管理したいリソース(テーブル)を設定<ul>
<li>今回はデータベースを設定しないため、データベースはIAMポリシーでのアクセス制御が継続される</li>
</ul>
</li>
<li>アクセス制御したいプリンシパルを設定</li>
</ul>
<h2>動作確認</h2>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/confidential_table_001.png" alt="&quot;confidential&quot;.&quot;table_001&quot;">
アクセスさせないテーブルについて</p>
<ul>
<li>lf-test-001-user にスイッチロールするとConfidential=Trueであるtable_001はUI上から見えない</li>
<li>confidential.table_001 へのクエリは失敗する</li>
</ul>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/confidential_table_002.png" alt="&quot;confidential&quot;.&quot;table_002&quot;">
アクセス許可しているテーブルについて</p>
<ul>
<li>Confidential=Falseであるtable_002 はUI上から見える</li>
<li>confidential.table_002 へのクエリは成功する</li>
</ul>
<h2>解除手順</h2>
<p><img src="/assets/blog/authors/knishi/2025-12-16-lakeformation-hybrid-access-mode/remove_hybrid_access_mode.png" alt="&quot;remove_hybrid_access_mode&quot;"></p>
<ul>
<li>ハイブリッドアクセスモードをRemove（解除）することでLake Formationによるアクセス制御は解除される</li>
</ul>
<h1>注意点</h1>
<p>最後にLake Formationを触ってみて気づいた注意点を書いておきます。</p>
<ul>
<li>異なるキーのLFタグによるリソースへのアクセス制御はLFタグの AND条件ではなく OR条件になる<ul>
<li>例：キーがConfidential=TrueのLFタグとキーがDepartment=SalesのLFタグが付与されたリソースに対しては、プリンシパルにキーがConfidential=TrueのLFタグへのアクセス権またはキーがDepartment=SalesのLFタグへのアクセス権を持つ場合、アクセス可能になる</li>
</ul>
</li>
<li>運用時にはハイブリッドアクセスモードでは新規作成されたIAMロールには対応できない。Lake Formation管理者とIAM管理者が異なる場合、Lake Formation管理者が予期せぬIAMロールがGlueデータカタログにアクセスする可能性がある</li>
<li>ハイブリッドアクセスモードのオプトイン操作はCloudFormationには現時点で非対応と思われる。<ul>
<li>ハイブリッドアクセスモードのオプトインの実施（CreateLakeFormationOptIn）が必要だが、CloudFormationには現時点では見当たらない。<a href="https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/TemplateReference/aws-resource-lakeformation-resource.html">^2</a></li>
<li>CloudFormationでリソースの作成後、下記が必要なのではないかと思われる。（今後の検証が必要）<ul>
<li>Lake FormationコンソールやAWS CLIなどでプリンシパルのオプトインの設定を行う</li>
<li>CloudFormationのカスタムリソースを用いて、Lambda関数からCreateLakeFormationOptIn APIを実行する</li>
</ul>
</li>
</ul>
</li>
<li>Athenaで作成したビューに対するアクセス制御は基礎となるテーブルの権限と一致させておく必要がある。そのためAthenaビューとテーブルのアクセス制御は同じものとなる<a href="https://docs.aws.amazon.com/ja_jp/athena/latest/ug/lf-athena-limitations.html#lf-athena-limitations-permissions-to-views">^3</a><ul>
<li>※１ データカタログビューを使うとテーブルとは異なるアクセス制御をビューに設定できる。<a href="https://docs.aws.amazon.com/ja_jp/athena/latest/ug/views.html#when-to-use-views-gdc">^4</a> </li>
<li>※２ データカタログビューはハイブリッドアクセスモードのIAMAllowedPrincipalsを排除が不要という恩恵を受けられない <a href="https://docs.aws.amazon.com/ja_jp/athena/latest/ug/views-glue.html#views-glue-limitations">^5</a></li>
<li>データカタログビューの作成には下記が必要<ul>
<li>データカタログビュー作成にはIAMAllowedPrincipalsのアクセス権限をビューの基のテーブルから外す</li>
<li>テーブルが所属するデータベースからデフォルトで設定されている[Use only IAM access control for new databases] を外す</li>
<li>データカタログビューを作成するIAMロールに対して、Lake Formationでビューの基のテーブルに対するアクセス権限を設定する</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>あとがき</h1>
<p>Lake Formationを使うとGlueデータカタログのリソースに対してきめ細かいアクセス制御が可能になることは分かりました。</p>
<p>一方で、Lake Formation自体の理解と設定に学習コストがかかる印象を受けました。
そのためチーム内で知見を十分に共有できていないと、誰もアクセスできないリソースが発生しそれを解決できる人間がわずかしかいないという状況に陥りかねないと感じました。ドキュメントの整備、ハンズオンの実施などでチーム内の知見を高めることが重要と思います。</p>
<p>また、Lake Formation管理下のテーブルとビューのアクセス制御については、テーブルとビューで異なるアクセス制御を実施したい場合は注意が必要です。</p>
<p>既存のIAMロールの管理体制、Glueデータカタログの運用方法とLake Formationの仕様を十分に検証することが重要と思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[TanStack Queryのキャッシュ周辺の挙動を理解してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-16-understanding-tanstack-query-cache/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-16-understanding-tanstack-query-cache/</guid>
            <pubDate>Tue, 16 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[TanStack Queryのキャッシュ周辺の挙動に振り回されないよう、公式ドキュメントを読んで、筆者自身が基本から理解するための記事です]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO テクノロジーズ Advent Calendar 2025</a> の 16 日目の記事です 🎅🎄</p>
<h2>はじめに</h2>
<p>こんにちは！
KINTO 開発部 KINTO バックエンド開発 G マスターメンテナンスツール開発チーム、技術広報 G 兼務、Osaka Tech Lab 所属の high-g（<a href="https://x.com/high_g_engineer">@high_g_engineer</a>）です。フロントエンドエンジニアをやっています。</p>
<p>現在、筆者はプロジェクトで TanStack Query を利用しています。
TanStack Query は、 <code>useQuery</code> や <code>useMutation</code> のおかげで、 React Hooks を扱う感覚でデータの取得・更新が可能で非常に便利なライブラリです。
しかし、キャッシュの扱いで少し癖があり、理解が浅いと、以下の様な挙動に振り回されてしまいます。</p>
<ul>
<li>データをキャッシュで賄えるはずの場面で無駄にフェッチしてしまっている</li>
<li>サーバーのデータを更新したはずなのに、古い情報が画面に表示されてしまっている</li>
<li>闇雲にキャッシュ削除を連発してしまっている</li>
</ul>
<p>本記事は、こういった挙動に振り回されないように、TanStack Query の公式ドキュメントを読んで、 <strong>筆者自身がキャッシュ周辺の挙動を理解するため</strong> の内容となっております。</p>
<h2>本記事の構成</h2>
<ul>
<li>TanStack Query とは？</li>
<li>TanStack Query のキャッシュの基本である <code>stale</code> と <code>gc</code> を理解する</li>
<li>TanStack Query のキャッシュの挙動を理解する</li>
<li>クエリの無効化</li>
<li>ミューテーション時のクエリの無効化</li>
<li>プロジェクト内でよく利用している <code>QueryClient</code> のメソッドの紹介</li>
</ul>
<h2>TanStack Query とは？</h2>
<p><a href="https://tanstack.com/query/v5">https://tanstack.com/query/v5</a></p>
<p>本記事では、TanStack Query v5 の利用を前提にしています。</p>
<p>TanStack Query（旧 React Query）は、フロントエンドアプリケーションにおけるサーバー状態管理のためのライブラリです。<br>サーバー状態とは、API から取得するデータのように、アプリの外部に存在し、非同期で取得・更新が必要なデータのことです。<br>以下を非常にシンプルなコードで実現してくれます。</p>
<ul>
<li>データ取得＆データ更新のシンプル化</li>
<li>キャッシュ管理</li>
<li>バックグラウンド更新</li>
<li>ローディング・エラー状態の管理</li>
</ul>
<h2>TanStack Query のキャッシュの基本である <code>stale</code> と <code>gc</code> を理解する</h2>
<p>まず、TanStack Query のキャッシュの挙動を理解するために、<code>stale</code>（古いデータ） と <code>gc</code>（ガベージコレクション） について理解する必要があります。
以下の記事が TanStack Query のキャッシュを理解するうえで一番最初の入口として読むべきドキュメントです。</p>
<p><a href="https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults">https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults</a></p>
<h3><code>stale</code> について</h3>
<ul>
<li><code>stale</code> とは、「古くなった」または「更新が必要な可能性がある」状態のデータを指します</li>
<li><code>useQuery</code> または、 <code>useInfiniteQuery</code> のようなデータ取得系のフックは、デフォルトでクエリ(※)のデータを <code>stale</code> として扱います</li>
<li><code>stale</code> は、<code>staleTime</code> を設定することで再フェッチの頻度を制御できます</li>
<li><code>staleTime</code> のデフォルトは 0 なので、データ取得直後に <code>stale</code> となります</li>
<li><code>stale</code> なデータは、新しいクエリのマウント、ウィンドウの再フォーカス、ネットワーク再接続時に自動で再フェッチされます</li>
</ul>
<p>※ クエリとは、取得データ、状態、メタ情報、キー、データ取得関数をひとまとめにした管理単位のことです。</p>
<h3><code>gc</code> について</h3>
<ul>
<li>使用しなくなったキャッシュを削除する機能のことです</li>
<li><code>useQuery</code>, <code>useInfiniteQuery</code> はアクティブなインスタンスがなくなった場合、「非アクティブ」というラベル付けが行われ、再使用される場合に備えてキャッシュに残ります</li>
<li>「非アクティブ」の場合、デフォルトだと、5 分後に <code>gc</code> されます</li>
<li><code>gcTime</code> を設定することで、<code>gc</code> が発生するまでの時間を変更できます</li>
</ul>
<p><code>stale</code> と <code>gc</code> について、 <code>staleTime</code> と <code>gcTime</code>（v4 より以前は <code>cacheTime</code> という名称） をもとに考えると分かりやすいです。</p>
<p><code>staleTime</code> → クエリを再取得するまでの時間
<code>gcTime</code> → <code>gc</code> するまでの時間（キャッシュにメモリを保持する時間）</p>
<p><code>staleTime</code> が過ぎても、クエリはキャッシュに残っていますが、<code>gcTime</code> が過ぎると、クエリがキャッシュから消えるということになります。
それ以外の細かいオプションなども書かれてはいますが、 TanStack Query のキャッシュの基本を理解するために、まずは <code>stale</code> と <code>gc</code> が理解できていれば問題ないと思います。</p>
<h2>TanStack Query のキャッシュの挙動を理解する</h2>
<p>以下のドキュメントでは、TanStack Query の <code>useQuery</code> がクエリのデータをキャッシュ、キャッシュしたデータの利用、そして <code>gc</code> するまでの流れが解説されています。</p>
<p><a href="https://tanstack.com/query/latest/docs/framework/react/guides/caching">https://tanstack.com/query/latest/docs/framework/react/guides/caching</a></p>
<p><code>useQuery</code> は、 <code>useQuery({ queryKey: [&quot;todos&quot;], queryFn: fetchTodos });</code> といった記述が基本になりますが、こちらがどのように動作するかを順番に解説します。</p>
<h3>本題の前に <code>useQuery</code> の基本コードを簡単に紹介</h3>
<p>キャッシュの挙動理解の本題に入るまでに、<code>useQuery</code> を利用した簡単なサンプルを紹介します。
<code>useQuery</code> が利用される場合、以下のようなコードが記述されることが一般的です。</p>
<pre><code class="language-ts">// データ取得関数
const fetchTodos = async () =&gt; {
  const response = await fetch(&quot;/api/todos&quot;);
  return response.json();
};

// コンポーネント内で使用
const TodoList = () =&gt; {
  // useQuery の記述
  const { data, isPending, error } = useQuery({
    queryKey: [&quot;todos&quot;],
    queryFn: fetchTodos,
  });

  if (isPending) return &lt;p&gt;読み込み中...&lt;/p&gt;;
  if (error) return &lt;p&gt;エラーが発生しました&lt;/p&gt;;

  return (
    &lt;ul&gt;
      {data.map((todo) =&gt; (
        &lt;li key={todo.id}&gt;{todo.title}&lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}
</code></pre>
<p><code>useQuery</code> の引数として与えられているオブジェクトには、 <code>queryKey</code> と <code>queryFn</code> というプロパティがありますが、それぞれについては以下の意味があります。</p>
<p><code>queryKey</code> → キャッシュを識別するためのキー。配列で指定。同じキーを持つクエリはキャッシュを共有
<code>queryFn</code> → データをフェッチするクエリ関数</p>
<h3>TanStack Query のキャッシュの一連のライフサイクルを理解する</h3>
<p>ここまで出た <code>queryKey</code>, <code>stale</code>, <code>gc</code> を元に <code>useQuery</code> のキャッシュ周辺の流れを整理すると、以下のようになります。</p>
<h4>1. 初回の <code>useQuery({ queryKey: [&#39;todos&#39;], queryFn: fetchTodos })</code> を実行</h4>
<p>まだ、<code>queryKey: [&#39;todos&#39;]</code> に紐づけられたクエリのデータが存在しないため、データを取得する為に <code>queryFn</code> で指定された <code>fetchTodos</code> を実行します。
クエリ関数の実行が完了すると、レスポンスは、 <code>[&#39;todos&#39;]</code> キーのもとにキャッシュされます。
<code>staleTime</code> はデフォルトで 0 なので、この時のデータは即座に <code>stale</code> になります。</p>
<h4>2. 再度、 <code>useQuery({ queryKey: [&#39;todos&#39;], queryFn: fetchTodos })</code>　を実行</h4>
<p>この時、<code>[&#39;todos&#39;]</code> キーのもとにキャッシュされたデータが <code>stale</code> として存在するため、<code>stale</code> なデータがレンダリングに利用されます。
それと同時に <code>queryFn</code> で指定された <code>fetchTodos</code> がバックグラウンド実行されます。
クエリ関数の実行が完了した時点で、キャッシュが新しいデータで更新されます。（初回と同じく、<code>staleTime</code> が 0 なので、即座に <code>stale</code> になります）
そして、この時点で更新されたデータを元にまたレンダリングが更新されます。</p>
<h4>3. <code>useQuery({ queryKey: [&#39;todos&#39;], queryFn: fetchTodos })</code> がマウント解除され、使用されなくなる</h4>
<p>この時、アクティブなインスタンスがなくなったため、クエリは「非アクティブ」のラベルが付与され、<code>gcTime</code> を利用して、<code>gc</code> を実行するための時間設定を行います。
<code>gcTime</code> のデフォルトは 5 分なので、何もなければ、5 分後に <code>[&#39;todos&#39;]</code> キーに紐づいたキャッシュデータは <code>gc</code> されますが、5 分以内に再度<code>useQuery({ queryKey: [&#39;todos&#39;], queryFn: fetchTodos })</code> が実行されるようなことがあれば、<code>gc</code> のタイマーはリセットされ、同じ様な流れでキャッシュの更新が行われます。</p>
<p>一連のライフサイクルを図示すると以下のようになります。</p>
<p><img src="/assets/blog/authors/high-g/20251216/img1.jpg" alt="TanStack Queryのキャッシュライフサイクル"></p>
<h3><code>queryKey</code> によるキャッシュ管理について</h3>
<p>少しだけ脱線しますが、<code>queryKey</code> については、以下のドキュメントで詳しく解説されています。</p>
<p><a href="https://tanstack.com/query/latest/docs/framework/react/guides/query-keys">https://tanstack.com/query/latest/docs/framework/react/guides/query-keys</a></p>
<p>TanStack Query は、<code>queryKey</code> に基づいて、キャッシュを管理します。
<code>queryKey</code> の型は <code>unknown[]</code> なので、シンプルな文字列のみで構成された配列から文字列とオブジェクトのネストなどの複雑な配列まで対応しています。</p>
<p>大切なことは、TanStack Query は <code>queryKey</code> を一意のキーとして、キャッシュを管理するため、キャッシュしたクエリのデータを再利用したい場合は、正しく配列を指定する必要があります。</p>
<p>例えば、以下のように、<code>queryFn</code> に対して同じクエリ関数を指定しているが、異なった <code>queryKey</code> を指定している場合、キャッシュは全く別のもとして扱われます。</p>
<ul>
<li><code>useQuery({ queryKey: [&#39;todos&#39;], queryFn: fetchTodos })</code></li>
<li><code>useQuery({ queryKey: [&#39;todos1&#39;], queryFn: fetchTodos })</code></li>
</ul>
<p>また、逆に <code>queryFn</code> は異なったクエリ関数を指定しているが、同じ <code>queryKey</code> を指定している場合、同一のキャッシュが扱われます。</p>
<ul>
<li><code>useQuery({ queryKey: [&#39;todos&#39;], queryFn: fetchTodos1 })</code></li>
<li><code>useQuery({ queryKey: [&#39;todos&#39;], queryFn: fetchTodos2 })</code></li>
</ul>
<p><code>queryKey</code> の指定が正しくないだけで、そもそもキャッシュが正しく効かない状態になるので、注意が必要です。</p>
<p>ただし、以下のように、<code>queryKey</code> にオブジェクトが扱われている場合、オブジェクトの中身（キーと値）が同じであれば、オブジェクト内のキーの順序に関係なく、同じキャッシュとして扱われます。</p>
<ul>
<li><code>useQuery({ queryKey: [&#39;todos&#39;, { status: &#39;done&#39;, page: 1 }], queryFn: fetchTodos })</code></li>
<li><code>useQuery({ queryKey: [&#39;todos&#39;, { page: 1, status: &#39;done&#39; }], queryFn: fetchTodos })</code></li>
</ul>
<p>ここまでの内容が理解できれば、TanStack Query のキャッシュの基本はかなり押さえられたと思います。</p>
<h2>クエリの無効化</h2>
<p><code>staleTime</code> に基づいた自動的なキャッシュ更新のみだと、必ずしも効果的でない場面は存在します。<br>例えば、ユーザーの何らかの操作によって、キャッシュが古くなっていることが確実な時や、意図的にキャッシュを更新したい時などです。</p>
<p>そういった場面に利用できるのが、<code>QueryClient</code> に存在する <code>invalidateQueries</code> メソッドです。
これを利用することで、クエリのデータを強制的に <code>stale</code> にします。そのクエリが現在レンダリング中（アクティブ）であれば、即座にバックグラウンドで再取得されます。</p>
<pre><code class="language-ts">import { useQuery, useQueryClient } from &quot;@tanstack/react-query&quot;;

// QueryClient からコンテキスト取得
const queryClient = useQueryClient();

// キャッシュ内のすべてのクエリを無効化
queryClient.invalidateQueries();
// todos キーを持つ全てのクエリを無効化
queryClient.invalidateQueries({ queryKey: [&quot;todos&quot;] });
</code></pre>
<p>注意点として、<code>invalidateQueries()</code> はクエリのデータを <code>stale</code> にするだけであり、キャッシュを削除したり、<code>gc</code> したりするわけではありません。
キャッシュを削除する場合、<code>removeQueries()</code> を利用する必要があります。</p>
<h2>ミューテーション時のクエリの無効化</h2>
<p>TanStack Query を扱う上で最も挙動に振り回されるタイミングがミューテーション（更新、削除などの操作）後のレンダリングだと筆者は思います。<br>正直なところ、ここまでの説明は、この項目を説明するための長い前置きでした。<br>（本当に長くてすみません・・・。）</p>
<p>ドキュメントとしては、以下を読んで終わりなのですが...</p>
<p><a href="https://tanstack.com/query/latest/docs/framework/react/guides/invalidations-from-mutations">https://tanstack.com/query/latest/docs/framework/react/guides/invalidations-from-mutations</a></p>
<p>ドキュメントで扱われているサンプルコードを記載します。</p>
<pre><code class="language-ts">import { useMutation, useQueryClient } from &quot;@tanstack/react-query&quot;;

const queryClient = useQueryClient();

const mutation = useMutation({
  mutationFn: addTodo,
  onSuccess: async () =&gt; {
    // パターン1: 単一のクエリのデータを無効化する場合
    await queryClient.invalidateQueries({ queryKey: [&quot;todos&quot;] });

    // パターン2: 複数のクエリのデータを無効化する場合
    // ※ 実際はパターン1かパターン2のどちらかを使用します
    await Promise.all([
      queryClient.invalidateQueries({ queryKey: [&quot;todos&quot;] }),
      queryClient.invalidateQueries({ queryKey: [&quot;reminders&quot;] }),
    ]);
  },
});
</code></pre>
<p>ミューテーション後に<code>queryClient.invalidateQueries({ queryKey: [&quot;todos&quot;] })</code> を実行することで、キャッシュに保持しているクエリのデータを手動で無効化し、バックグラウンドで再取得が行われます。これにより、キャッシュのデータを最新の状態に更新できます。</p>
<p>この記述がない場合、キャッシュの更新が <code>staleTime</code> に依存するため、タイミングによってはミューテーションでデータを更新したにもかかわらず、画面の表示が古いままになることがあります。</p>
<p>ここまでで紹介したドキュメントに目を通した方なら、なぜ <code>onSuccess</code> のタイミングで、 <code>queryClient.invalidateQueries({ queryKey: [&#39;todos&#39;] })</code> を実行しているのか？を正しく理解できると思います。
ただ、このドキュメントを読むだけだと正しい理解ができず、更にこのドキュメントでもそこまで触れられていない為、<code>useMutation</code> を利用した後は、対応する <code>queryKey</code> を <code>invalidateQueries()</code> に記述するだけで終わってしまうのではないかと思います。<br>実際、筆者はそうでしたし、未だにプロジェクト内にはそういったコードが散在しています。</p>
<h2>プロジェクト内でよく利用している<code>QueryClient</code> のメソッドの紹介</h2>
<p>最後に、プロジェクト内でよく利用している <code>QueryClient</code> のメソッドを紹介します。</p>
<h3><code>invalidateQueries</code></h3>
<pre><code class="language-ts">queryClient.invalidateQueries({ queryKey: [&quot;todos&quot;] });
</code></pre>
<p>指定したクエリキーのクエリを無効化し、キャッシュを <code>stale</code> にマークします。</p>
<ul>
<li>キャッシュは即座に削除されません</li>
<li>そのクエリを使用しているコンポーネントがアクティブな場合、バックグラウンドで再フェッチが自動的に実行されます</li>
<li>用途: データ更新後に関連するクエリを再取得させたいとき</li>
</ul>
<h3><code>refetchQueries</code></h3>
<pre><code class="language-ts">await queryClient.refetchQueries({ queryKey: [&quot;todos&quot;] });
</code></pre>
<p>指定したクエリキーのクエリを即座に再フェッチします。</p>
<ul>
<li><code>invalidateQueries</code> とは異なり、アクティブなコンポーネントの有無に関係なく強制的に再取得</li>
<li><code>await</code> で完了を待つことが必要</li>
<li>用途: 更新後に最新データが確実に取得されていることを保証したいとき</li>
</ul>
<h3><code>removeQueries</code></h3>
<pre><code class="language-ts">queryClient.removeQueries({ queryKey: [&quot;todos&quot;] });
</code></pre>
<p>指定したクエリキーのキャッシュを完全に削除します。</p>
<ul>
<li>キャッシュからデータが消えるため、次回アクセス時は新規フェッチが必要</li>
<li>用途: エンティティが削除された後など、キャッシュに残っていると問題になるケース</li>
</ul>
<h3><code>setQueryData</code></h3>
<pre><code class="language-ts">queryClient.setQueryData([`/v1/versions/${variables.id}`], data);
</code></pre>
<p>指定したクエリキーのキャッシュデータを直接書き換えます。</p>
<ul>
<li>サーバーへのリクエストなしでキャッシュを更新</li>
<li>用途: <code>Mutation</code> の結果をそのままキャッシュに反映したいとき（楽観的更新など）</li>
</ul>
<h2>まとめ</h2>
<p>TanStack Query というライブラリは処理を非常にシンプルに記述でき、ドキュメントをそこまで読まなくても利用できてしまうほど便利なライブラリです。</p>
<p>ただ、キャッシュ周辺に関しては、ある程度ドキュメントを読み込んで理解しておかないと、挙動に振り回されて、書かなくても良いようなコードを書いて、場当たり的な対応をしてしまうことになります。</p>
<p>この記事が TanStack Query のキャッシュ周辺で悩んでいる方の助けに少しでもなれば幸いです。</p>
<p>最後まで読んでいただきありがとうございました。</p>
<h2>参考記事</h2>
<p><a href="https://tanstack.com/query/v5">https://tanstack.com/query/v5</a>
<a href="https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults">https://tanstack.com/query/latest/docs/framework/react/guides/important-defaults</a>
<a href="https://tanstack.com/query/latest/docs/framework/react/guides/caching">https://tanstack.com/query/latest/docs/framework/react/guides/caching</a>
<a href="https://tanstack.com/query/latest/docs/framework/react/guides/query-keys">https://tanstack.com/query/latest/docs/framework/react/guides/query-keys</a>
<a href="https://tanstack.com/query/latest/docs/framework/react/guides/query-invalidation">https://tanstack.com/query/latest/docs/framework/react/guides/query-invalidation</a>
<a href="https://tanstack.com/query/latest/docs/framework/react/guides/invalidations-from-mutations">https://tanstack.com/query/latest/docs/framework/react/guides/invalidations-from-mutations</a>
<a href="https://tanstack.com/query/latest/docs/reference/QueryClient">https://tanstack.com/query/latest/docs/reference/QueryClient</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/high-g/20251216/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[How We Improve KINTO Used Vehicle Site Every Day!]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-20-UpTeamWebsiteImprovement-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-20-UpTeamWebsiteImprovement-en/</guid>
            <pubDate>Mon, 15 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[An article introducing the improvements and team structure of the KINTO used vehicle site]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello. I&#39;m Sato, a frontend developer at KINTO Technologies Corporation (KTC).
In this article, I&#39;d like to introduce my team developing a website for KINTO used vehicles, sharing how we proceed with the development and how our team structure looks like.</p>
<p>I recently started using a sun umbrella for the first time in my life. For learning more about myself, please check <a href="https://www.kinto-technologies.com/members/interview01/">here</a></p>
<h1>What Is the KINTO Used Vehicle Website Development Team?</h1>
<p>In general, when you sign up with KINTO, a subscription with a new vehicle is started. At the end of the subscription, you need to return the vehicle.</p>
<p>Our team mainly work on developing and operating <a href="https://up.kinto-jp.com/">KINTO used vehicles (only in Japanese)</a>, a website that sells the returned vehicles as used vehicle subscriptions online.</p>
<p>A major difference of the e-commerce site from the one for KINTO new vehicles is that vehicle inventory fluctuates daily as used vehicles are basically one-of-a-kind items sold on a first-come, first-served basis.</p>
<p>We implement e-commerce-specific initiatives and approaches, considering how to display inventory and recommendation sections, which is very worthwhile.</p>
<h3>Team Structure (as of November 2025)</h3>
<ul>
<li>One product manager</li>
<li>One producer</li>
<li>Five frontend engineers</li>
<li>Eight backend engineers</li>
</ul>
<h3>Team Atmosphere</h3>
<p><img src="/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/office_1.jpg" alt="チームの様子">
<em>My team members working at our office</em></p>
<p>Our frontend (FE) team, backend (BE) team, and contract partners are able to communicate casually without barriers and freely share opinions.</p>
<p>Since we&#39;re close to the business teams related to used vehicles, I think we are in the mindset of sharing opinions and growing the used vehicle e-commerce site together!</p>
<p>Sometimes people from other divisions join our regular project meetings, giving very pleasant comments like, &quot;This meeting has a really great atmosphere.&quot;</p>
<br>
**Introducing Some Team Members!**
Some of our used vehicle frontend team members have written articles, so please check them out too!


<p><img src="/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/nakahara.png" alt="NAKAHARA Yuki">
NAKAHARA Yuki</p>
<ul>
<li>Loves cheering for soccer</li>
<li>Boosting work efficiency with his custom-built keyboard!</li>
<li>NAKAHARA Yuki&#39;s article (below)<ul>
<li><a href="/posts/2023-12-18-optimized-image-distribution-with-nextjs-and-lambda-at-edge/">Website Image Optimization with Lambda@Edge &amp; Next.js</a></li>
</ul>
</li>
</ul>
<br>
![Ren.M](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/minakawa.png)
Ren.M
 - Basketball club captain! Shining as a point guard
 - When it comes to iPhone, the high-end model is the best!
 - Ren.M's articles (below)
   - [Introduction to TypeScript](/posts/2024-04-10-TypeScript入門/)
   - [Introducing In-House Club Activities: Basketball Club](/posts/2024-09-26-社内部活動紹介-バスケ部編/)

   
<h1>Project Team Structure</h1>
<p>The used vehicle e-commerce site is developed and operated by KTC members in different teams.</p>
<table>
<thead>
<tr>
<th>Team</th>
<th>Role</th>
<th>Affiliation</th>
</tr>
</thead>
<tbody><tr>
<td>Used vehicle business team</td>
<td>Business owner</td>
<td>KINTO Corporation</td>
</tr>
<tr>
<td>Used vehicle site development team</td>
<td>Handles all development</td>
<td>KTC</td>
</tr>
<tr>
<td>Creative team</td>
<td>Handles project direction and website design</td>
<td>KTC</td>
</tr>
<tr>
<td>Analytics Team</td>
<td>Manages numbers of website access and CVR</td>
<td>KTC</td>
</tr>
</tbody></table>
<p>:::message
Did you know the role of KINTO Technologies (KTC)?
:::</p>
<p>KTC is a tech company founded to drive mobility services developed by companies in the Toyota Group using diverse technologies and our creativity. It handles in-house development of KINTO, which is one of Toyota&#39;s mobility services.</p>
<p>By dividing business domains into two organization—KINTO Corporations (KINTO’s service operation domain) and KTC (KINTO’s development domain)—and working together, we are able to leverage each other&#39;s strength.</p>
<h1>How We Develop Products</h1>
<p>In developing the used vehicle e-commerce site, we determine what to do based on regular project meetings, and we growth hack the website through running PDCA cycles using the HCD development methodology.</p>
<p>We proceed with the development in four different teams led by the business team, and everyone shares opinions while developing with a sense of ownership.</p>
<h2>What Is HCD?</h2>
<p><img src="/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/user_test.jpg" alt="ユーザーテストの様子">
<em>User testing is underway</em></p>
<p>HCD stands for Human Centered Design, a methodology that focuses on understanding user needs and how to translate them into products.</p>
<p>We, the used vehicle team, specifically conduct surveys and user testing, organize website improvement points and user demands to make a better e-commerce site.</p>
<p>User testing involves communicating with users about what they&#39;re thinking while operating the website, which helps us to explore unexpected insights and is a very valuable experience for the website developers as well.</p>
<h2>Example of How We Proceed</h2>
<table>
<thead>
<tr>
<th>PDCA Cycle</th>
<th>Content</th>
<th>Responsible Team</th>
</tr>
</thead>
<tbody><tr>
<td>Plan</td>
<td>Set goals and improvement plans based on<br>analytics team reports, customer feedback, and<br> achievements as a commercialized project<br><br>Everyone shares opinions to make decisions on<br>structure, design, etc.<br>(repeat the above activities several times until we finalized the project)</td>
<td>・Used vehicle business<br>・Used vehicle development<br>・Creative<br>・Analytics</td>
</tr>
<tr>
<td>Do</td>
<td>Development to release</td>
<td>・Development</td>
</tr>
<tr>
<td>Check</td>
<td>Check metrics<br>Discuss results</td>
<td>・Used vehicle business<br>・Used vehicle development<br>・Creative<br>・Analytics</td>
</tr>
<tr>
<td>Action</td>
<td>Plan next steps based on checked points, if needed</td>
<td>・Used vehicle business<br>・Used vehicle development<br>・Creative<br>・Analytics</td>
</tr>
</tbody></table>
<h2>Regular Project Meetings</h2>
<p><img src="/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/kaigi_1.jpg" alt="ビジネス定例の様子">
<em>Project members attending regular business meeting</em></p>
<p>In weekly meetings, we share information with the used vehicle business team while discussing improvements and considering service expansion.
The used vehicle business team, creative team, and analytics team attend meetings from their own working positions.</p>
<p>I personally like how Kitahata-san from the used vehicle business team facilitates—there&#39;s no formal icebreaker segment, but he keep things running in a great atmosphere throughout.
For more about Kitahata-san, see <a href="https://corp.kinto-jp.com/recruit/interview/23/">here</a></p>
<p>The regular meeting agenda is structured like this:</p>
<ul>
<li>Updates from individual teams<ul>
<li>Used vehicle business</li>
<li>Used vehicle development</li>
<li>Analytics</li>
<li>Creative</li>
</ul>
</li>
<li>Topics<ul>
<li>Planning improvement measures based on inquiries received</li>
<li>Proposing ideas on what we want to do</li>
<li>Sharing results of initiatives, etc.</li>
</ul>
</li>
</ul>
<h1>Development Team Initiatives</h1>
<p>We implement various initiatives alongside development.
Recently, we&#39;ve been actively adopting AI-powered development and using the external service Findy Team+ to improve our development process.</p>
<table>
<thead>
<tr>
<th>Item</th>
<th>Details</th>
</tr>
</thead>
<tbody><tr>
<td>AI-related</td>
<td>・Devin<br>　・Minor fixes, document creation, etc.<br>・Copilot<br>　・Source code review<br>　・Implementation assistance<br>・RCA (Root Cause Analysis)<br>　・AI-powered critical alert root cause analysis</td>
</tr>
<tr>
<td>Internal development meetings</td>
<td>・Scrum events<br>・Study sessions<br>・Pair programming</td>
</tr>
<tr>
<td>Development process improvement</td>
<td>・Findy Team+<br>・Process improvements related to code review</td>
</tr>
</tbody></table>
<h1>Interview</h1>
<p>I, Sato, interviewed some of members involved with the e-commerce site development.</p>
<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
Sato, leader of the development FE team
"Nobe-san, it's been about 3 months since you joined. What are your impressions of the used vehicle website development team?"


<br>
 ![開発P野辺](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/nobe.png)
Nobe, producer of the development team
"By participating in various development meetings, I was particularly impressed that everyone speaks up and thought that this is an environment allowing everyone to share opinions."
"And I could feel that opinions are quickly adopted, and everyone wants to make things better together."

<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
Sato, leader of the development FE team
"Thank you. I'm very grateful that you've already shared your opinions and made many improvement suggestions."

<p>&quot;How are things going with the BE team lately, Kaneda-san?&quot;</p>
<br>
![開発BE(リーダー)金田](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/kaneda.png)
Kaneda, leader of the development BE team
"Well, the BE team is also trying to boost productivity and holding study sessions to raise the skill level of team members."
"Recently, we've been focusing on domain-driven development."

<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
Sato, leader of the development FE team
"Thank you. The BE team's initiatives and success stories that you share are always helpful for my FE team’s improvements too."
"I'd like us to enhance our products as a whole team, regardless of FE or BE."

<p>&quot;Let&#39;s also talk with Kitahata-san from the used vehicle business team. What do you think about the used vehicle website development structure?&quot;</p>
<br>
![中古車ビジネスチーム北畑](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/kitahata.png)
Kitahata, member of the used vehicle business team
"I always feel that new features are released at a good pace and progress are being made daily. The style of 'just try it first and run PDCA' feels like a very good match for us."
"What's great about this project team is that anyone can say anything regardless of job title or position. The development side often proposes things we, the business side, never thought of, and it feels like we're all doing business together."

<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
Sato, leader of the development FE team
"Thank you. I also feel that each team is united in development and operations."
"I'd like to continue increasing KINTO fans one by one."

<p>&quot;Finally, a question for Mizuuchi-san, our product manager. What do you expect from the development team?&quot;</p>
<br>
![開発PDM水内](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/mizuuchi_1.png)
Mizuuchi, product manager of the development team
"It's not just about doing development. We discuss in meetings how to improve the website, expecting the service to grow in mind. That's why I want project members to speak up and make the website even better."
"As an ideal way of working, I don't intend to fix a role, like 'this is your job,' so I want people to proactively do what they can and what they notice for any improvements."
"Technology is a means of achieving what you want to do, so I want the members who can develop with a service-first mindset rather than technology-first. We bring members with that perspective in mind."

<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
Sato, leader of the development FE team
"So it is ideal that we have members who are service-first and can develop proactively."
"I also thought this is an important perspective in terms of growing all services in KTC. Thank you."


<h1>In Closing</h1>
<p>I hope this has given you at least a glimpse of how we develop the used vehicle e-commerce site and the atmosphere of our team.</p>
<p>We&#39;ve also introduced our team atmosphere and processes here!
If you&#39;re interested, please read it, which makes me very happy.
<a href="https://www.wantedly.com/companies/company_7864825/post_articles/985245">A Development Culture That Starts with &quot;Let&#39;s Just Try It.&quot; The KINTO Used vehicle Team Talks About the Driving Force and Flexibility Behind the Scenes (only in Japanese)</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/eyecatch.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[KINTOの中古車サイト改善は日々こう進められている！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-20-UpTeamWebsiteImprovement/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-20-UpTeamWebsiteImprovement/</guid>
            <pubDate>Mon, 15 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOの中古車サイトの改善やチーム体制などをお伝えしている記事です]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは。KINTOテクノロジーズ株式会社（以下、KTC）でフロントエンド開発をしている佐藤です。
この記事では私が属しているKINTO中古車サイト開発チームについて紹介させていただきます。
主に開発の進め方や体制などをお伝えできればと思います。</p>
<p>最近日傘デビューしました。佐藤について詳しくは<a href="https://www.kinto-technologies.com/members/interview01/">こちら</a></p>
<h1>KINTO中古車サイト開発チームって？</h1>
<p>通常KINTOで契約すると新車でサブスク開始となり
その後契約終了するとその車両が返却されます。</p>
<p>返却された車両を中古車のKINTOとして
再びEC販売する「<a href="https://up.kinto-jp.com/">KINTO 中古車</a>」のプロダクトを
開発、運用をメインとしているチームです。</p>
<p>「KINTO 新車」と大きく違うポイントとして、
中古車は基本的に早い物勝ちの一点物商品となりますので
日々在庫変動があるECサイトとなります。</p>
<p>在庫の見せ方、おすすめコーナーなど
ECサイトならではの施策や打ち出し方がやりがいの一つでもあります。</p>
<h3>チーム内体制(2025年11月時点）</h3>
<ul>
<li>プロダクトマネージャー 1名</li>
<li>プロデューサー 1名</li>
<li>フロントエンドエンジニア 5名</li>
<li>バックエンドエンジニア 8名</li>
</ul>
<h3>チームの雰囲気</h3>
<p><img src="/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/office_1.jpg" alt="チームの様子">
<em>チームの様子</em></p>
<p>フロントエンド(FE)、バックエンド(BE)チーム、業務委託のパートナーさんとも
垣根なく気軽に会話コミュニケーション、意見が言い合える雰囲気です。</p>
<p>また中古車のビジネスチームとも距離が近く、
意見を言ったり一緒に中古車サイトを育てていこう！という気持ちで
皆同じ方向を向いていると思います。</p>
<p>たまに他部署の方がビジネス定例会議に参加される事があるのですが、
「非常に雰囲気の良い会議ですね」のようなありがたいお言葉を
耳にすることもあり嬉しい限りです。</p>
<br>
**チームメンバーを一部ご紹介！**
中古車フロントエンドのチームメンバーが執筆した記事もありますので
ぜひこちらもチェックしてみてください！


<p><img src="/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/nakahara.png" alt="NAKAHARA Yuki">
NAKAHARA Yuki</p>
<ul>
<li>サッカーの応援が大好き</li>
<li>自作キーボードで作業効率UP！</li>
<li>NAKAHARA Yukiさんの記事↓↓<ul>
<li><a href="/posts/2023-12-18-optimized-image-distribution-with-nextjs-and-lambda-at-edge/">Lambda@Edge &amp; Next.jsによるサイト画像最適化</a></li>
</ul>
</li>
</ul>
<br>
![Ren.M](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/minakawa.png)
Ren.M
 - バスケ部部長！ポイントガードで輝いています
 - iPhoneはハイエンド機種に限る！
 - Ren.Mさんの記事↓↓
   - [TypeScript入門](/posts/2024-04-10-TypeScript入門/)
   - [社内部活動紹介-バスケ部編-](/posts/2024-09-26-社内部活動紹介-バスケ部編/)

   
<h1>体制</h1>
<p>中古車サイトは様々なステークホルダーと
関わりながら運用されています。</p>
<table>
<thead>
<tr>
<th>チーム</th>
<th>役割</th>
<th>所属</th>
</tr>
</thead>
<tbody><tr>
<td>中古車ビジネスチーム</td>
<td>ビジネスオーナー</td>
<td>株式会社KINTO</td>
</tr>
<tr>
<td>中古車サイト開発チーム</td>
<td>開発全般を担当</td>
<td>株式会社KINTOテクノロジーズ</td>
</tr>
<tr>
<td>クリエイティブチーム</td>
<td>ディレクションやサイトデザインを担当</td>
<td>株式会社KINTOテクノロジーズ</td>
</tr>
<tr>
<td>分析チーム</td>
<td>アクセス数やCVRなどを管理</td>
<td>株式会社KINTOテクノロジーズ</td>
</tr>
</tbody></table>
<p>:::message
豆知識「KINTOテクノロジーズ(KTC)の役割」
:::</p>
<p>KTCはトヨタグループ各社が展開するモビリティサービスをテクノロジーとクリエイティビティでリードするために創設されたテックカンパニーです。KINTOもトヨタモビリティサービスの一つとして、KTCが内製開発を担っています。</p>
<p>KINTO(KINTOの運営領域)、KTC(KINTOの開発領域)と
専門領域を分担し協業することによって強みを活かしております。</p>
<h1>開発の進め方</h1>
<p>中古車サイトの開発ではビジネス定例を軸としてやることを決めていき、
HCDという開発手法を元にPDCAのサイクルでECサイトをグロースハックしています。</p>
<p>ビジネスチームを中心とした4チームで進めており、
皆で意見を出し合いながら主体性を持って開発しております。</p>
<h2>HCDとは</h2>
<p><img src="/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/user_test.jpg" alt="ユーザーテストの様子">
<em>ユーザーテストの様子</em></p>
<p>人間中心設計(Human Centered Designの略称)のことを指し、
ユーザーのニーズを理解し、
どうやって製品に反映させるかを中心に設計する手法となります。</p>
<p>中古車チームでは
具体的に実際にアンケートやユーザーテストを行い、
サイトの改善点、ユーザーの欲求などを整理して改善に繋げています。</p>
<p>ユーザーテストは何を思って操作しているかなど、
コミュニケーションをとりながら実施するので
予想外な気づきがあったり
開発者としても非常によい経験となっております。</p>
<h2>進め方の例</h2>
<table>
<thead>
<tr>
<th>PDCAサイクル</th>
<th>内容</th>
<th>担当チーム</th>
</tr>
</thead>
<tbody><tr>
<td>Plan（計画）</td>
<td>分析チームのレポートやお客様の声、<br>ビジネスとして実現した事などを元に目標、<br>改善案などを設定<br><br>皆で意見出し合い、構成、デザインなどを決めていく<br>(fixするまで何度か繰り返す)</td>
<td>・中古車ビジネス<br>・中古車開発<br> ・クリエイティブ<br>・分析</td>
</tr>
<tr>
<td>Do（実行）</td>
<td>開発〜リリース</td>
<td>・開発</td>
</tr>
<tr>
<td>Check（評価）</td>
<td>数値をチェック<br>結果の議論</td>
<td>・中古車ビジネス<br>・中古車開発<br>・クリエイティブ<br>・分析</td>
</tr>
<tr>
<td>Action（改善）</td>
<td>評価を元に必要であれば次の計画へ</td>
<td>・中古車ビジネス<br>・中古車開発<br>・クリエイティブ<br>・分析</td>
</tr>
</tbody></table>
<h2>ビジネス定例</h2>
<p><img src="/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/kaigi_1.jpg" alt="ビジネス定例の様子">
<em>ビジネス定例の様子</em></p>
<p>週次ミーティングで中古車ビジネスチームと情報共有をしつつ、
改善の相談やサービス拡大の対応を検討しています。
中古車ビジネスチーム、クリエイティブチーム、分析チームがあり
ミーティングではそれぞれの立場で参加しています。</p>
<p>中古車ビジネスチーム北畑さんのファシリテートが私は好きで
アイスブレイクなど形式張ったコーナーはないのですが
終始とても良い雰囲気で進行してくださります。
北畑さんについては<a href="https://corp.kinto-jp.com/recruit/interview/23/">こちら</a></p>
<p>定例はこのようなアジェンダ構成になります。</p>
<ul>
<li>各チーム共有<ul>
<li>中古車ビジネス</li>
<li>中古車開発</li>
<li>分析</li>
<li>クリエイティブ</li>
</ul>
</li>
<li>トピックス<ul>
<li>問い合わせがあった内容をもとに改善策を皆で考える</li>
<li>やりたい事の相談</li>
<li>施策の結果など</li>
</ul>
</li>
</ul>
<h1>開発チームでの取り組み</h1>
<p>開発に伴い、さまざまな取り組みを実施しています。
最近ではAIを使った開発や
外部サービスのFindy Team+を活用した開発プロセスの改善など
を積極的に取り入れています。</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>詳細</th>
</tr>
</thead>
<tbody><tr>
<td>AI関係</td>
<td>・Devin<br>　・軽微な修正や、資料作成など<br>・Copilot<br>　・ソースコードレビュー<br>　・実装の補佐<br>・RCA(Root Cause Analysis)<br>　・AIによるクリティカルアラート原因分析</td>
</tr>
<tr>
<td>開発内部定例</td>
<td>・スクラムイベント<br>・勉強会<br>・ペアプロ</td>
</tr>
<tr>
<td>開発プロセス改善</td>
<td>・Findy Team+<br>・コードレビュー周りのプロセス改善</td>
</tr>
</tbody></table>
<h1>インタビュー</h1>
<p>私、佐藤が関係者にインタビューしてみました。</p>
<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
開発FEリーダー佐藤
「野辺さん入社して3ヶ月ほど経ちましたが中古車サイト開発チームについて感じた事などありますか？」


<br>
 ![開発P野辺](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/nobe.png)
開発P野辺
「各開発定例に参加させていただき、皆発言していて意見が言いやすい環境なんだなと特に印象的に感じました」
「そしてすぐ意見を取り入れ皆でよくしていこうという気持ちが伝わってきましたね」

<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
開発FEリーダー佐藤
「ありがとうございます。野辺さんからも早速ご意見いただいたり、改善の提案をたくさんいただいているので非常に感謝しています」

<p>「BEチームの最近はいかがでしょうか？金田さん」</p>
<br>
![開発BE(リーダー)金田](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/kaneda.png)
開発BE(リーダー)金田
「そうですね、BEチームも生産性アップの試みや勉強会なども開催し、チームメンバーのスキル底上げをしていますね」
「最近だとドメイン駆動開発に注力しています」

<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
開発FEリーダー佐藤
「ありがとうございます。いつもBEチームの取り組みや成功事例などご共有していただきFEチームの改善にも参考になっております」
「FE、BE問わずチーム全体としてよりよくしていきたいですね」

<p>「中古車ビジネスチーム北畑さんともお話ししてみましょう。中古車サイトの開発体制についていかがでしょうか？」</p>
<br>
![中古車ビジネスチーム北畑](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/kitahata.png)
中古車ビジネスチーム北畑
「いつもテンポよく新しい機能などがリリースされ日々改善されていく事を実感しています。まずやってみてPDCAを回すスタイルは我々にとって非常にマッチしたやり方だと感じています」
「職責や役職に関係なく、誰が何を言ってもよいのがこのチームの良いところです。ビジネス側で思いもつかなかったことを開発側が提案してくれることも多く、皆でビジネスをしている感覚です」

<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
開発FEリーダー佐藤
「ありがとうございます。それぞれのチームが一丸となって開発、運用ができていると私自身も感じております」
「引き続きKINTOのファンを一人でも増やしていきたいですね」

<p>「最後にPDM水内さんに質問です。開発チームに期待していることはどんな事ですか？」</p>
<br>
![開発PDM水内](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/mizuuchi_1.png)
開発PDM水内
「開発だけやっていればいいわけではなく、サービスが成長することを念頭にサイトをどう改善していくかをミーティングで会話している。そのためメンバーにも発言してもらってより良いサイトにしていきたい」
「理想の働き方として、あなたの業務はこれですと決めるつもりはないので各自出来ること、気づいたことを自発的にやってもらいたいです」
「技術はやりたいことを叶えるための手段なので技術ファーストではなく、サービスファーストで開発できるメンバーであってほしいです。その観点でメンバーを採用しています」

<br>
![開発FEリーダー佐藤](/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/sato.png)
開発FEリーダー佐藤
「サービスファーストであり、自発的に開発できる方が理想なのですね」
「私も自社サービスを育てていくという意味で重要な視点だと思いました。ありがとうございます」


<h1>さいごに</h1>
<p>少しでも中古車サイトの開発の進め方や雰囲気が伝われば幸いです。</p>
<p>また他にもチームの雰囲気や進め方などこちらでもご紹介しております！
ご興味のある方はぜひ一度読んでいただけると幸いです。
<a href="https://www.wantedly.com/companies/company_7864825/post_articles/985245">“とりあえずやってみる”から始まる開発文化。KINTO 中古車チームが語る、推進力と柔軟性の裏側</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/Keita.S/2025-10-20-UpTeamWebsiteImprovement/eyecatch.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[JSONで永続化したデータを安全に保つテスト手法]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-15-android-persistence-json-unit-test/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-15-android-persistence-json-unit-test/</guid>
            <pubDate>Mon, 15 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[モバイルアプリでJSONとして保存したデータが、アプリ更新後にクラッシュを引き起こす問題と、それを防ぐためのテスト手法を紹介します]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の12日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p><a href="https://top.myroute.fun/">my route</a>のAndroidアプリを開発している<a href="https://x.com/gotlinan">長谷川</a>です。</p>
<p>開発をしていると、何らかの理由でメンテナンス性を犠牲にしてしまうことってありますよね。「今動けばいいや」という感じで。</p>
<p>この記事では、アプリがローカルにデータを保存（永続化）する際に陥りがちな罠と、それを防ぐテスト手法を紹介します。ここでは例として Android を取り上げていますが、ローカル永続化を扱うあらゆるアプリケーションに共通する問題であり、同様のアプローチで対策できます。</p>
<h2>ケーススタディ：チケット機能の実装</h2>
<h3>シンプルなチケットの永続化</h3>
<p>とあるアプリの開発では、お得なチケット機能があります。チケットには以下のような情報があります。</p>
<pre><code class="language-kotlin">data class Ticket(
	val id: String,
	val name: String,
)
</code></pre>
<p>このチケットはオフラインでも使える必要があります。オフライン状態ではサーバーから情報を取得できないため、ローカルに永続化する必要があります。</p>
<p>AndroidのSQLiteデータベースを扱いやすくする<a href="https://developer.android.com/training/data-storage/room">Room</a>ライブラリを使うと、シンプルなデータ構造なら、アノテーションをいくつかつけるだけで永続化することができます。</p>
<pre><code class="language-kotlin">@Entity
data class Ticket(
	@PrimaryKey
	val id: String,
	val name: String,
)
</code></pre>
<h3>現実は複雑...</h3>
<p>しかし、実際のチケット情報はもっと複雑です。</p>
<ul>
<li>価格情報</li>
<li>有効期限</li>
<li>利用条件</li>
</ul>
<p>AndroidのRoom (SQLite) などのRDB (リレーショナルデータベース) では、このような複雑なオブジェクトをそのまま保存できません。通常は以下のような対応が必要です。</p>
<ul>
<li>テーブル設計を工夫してリレーションを作る</li>
<li><code>TypeConverter</code> (複雑な型を基本型に変換する仕組み) を使って変換する</li>
</ul>
<p>しかし、これらの対応は結構大変です...</p>
<p><strong>「あ〜、今はそんなこと考えている時間ないなぁ！」</strong></p>
<h3>楽な方法：全部JSONにする！</h3>
<p>そこで思いつくのが、<strong>全部JSONにしてしまう</strong>という方法です。 </p>
<pre><code class="language-kotlin">// ビジネスロジックで使う実際のデータクラス
@Serializable // kotlinx.serializationを使用
data class Ticket(
    val id: String,
    val name: String,
    // ... その他複雑なプロパティ (価格、有効期限など)
)

// データベースに保存する用のクラス
@Entity
data class TicketForDB(
	@PrimaryKey
    val id: String,
    val json: String, // Ticketを丸ごとJSON文字列として格納
)

// 保存
fun write(ticket: Ticket) {
	db.write(
	    TicketForDB(
	        id = ticket.id,
	        json = Json.encodeToString(ticket) // オブジェクト → JSON文字列
	    )
	)
}

// 復元
fun read(id: String): Ticket {
    val ticketForDB = db.read(id)
    return Json.decodeFromString&lt;Ticket&gt;(ticketForDB.json) // JSON文字列 → オブジェクト
}
</code></pre>
<p><strong>やった〜！複雑なデータ構造を持つチケット情報を簡単に保存できた！</strong></p>
<p>この方法なら</p>
<ul>
<li>複雑なTypeConverterを書く必要なし</li>
<li>テーブル設計で悩まなくていい</li>
<li>開発スピードが速い</li>
</ul>
<h2>数ヶ月後...チケット機能の改善</h2>
<p>時は流れ、新機能の開発が始まります。</p>
<p>「一つのチケットで複数人が利用できる機能を追加しよう！」</p>
<p>今までは一つのチケットで一人しか利用できませんでしたが、グループ利用に対応するため、<code>maxUsersPerTicket</code>というパラメータを追加しました。</p>
<pre><code class="language-kotlin">@Serializable
data class Ticket(
	val id: String,
    val name: String,
    val maxUsersPerTicket: Int, // 追加！
    // ... その他のプロパティ
)
</code></pre>
<p><strong>やったね！これで便利な新機能も簡単に開発できた！</strong></p>
<p>テストも通ったし、リリース準備OK！</p>
<h3>リリース後、問題発生...</h3>
<p>リリース後しばらくして、</p>
<p><strong>「クラッシュ報告が来ています！オフラインでチケットが使えないという問い合わせが多数...」</strong></p>
<h4>何が起きたのか？</h4>
<p>原因は、<strong>新しく追加した<code>maxUsersPerTicket</code>プロパティ</strong>です。</p>
<p><strong>問題の流れ：</strong></p>
<pre><code>[v1.0] チケット保存
  {&quot;id&quot;:&quot;abc123&quot;, &quot;name&quot;:&quot;Sample Ticket&quot;}
    ↓
[v2.0にアップデート]
    ↓
[復元を試みる]
  maxUsersPerTicketが必要だが、JSONにない
    ↓
  クラッシュ！
</code></pre>
<p>永続化されたJSONには<code>maxUsersPerTicket</code>がありません。しかし、新しい<code>Ticket</code>クラスは<code>maxUsersPerTicket</code>を必須としています。JSONライブラリは値を決められず、エラーになります。</p>
<pre><code class="language-kotlin">// この時、永続化されたJSONには maxUsersPerTicket が存在しない
val ticket = Json.decodeFromString&lt;Ticket&gt;(json)
// → Field &#39;maxUsersPerTicket&#39; is required for type Ticket
</code></pre>
<h2>どうすれば良かったのか？</h2>
<h3>解決策1：RDBでリレーショナルモデリングを頑張る</h3>
<p>RDBはJSONとして保存するよりも、圧倒的に型に強いです。</p>
<p>丁寧にテーブル設計を行っていれば</p>
<ul>
<li>スキーマ変更時にRoomのコンパイルエラーで気づける</li>
<li>型安全性が保証される</li>
<li>データの整合性を保ちやすい</li>
</ul>
<p>一方でデメリットもあります。</p>
<ul>
<li>複雑なデータ構造の表現が難しい (RDBは表形式)</li>
<li>テーブル設計に時間がかかる</li>
<li>TypeConverterやリレーションの管理が煩雑</li>
<li>将来的なメンテナンス性も不透明</li>
</ul>
<p>→ <strong>準備が大変な割に、メンテナンス性が必ずしも高くなるとは限らない</strong></p>
<h3>解決策2：JSONを使いつつ、ユニットテストで安全性を確保する</h3>
<p>JSONの手軽さを維持しつつ、ユニットテストで問題を早期発見する方法です。</p>
<h4>課題</h4>
<p>将来の開発者 (または未来のあなた) が、何も知らずに<code>Ticket</code>のプロパティを追加/削除/変更しても気づけるようにするには？</p>
<h4>解決方法</h4>
<p><strong>過去のJSON形式をテストで保存し、デシリアライズをテストする</strong></p>
<p>まず、現在のバージョンで保存されるJSONをfixtureファイル (テストで使う固定サンプルデータ) として保存します。</p>
<pre><code class="language-json">// fixture.json (テストリソースとして保存)
{
  &quot;id&quot;: &quot;abc123&quot;,
  &quot;name&quot;: &quot;Sample Ticket&quot;
}
</code></pre>
<h4>テストコード</h4>
<p>以下の2つのテストを追加します。</p>
<pre><code class="language-kotlin">@Test
fun `古いバージョンのJSONをデシリアライズできる`() {
    // 過去に永続化されたJSONが、現在のコードで読み込めることを保証
    val jsonString = loadJson(&quot;fixture.json&quot;)

    val deserialized = runCatching {
        Json.decodeFromString&lt;Ticket&gt;(jsonString)
    }

    // 失敗したら、新しいプロパティに初期値が必要など、問題に気づける
    assert(deserialized.isSuccess) {
        &quot;デシリアライズに失敗しました: ${deserialized.exceptionOrNull()}&quot;
    }
}

@Test
fun `デシリアライズ後に再シリアライズすると元のJSONと一致する`() {
    // fixtureファイルの更新漏れを検知
    val jsonString = loadJson(&quot;fixture.json&quot;)
    val deserialized = Json.decodeFromString&lt;Ticket&gt;(jsonString)
    val serialized = Json.encodeToString(deserialized)

    // プロパティを削除したのにfixtureを更新していないケースなどを検知
    assert(jsonString == serialized) {
        &quot;JSONが一致しません。fixture.json の更新が必要かもしれません&quot;
    }
}
</code></pre>
<h4>このテストが守ってくれるもの</h4>
<ol>
<li><strong>古いJSONとの互換性</strong>：過去のバージョンのJSONが読み込めることを保証</li>
<li><strong>fixtureの更新漏れ防止</strong>：データ構造が変わったら気づける</li>
</ol>
<h2>実際にテストを動かしてみよう</h2>
<h3>ケース1：プロパティを追加した場合</h3>
<p>問題が発生した例と同じく、数ヶ月後、複数人利用機能が追加されたとします。</p>
<pre><code class="language-kotlin">@Serializable  
data class Ticket(  
    val id: String,  
    val name: String,  
    val maxUsersPerTicket: Int, // 追加
)
</code></pre>
<p>先ほどのユニットテストを実行してみます。使用しているJSONライブラリにもよりますが、概ね以下のようなエラーでテストが落ちます。</p>
<pre><code class="language-kotlin">Field &#39;maxUsersPerTicket&#39; is required
</code></pre>
<p>これで将来の開発者やAIはこのデータクラスが永続化されていて、追加されるパラメータには初期値が必要なことが分かります。なぜならすでに永続化されたデータには新しい値が当然考慮されていないからです。</p>
<p>次にプロパティの名前が変更されたとします。
ある開発者がnameという名前は抽象的だ！と言い出して、<code>displayName</code>に変更しました。</p>
<pre><code class="language-kotlin">@Serializable  
data class Ticket(  
    val id: String,
    val displayName: String, // 変更
)
</code></pre>
<p>先ほどのユニットテストを実行してみます。同じように以下のようなエラーにより、問題に気づくことができます。</p>
<pre><code class="language-kotlin">Field &#39;displayName&#39; is required
</code></pre>
<p>次に、Ticketから名前が不要になりました。</p>
<pre><code class="language-kotlin">@Serializable  
data class Ticket(  
    val id: String,
    // val displayName: String, // 削除
)
</code></pre>
<p>たいていのプロジェクトではJSONライブラリの設定で、<code>ignoreUnknownKeys</code>のような、知らない値が来たら無視するための設定を有効にしていることが多いと思います。
したがってこれによるクラッシュが発生することは少ないでしょう。
しかし永続化されているであろうfixtureファイルの更新を漏らしたくないです。</p>
<p>先ほどのユニットテストを実行してみます。
以下のようなエラーにより、fixtureファイルの更新漏れを防ぐことができるでしょう。</p>
<pre><code class="language-kotlin">&quot;JSONが一致しません。fixture.json の更新が必要かもしれません&quot;
</code></pre>
<p>(上記のテストではどのプロパティに差分があるかまでは分からないので、実際にはobjectとして比較する案が考えられる)</p>
<h3>まとめ</h3>
<p>本記事では、永続化したデータを安全に扱うためのテスト手法を紹介しました。</p>
<p><strong>今日から始められること</strong></p>
<ol>
<li>現在永続化しているデータのJSON例をfixtureとして保存</li>
<li>そのJSONをデシリアライズするテストを書く</li>
<li>CIに組み込む</li>
</ol>
<p>一度ユーザーの端末に保存されたデータは、簡単には消せません。それは「技術的負債」になりやすい部分ですが、裏を返せば、ここを堅牢に保つことこそがアプリの長期的な信頼性に繋がります。</p>
<p>また、今回紹介したFixtureを用いたテストは、<strong>JSONライブラリの置き換え</strong> (例：GsonからMoshi/Kotlin Serializationへの移行) などにおいても極めて強力な武器になります。同じFixtureに対してテストが通れば、ライブラリや環境が変わっても「以前と同じようにデータを復元できる」ことが保証されるからです。</p>
<p>「今動くコード」を書くのは当然として、私たちはそこから一歩進んで、「ライブラリが変わっても、担当者が変わっても、少しでも安全に動き続けるコード」を残せるエンジニアでありたいものです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/10497_gota_hasegawa/2025-12-12/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[51歳QAエンジニア、AIと歩んだ試行錯誤の一年：会社のAI推進機運と短納期リリース現場、両方に向き合った日々]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-15-qa-engineer-ai-trial-error-51/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-15-qa-engineer-ai-trial-error-51/</guid>
            <pubDate>Mon, 15 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[51歳QAエンジニアがAI活用に挑み、試行錯誤と成果、そして未来への展望を綴った一年の記録をまとめました]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の15日目の記事です🎅🎄</p>
<p>こんにちは。KINTOテクノロジーズ株式会社でQAエンジニアをしているおおしまです。今回は、51歳の私が今年一年間、AIに助けてもらいながら業務に向き合ってきた経験と、その活用の仕方についてご紹介します。ほぼ自分語りになりますが、お付き合いいただければ幸いです。</p>
<h1>前提：AI推進の機運と現場の課題に挟まれて — 51歳からの挑戦</h1>
<p>私の勤務するKINTOテクノロジーズでは、会社全体としてAI活用を強く推進する機運があり、専門部署の設立や数多くの研修機会の提供、社内環境の整備・アップデートなど、非常に恵まれた環境が整っています。そんな状況に身を置くと、<strong>「使わない手はない」という気持ちになる</strong>のは自然なことでした。</p>
<p>一方、私がメインで担当していた案件は、既存Webサービスの改善や機能追加を小さな単位で素早くリリースするものでした。規模は大きくありませんが、短期間で高品質を求められる開発・テストは常に厳しく、キャッチアップに苦労する日々。<strong>自動化や仕様整理の効率化は必須であり、AIはそれを支える重要な道具</strong>という認識は持っていました。</p>
<h1>黎明期：まずは触ってみる — AIとの距離を縮めたきっかけ</h1>
<p>昨年までは公私ともにAIに触れる機会はほとんどありませんでしたが、社内の機運もあり「学んで使えるようになりたい」という漠然とした思いはありました。そんな中、外部講師によるAI研修を受けたことが大きな転機になります。</p>
<p>研修は初歩的な内容でしたが、印象に残ったのは「毎日プロンプトを書いてAIと触れる」という講師のアドバイスでした。世の中の大きな成果を見上げるのではなく、少しずつでも使うことを習慣化する。これが私の中で「スモールスタートでも継続する」という意識に変わった瞬間でした。</p>
<p>この時期はまだ実務とは関係ない形で、<strong>AIと仲良くなるための時間を過ごして</strong>いました。</p>
<h1>習熟期：現場の必然に応える — 短納期対応でのAI活用模索</h1>
<p>最初は業務無関係にプロンプトを書いて、期待通りの答えなのか質問の仕方が悪いのかといった試行錯誤をしていました。そのうち実業務で効率化できないかと抱えてきた課題について、小規模なものからAIで解決できないか試すようになったのが研修から3か月ほど経過したくらいです。</p>
<p>それまでは実務ではAIは全く活用していませんでしたが、このくらいならできそうという感触を実務での困りごとの解決に生かすように仕向けてみました。</p>
<p>結果としてはいろいろできそうという感触がありつつ、<strong>まだ実戦投入にはしんどい状態</strong>というのがこの時期です（案件内容によっては現在もこの状態です）。</p>
<h1>開花期：自動化の可能性を掴む — コード生成とプロセス変革</h1>
<p>今年9月、社内では「Vibe Coding Challenge」という企画が行われました（詳細は<a href="https://blog.kinto-technologies.com/posts/2025-10-22-vibe-coding-week-qa-static/">別のテックブログ記事</a>で紹介しています）。QAというコーディングが主業務ではない私たちにとっても、この企画は業務の進め方を発展させるきっかけになりました。</p>
<p>具体的には、テスト実施やデータ作成に必要な自動化コード生成でAIが大いに役立ちました。</p>
<p>これが出来たらいいなと願いがありながらスキル不足でできなかったこれらの作業をAIの助力であっさりクリアできたこと。これまでマニュアルテストで確認していたもののうち<strong>自動で置き換えできる領域の拡大可能性が見えた</strong>ことが大きな成果でした。</p>
<h1>適応期：実戦投入と成果 — 高頻度リリース現場でのAI活用</h1>
<p>可能性が見えてきた一方、現場のスケジュールは相変わらず厳しく、むしろ以前よりタスク量は増加。期間延長はできない中で、もうAIに頼らざるを得ない状況でした。</p>
<p>とはいえ、まだ社内コンセンサスが十分ではないため、従来の手法で検証を行いながら、AIによる自動テストでカバーできる部分を徐々に増やす方針を採用しました。</p>
<p>案件詳細はお伝えできませんが、これまで「5車種を3日間で確認」していたペースを、AI活用によって「5日間で60車種以上」をこなすことに成功。今後も続いていく同種の案件では、この成功をベースにより効率的な進行が可能となり、<strong>大きな成功体験</strong>になりました。</p>
<h1>展開期：情報共有と発信 — 知見を広げる挑戦</h1>
<p>得られた知見はまずQAグループでの定例ミーティングで共有していきました。その後、社内外の勉強会で複数回登壇して成果を外部発信する機会をいただきました。まだまだ先を走る人から見ると小さな成果かもしれませんが、<strong>他社エンジニアとの交流から新しいアイデアを得られたのは大きな収穫</strong>でした。</p>
<h3>2025年登壇イベント一覧</h3>
<table>
<thead>
<tr>
<th>日時とイベント名</th>
<th>登壇タイトル</th>
<th>内容</th>
</tr>
</thead>
<tbody><tr>
<td>3月27日 <a href="https://jasst.jp/tokyo/25-about/">JaSST Tokyo</a></td>
<td>QA作業における生成AI活用事例</td>
<td><a href="https://speakerdeck.com/kintotechdev/qazuo-ye-niokerusheng-cheng-aihuo-yong-shi-li">SpeakerDeck</a>※弊社ブース前でのミニセッションでプログラムには記載なし</td>
</tr>
<tr>
<td>9月24日 <a href="https://kinto-technologies.connpass.com/event/367578/">CO-LAB Tech Night vol.3</a></td>
<td>生成AIを自動テストに活用していくための試行錯誤と見えたもの</td>
<td>—</td>
</tr>
<tr>
<td>11月14日 <a href="https://tokyotestfest.com/">TokyoTestFest</a></td>
<td>Claude Code × Playwright環境で自然言語による指示のみでフロントエンドテストを自動実行できた話</td>
<td>※弊社ブース前でのミニセッションでプログラムには記載なし</td>
</tr>
<tr>
<td>11月20日 <a href="https://lapras.connpass.com/event/374054/">AI時代の開発現場 — 成功と失敗のリアル共有会</a></td>
<td>Claude Code × Playwright環境で自然言語による指示のみでフロントエンドテストを自動実行できた話</td>
<td><a href="https://speakerdeck.com/kintotechdev/claude-code-x-playwrighthuan-jing-de-zi-ran-yan-yu-niyoruzhi-shi-nomide-hurontoendotesutowozi-dong-shi-xing-dekitahua">SpeakerDeck</a>※11/14とほぼ同じ</td>
</tr>
</tbody></table>
<p>登壇機会が多かった理由としては会社がシンポジウムのスポンサーになったり、勤務先のOsaka Tech Labにイベントスペースがあって会場として多くの勉強会が開催されたりしたことが大きな助けになりました。そのほか、稚拙な内容ながらも恥を忍んで登壇できたのは、50歳を超えた厚かましさのおかげかもしれません。</p>
<h1>終わりに：未来への展望 — AI4QAからQA4AIへ、次の挑戦</h1>
<p>今年の取り組みは、とある勉強会で知ることになった今風の用語をお借りして「AI4QA：QA業務にAIを活用」の実現となります。一部できつつあるものの、まだまだ完成には程遠い出来というところも実感しています。この取り組みは来年以降も継続していきます。</p>
<p>その一方で、「QA4AI：AIに対してQAする」も世間的には進んできているようです。弊社でも各サービスや製品をAI活用を前面に押したものが増えるのではないかと想像しています。となると、これまで個人的には想定していなかったAI自体に立ち向かう時代も遠くないと感じています。</p>
<p>これはAI4QA以上の難関で、何をすればいいのかも見当が付きません。ただ、QA4AIが求められるのは、厳しくしんどい仕事である一方、51歳の私でも伸びしろを感じられる刺激的な面がある楽しみもあります。</p>
<p>とはいえ、自分自身の向上も大事ですが、エンジニアの仕事はいいサービス・製品を提供することが第一です。AI4QAでこれまで出来なかったことの実現や時間がかかったことが短縮できた成果を広げていくことは大事ですが、効率化による時間的・精神的余白が出来たことで人間だからできる品質向上への貢献ができれば、高速リリースの時代でもお客様に満足していただけるリリースに貢献できるのではと思います。</p>
<p>今年はこれまで触れる機会がなかったAIにある程度向き合うことができました。来年以降もしっかり向き合っていきたいと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/oshima/AI-trial-and-error-v2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ついに(一部で)EKSを入れたお話 - OpenSearchのEOLに伴うリプレイスを添えて -]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-14-monitoringplatform-renewal/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-14-monitoringplatform-renewal/</guid>
            <pubDate>Sun, 14 Dec 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>こんにちは。プラットフォームGでPlatformEngineeringの考え方をベースにツール周りの開発・運用・展開の役割(とエンジニアリングマネージャーと本格的にアプリケーション開発もやり始めて、よくわからなくなった)<a href="https://twitter.com/sokasanan">島村</a>です。</p>
<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の14日目の記事です🎅🎄</p>
<p>社内モニタリング基盤をリプレイスするにあたって、ECSやManagedServiceではなく、EKS上に構築した話についてお話しをします。</p>
<h1>背景</h1>
<p>弊社では元々、</p>
<ul>
<li><a href="https://blog.kinto-technologies.com/posts/2022-12-09-AWS_Prometehus_Grafana_o11y/">社内モニタリング基盤</a><ul>
<li>OpenSearch (Log + Alert + Visualization)</li>
<li>Managed Prometheus (Metrics)</li>
<li>Managed Grafana (Alert + Visualization)</li>
<li>X-Ray (Trace + Visualization)</li>
</ul>
</li>
<li>SaaS<ul>
<li>NewRelic (ALL)</li>
</ul>
</li>
</ul>
<p>となっていました。基本、そんなに監視運用レベルが求められないものはモニタリング基盤、ガッツリ色々見たい＋関連サービスがあるものはNewRelicというような感じです。</p>
<p><img src="/assets/blog/authors/JumpeiShimamura/20251214/old_platform.webp" alt="旧構成図"></p>
<p>OpenSearchについては、本当はバージョン追従をするべきなんですが、古いままで運用し、<a href="https://aws.amazon.com/jp/blogs/news/amazon-opensearch-service-announces-standard-and-extended-support-dates-for-elasticsearch-and-opensearch-versions/">2025/11についにEOLになりました。</a>
コストや運用の手間を減らす目的と、AIのベクトルストアで使えるか確認のため、Serverlessの検証なども行ったんですが、「確認の際に横断的に見えないというのは課題だ」、ということで、全体的な刷新を行うことにしました。</p>
<p>合わせて、RCA(RootCauseAnalysis)の情報取得元としても使いやすくしようということで、関連プロジェクトとしてプロジェクト化しました。
<a href="https://aisysops.connpass.com/event/376375/">（RCAの概要はこちらのイベントのLT参照）</a></p>
<h1>まずはStackを決める</h1>
<p>まず、弊社のワークロードの基本構成はECS+Fargateです。AWS上に環境を作る場合、PackModuleと呼ばれているTerraformのモジュール群があります。→<a href="https://www.youtube.com/watch?v=4h_0B0QYo2g">過去参考動画</a></p>
<p>なので、ECSをベースに考えたのですが、制限事項も多いことから、まずは求められること(要件)を整理しました。</p>
<h2>したいこと</h2>
<ol>
<li>横断的にLog/Metrics/Traceが可視化できること</li>
<li>(リソース、ライセンス的に)安価であること</li>
<li>切り替えが容易であること<ul>
<li>Fluentbitでログを、AdotCollectorでMetrics/Traceを転送している部分を大きく変更しない。</li>
</ul>
</li>
<li>永続ログはS3に保存できること<ul>
<li>モニタリング基盤から保存ではなく、途中でAWSコンポーネントからの転送でもOK</li>
</ul>
</li>
<li>NewRelicより可能なら長く検索できる期間があること</li>
</ol>
<p>特に、２番目については、NewRelicというSaaSツールも既にありますから、使用するアプリケーションユーザ側の予算的に今までより高価になると意味がありませんでした。</p>
<h2>しないこと</h2>
<p>こちらも重要だと思います。最低限必要なものを整理して、あれもこれも…とやると時間と手間が増えます。</p>
<ul>
<li>AWSのアカウントは既存と同じにする<ul>
<li>運用アカウント分割を過去は検討したが、現状ではやらない</li>
<li>CloudInfraチームやSCoEなど、アカウント増やすなら関係各所が増えるのと、アカウント自体のセキュリティールールの運用のため</li>
</ul>
</li>
<li>Profile可視化(Pyroscope)<ul>
<li>アプリケーション担当者へのトランスファーや運用手順が増える</li>
</ul>
</li>
<li>S3にある永続ログを検索できるように戻したりとかはしない</li>
<li>生成AI周りの監視基盤<ul>
<li>最終的にはLangFuseが生えましたが、最初の時点ではスコープ外</li>
</ul>
</li>
</ul>
<h2>決まったこと</h2>
<h3>使うOSS</h3>
<table>
<thead>
<tr>
<th>名称</th>
<th>機能</th>
<th>概要</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://grafana.com/ja/docs/loki/latest/">Loki</a></td>
<td>Log保存</td>
<td>ログ保存。OpenSearchのOSSやElasticも悩みましたが、全体揃ってるので基本GrafanaLabs系で統一</td>
</tr>
<tr>
<td><a href="https://grafana.com/docs/tempo/latest/">Tempo</a></td>
<td>Trace保存</td>
<td>トレース保存</td>
</tr>
<tr>
<td><a href="https://grafana.com/docs/grafana/latest/">Grafana</a></td>
<td>可視化</td>
<td>各種可視化とアラート。Grafanaの構築単位は任意のアプリ群にしました</td>
</tr>
<tr>
<td><a href="https://grafana.com/docs/alloy/latest/">Alloy</a></td>
<td>AWSログ転送</td>
<td>Lambda/WAF/RDS系のログがKinesisFirehose(HTTP)からAlloyを経由してLokiに転送</td>
</tr>
<tr>
<td><a href="https://thanos.io/">Thanos</a></td>
<td>Metrics保存</td>
<td>Mimirではなくこちらで。メンバーのナレッジがあったため</td>
</tr>
<tr>
<td>自作Logger</td>
<td>AWSログ転送</td>
<td>ALB/Cloudfrontの、S3にしか出力できないログの転送用。現行はLogStashを使用</td>
</tr>
<tr>
<td><a href="https://argo-cd.readthedocs.io/">ArgoCD</a></td>
<td>GitOps</td>
<td>GitOpsのためこちらを選択</td>
</tr>
</tbody></table>
<h3>制限事項</h3>
<ul>
<li>Thanos/MimirともにNFSをサポートしてないので、ECS+Fargateだと厳しい</li>
<li>CNDW2025でLokiをECS+Fargateで構築した人がいらっしゃったんですけど、コンポーネントとか考慮点が多い</li>
<li>Grafana系だとLoki以外はDockerでのDeploy手順が公式でサポートされていない<ul>
<li>Prod環境だとHelmかTankaでのデプロイメントが推奨</li>
</ul>
</li>
</ul>
<p><strong>あ、EKSで動かすしか無いか…</strong></p>
<p>もともと、EKSはアプリケーション要件があれば導入を検討しようという話もありました。そのため、ちょうど良い機会としてEKSを実行サービスとして決めました。ただ、Webアプリケーションを動かしているワークロードについてはECSのままとしています。</p>
<p>PlatformEngineeringTeamとして、CICDのテンプレートやその他ツール群を提供していますが、アプリケーション部門にEKSを提供するメリットがあまり見えなかったことと、提供のための各種修正を考慮した結果です。</p>
<h3>構成</h3>
<p><img src="/assets/blog/authors/JumpeiShimamura/20251214/monitoring.webp" alt="全体構成図"></p>
<p>※NewRelic経路は変わらないので割愛</p>
<h2>AWSコンポーネント</h2>
<p>この時点で、ちょうどEKSにAutoModeが発表されました。制限事項の1つ目のEFSの対応という点では、EKS+Fargateでも対応不可だったので、AutoModeを使ってみようということになりました。</p>
<p>AutoModeでも、ARM64を指定した設定ができ、起動するEC2のインスタンスが安価になるので、構築後にArmアーキテクチャのNodePoolを設定しています。</p>
<h1>EKS構築以外の新モニタリング基盤の推進に向けたおしごと</h1>
<h2>PackModuleの修正</h2>
<p><a href="#%E3%81%BE%E3%81%9A%E3%81%AFstack%E3%82%92%E6%B1%BA%E3%82%81%E3%82%8B">最初の方</a>にあった、Moduleの修正です。
意識しないと移行しない(=Default値が変わらないようにする)形で修正しました。</p>
<ul>
<li>KinesisFirehose(HTTPエンドポイント向け)のModule作成<ul>
<li>Backupの取得をALLにしてS3へ保存(<a href="#%E3%81%97%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8">要件4</a>に関連する)</li>
</ul>
</li>
<li>呼び出す各コンポーネントModuleの修正<ul>
<li>Lambda</li>
<li>WAF</li>
<li>RDS</li>
</ul>
</li>
</ul>
<h2>セキュリティー調整</h2>
<p>Firehoseですが、VPC内部に作成されないので、Alloyの前段のALBにインターネット経由での通信経路となります。その場合、さすがに認証認可もなしでALBにログを送るのもNGでしたので、Headerでの認証を追加しました。
構築時にSecretManagerにKEYを保存して、Firehoseからはそれを付与して送信。ALBではその値とマッチしているか？と確認しています。</p>
<p>コンテナの脆弱性を検知する仕組みをECR＋通知で作っており、DockerHubから直接呼び出さずに、ECRに一時的に保管する運用も行なっています。が、かなり運用負荷が高いので、どうにかしたいのが課題となっています。</p>
<h2>移行のお願い</h2>
<p>一番泥臭く、CustomerSuccessEngineerチーム(CSE)が各担当にお声がけをしてチケットを切って管理する形で行なっています。一部は開発タスク優先で期限からはみ出ましたが、多くのプロダクトはありがたいことに協力いただき、年内での切り替えを想定しています。</p>
<p>アプリケーションの担当者の変更作業としては</p>
<ul>
<li>Fluentbitの使用バージョンの変更(LokiPlugin向け)</li>
<li>各種Configの修正<ul>
<li>AdotCollector</li>
<li>ECS TaskDefinition</li>
</ul>
</li>
</ul>
<p>こちらは、PlatformGでConfluenceに作業手順を準備して、提供しています。</p>
<p>Firehoseなどの切替は、アプリケーション担当者と日程を調整し、PlatformGとCloudInfraGでTerraformの修正という形で行なっています。</p>
<h1>おまけ</h1>
<h2>アカウント分割の対応</h2>
<p>移行中ではあるんですが、社内のAWSアカウントの最適化が進んでおり、そちらの変更反映も実施する予定です。
ただ、EKSの前にはNLB/ALBを挟んでいるため、VPCPeeringかVPCEndpointによる対応で済む見込みです。</p>
<p><strong>(´・ω・) 運用アカウントに分割する日は、何時来るのかな</strong></p>
<h2>LLM監視や追加機能のデプロイ</h2>
<p>RCAのためのLangFuse、実行時間やメモリなどの関係からCode分析機能がLambdaをやめてEKS上に移行、AWSメトリクス収集のための<a href="https://github.com/prometheus-community/yet-another-cloudwatch-exporter">YACE</a>のデプロイなど、モニタリング・RCA関連が色々と追加で載るようになりました。</p>
<h1>所感</h1>
<p>基本、機能要件とか技術制限など、何かしらの理由がない場合は、AWSでコンテナなどを動かすにはECSで十分だと思います。実際、n8nとか他のOSS系はECSで起動するようにしています。
新しくジョインしたメンバーがEKSと各種Grafanaスタックに経験が多かったこともあって、ある程度、スムーズに構築や移行できたこともあります。</p>
<p>ただ、AutoModeの運用経験や、実際に各環境のログなどを投入していくと問題、エラーなどが出てきました。安定運用までに色々と手を打たないといけないと考えています。
やはり、K8s運用は一筋縄ではいかないと実感しました。</p>
<h1>さいごに</h1>
<p>PlatformEngneeringチームは、社内向けの横断ツールを統制して必要なものを開発しています。
必要なものを新規作成や既存のものをマイグレーションしたり、ツールを使ってもらうためのEnablingの活動や、マーケティング技術を使った内部展開などのチームもあります。
GoldenPathの整理(そもそも業務フローの可視化・改善)も実施していく予定です。
こういった活動に少しでも興味を持ったり話を聞いてみたいと思った方は、お気軽にご連絡いただければと思います。</p>
<p>@<a href="https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303169">card</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/JumpeiShimamura/20251214/1214-title.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[Struggle Story of QAG Members Who Joined in 2025]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-14-qa-advent-calendar-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-14-qa-advent-calendar-en/</guid>
            <pubDate>Sun, 14 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A summary of initiatives by QAG members who joined in 2025]]></description>
            <content:encoded><![CDATA[<p>This article is the Day 14 entry for the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> 🎅🎄</p>
<h2>Introduction</h2>
<p>We, members who joined KINTO Technologies&#39; QA Group in 2025, have participated in this Advent Calendar campaign!
This blog describes the topics we have worked on as QA team members from our joining the company to date.
We hope it serves as inspiration for QA professionals and helps developers understand that QA has tasks which are not perceived well.</p>
<hr>
<h2>Creating a Web App for Test Efficiency</h2>
<ul>
<li><p>Self-introduction
I&#39;m Tomiyoshi. I worked as a QA in third-party verification at my previous job. I have almost no development experience.</p>
</li>
<li><p>Content
One day, something came up.
We discussed how to efficiently test text input in a chat on mobile devices.
Someone said, &quot;Why don’t we just use AI to create a web app?
So, I built one!!</p>
</li>
</ul>
<p><img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi_1.png" alt="Zapier質問一覧">
<img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi.gif" alt="WebAppGif"></p>
<table>
<thead>
<tr>
<th>Copy</th>
<th>Paste</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi_copy.png" alt="Zapierコピー"></td>
<td><img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi_paste.png" alt="Zapierペースト"></td>
</tr>
</tbody></table>
<p>The system uses Zapier, a process automation tool.
After questions generated by AI are stored in Google Spreadsheet as a database,
we use Google Apps Script to create the web app.</p>
<table>
<thead>
<tr>
<th></th>
<th>Tool</th>
<th>What a Tool Does</th>
<th>Reference Image</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Zapier</td>
<td>Creating a form screen<br>Zapier&#39;s Interfaces feature allows you to create forms.</td>
<td><img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi_form.png" alt="Zapier Question Generation Form Reference"></td>
</tr>
<tr>
<td>2</td>
<td>Zapier</td>
<td>AI-powered question generation<br>Generates questions based on input from the form screen.</td>
<td>—</td>
</tr>
<tr>
<td>3</td>
<td>Zapier</td>
<td>Integration with Google Spreadsheet<br>Links generated questions to Google Spreadsheet. This integration is enabled as a standard Zapier feature.</td>
<td>—</td>
</tr>
<tr>
<td>4</td>
<td>Google Spreadsheet</td>
<td>Used as a database substitute<br>Stores question content shared from Zapier</td>
<td>—</td>
</tr>
<tr>
<td>5</td>
<td>Google Apps Script</td>
<td>Displays the web app based on the question content stored in Google Spreadsheet</td>
<td>—</td>
</tr>
</tbody></table>
<p>When I started creating the app, I was in doubt that I could finish it.
I proceeded by asking AI for details about how to accomplish each step for the app creation.</p>
<pre><code>I was able to link Zapier to the spreadsheet. The linked content includes the generated question JSON (in Column D).
I want to create a web page that lists the contents of the questions in this JSON and allows for copying each one individually.
Attachment: Downloaded Google Spreadsheet Excel file
</code></pre>
<p>Of course, it didn&#39;t work on the first attempt, and errors occurred when I used the code generated by AI as-is.
By checking each error with AI one by one, I was able to create the app in the current format.</p>
<p>Initially, the app didn’t have a search function, so it somewhat lacked in convenience.
However, I believe the key to success is not to request too many things at once but instruct AI to add detailed features step by step.</p>
<p>Thanks to this approach, I rarely modified the code on my own.
I simply kept writing about what I wanted to do on prompt, and AI gave shape to my ideas by coding for about 1 to 2 days.</p>
<p>Once I registered template texts in advance to input into the chat using this web app,
all I need to do is to access it from a mobile device and copy-paste,
rather than to manually type texts on a keyboard, which increased the input task efficiency.</p>
<p>This was my first web app creation, but I managed to build it, collaborating with AI.</p>
<p>Despite a lack of development experience, I was able to create something like this, so I will continue trying to develop new things.</p>
<h4>Appendix</h4>
<p>Zapier: <a href="https://zapier.com/">https://zapier.com/</a>
Google Spreadsheet: <a href="https://workspace.google.com/intl/ja/products/sheets/">https://workspace.google.com/intl/ja/products/sheets/</a>
Google Apps Script: <a href="https://developers.google.com/apps-script?hl=ja">https://developers.google.com/apps-script?hl=ja</a></p>
<hr>
<h2>Presenting My Experience as a Beginner Setting Up an Appium Environment</h2>
<ul>
<li><p>Self-introduction
I&#39;m Roki. At my previous job, I worked as a QA at a news app company. I have no development experience.</p>
</li>
<li><p>Content
Hello!
Recently, I gave a lightning talk at <a href="https://appiummeetuptokyo.connpass.com/event/368989/">Appium Meetup Tokyo #3</a> about &quot;Stumbling Blocks for Beginners in Appium Environment Setup.&quot;
The event took place both online and onsite. I talked for 15 minutes about introducing four key points using slides.</p>
</li>
</ul>
<p>The reason I felt interested in Appium was that I thought this could be my first step into coding!
However, as a beginner, getting stuck on environment setup may be discouraging.
Since I struggled quite a bit myself, I wanted to present my experience, hoping it might be helpful for others facing similar issues.</p>
<p>Here are the points I presented:</p>
<ul>
<li>AI is surprisingly capable of providing solutions when you ask about errors</li>
<li>Checking the official Appium website ensures the correct versions</li>
<li>Only launching necessary tools prevents tool conflicts</li>
<li>Getting hands-on experience is the fastest way to learn Git commands</li>
</ul>
<p><img src="/assets/blog/authors/yoshitomi/advent_calender/koroki_1.png" alt="興梠イベント登壇画像"></p>
<p>After my lightning talk, I was happy when participants told me that they could really relate to all common issues.
Speaking in front of people is still nerve-wracking, but I felt relieved when it ended.
I want to continue coding little by little and expand what I can do.</p>
<h4>Appendix</h4>
<p><a href="https://speakerdeck.com/kintotechdev/appiumwodong-kasumatenotumatukihointo-chu-xin-zhe-kagan-sitariarunabi-toxue-hi">https://speakerdeck.com/kintotechdev/appiumwodong-kasumatenotumatukihointo-chu-xin-zhe-kagan-sitariarunabi-toxue-hi</a></p>
<hr>
<h2>Automation with Playwright</h2>
<ul>
<li><p>Self-introduction
I&#39;m Higashi. At my previous job, I worked as a QA in third-party verification. I have almost no development experience.</p>
</li>
<li><p>Content
The most memorable experience since I joined the company was testing automation.
I had never done it at my previous job, but when my manager and colleagues said, &quot;Would you like to try it?&quot; I was so excited that I immediately said, &quot;Absolutely!&quot;</p>
</li>
</ul>
<p>The project I joined required regression testing to verify the entire process up to application completion under various conditions.
To automate the process, we decided to create a regression test script specifically for this project.</p>
<p>Also, my team has adopted Playwright as a tool for E2E testing automation,
and my colleagues had already completed data creation and regression test scripts to some extent.
Based on the scripts, I created ones for specified application conditions.</p>
<p>Although I had reference scripts,
when even one component operation differed—such as dropdown selections, text box inputs, or checkboxes and buttons to click—element retrieval errors would occur.
This hindered me from making progress as smoothly as I expected...
That&#39;s when I found a Playwright feature that helped me the most: the code generation feature.
It records browser operations and outputs automation code for them.
Using this feature for performing browser operations in line with the application conditions,
even beginners can easily generate code and retrieve elements. Thanks to the feature, errors are solved.</p>
<p>(Example: Recording the operation of clicking &quot;View vehicle list&quot;)
<img src="/assets/blog/authors/yoshitomi/advent_calender/higashizi_1.png" alt="PlayWrightクリック操作録画"></p>
<p>There are still many convenient Playwright features I don&#39;t know about,
so I want to gradually learn how to use them by asking colleagues and researching on my own, thereby getting more familiar with Playwright over time.</p>
<hr>
<h2>Handling Appium-Related Tasks and Establishing Development Rules</h2>
<ul>
<li><p>Self-introduction
I&#39;m m. I made a job change to QA when I joined KTC! Until my previous job, I was developing Android apps.</p>
</li>
<li><p>Content
Recently, I had an opportunity to get involved with E2E testing automation using Appium, so I&#39;ll share that experience.
I&#39;ll touch on two main points: the differences between unit tests and E2E tests along with my impressions, and the establishment of development rules.</p>
</li>
</ul>
<h4>1. Introduction of E2E Testing with Appium</h4>
<p>E2E testing reproduces the operations that users actually perform on devices and verifies the overall behavior of the app.
I worked on creating E2E tests for Android and iOS apps using Appium.</p>
<p>The particular issue was the execution time.
Wait times are unavoidable in E2E testing. Specifically, the following wait times occur:</p>
<ul>
<li>Waiting for UI operations</li>
<li>Waiting for screen rendering</li>
<li>Waiting for network communication</li>
</ul>
<p>The more waiting time we have, the more test execution time and costs we need to spend in testing.
Therefore, it is significant to decide which scenarios to prioritize for testing and to design retry processing for cases where tests cannot execute normally due to external factors.</p>
<p>Currently, we are working on speeding up execution through discussions about processing content in our peer review phase.
As a result, we have achieved reduced load on some processes. We will continue to discuss and address these issues.</p>
<h4>2. Establishing Development Rules</h4>
<p>As the number of project members increased, we established development-related rules, such as reviewing branch strategies, branch protection settings, and PR rules.</p>
<h5>Changing Branch Strategies</h5>
<p>Previously, we used GitHub Flow, but we have now changed the version control system to Git Flow.
That is mainly because we wanted to manage releases for each version of the app we use.
Adopting Git Flow enabled clearer feature development and release management, reducing confusion when we handle multiple versions simultaneously.</p>
<h5>Reviewing Branch Protection Settings</h5>
<p>On the main branch running in the production environment,
we faced an issue that the application didn’t work due to individual environment differences among newly assigned developers, including myself. 
To overcome the issue, we changed the branch settings to enable merging in creating PRs for the main and release branches only
when GitHub Actions workflows automatically verify that operation checks (running all tests) pass.</p>
<h5>Establishing PR Rules</h5>
<p>To clarify what each developer was doing for previous PRs,
we prepared a PR template including the following information:</p>
<ul>
<li>Summary of changes</li>
<li>Details</li>
<li>What was verified</li>
<li>What reviewers should check</li>
</ul>
<p>This template was designed to organize PR content and make reviews more efficient.
It helped us establish a foundation for more secure and safe development. We want to work together as a team to create an even better development environment.</p>
<p>We continuously share initiatives like E2E testing automation using Appium and development rule establishment from a QA perspective at internal events.</p>
<p>We currently hold a monthly event called CO-LAB Tech Night at our Osaka branch (Osaka Tech Lab), so please join us...!
Here&#39;s a photo of the event when I presented at the QA session.
<img src="/assets/blog/authors/yoshitomi/advent_calender/kobayashi_1.webp" alt="小林イベント登壇画像"></p>
<hr>
<h2>Conclusion</h2>
<p>Thank you for reading to the end.
We hope this content serves as inspiration for your QA activities.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yoshitomi/advent_calender/qa_advent_calendar_cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年QAG入社メンバー奮闘記]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-14-qa-advent-calendar/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-14-qa-advent-calendar/</guid>
            <pubDate>Sun, 14 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年に入社したQAGメンバーの取り組み内容まとめです。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の14日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>2025年にKINTOテクノロジーズのQAGに入社したメンバーで、アドベントカレンダーに参加しました！
入社してからこれまでのQAとして取り組んできたトピックをまとめています。
同じQAの方々にはアイデアのきっかけとして、開発の方々には「QAってこんなこともやるんだ」という認知の拡大につながればと思います。</p>
<hr>
<h2>テスト効率化のためのWebAppの作成</h2>
<ul>
<li><p>自己紹介
とみよしです。前職でも第三者検証でQAをしていました。開発経験はほとんどありません。</p>
</li>
<li><p>内容
ある日こんなことが。
スマホ端末でチャットに文言入力するのに効率よくテストをするためにはどうすればいいだろうかという話に。
いっそのことAIを使ってWebAppを作るか！という案が出ました。
なので作りました！！</p>
</li>
</ul>
<p><img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi_1.png" alt="Zapier質問一覧">
<img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi.gif" alt="WebAppGif"></p>
<table>
<thead>
<tr>
<th>コピー</th>
<th>ペースト</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi_copy.png" alt="Zapierコピー"></td>
<td><img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi_paste.png" alt="Zapierペースト"></td>
</tr>
</tbody></table>
<p>仕組みとしては作業自動化ツールのZapierを使用し、
AIで生成した質問をGoogle SpreadSheetにDBとして格納、
Google App Scriptを使用してWebAppとして作成しました。</p>
<table>
<thead>
<tr>
<th></th>
<th>ツール</th>
<th>行っていること</th>
<th>参考画像</th>
</tr>
</thead>
<tbody><tr>
<td>1</td>
<td>Zapier</td>
<td>form画面の作成。<br>Zapierの機能としてInterfacesからformの作成を行うことができます。</td>
<td><img src="/assets/blog/authors/yoshitomi/advent_calender/yoshitomi_form.png" alt="Zapier質問生成フォーム参考"></td>
</tr>
<tr>
<td>2</td>
<td>Zapier</td>
<td>AIによる質問生成<br>form画面から入力された内容を元に質問を生成します。</td>
<td>ー</td>
</tr>
<tr>
<td>3</td>
<td>Zapier</td>
<td>Googleスプレッドシートへの連携<br>生成した質問をGoogleスプレッドシートに連携します。Zapierの標準機能としてこちらの連携が行えます。</td>
<td>ー</td>
</tr>
<tr>
<td>4</td>
<td>Googleスプレッドシート</td>
<td>DBの代わりとして使用<br>Zapierから連携した質問内容を保存する</td>
<td>ー</td>
</tr>
<tr>
<td>5</td>
<td>GoogleAppsScript</td>
<td>Googleスプレッドシートに保存されている質問の内容をもとにWebAppとして表示</td>
<td>ー</td>
</tr>
</tbody></table>
<p>これを作成するにあたって最初はできるのか？と思いながら進めていましたが、
AIにどうすればできるのか、どうやればいけるのかなど細かく聞きながら進めていきました。</p>
<pre><code>Zapierでスプレッドシートに連携することはできました。連携している内容に生成された質問JSON（D列）があります。
このJSONのquestionsの内容を一覧にして一つ一つコピーできるようなwebを作成したいです
添付：ダウンロードしたGoogleスプレッドシートのExcelファイル
</code></pre>
<p>もちろん一発でできるわけもなく、AIに生成してもらったコードをそのまま利用してもエラーが発生しました。
そのエラーに対してもAIに一つ一つ確認していくことで今のような形になりました。</p>
<p>当初は検索機能などもなかったため、利便性の面ではやや物足りない状態でした。
ただ一度に大量のことを要求することはせず、細かく機能追加を行うように指示したのが今回はうまく行った要因かと思っています。</p>
<p>このように行っていったおかげでコードについてもほとんど自分からは手を加えてはいません。
ひたすらこうして欲しいと言ったのを繰り返し、コードと向き合っていた期間としては1〜2日程度で作成してくれました。</p>
<p>このWebAppであらかじめチャットに入力する内容を登録しておき、
スマホ端末でアクセスしてコピペをするだけになったことで、
ひたすらキーボード入力するよりは効率的に実施できるようになりました。</p>
<p>初のWebApp作成でしたがAIと二人三脚で何とか作り上げることができました。</p>
<p>開発ほぼ未経験でもこうして作り上げることができるので今後も挑戦していきたいと思います。</p>
<h4>付録</h4>
<p>Zapier：<a href="https://zapier.com/">https://zapier.com/</a>
Googleスプレッドシート：<a href="https://workspace.google.com/intl/ja/products/sheets/">https://workspace.google.com/intl/ja/products/sheets/</a>
Google Apps Script ：<a href="https://developers.google.com/apps-script?hl=ja">https://developers.google.com/apps-script?hl=ja</a></p>
<hr>
<h2>Appium環境構築初心者の体験談を発表してきました</h2>
<ul>
<li><p>自己紹介
ろきです。前職ではニュースアプリの会社でQAをやっていました。開発未経験です。</p>
</li>
<li><p>内容
こんにちは！
先日、<a href="https://appiummeetuptokyo.connpass.com/event/368989/">Appium Meetup Tokyo #3</a>で「Appium環境構築の初心者つまづきポイント」についてLTしてきました。
形式はオンラインとオフライン両方。時間は15分くらいで、スライドを使って4つのポイントを紹介しました。</p>
</li>
</ul>
<p>そもそもAppiumに興味を持ったのは、「コーディングの第一歩を踏み出せそう！」と思ったから。
とはいえ、初心者だと環境構築で詰まってしまって心が折れることもありますよね。
私自身もかなりハマったので、「同じようなところで困っている人に少しでも役立てば…」と思って発表しました。</p>
<p>話したポイントはこんな感じです。</p>
<ul>
<li>エラーはAIに聞くと意外と解決できる</li>
<li>バージョン合わせはAppium公式HPを見ると確実</li>
<li>必要なツールだけ起動してツールの競合を防ぐ</li>
<li>Gitコマンドは触って慣れるのが早い</li>
</ul>
<p><img src="/assets/blog/authors/yoshitomi/advent_calender/koroki_1.png" alt="興梠イベント登壇画像"></p>
<p>登壇後、参加者の方から「あるあるばかりで共感できました！」と言ってもらえたのが嬉しかったです。
人前で話すのはやっぱり緊張しますが、終わったあとはホッとしました。
これからも少しずつコーディングに挑戦して、できることを広げていきたいです。</p>
<h4>付録</h4>
<p><a href="https://speakerdeck.com/kintotechdev/appiumwodong-kasumatenotumatukihointo-chu-xin-zhe-kagan-sitariarunabi-toxue-hi">https://speakerdeck.com/kintotechdev/appiumwodong-kasumatenotumatukihointo-chu-xin-zhe-kagan-sitariarunabi-toxue-hi</a></p>
<hr>
<h2>Playwrightによる自動化</h2>
<ul>
<li><p>自己紹介
ひがしです。前職では第三者検証のQAをしていました。開発経験はほとんどありません。</p>
</li>
<li><p>内容
僕が入社して印象深かった経験は『テスト自動化』です。
前職では全くしたことがありませんでしたが、上長と先輩社員から「やってみる？」とお話を頂いた時は「ぜひ！」と二つ返事するくらいワクワクでいっぱいでした！</p>
</li>
</ul>
<p>ジョインした案件では、様々な申込条件で申込完了までの一連のフローを確認するリグレッションテストが必要となりました。
そこで、それを自動化で実施するために、この案件専用のリグレッションテスト用スクリプトを作成することになりました。</p>
<p>また、僕の所属チームでは、E2Eテスト自動化のためのツールとして『Playwright』を採用しており、
既に先輩社員がデータ作成用やリグレッションテスト用のスクリプトをある程度完成させている状態でした。
それらのスクリプトを参考に、指定された申込条件のスクリプトを作成するお手伝いをさせていただきました。</p>
<p>参照するスクリプトがあるものの、
プルダウンの選択値やテキストボックスの入力値、押下するチェックボックスやボタンなど、コンポーネントの操作が1つ異なるとそこで要素取得エラー等が出て、
なかなか思うように作業を進めることができないことがありました…。
そこで僕がお世話になったPlaywrightの機能が『コード生成機能』です。
この機能は、ブラウザ操作を録画し、その操作に対応する自動化用コードを出力してくれるものになります。
この機能を使い、一度申込条件に沿ったブラウザ操作を行なってみることで、
初心者でもすごく簡単にコード生成および要素の取得ができ、エラーを解決することができました。</p>
<p>(例：”取扱車種一覧を見る”をクリックする操作を録画)
<img src="/assets/blog/authors/yoshitomi/advent_calender/higashizi_1.png" alt="PlayWrightクリック操作録画"></p>
<p>他にもまだ知らないPlaywrightの便利な機能があると思いますので、
先輩社員に尋ねたり自身で調べながら少しずつ使用できるようになり、徐々にPlaywrightに慣れていきたいです。</p>
<hr>
<h2>Appium周りの対応と開発ルール周り整備を行った話</h2>
<ul>
<li><p>自己紹介
mです。KTC入社と同時にQAへジョブチェンジしました！前職まではAndroidアプリの開発をしていました。</p>
</li>
<li><p>内容
最近、Appiumを用いたE2Eテストの自動化に挑戦する機会がありましたのでその内容についてです。
主に2つのポイント、単体テストとE2Eテストの違いと所感、そして開発ルール周りの整備について触れます。</p>
</li>
</ul>
<h4>1. AppiumによるE2Eテストの導入</h4>
<p>E2Eテストとはユーザーが実際に行う操作を端末上で再現し、アプリ全体の動作を検証するものです。
Appiumを使ってAndroidアプリとiOSアプリのE2Eテストの作成に取り組みました。</p>
<p>その中で特に課題となったのが実行にかかる時間です。
E2Eテストでは待機時間の発生が避けられません。具体的には、以下のような待機時間が発生します。</p>
<ul>
<li>UI操作の待機</li>
<li>画面描画の待機</li>
<li>通信処理の待機</li>
</ul>
<p>待機時間の増加に伴い、テスト実行時間とコストも増大します。
そのため、どのシナリオからテストを優先するかの判断や、外的要因によってテストが正常に実行できない場合に備えたリトライ処理の設計が重要になります。</p>
<p>現在は相互レビュー時に処理内容について相談することで、実行速度の高速化を進めています。
その結果、一部処理の負荷軽減を実現できました。引き続き検討と対応を進めていきたいと思います。</p>
<h4>2. 開発ルール周りの整備について</h4>
<p>プロジェクトのメンバーが増えてきたため、ブランチ戦略やブランチ保護の設定見直し、PRのルール策定など開発にまつわるルールの整備を行いました。</p>
<h5>ブランチ戦略の変更</h5>
<p>以前はGitHub Flowを採用していましたが、現在はGit Flowに変更しました。
この変更の主な理由は、利用するアプリのバージョンごとにリリースを管理したかったためです。
Git Flowを採用することで、機能開発やリリースの管理が明確になり、複数のバージョンを同時に扱う際の混乱を軽減できます。</p>
<h5>ブランチ保護の設定見直し</h5>
<p>実運用環境で動作しているmainブランチで、
自身含めて新規でアサインがあった開発者の各個人の環境によって動作しない事象が発生していました。そのため、mainおよびreleaseブランチへのPR作成時に
GitHub Actionsのワークフローを利用して
自動的に動作確認（全テスト実行）が通るかどうか確認できた場合のみマージ可能とする設定に変更しました。</p>
<h5>PRのルール策定</h5>
<p>開発者がそれぞれ以前のPRで何を対応していたかを明確にするため、
以下の項目を含むPRテンプレートを用意しました。</p>
<ul>
<li>対応内容概要</li>
<li>詳細</li>
<li>動作確認した内容</li>
<li>レビュー時に確認して欲しいこと</li>
</ul>
<p>このテンプレートにより、PRの内容が整理されレビューが効率的になることを目指しています。
これにより、さらに安定・安全に開発を進めるための基盤を整えることができました。チーム全体で協力し、より良い開発環境を作り上げていきたいと思っています。</p>
<p>今回ご紹介したようなAppiumを用いたE2Eテスト自動化の取り組みや、
QA観点での開発ルール整備については社内イベントでも継続的に発信しています。</p>
<p>大阪支社(Osaka Tech Lab)にてCO-LAB Tech Nightというイベントを毎月実施しておりますのでぜひご参加ください、、、！
QA回で登壇させていただいたときの様子です。
<img src="/assets/blog/authors/yoshitomi/advent_calender/kobayashi_1.webp" alt="小林イベント登壇画像"></p>
<hr>
<h2>最後に</h2>
<p>最後まで読んでいただきありがとうございます。
今回の内容が皆様のQA活動のきっかけの一つになれば幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yoshitomi/advent_calender/qa_advent_calendar_cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Behind the Scenes of Engineer Recruiting and PR at KINTO Technologies—A Day in the Life of a Recruiter]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-13-kinto-tech-engineer-recruiter-daily-routine-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-13-kinto-tech-engineer-recruiter-daily-routine-en/</guid>
            <pubDate>Sat, 13 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A recruiter at KINTO Technologies shares the real daily life of engineer recruiting and recruitment PR, including her story of starting from scratch and conversations with candidates, along with a message to future teammates.]]></description>
            <content:encoded><![CDATA[<p>This article is the Day 13 entry for the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> 🎅🎄</p>
<h2>Introduction</h2>
<p>Nice to meet you if this is our first encounter, and hello again if you&#39;ve been following along!</p>
<p>I&#39;m Hikaru Takeno (<a href="https://x.com/t_hikarutaaaan">@t_hikarutaaaan</a>), and I handle engineer recruiting and recruitment PR at KINTO Technologies (KTC).</p>
<p>The annual Advent Calendar tradition—I signed up for it again this year as naturally as breathing! (lol)</p>
<p>This time, I&#39;ll give you a casual look at the behind the scenes of engineer recruiting and recruitment PR through a day in my life.</p>
<h2>Brief Self-Introduction</h2>
<p>It&#39;s been about 4 years since I joined KTC and about 2 years since I became a recruiter in the company.</p>
<p>Back then, I started with absolutely zero experience in HR or recruiting.</p>
<p>I could barely understand half of the technical discussions and would get frustrated with just sending a single scout email. In addition, at interviews, I was so nervous, so all I could do was just read my script in a monotone voice. (Now, it’s a funny story. lol)</p>
<p>As I have been committed to recruitment without stopping, I can say this now:</p>
<p>&quot;I truly love this job.&quot;</p>
<p>KTC is a company that genuinely entrusts responsibilities to members who raise their hand and say they want to try.</p>
<p>Thanks to that corporate culture, I was able to move forward even with no recruitment experience.</p>
<p><img src="/assets/blog/authors/takeno/02/IMG_4064.jpg" alt="やっと笑ってイベント司会ができるようになりました">
<em>Now, I can host events with a smile.</em></p>
<h2>Engineers Are Basically Wizards</h2>
<p>A little while after becoming a recruiter, I built a small app that could analyze recruiting data using generative AI for writing the app code for me. (It may not as well-developed as apps at a professional level, though. lol)</p>
<p>With a single click on the app, a graph appeared on screen, which I couldn&#39;t help but speak out loud,</p>
<p>&quot;Wow, that&#39;s amazing! It’s like magic.&quot;</p>
<p>I was simply impressed by how technology can solve someone&#39;s problem in an instant.</p>
<p>And that&#39;s when I thought:</p>
<p>&quot;I need to engage in this job more seriously.&quot;</p>
<p>I wanted to understand what kind of passion onsite members have when they build products, thereby introducing them to the public even in a more appropriate manner.</p>
<p>Since then, I&#39;ve been busy working on my job every day. (lol)</p>
<p>From here, let me casually show you a day in my life!</p>
<table>
<thead>
<tr>
<th>Time</th>
<th>Activity</th>
<th>What I Actually Did</th>
<th>How I Feel About It (Example)</th>
</tr>
</thead>
<tbody><tr>
<td>09:00</td>
<td>Scouting time</td>
<td>Selecting target candidates, reading profiles, drafting &amp; sending scout messages.</td>
<td>With a consideration of which platform should I use, I found a great candidate, always writing &quot;why I want to reach out that person.&quot;</td>
</tr>
<tr>
<td>10:00</td>
<td>Resume screening</td>
<td>Reviewing work history, sharing evaluations with onsite team members.</td>
<td>Hmm... this person is really impressive...!</td>
</tr>
<tr>
<td>11:00</td>
<td>Onsite team meeting</td>
<td>Sharing recruiting status, checking if the potential candidates meet our requirements, catching up project updates.</td>
<td>I ask honestly when I don&#39;t understand. I’m grateful for the culture where team members kindly answer my questions all the time. 🙏</td>
</tr>
<tr>
<td>12:00</td>
<td>Lunch time</td>
<td>Gobble a tamago kake gohan (rice with a raw egg).</td>
<td>Bonito flake is a great combination with the egg. I’m so full.</td>
</tr>
<tr>
<td>13:00</td>
<td>Casual interview</td>
<td>Learning about candidates’ career, introducing our products</td>
<td>The best moment is when we can have a real two-way conversation. I often don’t enough time to understand candidates just in the single interview.</td>
</tr>
<tr>
<td>15:30</td>
<td>Recruitment PR planning</td>
<td>Researching recruiting trends and competitors’ strategies, drafting plans, organizing materials</td>
<td>I’m stuck..., needing someone to bounce ideas off.</td>
</tr>
<tr>
<td>17:00</td>
<td>Casual interview</td>
<td>Learning about candidates’ career, introducing our products</td>
<td>I couldn&#39;t answer some technical questions...so got to ask them to team members later.</td>
</tr>
<tr>
<td>19:00</td>
<td>Event operations</td>
<td>Reception, photo shooting, making announcements, assisting attendees</td>
<td>I basically running around the event venue (lol), taking photos, talking to people... OK, coming right up!</td>
</tr>
</tbody></table>
<p><img src="/assets/blog/authors/takeno/02/IMG_0577.jpg" alt="コスパ＆タイパ最高の卵かけごはん">
<em>Tamago kake Gohan—the most cost-effective and time-efficient meal to me</em></p>
<h2>Facing the Difficulties in Talent Recruitment Every Day</h2>
<p>Recruiting is a field where we can’t judge individuals on their knowledge, skills, and other characteristics.</p>
<ul>
<li>The atmosphere of a conversation with them</li>
<li>Subtle changes in their facial expression</li>
<li>The values behind their choices</li>
</ul>
<p>You need to carefully perceive these hard-to-see signs from candidates.</p>
<p>I can&#39;t forget what one said:</p>
<p>&quot;To me, the important factor when choosing a new job is &#39;who I work with.&#39;&quot;
This made me realize something—</p>
<p>I had been so focused on conveying attractive points about working at our company.</p>
<p>But what candidates really want to know is:
&quot;Can I entrust my career to this team?&quot;</p>
<p>Since then, I&#39;ve strive to talk openly and honestly to candidates about our teams’ atmosphere, corporate culture, and even what we struggle with and concerns arising from our working environment.</p>
<p>I still remember when a candidate said at the end of an interview:
&quot;I can clearly picture myself working together with you.&quot;
After hearing that, I made a little fist pump in my mind. (lol)</p>
<h2>Efforts I Made for Recruitment PR</h2>
<p>This year, as part of recruitment PR initiatives, I led a speaking session at <a href="https://www.japan-mobility-show.com/">Japan Mobility Show 2025</a>, one of the largest mobility events in Japan.</p>
<p>It was a project that involved stakeholders within the TOYOTA Group where KTC belongs, as well as people engaging in both real-world and IT domains. To be honest, it was the most challenging project I&#39;ve ever experienced with such large number of tasks for the event coordination and high-level difficulty in handling them. (lol)</p>
<p>Despite that, I really wanted to make the project a success—that is because</p>
<blockquote>
<p>I wanted to properly convey to the public our strength of taking on new things collaborating with real and IT areas.
I also wanted to share the passion of our senior managers who readily agreed to work the project together.</p>
</blockquote>
<p>I wanted to deliver through live words the atmosphere in KTC and our passion that can&#39;t be conveyed through job postings alone.</p>
<p>After the event, team members who had been cheering me on said,
&quot;That was really great!&quot; and it made me so happy.</p>
<p>When the video of the speaking session was released, employees from group companies who I work with on various tasks also gave us the following comments:</p>
<blockquote>
<p>&quot;I watched your YouTube video!&quot; &quot;Great content!&quot; &quot;That was cool!&quot;</p>
</blockquote>
<p>And the senior manager, who spoke at the event session, gave me these words:</p>
<blockquote>
<p>&quot;The recruiting impact might come a bit later, but it&#39;ll definitely work for internal PR and KTC&#39;s self-branding.&quot;</p>
</blockquote>
<p>I was so incredibly happy but couldn’t find words to clearly express my feeling. (lol)</p>
<p>Recruitment PR is a job to facilitate sharing information about the company&#39;s initiatives with future teammates.
I want to be the bridge between them and our company.</p>
<p><img src="/assets/blog/authors/takeno/02/IMG_0294.jpg" alt="会場の写真を一生懸命撮ってます">
<em>Myself working hard to take photos at the event venue</em></p>
<p>By the way, here&#39;s the archived video of the speaking session I mentioned above!
I hope you enjoy this 50-minute video packed full of passion♪</p>
<p><a href="https://youtu.be/NXM2lyapia0?si=QiK5GP2XFtCh0c9c">https://youtu.be/NXM2lyapia0?si=QiK5GP2XFtCh0c9c</a></p>
<h2>Recruiting Is Achieved through Teamwork to Build Future</h2>
<p>There&#39;s no single right answer in recruiting talents.</p>
<p>Every time, I get lost, worry, or waver.
(Honestly, I feel restless for so many days because I can’t often find any solutions for my concerns. lol)</p>
<p>But there&#39;s one thing I&#39;m sure about:</p>
<p>Good recruiting can only come from good dialogue with candidates.</p>
<p>At KTC, there are 5 recruiters including myself. Each of us is assigned as a dedicated recruiter for specific divisions.</p>
<p>That&#39;s why we are able to communicate super closely with the division members on the team,</p>
<p>sharing information about products and cultures in each division, as well as
happy things and tough things in their work—we think through everything together.</p>
<p>That&#39;s how I feel we&#39;re building recruiting as a team.</p>
<p>I think this is truly something to be proud of.</p>
<h2>Finally, to All Reading This Article</h2>
<p><strong>To all engineers:</strong>
&nbsp;&nbsp;I respect you. I mean that seriously.
&nbsp;&nbsp;The way you change the future with technology has always looked like magic to me 🪄
<strong>To everyone involved in product development:</strong>
&nbsp;&nbsp;I understand your dedication to seriously engaging in product development through constant communication and trials and errors to create a single value.
&nbsp;&nbsp;Those real stories always struck me so deeply.
<strong>To my teammates in the Human Resources Group:</strong>
&nbsp;&nbsp;Thank you for always worrying alongside me while I&#39;m running around all over the place!!
&nbsp;&nbsp;Let&#39;s keep running together!! (/・ω・)/
<strong>To everyone at KTC:</strong>
&nbsp;&nbsp;Thank you for always helping me!!
&nbsp;&nbsp;I&#39;ll contribute to making my beloved company even stronger with all my efforts as an employee working at the recruitment frontline!!!
<strong>To everyone who read this article:</strong>
&nbsp;&nbsp;If anything resonated with you even a little,
&nbsp;&nbsp;I&#39;d love to meet you somewhere—at an event, a casual interview, or anywhere else!!</p>
<h2>To Future Teammates</h2>
<p>We at KINTO Technologies are taking initiatives in building an organization focusing on internal product development from scratch, right in the middle of the transforming automotive industry.</p>
<p>Opportunities to be part of such dynamism don&#39;t come so often.</p>
<p>We&#39;re waiting for teammates who will create the future together🙌</p>
<p>That&#39;s all for a day in the life of a recruiter and PR person!
Thank you so much for reading!</p>
<p><img src="/assets/blog/authors/takeno/02/IMG_0096.jpg" alt=""></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/takeno/02/KINTOテクノロジーズの エンジニア採用と広報の舞台裏.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[KINTOテクノロジーズのエンジニア採用と広報の舞台裏～担当者のとある1日～]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-13-kinto-tech-engineer-recruiter-daily-routine/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-13-kinto-tech-engineer-recruiter-daily-routine/</guid>
            <pubDate>Sat, 13 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズの採用担当が語る、エンジニア採用と採用広報のリアルな日常。未経験からの挑戦、候補者との対話、そして未来の仲間へのメッセージ。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の13日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>はじめまして！の方も、いつもありがとうございますの方もこんにちは！！</p>
<p>KINTOテクノロジーズ（以下、KTC）でエンジニア採用と採用広報を担当している
たけの ひかる（<a href="https://x.com/t_hikarutaaaan">@t_hikarutaaaan</a>）です。</p>
<p>毎年恒例のアドベントカレンダー企画、
今年も呼吸をするようにエントリーしてみました！（笑）</p>
<p>今回は、
「エンジニア採用と採用広報の舞台裏」 をテーマに、
私の “とある1日” をゆるっと紹介してみようと思います。</p>
<h2>すこしだけ自己紹介</h2>
<p>入社して約4年、採用担当になって約2年。</p>
<p>当時は 人事も採用も完全にゼロ経験からのスタート。</p>
<p>技術の話は半分も理解できず、
スカウト1通送るだけであたふたして、
面談ではガチガチに緊張してスクリプトを棒読み。（今では完全にネタｗ）</p>
<p>でも、止まらずに挑戦し続けたからこそ今言えます。</p>
<p>「採用という仕事が、本当に好きです。」</p>
<p>そしてKTCは、
“やってみたい” と手を挙げた人に本気で任せてくれる会社。</p>
<p>未経験の私が走り続けられたのは、そのカルチャーのおかげです。</p>
<p><img src="/assets/blog/authors/takeno/02/IMG_4064.jpg" alt="やっと笑ってイベント司会ができるようになりました">
<em>やっと笑ってイベント司会ができるようになりました</em></p>
<h2>エンジニアって、魔法使いじゃん</h2>
<p>実は、採用担当になって少し経ったころ、生成AIにコードを書いてもらいながら
採用データを分析できる小さなアプリをつくったことがあります。（アプリって言っていいレベルではないですがｗ）</p>
<p>ワンクリックで画面にグラフが表示された瞬間、思わず声が出ました。</p>
<p>「え、すご…魔法じゃん。」</p>
<p>技術が人の困りごとを一瞬で解決してしまう瞬間にシンプルに感動しました。</p>
<p>で、そのとき思ったんですよね。</p>
<p>「あ、もっとちゃんと向き合わなきゃ」って。</p>
<p>現場の人たちがどんな想いでプロダクトつくってるのか、
「もっとちゃんと知りたいし、もっともーっとちゃんと伝えたい」と。</p>
<p>そこから更に毎日あたふたしながら走り回ってます。（笑）</p>
<p>というわけでここからは、
そんな私の 「とある1日」 をゆる〜く紹介してみます！</p>
<table>
<thead>
<tr>
<th>時間</th>
<th>内容</th>
<th>実際にやったこと</th>
<th>こんな気持ちでやってます（例）</th>
</tr>
</thead>
<tbody><tr>
<td>09:00</td>
<td>スカウトtime</td>
<td>ターゲット選定、プロフィール読み込み、スカウト文作成＆送付</td>
<td>どの媒体にしようかな。良い人発見。「なぜ声をかけたいのか」を必ず書く</td>
</tr>
<tr>
<td>10:00</td>
<td>書類選考</td>
<td>職務経歴確認、現場メンバーと評価すり合わせ</td>
<td>むむ…めちゃくちゃ良い人…！</td>
</tr>
<tr>
<td>11:00</td>
<td>現場MTG</td>
<td>採用状況共有、採用要件摺り合わせ、PJ状況ヒアリング</td>
<td>分からないことは素直に聞く。優しく答えてくれる文化に感謝 🙏</td>
</tr>
<tr>
<td>12:00</td>
<td>ランチtime</td>
<td>卵かけごはんをすする</td>
<td>かつお節が合う。おなかいっぱい。</td>
</tr>
<tr>
<td>13:00</td>
<td>カジュアル面談</td>
<td>キャリアヒアリング、プロダクト紹介</td>
<td>双方コミュニケーションできた時が最高。時間足りない日も多い。</td>
</tr>
<tr>
<td>15:30</td>
<td>採用広報企画</td>
<td>採用トレンド調査、競合リサーチ、企画案作成、資料構成</td>
<td>煮詰まった…。壁打ち相手求む。</td>
</tr>
<tr>
<td>17:00</td>
<td>カジュアル面談</td>
<td>キャリアヒアリング、プロダクト紹介</td>
<td>技術質問に少し答えられず…あとで現場に確認。</td>
</tr>
<tr>
<td>19:00</td>
<td>イベント運営</td>
<td>受付、撮影、アナウンス、来場者サポート</td>
<td>基本、走り回ってます（笑）。写真撮って声かけて…ハイハイ！</td>
</tr>
</tbody></table>
<p><img src="/assets/blog/authors/takeno/02/IMG_0577.jpg" alt="コスパ＆タイパ最高の卵かけごはん">
<em>コスパ＆タイパ最高の卵かけごはん</em></p>
<h2>採用の難しさと向き合う日々</h2>
<p>採用は、スペックだけでは測れない世界です。</p>
<ul>
<li>会話の空気</li>
<li>ちょっとした表情の変化</li>
<li>選択の背景にある価値観</li>
</ul>
<p>そういった“見えないもの”を丁寧に受け止める必要があります。</p>
<p>ある候補者の言葉が忘れられません。</p>
<p>「転職の決め手は、“誰と働くか”なんです」
その一言で、ハッとしました。</p>
<p>私はずっと会社としての魅力を伝えることに精一杯でした。</p>
<p>でも候補者の方が本当に知りたいのは、
「このチームなら、自分の挑戦を託せるか」</p>
<p>それからは、
現場の空気やカルチャー、そこで生じる悩みや挑戦も含めた本音を届けることを大切にしています。</p>
<p>面談の最後に
「一緒に働く姿が想像できました」
と言ってもらえた日は、心の中で小さくガッツポーズしています。（笑）</p>
<h2>採用広報で挑戦したこと</h2>
<p>今年、採用広報として
国内最大級のモビリティイベントである<a href="https://www.japan-mobility-show.com/">Japan Mobility Show 2025</a>の登壇企画を担当しました。</p>
<p>KTCが所属するTOYOTAグループ内の関係者や、
リアル領域とIT領域それぞれの担当者を巻き込みながら進めるプロジェクトで、
調整量も難易度も、正直これまでで一番大変でした。（笑）</p>
<p>でも、どうしても実現したかった理由があります。</p>
<blockquote>
<p>私たちの強みである「リアル × IT」での挑戦を、ちゃんと外に届けたかった。
そして、「一緒にやろう」と快諾してくれた部長の熱量を発信したかった。</p>
</blockquote>
<p>求人票だけでは伝わらない空気や想いを、
生の言葉で届けたかったんです。</p>
<p>本番のあと、応援してくれていた社内メンバーから
「めちゃくちゃよかったよ！」 と声をかけてもらえて、すごく嬉しかったです。</p>
<p>さらに後日、登壇企画の動画が公開されたあと、
日々いろいろな業務で関わるグループ企業の方々にも</p>
<blockquote>
<p>「YouTube見たよ！」「いい内容だった」「かっこよかった」</p>
</blockquote>
<p>と声をかけてもらえたと。</p>
<p>さらに、登壇してくれた部長はこんな言葉をくれました。</p>
<blockquote>
<p>「採用に効いてくるのはもう少し先かもしれないけど、
社内広報とか、KTCのセルフブランディングには効いてくるね」</p>
</blockquote>
<p>めちゃめちゃ嬉しかったぁ。なんだろ。うまく言葉にできないけど（笑）</p>
<p>採用広報は、会社の挑戦と未来の仲間をつなぐ仕事。
私はその“橋渡し役”でありたいと思っています。</p>
<p><img src="/assets/blog/authors/takeno/02/IMG_0294.jpg" alt="会場の写真を一生懸命撮ってます">
<em>会場の写真を一生懸命撮ってます</em></p>
<p>ちなみに以下がこの記事で触れた登壇企画のアーカイブ動画です！
あつーーく、ギュギュっと詰まった50分なので是非ご視聴ください♪</p>
<p><a href="https://youtu.be/NXM2lyapia0?si=QiK5GP2XFtCh0c9c">https://youtu.be/NXM2lyapia0?si=QiK5GP2XFtCh0c9c</a></p>
<h2>採用はチーム戦であり、未来づくり</h2>
<p>採用に、これが正解！っていう形はありません。</p>
<p>毎回迷うし、悩むし、揺れ続けます。
（正直、答えが分からなすぎてソワソワする日もある。笑）</p>
<p>でも、ひとつだけ確信していることがあります。</p>
<p>いい採用は、いい“対話”からしか生まれない。</p>
<p>KTCには、私を含めて5名の採用担当がいます。それぞれが部門に専任としてつく部門担当制。</p>
<p>だからこそ、現場とめちゃくちゃ密にコミュニケーションができるんです。</p>
<p>プロダクトの話も、カルチャーの話も、
嬉しいことも、しんどいことも、ぜんぶ一緒に考える。</p>
<p>そうやって、チームで採用をつくっている感覚がある。</p>
<p>これは本当に誇れるポイントだと思っています。</p>
<h2>最後に、読者のみなさまへ</h2>
<p><strong>エンジニアのみなさまへ</strong>
&nbsp;&nbsp;尊敬しています。本気で。
&nbsp;&nbsp;技術で未来を変えていく姿は、私にとってずっと魔法のように見えています🪄
<strong>プロダクト開発に関わるみなさまへ</strong>
&nbsp;&nbsp;本気でプロダクトと向き合う姿勢や、ひとつの価値をつくるためのコミュニケーションや試行錯誤。
&nbsp;&nbsp;そのリアルなストーリーに、私はいつも心を動かされています。
<strong>人事グループの仲間へ</strong>
&nbsp;&nbsp;いつもわちゃわちゃしている私と一緒に悩んでくれて感謝です！！
&nbsp;&nbsp;これからも一緒に走ってね！！(/・ω・)/
<strong>KTCのみなさまへ</strong>
&nbsp;&nbsp;いつも助けてくれてありがとうございます！！
&nbsp;&nbsp;私の大好きな会社がもっと強くなるために、まずは採用という場所から全力で貢献します！！！
<strong>この記事を読んでくださったみなさまへ</strong>
&nbsp;&nbsp;もし少しでも何か響くものがあったら、
&nbsp;&nbsp;ぜひイベントで、カジュアル面談で、、、、どこかでお会いできたら嬉しいです！！</p>
<h2>未来の仲間へ</h2>
<p>私たちKINTOテクノロジーズは変革期の自動車産業のど真ん中で、
内製開発組織をゼロからつくる挑戦をしています。</p>
<p>こんなダイナミズムに関われるチャンスは、そう多くありません。</p>
<p>未来を一緒に創る仲間をお待ちしています🙌</p>
<p>以上、採用担当・広報担当の“とある1日”でした！
読んでくださり、本当にありがとうございました～～～！</p>
<p><img src="/assets/blog/authors/takeno/02/IMG_0096.jpg" alt=""></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/takeno/02/KINTOテクノロジーズの エンジニア採用と広報の舞台裏.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Railway Oriented Programming Practice Using Rust]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-13-rust-railway-oriented-programming-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-13-rust-railway-oriented-programming-en/</guid>
            <pubDate>Sat, 13 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I practiced Railway Oriented Programming in a project developed with Rust. I will share my experiences, as well as benefits and issues that I found.]]></description>
            <content:encoded><![CDATA[<p>This article is the Day 13 entry for the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> 🎄</p>
<h2>Introduction</h2>
<p>I&#39;m yuki.n (<a href="https://x.com/yukidotnbysh">@yukidotnbysh</a>) from the Master Maintenance Tool Development Team in the KINTO Backend Development Group, KINTO Development Division, based in the Osaka Tech Lab.</p>
<p>Our team develops management systems that integrate with various services. These systems serve not only as administrative tools but also as solutions to business issues.</p>
<p>To address these challenges, our management systems cannot be simple CRUD applications. Each system and its business requirements bring various complexities. I believed Railway Oriented Programming would be effective for handling these complexities in our business logic, so I introduced it in a project. Based on this case study, I would like to share the benefits we gained and the issues we faced.</p>
<h2>What is Railway Oriented Programming?</h2>
<ul>
<li><a href="https://fsharpforfunandprofit.com/rop/">Railway Oriented Programming</a></li>
</ul>
<p>Railway Oriented Programming is an error handling approach in functional programming proposed by Scott Wlaschin, who runs <a href="https://fsharpforfunandprofit.com/">F# for Fun and Profit</a> and authored <a href="https://asciidwango.jp/post/754242099814268928/%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%A2%E3%83%87%E3%83%AA%E3%83%B3%E3%82%B0">Domain Modeling Made Functional</a>. Looking at <a href="https://fsharpforfunandprofit.com/posts/recipe-part2/">the article with the same title</a> posted on <a href="https://fsharpforfunandprofit.com/">F# for Fun and Profit</a>, it seemingly have been published at least as early as 2013.</p>
<p>In Railway Oriented Programming, functions are compared to railways, with success and failure handling represented as two separate tracks.</p>
<p><img src="/assets/blog/authors/yuki.n/2025-12-13/railway1.png" alt="上段の緑の線路と下段の赤い線路に分岐する線路"></p>
<p>The following diagram shows multiple of these railways connected together.</p>
<p><img src="/assets/blog/authors/yuki.n/2025-12-13/railway3.png" alt="3つの二分岐の線路が繋がっている。各分岐点を覆うように半透明の四角い枠が3つ並んでいる。それぞれの四角の枠内には左から「Validate function」「Update function」「Send function」と書かれている。"></p>
<p>Each processing step functions as a switch: if successful, processing continues on the success track; if it fails, it switches to the failure track. Once on the failure track, subsequent processing never succeeds, and the error flows through to the end.</p>
<p>Specifically, this involves chaining functions that return Result types through a pipeline.</p>
<h2>Why Did We Adopt Railway Oriented Programming?</h2>
<p>Our team already had experience developing with Rust, and we adopted it as the backend server development language for this project as well.</p>
<p>We designed the project architecture, according to the Clean Architecture diagram—for convenience, simply hereinafter referred to as Clean Architecture.</p>
<p><img src="/assets/blog/authors/yuki.n/2025-12-13/clean-architecture.jpg" alt="The Clean Architectureの図。外側から中心に向かって青、緑、赤、黄色と4つの円になっている。青は「Devices」や「DB」など、緑は「Controller」や「Presenter」など、赤は「Use Cases」、黄色は「Entities」と書かれている"></p>
<p>When we introduced Clean Architecture in past projects, we found that the processing in the Use Cases layer (as depicted in the circle diagram above) often results in an unnecessarily complex and clunky structure design. Even when we carved some processing as domain services to combine them into the Entities layer, the readability of the Use Cases layer still suffered.</p>
<p>In this situation, I learned about Railway Oriented Programming, which led to its adoption.</p>
<h2>Railway Oriented Programming with Rust</h2>
<h3>Overall Structure</h3>
<p>The Use Cases layer we implemented has roughly the following structure.</p>
<pre><code class="language-rust">#[derive(Debug, thiserror::Error)]
pub enum CreateUserUseCaseError {
    // Error type definitions
}

pub trait UsesCreateUserUseCase {
    /// Workflow
    fn handle(
        &amp;self,
        input: CreateUserInputData,
    ) -&gt; impl Future&lt;
        Output = Result&lt;CreateUserOutputData, CreateUserUseCaseError&gt;,
    &gt; + Send;
}

pub trait CreateUserUseCase:
    // Dependencies
    ProvidesUserFactory
    + ProvidesUserRepository
{
}

impl&lt;T: CreateUserUseCase + Sync&gt; UsesCreateUserUseCase for T {
    async fn handle(
        &amp;self,
        input: CreateUserInputData,
    ) -&gt; Result&lt;CreateUserOutputData, CreateUserUseCaseError&gt; {
        // Chain of functions defined in the railway module
    }
}

mod railway {
    type RailwayResult&lt;T&gt; = Result&lt;T, super::CreateUserUseCaseError&gt;;

    pub(super) fn validate_input(/* ... */) -&gt; RailwayResult&lt;(Email, UserName)&gt; { /* ... */ }
    pub(super) async fn check_email_not_exists(/* ... */) -&gt; RailwayResult&lt;(Email, UserName)&gt; { /* ... */ }
    pub(super) fn build_user(/* ... */) -&gt; RailwayResult&lt;User&gt; { /* ... */ }
    pub(super) async fn save_user(/* ... */) -&gt; RailwayResult&lt;User&gt; { /* ... */ }
    pub(super) fn end(/* ... */) -&gt; CreateUserOutputData { /* ... */ }
}
</code></pre>
<p>We use the Cake Pattern introduced in <a href="https://techblog.paild.co.jp/entry/2023/06/12/170637">Thinking About DI in Rust — Part 2: Organizing DI Approaches Using Rust (only in Japanese)</a> (I will omit the details as this diverges from the main topic of this blog).</p>
<p>We define functions in the <code>railway</code> module and combine them within the <code>handle</code> method of <code>UsesCreateUserUseCase</code>.</p>
<p>(Note: In Domain Modeling Made Functional, the part corresponding to the <code>handle</code> method is called workflow, and the arguments are called commands. This article follows that convention.)</p>
<p>Let&#39;s break down each of these elements.</p>
<h3>Error Type Definition</h3>
<pre><code class="language-rust">#[derive(Debug, thiserror::Error)]
pub enum CreateUserUseCaseError {
    // Error type definitions
    #[error(&quot;Email address already exists.&quot;)]
    AlreadyExistsEmail,
    #[error(&quot;Invalid email address.&quot;)]
    InvalidEmail,
    #[error(&quot;Invalid username.&quot;)]
    InvalidUserName,
    #[error(&quot;UserFactoryError&quot;)]
    UserFactoryError(#[from] UserFactoryError),
    #[error(&quot;UserRepositoryError&quot;)]
    UserRepositoryError(#[from] UserRepositoryError),
}
</code></pre>
<p>We always define one error type for each Use Case.</p>
<p>In Rust, error types can be defined as Enums. While the standard approach requires implementing the <code>std::error::Error</code> trait, using the <a href="https://github.com/dtolnay/thiserror">thiserror</a> crate simplifies error type definitions.</p>
<p>Additionally, when defining with the thiserror crate, setting the <code>#[from]</code> attribute implements the <code>From</code> trait, allowing automatic conversion to the target error type without explicit conversion when the corresponding error occurs.</p>
<h3><code>RailwayResult</code> Type</h3>
<pre><code class="language-rust">mod railway {
    // Result type specific to this use case
    type RailwayResult&lt;T&gt; = Result&lt;T, CreateUserUseCaseError&gt;;
}
</code></pre>
<p>Since defining Use Case-specific errors for all functions would be cumbersome, we define a <code>RailwayResult</code> type alias so we only need to specify the return value.</p>
<h3><code>railway</code> Module</h3>
<pre><code class="language-rust">mod railway {
    /// Validates input values and converts them to value objects.
    pub(super) fn validate_input(
        input: CreateUserInputData,
    ) -&gt; RailwayResult&lt;(Email, UserName)&gt; {
        let email = Email::try_from(input.email)
            .map_err(|_| CreateUserUseCaseError::InvalidEmail)?;
        let name = UserName::try_from(input.name)
            .map_err(|_| CreateUserUseCaseError::InvalidUserName)?;
        Ok((email, name))
    }

    /// Confirms that the email address does not already exist.
    pub(super) async fn check_email_not_exists(
        (email, name): (Email, UserName),
        impl_repository: &amp;impl UsesUserRepository,
    ) -&gt; RailwayResult&lt;(Email, UserName)&gt; {
        impl_repository
            .find_by_email(&amp;email)
            .await
            .map_err(CreateUserUseCaseError::UserRepositoryError)?
            .map_or(Ok((email, name)), |_| Err(CreateUserUseCaseError::AlreadyExistsEmail))
    }

    /// Creates a new user.
    pub(super) fn build_user(
        (email, name): (Email, UserName),
        impl_factory: &amp;impl UsesUserFactory,
    ) -&gt; RailwayResult&lt;User&gt; {
        impl_factory
            .build(UserFactoryParams { email, name })
            .map_err(CreateUserUseCaseError::UserFactoryError)
    }

    /// Saves the user.
    pub(super) async fn save_user(
        output: User,
        impl_repository: &amp;impl UsesUserRepository,
    ) -&gt; RailwayResult&lt;User&gt; {
        impl_repository
            .save(output)
            .await
            .map_err(CreateUserUseCaseError::UserRepositoryError)
    }

    /// Returns the result and terminates processing.
    pub(super) fn end(
        output: User,
    ) -&gt; CreateUserOutputData {
        CreateUserOutputData {
            user: output.into(),
        }
    }
}
</code></pre>
<p>We create a module called <code>railway</code> and define the functions that form the tracks within it.
This is not a Railway Oriented Programming convention but simply a guide we use for easier identification.</p>
<p>In the code for this blog, we assume the following process flows:</p>
<ol>
<li>Validate input values (email address and username)</li>
<li>Check email address existence</li>
<li>Create <code>User</code> entity</li>
<li>Save <code>User</code> entity</li>
<li>Convert the saved <code>User</code> entity to a DTO for passing to the upper layer, and end the process</li>
</ol>
<p>Since the return value of the previous function is set as the input value for the next function, the first argument is named <code>output</code>.
However, when <code>output</code> is a tuple, we destructure it from the start. This is because handling tuples as-is causes issues with variable ownership.</p>
<p>Ideally, only the previous value should be set as the input for the next function, but we determined that the drawbacks outweighed the benefits—such as needing to pass unnecessary values as if through a bucket brigade. That is the reason we adopted a rule allowing new input values to be passed to each function.</p>
<h3>Overall Workflow</h3>
<p>The original programming convention uses F#, but Railway Oriented Programming is applicable in any language (or with libraries that supplement it) that has the concept of Result or Either types and the ability to compose functions.</p>
<p>Fortunately, Rust comes standard with the following features essential for implementing Railway Oriented Programming, in addition to the Result type:</p>
<ul>
<li><code>?</code> operator: Expresses stopping processing when an error occurs</li>
<li><code>map</code> and <code>and_then</code> functions: Function composition</li>
</ul>
<p>By combining these, you can build a pipeline as follows:</p>
<pre><code class="language-rust">impl&lt;T: CreateUserUseCase + Sync&gt; UsesCreateUserUseCase for T {
    async fn handle(
        &amp;self,
        input: CreateUserInputData,
    ) -&gt; Result&lt;CreateUserOutputData, CreateUserUseCaseError&gt; {
        railway::validate_input(input)
            .map(|output| {
                railway::check_email_not_exists(output, self.user_repository())
            })?
            .await
            .and_then(|output| railway::build_user(output, self.user_factory()))
            .map(|output| railway::save_user(output, self.user_repository()))?
            .await
            .map(railway::end)
    }
}
</code></pre>
<h2>Benefits I Experienced in Practice</h2>
<p>The following are the benefits I experienced from practicing Railway Oriented Programming.</p>
<h3>Processing Flow Became Clear, Making Feature Addition Easier</h3>
<p>Since the workflow contents are connected through a pipeline, you can understand at a glance what processing is being performed.</p>
<p>Of course, complex specifications inevitably lead to longer workflows, but even so, tracking the function flow helps us to roughly identify where and what is happening.</p>
<p>With each process in the workflow extracted into functions, the scope of each process and its variables also became clear.</p>
<p>This helped us add features simply by inserting new functions and modify them by changing the relevant function, which enhanced the system maintainability.</p>
<h3>Processing Input/Output Can Now Be Expressed Through Types</h3>
<p>I don&#39;t think this is a direct effect of Railway Oriented Programming, but we can now check by type level what data each function&#39;s arguments and return values represent.</p>
<p>As compile errors can prevent type mistakes, we are able to avoid problems where incorrect values are passed during processing.</p>
<h3>Simplified Unit Test Scenarios</h3>
<p>Since implementing all success and failure tests for workflows was extremely labor-intensive and time-consuming, we adopted an approach of thoroughly testing individual railway functions, then only testing the happy path for the workflows.</p>
<p>There may be controversies about whether tests for private functions are necessary, but I personally felt it was helpful to test each function in the railway module. So far, I haven&#39;t experienced any major problems with this approach.</p>
<p>Additionally, as a secondary effect, when features are added, we can confirm there are no issues by adding tests for those functions and ensuring existing tests pass, which I think was beneficial.</p>
<h2>Issues I Experienced in Practice</h2>
<p>While gaining benefits from practice, we also faced some issues.</p>
<h3>Railway Oriented Programming Takes Some Getting Used To</h3>
<p>For those already familiar with functional languages, this approach probably doesn&#39;t feel unusual, but of course there are team members, including myself, who are not. The approach implementation is difficult until you get used to the style of writing. In fact, I struggled quite a bit when examining whether Railway Oriented Programming could be implemented using Rust.</p>
<p>Currently, AI has significantly lowered the technical barriers for the implementation, but we humans still need to understand Railway Oriented Programming at some level to determine whether the outcomes are appropriately generated. That’s where supporting team members come in.</p>
<p>In practice, the process of reviewing outcomes placed a significant burden on us in the early development phase of this project. Depending on the project&#39;s situation and conditions—such as development scale and deadlines—we suggest that you seek another solution instead of Railway Oriented Programming.</p>
<h3>We May Face <code>fatal runtime error: stack overflow</code></h3>
<p>Depending on what kind of process is executed, Stack Overflow errors may occur at runtime.</p>
<p>It is particularly troublesome because the error is not detected as a compile error, and you cannot determine which specific location contains issues just by searching around the source code.</p>
<p>To find the error cause, you can use rust-lldb to check the stack trace and identify the code where the Stack Overflow error occurred.</p>
<pre><code class="language-shell"># Start binary with LLDB
rust-lldb target/debug/your-bin

# Execute
run

# Check backtrace when Stack Overflow occurs
thread backtrace all
</code></pre>
<p>When we address issues that are difficult to solve using a method chaining with <code>map</code> and <code>and_then</code>, we take an alternative solution to stop the method and write the processing line by line instead. This is the safest and easiest way to overcome tough issues.</p>
<pre><code class="language-rust">async fn handle(&amp;self, input: InputData) -&gt; Result&lt;OutputData, UseCaseError&gt; {
    railway::begin(self.uow()).await?;
    let output = railway::validate_email(&amp;input.email)?;
    let output = railway::authenticate(output, input.password, self.authenticator()).await?;
    let output = railway::update_last_access(output, self.user_repository()).await?;
    let output = railway::commit(output, self.uow()).await?;
    railway::end(output)
}
</code></pre>
<p>The above solution isn&#39;t bad in itself, but it eliminates the pipeline through method chaining, creating the risk that we can write codes on a no-holds-barred basis. So, I think it&#39;s safest to only switch to this format when we hardly solve issues.</p>
<p>As another option, you can expand the stack area by adding a value to the <code>RUST_MIN_STACK</code> variables. However, this merely postpones the issue and may cause the recurrence of the Stack Overflow errors. Therefore, I don’t recommend this solution.</p>
<p>In my case, the error occurred in debug builds in the meantime when I executed asynchronous processing within functions defined in the railway module.
async/await is syntactic sugar for the <code>Future</code> type, but, according to <a href="https://github.com/rust-lang/rust/issues/132050">rust-lang/rust#132050</a>, the state held by the <code>Future</code> type is expanded to the stack area at runtime, which causes Stack Overflow errors at the execution of many <code>async</code> functions.
In this case, I was able to avoid the error by storing the <code>Future</code> type data I wanted to simultaneously process into the <code>Vec</code> type data (because <a href="https://doc.rust-lang.org/std/vec/struct.Vec.html#guarantees"><code>Vec</code> type values are stored in the heap</a>).</p>
<h3>Increase in Codes in Return for Clarified Processing Flow</h3>
<p>When introducing Railway Oriented Programming in Rust, you end up connecting each function with <code>map</code> and <code>and_then</code>. Additionally, you need to define each of those functions, so the overall code volume increases compared to the one when you usually write codes.</p>
<p>For example, if Railway Oriented Programming were not applied, the <code>handle</code> method would look like as follows:</p>
<pre><code class="language-rust">impl&lt;T: CreateUserUseCase + Sync&gt; UsesCreateUserUseCase for T {
    async fn handle(
        &amp;self,
        input: CreateUserInputData,
    ) -&gt; Result&lt;CreateUserOutputData, CreateUserUseCaseError&gt; {
        // Input validation
        let email = Email::try_from(input.email)
            .map_err(|_| CreateUserUseCaseError::InvalidEmail)?;
        let name = UserName::try_from(input.name)
            .map_err(|_| CreateUserUseCaseError::InvalidUserName)?;

        // Email duplication check
        let existing_user = self
            .user_repository()
            .find_by_email(&amp;email)
            .await
            .map_err(CreateUserUseCaseError::UserRepositoryError)?;
        if existing_user.is_some() {
            return Err(CreateUserUseCaseError::AlreadyExistsEmail);
        }

        // User creation
        let user = self
            .user_factory()
            .build(UserFactoryParams { email, name })
            .map_err(CreateUserUseCaseError::UserFactoryError)?;

        // Saving user
        let saved_user = self
            .user_repository()
            .save(user)
            .await
            .map_err(CreateUserUseCaseError::UserRepositoryError)?;

        // Result conversion
        Ok(CreateUserOutputData {
            user: saved_user.into(),
        })
    }
}
</code></pre>
<p>Without functions, the implementation would be within the <code>handle</code> method (or with some functions partially extracted). Therefore, depending on the case, this approach might be simpler.</p>
<p>So for applications with simple processing and mostly branching, it may be safer to unnecessarily adopt Railway Oriented Programming.</p>
<h2>P.S.: Where’s the Repository Pattern?</h2>
<p>While not directly related to the main topic of this blog, Domain Modeling Made Functional addresses the repository pattern in a section named “Where’s the Repository Pattern?” The book states the pattern in the functional approach as follows:</p>
<blockquote>
<p>“...when we model everything as functions and push persistence to the edges, then the Repository pattern is no longer needed.”</p>
</blockquote>
<p>However, since I couldn&#39;t fully grasp the intent and method behind this, we adopted the repository pattern for the code and our project described in this blog.</p>
<h2>Conclusion</h2>
<p>This concludes my explanation about practicing Railway Oriented Programming in Rust.</p>
<p>Rust is an extremely expressive language with various features, and I feel that adopting Railway Oriented Programming can enhance it further.</p>
<p>If you consider adopting Railway Oriented Programming in Rust, I hope this article can serve you as a helpful reference.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yuki.n/2025-12-13/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Rust で Railway Oriented Programming を実践してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-13-rust-railway-oriented-programming/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-13-rust-railway-oriented-programming/</guid>
            <pubDate>Sat, 13 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Rust で開発したプロジェクトにおいて、Railway Oriented Programming を実践しました。その中で感じたことやメリットなどについてご紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の 13 日目の記事です🎄</p>
<h2>はじめに</h2>
<p>KINTO開発部 KINTOバックエンド開発G マスターメンテナンスツール開発チーム・Osaka Tech Lab 所属の yuki.n（<a href="https://x.com/yukidotnbysh">@yukidotnbysh</a>）です。</p>
<p>わたしたちのチームでは各サービスと連携する管理システムを開発しています。これらは管理システムであると同時に、業務課題を解決するためのものでもあります。</p>
<p>そのため管理システムと言えども単純な CRUD システムというわけにはいかず、開発するシステムや業務によって様々複雑な課題が発生します。これらをビジネスロジックに落とし込むにあたって Railway Oriented Programming が効果的ではないかと考え、実際のプロジェクトで導入しました。その事例をもとにどのようなメリット・見えてきた課題があったのかをご紹介したいと思います。</p>
<h2>Railway Oriented Programming について</h2>
<ul>
<li><a href="https://fsharpforfunandprofit.com/rop/">Railway Oriented Programming</a></li>
</ul>
<p>Railway Oriented Programming とは <a href="https://fsharpforfunandprofit.com/">F# for Fun and Profit</a> の運営や「<a href="https://asciidwango.jp/post/754242099814268928/%E9%96%A2%E6%95%B0%E5%9E%8B%E3%83%89%E3%83%A1%E3%82%A4%E3%83%B3%E3%83%A2%E3%83%87%E3%83%AA%E3%83%B3%E3%82%B0">Domain Modeling Made Functional（関数型ドメインモデリング）</a>」の作者である Scott Wlaschin 氏が提唱した、関数型プログラミングにおけるエラーハンドリングの手法です。日本語では「鉄道指向プログラミング」と呼ばれています。<a href="https://fsharpforfunandprofit.com/">F# for Fun and Profit</a> に投稿された<a href="https://fsharpforfunandprofit.com/posts/recipe-part2/">同じタイトルの記事</a>を見ると、少なくとも 2013 年には公開されていたようです。</p>
<p>Railway Oriented Programming では、関数を「線路（Railway）」に例え、正常系の処理と異常系の処理を 2 本の線路として表現します。</p>
<p><img src="/assets/blog/authors/yuki.n/2025-12-13/railway1.png" alt="上段の緑の線路と下段の赤い線路に分岐する線路"></p>
<p>この「線路」を複数つなぎ合わせたのが以下のイメージ図です。</p>
<p><img src="/assets/blog/authors/yuki.n/2025-12-13/railway3.png" alt="3つの二分岐の線路が繋がっている。各分岐点を覆うように半透明の四角い枠が3つ並んでいる。それぞれの四角の枠内には左から「Validate function」「Update function」「Send function」と書かれている。"></p>
<p>各処理ステップは「スイッチ」として機能し、成功すれば正常系の処理（線路）を進み、失敗すれば異常系の処理（線路）に切り替わります。一度異常系に入ると、以降の処理では成功することはなく、エラーとして最後まで流れていきます。</p>
<p>具体的には Result 型を返す関数をパイプラインで次々とつなぎ合わせていくイメージです。</p>
<h2>Railway Oriented Programming を取り入れた理由</h2>
<p>もともとわたしたちのチームでは Rust の開発実績があり、このプロジェクトでもバックエンドサーバーの開発言語として Rust を採用しています。</p>
<p>アーキテクチャとしては「The Clean Architecture」の図を参考にしています（以後、便宜的にあえて「Clean Architecture」と書きます）。</p>
<p><img src="/assets/blog/authors/yuki.n/2025-12-13/clean-architecture.jpg" alt="The Clean Architectureの図。外側から中心に向かって青、緑、赤、黄色と4つの円になっている。青は「Devices」や「DB」など、緑は「Controller」や「Presenter」など、赤は「Use Cases」、黄色は「Entities」と書かれている"></p>
<p>過去のプロジェクトで Clean Architecture を導入した時、上の図で描かれている「Use Cases」層の処理が複雑かつ肥大化していく傾向にあり、一部処理をドメインサービスとして「Entities」層へ切り出したとしても、やはり Use Cases 層の可読性が落ちてしまうという課題がありました。</p>
<p>そんな中で Railway Oriented Programming の存在を知り、導入することになりました。</p>
<h2>Rust での Railway Oriented Programming</h2>
<h3>全体の構造</h3>
<p>わたしたちが実装した Use Cases 層は概ね以下のような構造になっています。</p>
<pre><code class="language-rust">#[derive(Debug, thiserror::Error)]
pub enum CreateUserUseCaseError {
    // エラー型の定義
}

pub trait UsesCreateUserUseCase {
    /// Workflow
    fn handle(
        &amp;self,
        input: CreateUserInputData,
    ) -&gt; impl Future&lt;
        Output = Result&lt;CreateUserOutputData, CreateUserUseCaseError&gt;,
    &gt; + Send;
}

pub trait CreateUserUseCase:
    // 依存関係
    ProvidesUserFactory
    + ProvidesUserRepository
{
}

impl&lt;T: CreateUserUseCase + Sync&gt; UsesCreateUserUseCase for T {
    async fn handle(
        &amp;self,
        input: CreateUserInputData,
    ) -&gt; Result&lt;CreateUserOutputData, CreateUserUseCaseError&gt; {
        // railway モジュールで定義した関数のチェーン
    }
}

mod railway {
    type RailwayResult&lt;T&gt; = Result&lt;T, super::CreateUserUseCaseError&gt;;

    pub(super) fn validate_input(/* ... */) -&gt; RailwayResult&lt;(Email, UserName)&gt; { /* ... */ }
    pub(super) async fn check_email_not_exists(/* ... */) -&gt; RailwayResult&lt;(Email, UserName)&gt; { /* ... */ }
    pub(super) fn build_user(/* ... */) -&gt; RailwayResult&lt;User&gt; { /* ... */ }
    pub(super) async fn save_user(/* ... */) -&gt; RailwayResult&lt;User&gt; { /* ... */ }
    pub(super) fn end(/* ... */) -&gt; CreateUserOutputData { /* ... */ }
}
</code></pre>
<p>「<a href="https://techblog.paild.co.jp/entry/2023/06/12/170637">Rust の DI を考える –– Part 2: Rust における DI の手法の整理</a>」で紹介されている Cake Pattern を用いています（本記事の本筋とは逸れるため詳細は割愛します）。</p>
<p><code>railway</code> モジュールに関数を定義し、それらを <code>UsesCreateUserUseCase</code> の <code>handle</code> メソッド内で結合する、という形です。</p>
<p>（なお、「関数型ドメインモデリング」では <code>handle</code> メソッドにあたる部分を「ワークフロー」、引数は「コマンド」と表現されています。本記事でもこれに倣います）</p>
<p>これらの要素をひとつずつ分解していきます。</p>
<h3>エラー型の定義</h3>
<pre><code class="language-rust">#[derive(Debug, thiserror::Error)]
pub enum CreateUserUseCaseError {
    // エラー型の定義
    #[error(&quot;メールアドレスが既に存在します。&quot;)]
    AlreadyExistsEmail,
    #[error(&quot;無効なメールアドレスです。&quot;)]
    InvalidEmail,
    #[error(&quot;無効なユーザー名です。&quot;)]
    InvalidUserName,
    #[error(&quot;UserFactoryError&quot;)]
    UserFactoryError(#[from] UserFactoryError),
    #[error(&quot;UserRepositoryError&quot;)]
    UserRepositoryError(#[from] UserRepositoryError),
}
</code></pre>
<p>Use Case ひとつに対し、エラー型を必ずひとつ定義する形で運用しています。</p>
<p>Rust ではエラー型を Enum で定義することができます。標準のままだと <code>std::error::Error</code> トレイトを実装する必要があるのですが、<a href="https://github.com/dtolnay/thiserror">thiserror</a> クレートを使うことでエラー型の定義を簡略化できます。</p>
<p>また thiserror クレートで定義した場合、<code>#[from]</code> アトリビュートを設定することで <code>From</code> トレイトが実装されるので、該当エラーが発生した時に明示的に変換をせずとも、自動的に目的のエラー型へ変換できるようになります。</p>
<h3><code>RailwayResult</code> 型</h3>
<pre><code class="language-rust">mod railway {
    // このユースケース専用のResult型
    type RailwayResult&lt;T&gt; = Result&lt;T, CreateUserUseCaseError&gt;;
}
</code></pre>
<p>Use Case 専用のエラーをすべての関数に定義するのは大変なので、<code>RailwayResult</code> 型という型エイリアスを定義して、戻り値だけ設定するようにしています。</p>
<h3><code>railway</code> モジュール</h3>
<pre><code class="language-rust">mod railway {
    /// 入力値を検証し、値オブジェクトに変換します。
    pub(super) fn validate_input(
        input: CreateUserInputData,
    ) -&gt; RailwayResult&lt;(Email, UserName)&gt; {
        let email = Email::try_from(input.email)
            .map_err(|_| CreateUserUseCaseError::InvalidEmail)?;
        let name = UserName::try_from(input.name)
            .map_err(|_| CreateUserUseCaseError::InvalidUserName)?;
        Ok((email, name))
    }

    /// メールアドレスが存在していないことを確認します。
    pub(super) async fn check_email_not_exists(
        (email, name): (Email, UserName),
        impl_repository: &amp;impl UsesUserRepository,
    ) -&gt; RailwayResult&lt;(Email, UserName)&gt; {
        impl_repository
            .find_by_email(&amp;email)
            .await
            .map_err(CreateUserUseCaseError::UserRepositoryError)?
            .map_or(Ok((email, name)), |_| Err(CreateUserUseCaseError::AlreadyExistsEmail))
    }

    /// ユーザーを新規作成します。
    pub(super) fn build_user(
        (email, name): (Email, UserName),
        impl_factory: &amp;impl UsesUserFactory,
    ) -&gt; RailwayResult&lt;User&gt; {
        impl_factory
            .build(UserFactoryParams { email, name })
            .map_err(CreateUserUseCaseError::UserFactoryError)
    }

    /// ユーザーを保存します。
    pub(super) async fn save_user(
        output: User,
        impl_repository: &amp;impl UsesUserRepository,
    ) -&gt; RailwayResult&lt;User&gt; {
        impl_repository
            .save(output)
            .await
            .map_err(CreateUserUseCaseError::UserRepositoryError)
    }

    /// 戻り値を返し、処理を終了します。
    pub(super) fn end(
        output: User,
    ) -&gt; CreateUserOutputData {
        CreateUserOutputData {
            user: output.into(),
        }
    }
}
</code></pre>
<p><code>railway</code> というモジュールを作り、その中に「線路」となる関数群を定義していきます。
これは Railway Oriented Programming の流儀ではなく、単純にわたしたちが確認しやすいように目印として設けています。</p>
<p>この記事のコードの場合では以下の流れを想定しています。</p>
<ol>
<li>入力値（メールアドレス・ユーザー名）の検証</li>
<li>メールアドレスの存在チェック</li>
<li><code>User</code> エンティティの生成</li>
<li><code>User</code> エンティティの保存</li>
<li>保存した <code>User</code> エンティティを上位層に渡すための DTO に変換し、終了</li>
</ol>
<p>前回の関数の戻り値が次の関数の入力値になるため、第一引数は <code>output</code> と命名しています。
ただし <code>output</code> がタプルだった場合は始めから展開しています。これはタプルのままだと変数の所有権の問題で取り扱いが面倒なためです。</p>
<p>基本的には前回の値だけがそのまま次の関数の入力値になることが望ましいとは思うのですが、バケツリレーのように不要な値まで渡し続ける必要が発生するなどデメリットの方が多いと判断し、各関数で新たな入力値を渡しても良いというルールにしています。</p>
<h3>ワークフロー全体</h3>
<p>原典では F# が使われていますが、Result 型や Either 型の概念があり、かつ関数を合成する機能がある言語（またはそれを補完するライブラリなど）であれば Railway Oriented Programming の導入は可能です。</p>
<p>Rust では幸い、Result 型以外にも Railway Oriented Programming を実現するのに欠かせない以下の機能が標準で備わっています。</p>
<ul>
<li><code>?</code> 演算子：エラー発生時の処理停止を表現</li>
<li><code>map</code>・<code>and_then</code> 関数：関数の合成</li>
</ul>
<p>これらを組み合わせることで以下のようにパイプラインを構築することができます。</p>
<pre><code class="language-rust">impl&lt;T: CreateUserUseCase + Sync&gt; UsesCreateUserUseCase for T {
    async fn handle(
        &amp;self,
        input: CreateUserInputData,
    ) -&gt; Result&lt;CreateUserOutputData, CreateUserUseCaseError&gt; {
        railway::validate_input(input)
            .map(|output| {
                railway::check_email_not_exists(output, self.user_repository())
            })?
            .await
            .and_then(|output| railway::build_user(output, self.user_factory()))
            .map(|output| railway::save_user(output, self.user_repository()))?
            .await
            .map(railway::end)
    }
}
</code></pre>
<h2>実践して感じたメリット</h2>
<p>以下、Railway Oriented Programming を実践して感じたメリットです。</p>
<h3>処理の流れが明確になり、機能追加がしやすくなった</h3>
<p>ワークフローの中身がパイプラインで繋がっているので、どういう処理が行われているのかは一目である程度わかるようになりました。</p>
<p>もちろん複雑な仕様であればワークフローが長くなることは避けられませんが、それでも関数の流れを追えば、どこで何が行われているのかはだいたいの当たりをつけられるようになりました。</p>
<p>ワークフロー内の各処理も関数に切り出されていることで、それぞれの処理・変数のスコープも明確になりました。</p>
<p>このおかげで機能追加の場合は新たに関数を差し込むだけでよく、変更の場合は該当の関数のみ修正すれば良くなり、保守性も向上したように感じます。</p>
<h3>処理の入出力を型で表現できるようになった</h3>
<p>これは Railway Oriented Programming と直接結びつく効果ではないと思いますが、各関数の引数・戻り値が何のデータであるかを型レベルでチェックできるようになりました。</p>
<p>コンパイルエラーで型の誤りを防げるようになり、処理途中で誤った値が渡ってしまうといった問題を回避できるようになりました。</p>
<h3>単体テストが書きやすくなった</h3>
<p>ワークフローに対し正常系・異常系すべてのテストを実装するのは非常に大変だったため、各 railway 関数それぞれをしっかりテストして、ワークフローのテストではハッピーパスを通すというやり方を取っています。</p>
<p>Private な関数のテストコードが必要かどうかの是非はあると思いますが、それでも railway モジュールの各関数をテストできるのは個人的にメリットと感じました。いまのところは大きな問題も感じていません。</p>
<p>また、副次的効果として、機能追加があった場合でもその関数分のテストを追加し、既存のテストがパスすれば問題ないことが確認できるので、この点は良かったと思います。</p>
<h2>実践して感じた課題</h2>
<p>実践してメリットが得られた反面、やはりいくつか課題もありました。</p>
<h3>Railway Oriented Programming の慣れが必要</h3>
<p>普段から関数型言語に慣れている方であればそれほど違和感ない手法と思うのですが、もちろんわたし含めてそうでないメンバーもいます。そのため、この書き方に慣れるまではどうしても実装が難しくなりますし、実際、わたしも Rust で Railway Oriented Programming が実装できるか調べていた時はかなり苦戦しました。</p>
<p>現在は AI のおかげでかなり難易度は下がりましたが、できあがったものが適切な内容かどうかはやはりある程度の理解が必要です。そのため、メンバーに対してのフォローがどうしても必要になります。</p>
<p>実際のところ、このプロジェクトでも開発初期はどうしてもレビュー負荷が大きくなりました。そのため、開発規模や納期など、プロジェクトの状況・条件によっては Railway Oriented Programming の採用を見送ることも検討した方が良いかもしれません。</p>
<h3><code>fatal runtime error: stack overflow</code> が発生する場合がある</h3>
<p>実装内容によっては実行時に Stack Overflow エラーが発生する場合があります。</p>
<p>コンパイルエラーとして検出されないのが非常に厄介で、かつ具体的にどの箇所が問題なのか、ソースコードを見るだけでは判断がつきません。</p>
<p>原因の探し方ですが、rust-lldb を使ってスタックトレースを確認することで、Stack Overflow エラーが発生したコードを特定することができます。</p>
<pre><code class="language-shell"># LLDB でバイナリを起動する
rust-lldb target/debug/your-bin

# 実行
run

# Stack Overflow が発生したらバックトレースを確認
thread backtrace all
</code></pre>
<p>解決が難しい場合の別案として、もっとも安全かつかんたんな解決策は <code>map</code> や <code>and_then</code> によるメソッドチェーンをやめ、1 行ずつ処理を書いていく形です。</p>
<pre><code class="language-rust">async fn handle(&amp;self, input: InputData) -&gt; Result&lt;OutputData, UseCaseError&gt; {
    railway::begin(self.uow()).await?;
    let output = railway::validate_email(&amp;input.email)?;
    let output = railway::authenticate(output, input.password, self.authenticator()).await?;
    let output = railway::update_last_access(output, self.user_repository()).await?;
    let output = railway::commit(output, self.uow()).await?;
    railway::end(output)
}
</code></pre>
<p>これはこれで悪くありませんが、メソッドチェーンによるパイプラインがなくなり、どうにでも書けてしまうという問題が発生します。なので本当にどうしても解決が難しい場合のみこの形式に置き換えるというのが安全と思います。</p>
<p>なお、他の方法としては <code>RUST_MIN_STACK</code> 変数の値を追加することでスタック領域を拡張できます。しかしこれは問題を先送りにしているだけで、いつの日か Stack Overflow エラーが再発しかねません。そのため、この解決方法はあまりおすすめできません。</p>
<p>ちなみにわたしの事例では、デバッグビルドの時に railway モジュール内に定義した関数の中で、非同期処理を並列実行する場合に起こるケースがありました。
async/await は <code>Future</code> 型の糖衣構文ですが、<a href="https://github.com/rust-lang/rust/issues/132050">rust-lang/rust#132050</a> を見ると実行時に <code>Future</code> 型の持つ状態がスタック領域へ展開されるため、多くの <code>async</code> 関数を実行する時に Stack Overflow エラーを引き起こしてしまうようです。
このケースでは、並列で処理したい <code>Future</code> 型のデータを <code>Vec</code> 型のデータへ格納することで対処できました（<a href="https://doc.rust-lang.org/std/vec/struct.Vec.html#guarantees"><code>Vec</code> 型の値はヒープ領域に格納される</a>ためです）。</p>
<h3>処理フローが明確になる代わりにコードは増える</h3>
<p>Rust で Railway Oriented Programming を導入すると、各関数を <code>map</code> と <code>and_then</code> でつなぎ合わせていく形になります。それに加えて、それぞれの関数を定義していく必要があるので、ふつうに書くより全体のコード量は増えます。</p>
<p>たとえば Railway Oriented Programming を適用しなかった場合の <code>handle</code> メソッドは</p>
<pre><code class="language-rust">impl&lt;T: CreateUserUseCase + Sync&gt; UsesCreateUserUseCase for T {
    async fn handle(
        &amp;self,
        input: CreateUserInputData,
    ) -&gt; Result&lt;CreateUserOutputData, CreateUserUseCaseError&gt; {
        // 入力値の検証
        let email = Email::try_from(input.email)
            .map_err(|_| CreateUserUseCaseError::InvalidEmail)?;
        let name = UserName::try_from(input.name)
            .map_err(|_| CreateUserUseCaseError::InvalidUserName)?;

        // メールアドレスの重複チェック
        let existing_user = self
            .user_repository()
            .find_by_email(&amp;email)
            .await
            .map_err(CreateUserUseCaseError::UserRepositoryError)?;
        if existing_user.is_some() {
            return Err(CreateUserUseCaseError::AlreadyExistsEmail);
        }

        // ユーザーの作成
        let user = self
            .user_factory()
            .build(UserFactoryParams { email, name })
            .map_err(CreateUserUseCaseError::UserFactoryError)?;

        // ユーザーの保存
        let saved_user = self
            .user_repository()
            .save(user)
            .await
            .map_err(CreateUserUseCaseError::UserRepositoryError)?;

        // 結果の変換
        Ok(CreateUserOutputData {
            user: saved_user.into(),
        })
    }
}
</code></pre>
<p>となり、関数がなく代わりに <code>handle</code> メソッドの中（または部分的に関数を切り出すなど）で実装することになります。このため、場合によってはこの方がシンプルなこともあると思います。</p>
<p>なので、かんたんな処理・分岐がほとんどといったアプリケーションの場合、無理に Railway Oriented Programming を採用しない方が無難かもしれません。</p>
<h2>余談：「リポジトリパターンはどこにある？」</h2>
<p>この記事の本筋とは直接関係しませんが、「関数型ドメインモデリング」では「リポジトリパターンはどこにある？」という題でリポジトリパターンについて言及されていて、関数型のアプローチではリポジトリパターンを</p>
<blockquote>
<p>すべてを関数としてモデル化し、永続化を端に追いやることで、リポジトリパターンは必要なくなります。</p>
</blockquote>
<p>と書かれています。</p>
<p>しかしわたしがこのことについての意図・方法を読み切れなかったため、この記事のコードおよびわたしたちのプロジェクトではリポジトリパターンを採用しています。</p>
<h2>おわりに</h2>
<p>以上、Railway Oriented Programming を Rust で実践した内容についての紹介でした。</p>
<p>Rust は非常に表現力が豊かで様々な機能を持つ言語ですが、Railway Oriented Programming を採用することでより強化できるのではないかと感じています。</p>
<p>もし同じように Rust で Railway Oriented Programming の採用を検討している方がいらっしゃいましたら、この記事が少しでも参考になれば幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yuki.n/2025-12-13/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Reducing quota management workload with Service Quotas' automated management]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-12-aws-poc-service-quotas-automation-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-12-aws-poc-service-quotas-automation-en/</guid>
            <pubDate>Fri, 12 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Service Quotas の自動管理機能でクォータ監視と自動調整がマネージドでできるようになりました]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>This article is Day 12 of the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a>.</p>
<p>Hello. I&#39;m Matsuo from the Cloud Infrastructure Group.
While this wasn&#39;t announced at AWS re:Invent,
there was an update that I personally found exciting, so I&#39;d like to share it.</p>
<p>It&#39;s the automatic management feature for Service Quotas.</p>
<p>In October 2025, the automatic management settings feature became generally available,
enabling notifications when Service Quotas limits are approaching.
Then recently, the automatic adjustment mode that was announced at GA was added.
Previously, implementing quota monitoring required building a complex architecture yourself,
but with this feature, you can set up quota monitoring and automatic adjustment just by clicking through the console.</p>
<p><strong>And it&#39;s free.</strong></p>
<p>However, I should mention upfront that automatic adjustment doesn&#39;t support all quotas.
In this article, I&#39;ll share the specifications and constraints I discovered through actual testing!</p>
<h2>Update Timeline</h2>
<table>
<thead>
<tr>
<th>Date</th>
<th>Content</th>
</tr>
</thead>
<tbody><tr>
<td>October 2025</td>
<td>Automatic management feature added. At launch, only Notify Only mode was GA. Automatic adjustment mode was announced</td>
</tr>
<tr>
<td>November 2025</td>
<td>Notify and Adjust mode was added</td>
</tr>
</tbody></table>
<p>Official announcements:</p>
<ul>
<li><a href="https://aws.amazon.com/jp/about-aws/whats-new/2025/10/automatic-quota-management-service-quotas/">AWS Service Quotas の自動クォータ管理の一般提供を開始 - AWS</a></li>
<li><a href="https://aws.amazon.com/about-aws/whats-new/2025/11/automatic-quota-management-service-quota-management/">AWS Service Quotas now supports automatic quota adjustment</a></li>
</ul>
<h2>What Makes This Great</h2>
<h3>Before (Traditional Method)</h3>
<p>To implement quota monitoring, you needed an architecture like this:
<a href="https://aws.amazon.com/solutions/implementations/quota-monitor/">https://aws.amazon.com/solutions/implementations/quota-monitor/</a>
<img src="/assets/blog/authors/r.matsuo/service_quota_00.png" alt="Traditional Quota Monitor Architecture">
<em>Source: <a href="https://aws.amazon.com/jp/solutions/implementations/quota-monitor/">AWS Solutions Library</a></em></p>
<ul>
<li>Monitor metrics with CloudWatch Alarms</li>
<li>Lambda functions to call Service Quotas API and send quota increase requests</li>
<li>EventBridge trigger configuration</li>
<li>SNS for notifications</li>
<li>Amazon DynamoDB for managing monitored quotas</li>
</ul>
<p>This resulted in quite a complex architecture that was difficult to build and maintain.</p>
<h3>After (Automatic Management Feature)</h3>
<ul>
<li>Setup complete with just a few clicks in the console</li>
<li>Automatic notifications at 80%/95%</li>
<li>Automatic Adjustment mode automatically sends quota increase requests</li>
<li><strong>No charges for resources that were previously needed for quota monitoring</strong></li>
</ul>
<p>Compared to the previous method, there&#39;s no complex configuration. It seems much easier to adopt.</p>
<table>
<thead>
<tr>
<th></th>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody><tr>
<td>Setup effort</td>
<td>Required building Lambda/EventBridge/SNS/DynamoDB etc.</td>
<td>Just a few clicks in the console</td>
</tr>
<tr>
<td>Notification timing</td>
<td>Set thresholds yourself</td>
<td>Automatic notifications at 80%/95% (fixed)</td>
</tr>
<tr>
<td>Quota adjustment</td>
<td>Self-implemented with Lambda etc.</td>
<td>Handled by automatic adjustment mode</td>
</tr>
<tr>
<td>Cost</td>
<td>Charges for each resource</td>
<td>Free</td>
</tr>
</tbody></table>
<h2>Feature Specifications</h2>
<h3>Notification Timing</h3>
<p>According to the <a href="https://docs.aws.amazon.com/servicequotas/latest/userguide/automatic-management.html">official documentation</a>, notifications are sent at the following thresholds:</p>
<ul>
<li>When <strong>80%</strong> utilization is reached</li>
<li>When <strong>95%</strong> utilization is reached</li>
</ul>
<p><strong>Important: These thresholds are fixed.</strong></p>
<p>When configuring in the console, I thought &quot;Hmm, where do I set the threshold?&quot; but it turns out it&#39;s not configurable by design.
Customizations like &quot;I want notifications at 70%&quot; aren&#39;t possible, so you&#39;d still need to use CloudWatch Alarms for that.</p>
<h3>Automatic Management Modes</h3>
<table>
<thead>
<tr>
<th>Mode</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>Notify Only (NotifyOnly)</td>
<td>Sends notifications at 80% / 95%</td>
</tr>
<tr>
<td>Notify and Adjust (NotifyAndAdjust)</td>
<td>In addition to notifications, automatically sends quota increase requests</td>
</tr>
</tbody></table>
<h3>Notification Destinations</h3>
<p>It&#39;s integrated with AWS User Notifications, and you can configure the following notification destinations:</p>
<ul>
<li>Email (via SES; events from unsupported regions are sent via Virginia)</li>
<li>AWS Console Mobile Application (requires prior app installation and push notification enablement)</li>
<li>Chat channels (delivered to Slack/Teams via Amazon Q Developer; requires prior chat client configuration)</li>
</ul>
<h3>Pricing</h3>
<p><strong>It&#39;s free to use.</strong>
There are no additional charges for the automatic management feature itself.
AWS User Notifications used for notifications is also free,
but if you use SES, mobile push, or Amazon Q Developer behind the scenes,
or if you set up custom monitoring with CloudWatch Alarms separately from the automatic management feature, additional charges may apply.</p>
<h2>Configuration Method</h2>
<blockquote>
<p><strong>Note</strong>: As of November 2025, this feature is not yet supported in Terraform</p>
</blockquote>
<h3>Console Configuration</h3>
<ol>
<li>Open the Service Quotas console</li>
<li>Select Automatic management from the left menu</li>
<li>Click Start automatic management</li>
<li>Select the automatic management mode (Notify Only or Notify and Adjust)</li>
<li>Configure notification settings (optional)</li>
<li>Configure exception settings (optional)</li>
<li>Review and submit</li>
</ol>
<p>Super easy.</p>
<h3>CLI Configuration</h3>
<p>Here&#39;s an example configuration for the us-west-2 (Oregon) region.</p>
<pre><code class="language-bash"># When specifying User Notifications destination and automatic adjustment
aws service-quotas start-auto-management \
  --opt-in-level ACCOUNT \
  --opt-in-type NotifyAndAdjust \
  --notification-arn arn:aws:notifications::xxxxxxxxxxxx:configuration/xxxxx \
  --region us-west-2
</code></pre>
<p><a href="https://docs.aws.amazon.com/cli/latest/reference/service-quotas/start-auto-management.html">Official documentation</a></p>
<blockquote>
<p><strong>Note</strong>: Configuration is required for each region. Setting it in the Oregon region doesn&#39;t apply to the Tokyo region.
If you want to configure all regions via CLI, you&#39;ll need to write a script or similar.</p>
</blockquote>
<h2>Internal Mechanism (Event Pattern)</h2>
<p>When you configure automatic management, internally an event pattern like the following is set as a detailed filter in AWS User Notifications:</p>
<pre><code class="language-json">{
  &quot;source&quot;: [&quot;aws.health&quot;],
  &quot;detail-type&quot;: [&quot;AWS Health Event&quot;],
  &quot;detail&quot;: {
    &quot;service&quot;: [&quot;SERVICEQUOTAS&quot;],
    &quot;eventTypeCode&quot;: [
      &quot;AWS_SERVICEQUOTAS_THRESHOLD_BREACH&quot;,
      &quot;AWS_SERVICEQUOTAS_INCREASE_REQUEST_FAILED&quot;,
      &quot;AWS_SERVICEQUOTAS_APPROACHING_THRESHOLD&quot;
    ],
    &quot;eventTypeCategory&quot;: [&quot;accountNotification&quot;]
  }
}
</code></pre>
<p>Meaning of each event type (<a href="https://docs.aws.amazon.com/servicequotas/latest/userguide/eventbridge-integration.html">official documentation</a>):</p>
<table>
<thead>
<tr>
<th>eventTypeCode</th>
<th>Meaning</th>
</tr>
</thead>
<tbody><tr>
<td><code>AWS_SERVICEQUOTAS_APPROACHING_THRESHOLD</code></td>
<td>An <strong>adjustable</strong> quota is approaching the threshold. In automatic adjustment mode, a quota increase request is sent here</td>
</tr>
<tr>
<td><code>AWS_SERVICEQUOTAS_THRESHOLD_BREACH</code></td>
<td>A <strong>non-adjustable</strong> quota has exceeded the threshold. Since the quota cannot be increased, you need to optimize usage</td>
</tr>
<tr>
<td><code>AWS_SERVICEQUOTAS_INCREASE_REQUEST_FAILED</code></td>
<td>The quota increase request failed</td>
</tr>
</tbody></table>
<h2>Actual Testing</h2>
<h3>Test Environment</h3>
<ul>
<li>Region: us-west-2 (Oregon)</li>
<li>Automatic management mode: Notify and Adjust</li>
</ul>
<h3>Required Permissions</h3>
<p>The permissions required to use the automatic management feature and view permissions are as follows.</p>
<ul>
<li><p>Permissions required to use:</p>
<ul>
<li>ServiceQuotasFullAccess</li>
<li>AWSHealthFullAccess</li>
</ul>
</li>
<li><p>Permissions required to view:</p>
<ul>
<li>AWSHealthFullAccess</li>
</ul>
</li>
</ul>
<h3>Checking Supported Quotas</h3>
<p>I checked from View supported quotas in the console, but perhaps because it was just after release, the coverage was more limited than I expected.
I hope the supported resources will expand in the future.</p>
<p><strong>Examples of supported services:</strong></p>
<ul>
<li>AWS Systems Manager</li>
<li>Amazon EC2</li>
<li>WAF (Regional only)</li>
<li>ELB</li>
<li>Lambda</li>
<li>Route53</li>
<li>IAM</li>
</ul>
<p><strong>Examples of unsupported services:</strong></p>
<ul>
<li>VPC</li>
<li>API Gateway</li>
<li>EventBridge</li>
<li>Amazon Simple Email Service (SES)</li>
</ul>
<p><strong>For global services like Route53 and IAM, it seems only notifications are available and automatic adjustment is not supported</strong></p>
<h3>Verifying Automatic Adjustment Behavior</h3>
<p>I tested with AWS WAF&#39;s Maximum regex pattern sets per account in WAF for regional.
This quota has a default value of 10, so creating 8 would reach 80%.</p>
<h4>Test Procedure</h4>
<h3>1. Preparation</h3>
<p>First, configure automatic management in Service Quotas.</p>
<ul>
<li>Enable automatic management in Service Quotas with Notify and Adjust mode
   <img src="/assets/blog/authors/r.matsuo/service_quota_01.png" alt="">
   (I didn&#39;t capture the screen when setting up in the Oregon region, so this is the settings screen from the Virginia region)</li>
<li>Move to notification settings. Here you configure AWS User Notifications.
   This time, I set up Amazon Q Developer to send notifications to a Slack channel.<blockquote>
<p><strong>Note</strong>: You can select the event source region, but since you can only receive events from regions where automatic management is enabled,
   you need to configure automatic management for each region you want to monitor (it&#39;s a bit confusing because automatic management and AWS User Notifications are separate settings)</p>
</blockquote>
</li>
</ul>
<p>   <img src="/assets/blog/authors/r.matsuo/service_quota_07.png" alt=""></p>
<ul>
<li>You can select services to exclude from automatic management, but this time I created it without any settings
   <img src="/assets/blog/authors/r.matsuo/service_quota_02.png" alt=""></li>
</ul>
<h3>2. Create Regex Pattern Sets</h3>
<p>Next, create regex pattern sets to trigger the threshold.</p>
<ul>
<li>WAF console -&gt; Regex pattern sets -&gt; Create regex pattern set</li>
<li>Region: us-west-2 (Oregon) *Select Regional</li>
<li>Create 8 with arbitrary names
   <img src="/assets/blog/authors/r.matsuo/service_quota_03.png" alt=""></li>
</ul>
<h3>3. Check Utilization</h3>
<ul>
<li>Service Quotas -&gt; AWS WAF -&gt; Maximum regex pattern sets per account in WAF for regional</li>
<li>Confirmed that utilization was at 80%</li>
</ul>
<h3>4. Wait for Notification/Automatic Adjustment</h3>
<ul>
<li>Wait a few minutes...</li>
</ul>
<h4>Test Results</h4>
<ul>
<li><p>Time until notification/automatic adjustment: Not confirmed precisely, but completed in a few minutes</p>
</li>
<li><p>Quota value: Changed from 10 to 11. You can&#39;t specify the quota value after automatic adjustment, but it seems to update to a quota that falls below 80%.
 <img src="/assets/blog/authors/r.matsuo/service_quota_04.png" alt="">
 <img src="/assets/blog/authors/r.matsuo/service_quota_05.png" alt=""></p>
</li>
<li><p>Slack notifications are hard to read
Newline codes (<code>\n</code>) are displayed as-is, making long text appear as one block.
Also, the notification body doesn&#39;t include specific information about which quota is approaching the threshold,
so you need to check the Affected resources tab in the AWS Health Dashboard via the link attached to the notification.
<img src="/assets/blog/authors/r.matsuo/service_quota_06.png" alt=""></p>
</li>
</ul>
<p>As a notification, you can tell something is approaching the threshold, but it takes extra effort to check the details.</p>
<h2>Notes and Constraints</h2>
<h3>1. Limited Supported Quotas</h3>
<p>Honestly, this is the biggest constraint I felt at the moment.
It&#39;s disappointing that quotas for basic services like VPC and SES aren&#39;t supported.
Currently, automatic management of Service Quotas alone won&#39;t handle all quota-related issues.
We&#39;ll have to wait for the supported quotas to expand.</p>
<h3>2. Thresholds Cannot Be Customized</h3>
<p>The 80%/95% thresholds are fixed. Requirements like &quot;I want early notifications at 50%&quot; or &quot;No notifications needed until 100%&quot; cannot be accommodated.</p>
<h3>3. AWS Organizations Support Not Implemented</h3>
<p>As of November 2025, bulk configuration at the Organizations level is not supported,
so you need to configure each account individually.
There&#39;s an <code>--opt-in-level</code> option in the CLI command, so I hope it will be supported eventually.</p>
<h3>4. Automatic Adjustment Approval Is Not Guaranteed</h3>
<p>Automatic adjustment is merely a feature that automatically sends quota increase requests.
Whether the request is approved by AWS is a separate matter, and it may be rejected.</p>
<h3>5. Notifications/Automatic Increases May Take Time</h3>
<p>Right after the quota was raised to 11, I added 3 more regex patterns for a total of 11, bringing utilization to 100%.
However, this time there was no notification and no automatic adjustment.</p>
<p>But several hours later, I confirmed that automatic adjustment had occurred,
so it seems that notifications and automatic adjustments may take time in some cases.</p>
<h2>Conclusion</h2>
<p>The automatic management feature for Service Quotas is personally a really great update that significantly lowers the barrier to quota monitoring.
It&#39;s free to start and can be configured with just a few clicks from the console.</p>
<p>There are still constraints like limited supported quotas and no IaC (Terraform) support,
but I found that for supported quotas, it can actually reduce the effort required for quota expansion work.</p>
<p>At our company, we also operate a custom-built quota management tool,
and we plan to continue using it for quotas that aren&#39;t supported by the automatic management feature.</p>
<p>I hope you found this article helpful.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Service Quotasの自動管理機能でクォータ管理工数の削減が可能に]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-12-aws-poc-service-quotas-automation/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-12-aws-poc-service-quotas-automation/</guid>
            <pubDate>Fri, 12 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Service Quotasの自動管理機能でクォータ監視と自動調整がマネージドでできるようになりました]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の12日目の記事です🎅🎄</p>
<p>こんにちは。クラウドインフラ所属の松尾です。
AWS re:Inventで発表された新機能ではないですが、
個人的に熱いと感じたアップデートがあったので紹介します。</p>
<p>Service Quotasの自動管理機能です。</p>
<p>2025年10月に「自動管理設定」機能がGAとなり、
Service Quotasの上限値が近づくと通知が飛ぶようになりました。
そして先日、GAのタイミングで予告されていた「自動調整」モードが追加。
これまでクォータ監視を実装しようとすると、複雑な構成を自前で用意する必要がありましたが、
この機能を使えばコンソールからポチポチするだけでクォータの監視から自動調整までやってくれます。</p>
<p><strong>しかも無料です。</strong></p>
<p>ただ、先に言っておくと自動調整について、「全部のクォータに対応しているわけではない」という制約があります。
この記事では、実際に検証してわかった仕様や制約を書いていきます！</p>
<h2>アップデートの経緯</h2>
<table>
<thead>
<tr>
<th>日付</th>
<th>内容</th>
</tr>
</thead>
<tbody><tr>
<td>2025年10月</td>
<td>自動管理機能の追加。追加時は「通知のみ」モードがGA。自動調整モードの予告あり</td>
</tr>
<tr>
<td>2025年11月</td>
<td>「通知と自動調整」モードが追加</td>
</tr>
</tbody></table>
<p>公式アナウンス：</p>
<ul>
<li><a href="https://aws.amazon.com/jp/about-aws/whats-new/2025/10/automatic-quota-management-service-quotas/">AWS Service Quotas の自動クォータ管理の一般提供を開始 - AWS</a></li>
<li><a href="https://aws.amazon.com/about-aws/whats-new/2025/11/automatic-quota-management-service-quota-management/">AWS Service Quotas now supports automatic quota adjustment</a></li>
</ul>
<h2>何が嬉しいのか</h2>
<h3>Before（従来の方法）</h3>
<p>クォータ監視を実装するには、こんな構成が必要でした：
<a href="https://aws.amazon.com/solutions/implementations/quota-monitor/">https://aws.amazon.com/solutions/implementations/quota-monitor/</a>
<img src="/assets/blog/authors/r.matsuo/service_quota_00.png" alt="従来のQuota Monitor構成">
<em>出典: <a href="https://aws.amazon.com/jp/solutions/implementations/quota-monitor/">AWS Solutions Library</a></em></p>
<ul>
<li>CloudWatch Alarmsでメトリクスを監視</li>
<li>Lambda関数でService Quotas APIを叩いて上限緩和リクエストを送信</li>
<li>EventBridgeでトリガー設定</li>
<li>SNSで通知</li>
<li>Amazon DynamoDB 監視対象のクォータの管理</li>
</ul>
<p>これだとかなり複雑な構成になるので構築、運用が大変でした。</p>
<h3>After（自動管理機能）</h3>
<ul>
<li>コンソールで数クリックで設定完了</li>
<li>80%/95%で自動通知</li>
<li>「自動調整」モードなら自動で上限緩和リクエストまで送ってくれる</li>
<li><strong>今までクォータ監視で必要だったリソースの料金の発生はなし</strong></li>
</ul>
<p>従来までの方法と比べ、複雑な設定は無し。かなり導入がしやすそうです。</p>
<table>
<thead>
<tr>
<th></th>
<th>Before</th>
<th>After</th>
</tr>
</thead>
<tbody><tr>
<td>設定の手間</td>
<td>Lambda/EventBridge/SNS/DynamoDB等の構築が必要</td>
<td>コンソールで数クリック</td>
</tr>
<tr>
<td>通知タイミング</td>
<td>自分で閾値を設定</td>
<td>80%/95%で自動通知（固定）</td>
</tr>
<tr>
<td>クォータ調整</td>
<td>Lambda等で自前実装</td>
<td>自動調整モードで対応</td>
</tr>
<tr>
<td>料金</td>
<td>各リソースの料金が発生</td>
<td>無料</td>
</tr>
</tbody></table>
<h2>機能の仕様</h2>
<h3>通知タイミング</h3>
<p><a href="https://docs.aws.amazon.com/servicequotas/latest/userguide/automatic-management.html">公式ドキュメント</a>によると、以下のタイミングで通知が送信されます：</p>
<ul>
<li><strong>80%</strong> 使用率に達した時</li>
<li><strong>95%</strong> 使用率に達した時</li>
</ul>
<p><strong>重要：この閾値は固定です。</strong> </p>
<p>コンソールで設定するとき「あれ、閾値設定するところないな？」って思ったんですが、そもそも変更できない仕様でした。
「70%で通知が欲しい」みたいなカスタマイズはできないので、その場合は従来通りCloudWatch Alarmsを使う必要があります。</p>
<h3>自動管理モード</h3>
<table>
<thead>
<tr>
<th>モード</th>
<th>説明</th>
</tr>
</thead>
<tbody><tr>
<td>通知のみ（NotifyOnly）</td>
<td>80% / 95%で通知を送信</td>
</tr>
<tr>
<td>通知と自動調整（NotifyAndAdjust）</td>
<td>通知に加えて、自動でクォータ増加リクエストを送信</td>
</tr>
</tbody></table>
<h3>通知先</h3>
<p>AWS User Notificationsと統合されていて、以下の通知先を設定できます：</p>
<ul>
<li>Eメール（SES経由、サポートされていないリージョンからのイベントはバージニア経由で送信）</li>
<li>AWS コンソールモバイルアプリケーション（事前にアプリのインストールとプッシュ通知の有効化が必要）</li>
<li>チャットチャネル（Amazon Q Developer経由でSlack/Teamsに配信、事前にチャットクライアントの設定が必要）</li>
</ul>
<h3>料金</h3>
<p><strong>無料で使用できます。</strong> 
自動管理機能自体には追加料金はかかりません。
通知に使われるAWS User Notificationsも無料ですが、
裏で呼び出しているSES・モバイルプッシュ・Amazon Q Developerの使用や、
自動管理機能とは別にCloudWatch Alarmsで独自の監視を設定する場合は、別途料金がかかる場合があります。</p>
<h2>設定方法</h2>
<blockquote>
<p>📝 <strong>補足</strong>: 2025年11月時点では本機能は現在Terraformでは対応しておりません</p>
</blockquote>
<h3>コンソールでの設定</h3>
<ol>
<li>Service Quotasコンソールを開く</li>
<li>左メニューから「自動管理」を選択</li>
<li>「自動管理を開始」をクリック</li>
<li>自動管理モードを選択（「通知のみ」または「通知と自動調整」）</li>
<li>通知設定を構成（オプション）</li>
<li>例外設定を構成（オプション）</li>
<li>確認して送信</li>
</ol>
<p>めちゃくちゃ簡単。</p>
<h3>CLIでの設定</h3>
<p>以下はus-west-2（オレゴン）リージョンでの設定例です。</p>
<pre><code class="language-bash"># User Notificationsの通知先と自動調整を指定する場合
aws service-quotas start-auto-management \
  --opt-in-level ACCOUNT \
  --opt-in-type NotifyAndAdjust \
  --notification-arn arn:aws:notifications::xxxxxxxxxxxx:configuration/xxxxx \
  --region us-west-2
</code></pre>
<p><a href="https://docs.aws.amazon.com/cli/latest/reference/service-quotas/start-auto-management.html">公式ドキュメント</a></p>
<blockquote>
<p>⚠️ <strong>注意</strong>: リージョンごとに設定が必要です。オレゴンリージョンで設定しても、東京リージョンには適用されません。
CLIで全リージョン設定する場合はスクリプトを組むなどの対応が必要です。</p>
</blockquote>
<h2>内部の仕組み（イベントパターン）</h2>
<p>自動管理を設定すると、内部的にはAWS User Notificationsの詳細フィルターとして以下のようなイベントパターンが設定されます：</p>
<pre><code class="language-json">{
  &quot;source&quot;: [&quot;aws.health&quot;],
  &quot;detail-type&quot;: [&quot;AWS Health Event&quot;],
  &quot;detail&quot;: {
    &quot;service&quot;: [&quot;SERVICEQUOTAS&quot;],
    &quot;eventTypeCode&quot;: [
      &quot;AWS_SERVICEQUOTAS_THRESHOLD_BREACH&quot;,
      &quot;AWS_SERVICEQUOTAS_INCREASE_REQUEST_FAILED&quot;,
      &quot;AWS_SERVICEQUOTAS_APPROACHING_THRESHOLD&quot;
    ],
    &quot;eventTypeCategory&quot;: [&quot;accountNotification&quot;]
  }
}
</code></pre>
<p>各イベントタイプの意味（<a href="https://docs.aws.amazon.com/servicequotas/latest/userguide/eventbridge-integration.html">公式ドキュメント</a>）：</p>
<table>
<thead>
<tr>
<th>eventTypeCode</th>
<th>意味</th>
</tr>
</thead>
<tbody><tr>
<td><code>AWS_SERVICEQUOTAS_APPROACHING_THRESHOLD</code></td>
<td><strong>調整可能な</strong>クォータが閾値に近づいた。自動調整モードならここでクォータの引き上げリクエストが送信される</td>
</tr>
<tr>
<td><code>AWS_SERVICEQUOTAS_THRESHOLD_BREACH</code></td>
<td><strong>調整できない</strong>クォータが閾値を超過。クォータの引き上げできないので使用率を最適化する必要あり</td>
</tr>
<tr>
<td><code>AWS_SERVICEQUOTAS_INCREASE_REQUEST_FAILED</code></td>
<td>クォータの引き上げリクエストが失敗した</td>
</tr>
</tbody></table>
<h2>実際に検証してみた</h2>
<h3>検証環境</h3>
<ul>
<li>リージョン：us-west-2（オレゴン）</li>
<li>自動管理モード：通知と自動調整</li>
</ul>
<h3>必要な権限</h3>
<p>自動管理機能を使用するために必要な権限、および閲覧権限は以下の通りです。</p>
<ul>
<li><p>使用するために必要な権限</p>
<ul>
<li>ServiceQuotasFullAccess</li>
<li>AWSHealthFullAccess</li>
</ul>
</li>
<li><p>閲覧するために必要な権限</p>
<ul>
<li>AWSHealthFullAccess</li>
</ul>
</li>
</ul>
<h3>対応クォータの確認</h3>
<p>コンソールの「サポートされているクォータを表示」から確認してみたんですが、リリース直後だからか思ったより対応範囲が限定的でした。
今後、対応するリソースが増えることを期待したいです。</p>
<p><strong>対応していたサービスの例：</strong></p>
<ul>
<li>AWS Systems Manager</li>
<li>Amazon EC2</li>
<li>WAF（Regionalのみ）</li>
<li>ELB</li>
<li>Lambda</li>
<li>Route53</li>
<li>IAM</li>
</ul>
<p><strong>対応していなかったサービスの例：</strong></p>
<ul>
<li>VPC</li>
<li>API Gateway</li>
<li>EventBridge</li>
<li>Amazon Simple Email Service（SES）</li>
</ul>
<p><strong>Route53やIAMなどのグローバルサービスの場合、通知のみできるようで自動調整には対応していない印象でした</strong></p>
<h3>自動調整の動作確認</h3>
<p>AWS WAFの「Maximum regex pattern sets per account in WAF for regional」で検証してみました。
このクォータはデフォルト値が10なので、8個作成すれば80%に到達します。</p>
<h4>検証手順</h4>
<h3>1. 事前準備</h3>
<p>まずはService Quotasで自動管理の設定を行います。</p>
<ul>
<li>Service Quotasの自動管理を「通知と自動調整」モードで有効化
   <img src="/assets/blog/authors/r.matsuo/service_quota_01.png" alt="">
   （オレゴンリージョンで設定した際、キャプチャしてなかったのでバージニアリージョンでの設定画面になりますmm）</li>
<li>通知設定に移ります。ここではAWS User Notificationsの設定を行います。
   今回はAmazon Q Developerを用意し、Slackチャンネルに通知するようにしました。<blockquote>
<p>⚠️ <strong>注意</strong>: イベントソースのリージョンを選択できますが、自動管理を有効にしているリージョンのイベントしか受け取れないため、
   監視したいリージョンごとに自動管理の設定が必要です（自動管理とAWS User Notificationsは別設定だからちょっとややこしい）</p>
</blockquote>
</li>
</ul>
<p>   <img src="/assets/blog/authors/r.matsuo/service_quota_07.png" alt=""></p>
<ul>
<li>自動管理の例外とするサービスを選択できますが、今回は何も設定しないまま作成
   <img src="/assets/blog/authors/r.matsuo/service_quota_02.png" alt=""></li>
</ul>
<h3>2. 正規表現パターンセットを作成</h3>
<p>次に正規表現パターンセットを作成し、閾値に引っかかるようにします。</p>
<ul>
<li>WAFコンソール → 正規表現パターンセット → 「Create regex pattern set」</li>
<li>リージョン：us-west-2（オレゴン）※Regionalを選択</li>
<li>適当な名前で8個作成
   <img src="/assets/blog/authors/r.matsuo/service_quota_03.png" alt=""></li>
</ul>
<h3>3. 使用率の確認</h3>
<ul>
<li>Service Quotas → AWS WAF → 「Maximum regex pattern sets per account in WAF for regional」</li>
<li>使用率が80%になっていることを確認しました</li>
</ul>
<h3>4. 通知/自動調整の発動を待つ</h3>
<ul>
<li>数分待機...</li>
</ul>
<h4>検証結果</h4>
<ul>
<li><p>通知/自動調整反映までの時間：正確には確認できていませんが、数分程度で完了</p>
</li>
<li><p>クォータ値：10→11に。自動調整後のクォータ値の指定はできませんが、80%を切るようなクォータに更新してくれそう？
 <img src="/assets/blog/authors/r.matsuo/service_quota_04.png" alt="">
 <img src="/assets/blog/authors/r.matsuo/service_quota_05.png" alt=""></p>
</li>
<li><p>通知に関して、Slack通知は見づらい
改行コード（<code>\n</code>）がそのまま表示されてしまい、長文が一塊になっています。
また、通知本文には「どのクォータが閾値に近づいたか」の具体的な情報が含まれておらず、
通知文に添付のリンクからAWS Health Dashboardの「Affected resources」タブを確認する必要があります。
<img src="/assets/blog/authors/r.matsuo/service_quota_06.png" alt=""></p>
</li>
</ul>
<p>通知としては「何か閾値に近づいた」ことはわかるが、詳細確認には一手間かかる印象です。</p>
<h2>注意点・制約</h2>
<h3>1. 対応クォータが限定的</h3>
<p>正直、今のところこれが一番大きな制約だなと感じました。
VPCやSESといった基本的なサービスのクォータが対応していないのは残念です。
現時点ではService Quotaの自動管理だけでは「クォータ周りの調整がすべて解決」とはならない。
対応クォータの拡大を今後待つしかないです。</p>
<h3>2. 閾値のカスタマイズ不可</h3>
<p>80%/95%の閾値は固定。「50%で早めに通知が欲しい」「100%になるまでは通知不要」みたいな要件には対応できません。</p>
<h3>3. AWS Organizations対応は未実装</h3>
<p>2025年11月時点では、Organizations単位での一括設定には対応していないため、
各アカウントで個別に設定する必要があります。
CLIコマンドには<code>--opt-in-level</code>というオプションがCLIであるのでいつか対応されることを期待したいです。</p>
<h3>4. 自動調整の承認は保証されない</h3>
<p>自動調整はあくまで「上限緩和リクエストを自動送信する」機能です。
リクエストがAWSに承認されるかどうかは別の話で、拒否される可能性もあります。</p>
<h3>5. 通知・自動緩和までに時間がかかる場合がある</h3>
<p>クォータが11に上がった直後、正規表現パターンを3つ追加して合計11個にして使用率を100%にしてみました。
が、今度は通知もされず、自動調整もありませんでした。</p>
<p>しかし数時間後、自動調整されていることが確認できたことから、
場合によっては通知や自動調整までに時間がかかることがあるようです。</p>
<h2>まとめ</h2>
<p>Service Quotasの自動管理機能は、クォータ監視のハードルを大きく下げてくれる個人的にはかなり良いアップデートです。
無料で始められて、コンソールから数クリックで設定できます。</p>
<p>対応クォータがまだ限定的だったり、IaC（Terraform）未対応だったりとまだまだ制約はありますが、
サポートされているクォータに関しては実際にクォータ拡張作業の工数を削減できることがわかりました。</p>
<p>弊社では独自実装のクォータ管理ツールも運用しており、
自動管理機能でサポートされていないクォータについては引き続きそちらで対応していく予定です。</p>
<p>以上、この記事が誰かの参考になれば幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[We Exhibited at Techbook Fest 19!]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-12-techbookfest19-report-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-12-techbookfest19-report-en/</guid>
            <pubDate>Fri, 12 Dec 2025 01:00:00 GMT</pubDate>
            <description><![CDATA[The KINTO Technologies Writing Club exhibited at Techbook Fest 19. Here is an overview of how we prepared for the event.]]></description>
            <content:encoded><![CDATA[<p>This article is the 12th entry in the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a>.</p>
<h2>Introduction</h2>
<p>I&#39;m Uehara (<a href="https://x.com/penpen_77777">@penpen_77777</a>), a backend engineer in the FACTORY E-commerce Development Group at KINTO Technologies.
I participated in Techbook Fest 19 as part of the KINTO Technologies Writing Club, a circle (a small creator group) formed by volunteer engineers at KINTO Technologies.
In this article, I&#39;ll share our journey of creating a technical book with company volunteers.</p>
<p>Here&#39;s the introduction to the new book we created! It&#39;s free. If you&#39;re interested, please download the digital version.
<a href="https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement/">https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement/</a>
<a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6">https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6</a></p>
<h2>The Beginning</h2>
<p>I had written technical books before and had been wanting to write one again after a long break.
While tech blogs and presentations are sufficient for getting your writing read by others, there&#39;s something deeply satisfying about seeing your writing become a physical book.</p>
<p>Since our company had never organized a circle of employee volunteers to write technical books, I decided to take on this challenge.</p>
<h2>Building the Team</h2>
<h3>Finding Supportive People</h3>
<p>I started working on this in June, about six months before participating in Techbook Fest.
Finding supportive people early on is extremely helpful when moving tasks forward.</p>
<p>Our company has a Developer Relations Group that can provide consultation for employees&#39; external technical communications.
I truly believe we were able to accomplish this thanks to the Developer Relations Group. I&#39;m very grateful.</p>
<h3>Finding People Interested in Writing</h3>
<p>Since participating in Techbook Fest was a first-time endeavor, I was worried about whether we could actually gather participants.
Announcing a call for participants company-wide out of the blue felt like a high hurdle.</p>
<p>So I decided to reach out to people who seemed interested and gather those who were highly likely to participate in advance.
At our company, many people use times (personal update channels) on Slack, which is great for casually expressing your thoughts.
By hinting at my desire to exhibit at Techbook Fest, I was able to know who might be interested.</p>
<p>About five people gathered at this point, so I felt reassured that even if a company-wide announcement didn&#39;t attract more participants, we could still make the project work.</p>
<h2>Making the Announcement</h2>
<p>I created a simple slide to recruit writers company-wide.
It summarized information about Techbook Fest and the benefits of writing, designed to encourage participation.
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/announce.webp" alt="告知スライド"></p>
<p>The official sponsorship materials were helpful for introducing the scale of the event.
<a href="https://techbookfest.org/assets/tbf18/for-sponsors.pdf">https://techbookfest.org/assets/tbf18/for-sponsors.pdf</a></p>
<h2>Selecting a Printing Company</h2>
<p>Since we were creating a physical book, we needed to select a printing company.
Techbook Fest has designated backup printing companies, and by submitting your manuscript to one of them, they handle everything from printing to delivering it to the venue. It’s a very convenient system.
The backup printing companies are Nikko Kikaku and Neko no Shippo, and we chose Nikko Kikaku for this project.</p>
<h2>Selecting and Configuring the Typesetting System</h2>
<p>A typesetting system converts manuscripts into a print-ready format.
Re:VIEW is commonly used as a typesetting system for technical books.
By processing text files written in the proprietary Re:VIEW format through Re:VIEW, they are converted into formats commonly used for e-books such as EPUB and PDF.
The benefit is that the separation of page style settings from content allows writers to focus on the essential task of writing without worrying about appearance.</p>
<p>For this project, we used Vivliostyle^[It reads &quot;kumihan,&quot; but I always mistakenly read it as &quot;soban.&quot;
<a href="https://ja.wikipedia.org/wiki/%E7%B5%84%E7%89%88%5D">https://ja.wikipedia.org/wiki/%E7%B5%84%E7%89%88]</a> as our typesetting system.
<a href="https://vivliostyle.org/ja/">https://vivliostyle.org/ja/</a></p>
<p>Vivliostyle&#39;s distinguishing feature is the ability to change appearance using CSS, so anyone familiar with frontend technologies should be able to use it without difficulty.</p>
<p>Since setting everything up from scratch would be challenging, we utilized a template distributed by YUMEMI.
We&#39;re very grateful for the distribution of such a template. Thank you!</p>
<p><a href="https://github.com/yumemi-inc/daigirin-template">https://github.com/yumemi-inc/daigirin-template</a></p>
<p>Based on the above template, we made the following modifications:</p>
<ul>
<li>Updated the title, publisher, and colophon for our technical book</li>
<li>Changed the paper size to B5^[This is when I learned that B5 comes in both JIS B5 and ISO B5. Be careful, as they have different dimensions despite both being called B5.]</li>
<li>Enabled automatic table of contents output and adjusted the template for sections that weren&#39;t outputting correctly</li>
</ul>
<h2>Registering Your Circle for Techbook Fest</h2>
<p>While there&#39;s a lot to prepare, don&#39;t forget to submit your circle participation application.
It might be a good idea to add it to your calendar.</p>
<h2>Having Writers to Write</h2>
<p>I created and shared guidelines so writers wouldn&#39;t struggle with the writing process.</p>
<ul>
<li>Overall schedule and what to do in each phase</li>
<li>Writing methods (Vivliostyle Flavored Markdown^[A notation that extends common markdown to express book-specific structures.
<a href="https://vivliostyle.github.io/vfm/#/%5D">https://vivliostyle.github.io/vfm/#/]</a> syntax, PDF preview methods, etc.)</li>
</ul>
<p><img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/guideline.webp" alt="ガイドライン"></p>
<h3>Manuscript Management</h3>
<p>We managed manuscripts on GitHub, with each writer branching off from the main branch to write.
Having each writer create a PR is convenient for tracking progress.</p>
<h3>Using Slack Lists</h3>
<p>To prevent topic overlap within the book, I asked writers to add their topics to a Slack list once decided.
It&#39;s also helpful to include other information you want to track, such as self-introductions for the appendix and writing status.
While we could have managed this with tables in Confluence, I personally check Slack more frequently, so I managed it using Slack lists, which can be used similarly to Notion databases.
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/theme_list.webp" alt="テーマリスト"></p>
<h2>Getting the Book Cover Created</h2>
<p>When I was wondering whether to create the book cover myself, one of the participants suggested asking designers from the Creative Office for help.
Since we wanted to create it in a fun, collaborative way, we decided to hold a Generative AI × Live Visual Creation event where designers would create the cover using generative AI.</p>
<p><img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/vibe_painting_hour.webp" alt="Vibe Painting Hour">
<em>The event held to create this technical book: Vibe Painting Hour #01</em></p>
<p>Five designers joined the event and, while participants watched, used generative AI to create covers within a one-hour time limit.
Despite my vague request to create a cover with a sense of mobility and futuristic feel, all five final cover proposals were of such high quality that choosing only one seemed like a waste!
Through participant voting and my judgment, the cover for this technical book was selected.</p>
<p>![表紙](/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/cover.webp =300x)
<em>The cover of our new book, TECH BOOK By KINTO Technologies Vol.01.</em></p>
<h2>Setting Manuscript Deadlines</h2>
<p>This is important. The submission deadline is absolute, so you should set it with plenty of buffer time.
Also, it&#39;s best to assume that manuscripts won&#39;t be ready on the first try.
You should set not just one deadline but two or three backup deadlines, with minimum requirements for each deadline.</p>
<h2>Having Writers Review Each Other&#39;s Work</h2>
<p>We had writers review each other&#39;s work.
The review items followed our external communication review guidelines, while also keeping in mind that the final product would be a physical book.
Since you can&#39;t even start reviewing without all manuscripts being submitted, I recommend setting the second deadline early. </p>
<h2>Proofreading</h2>
<p>After compiling all manuscripts, we adjusted the overall page layout.
After noticing the font size was too small, I made it slightly larger. The table of contents had grown to 10 pages, so I narrowed down the items to include.
We also had to adjust the page count to be a multiple of four, or the submission wouldn&#39;t be accepted.
This caused display issues in some chapters, so we asked writers to make corrections.
Since we were working under time pressure, we decided to set the third deadline early.</p>
<h2>Submission</h2>
<p>Once the PDF file for submission was ready, we submitted it to the printing company.
With Nikko Kikaku, we paid in advance and then sent the cover and body data via a form.
If there were any data issues, they would point them out and we&#39;d need to resubmit. This time there were no issues and submission went smoothly.</p>
<h2>Booth Setup Simulation</h2>
<p>With help from a Developer Relations team member who was experienced with booth setup, we carried out a simulation the day before.
By visualizing the booth in advance and identifying what we needed, I think we avoided the situation where we&#39;d look at the actual booth on the day and feel like &quot;hmm, this isn&#39;t quite what I imagined...&quot;</p>
<h2>The Day of the Offline Event</h2>
<p>When I first saw the finished book at the offline venue, I thought it was thicker than expected.
Since we distributed this thick book for free, we heard surprised reactions from attendees.
We printed about 300 copies and distributed them all, so I think it was a great result for our first participation!</p>
<p><img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/booth.webp" alt="ブース"></p>
<p><a href="https://x.com/KintoTech_Dev/status/1989983294059168095">https://x.com/KintoTech_Dev/status/1989983294059168095</a></p>
<h2>Depositing at the National Diet Library</h2>
<p>The day after Techbook Fest, we deposited the book at the National Diet Library. Apparently, publications issued for distribution within Japan are generally subject to legal deposit, and even technical doujinshi like ours can be deposited.</p>
<p><a href="https://www.ndl.go.jp/jp/collect/deposit/deposit.html">https://www.ndl.go.jp/jp/collect/deposit/deposit.html</a></p>
<blockquote>
<p>Publications issued for distribution within Japan are, in principle, all subject to legal deposit (see &quot;Overview of the Legal Deposit System&quot;).
When you deposit publications with the National Diet Library, their bibliographic data becomes searchable as the &quot;National Bibliography&quot; through the NDL Search. Additionally, they are widely used as library materials, preserved as cultural assets shared by the nation, and passed down to future generations as records of the Japanese people&#39;s intellectual activities.</p>
</blockquote>
<p>I also referenced the following article for the deposit process.
<a href="https://qiita.com/mitsuharu_e/items/850ae6688a94ea3d67f3">https://qiita.com/mitsuharu_e/items/850ae6688a94ea3d67f3</a>:</p>
<h3>Off to Deposit</h3>
<p>Being a central government location, various train lines converge here, including the Ginza, Marunouchi, and Hanzomon lines.
Exit 2 is the closest to the National Diet Library, so depending on your line, you may need to walk a bit.
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/library_01.webp" alt="案内板">
<em>Area directory near the station. Exit 2 is the closest. I took the Ginza Line, so I had to walk quite a bit.</em></p>
<p><img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/library_02.webp" alt="国立国会図書館の案内">
<em>National Diet Library sign. There&#39;s a notice that those depositing materials should go to the service entrance (west gate).</em></p>
<p>Enter through the National Diet Library&#39;s service entrance (west gate) and tell the security guard at the building entrance that you want to deposit books, and they&#39;ll handle the procedure.
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/library_03.webp" alt="国立国会図書館の通用口(西口">
<em>National Diet Library service entrance (west gate)</em></p>
<p>At the deposit counter, hand over the book and fill out the paperwork. Once the procedure is complete, they&#39;ll give you a receipt.
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/library_04.webp" alt="![資料受領書](/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/library_04.webp)
">
<em>Material receipt</em></p>
<p>After depositing, the book appears in National Diet Library Search results and becomes available to read as a resource at the National Diet Library.
<a href="https://ndlsearch.ndl.go.jp/books/R100000002-I034410569">https://ndlsearch.ndl.go.jp/books/R100000002-I034410569</a></p>
<h2>Reflections After Completion</h2>
<h3>Don&#39;t Try to Do Everything Alone; Reach Out for Help</h3>
<p>Even creating just one book involves many decisions and tasks, and handling everything alone is quite difficult.
It&#39;s better to delegate tasks that others can do, or that others can do better than you.</p>
<h3>The Technical Book Expanded Our Connections</h3>
<p>As well as interacting with attendees at the Techbook Fest offline venue, I believe the connections among company volunteers deepened through creating the technical book.</p>
<h3>Set Multiple Early Deadlines</h3>
<p>Even though I thought I had set deadlines with plenty of buffer, I still ended up feeling short on time. Next time, I plan to build in an even larger margin. Setting multiple deadlines is important.</p>
<h2>Summary</h2>
<p>While there were many challenges on the road to participating in Techbook Fest 19, looking back, we gained a lot and it was truly enjoyable.
If this article has piqued your interest, please try taking on the challenge of creating a technical book.
We&#39;ll keep working hard so that we can show up even stronger at future Techbook Fests!</p>
<p><a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6">https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/thumbs.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[技術書典19へ出展しました！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-12-techbookfest19-report/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-12-techbookfest19-report/</guid>
            <pubDate>Fri, 12 Dec 2025 01:00:00 GMT</pubDate>
            <description><![CDATA[技術書典19へKINTOテクノロジーズ執筆部が出展しました。出展までの流れを紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の12日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>KINTOテクノロジーズのFACTORY EC開発グループでバックエンドエンジニアをやっている、うえはら(<a href="https://x.com/penpen_77777">@penpen_77777</a>)です。
技術書典19に、KINTOテクノロジーズの有志エンジニアによるサークル「KINTOテクノロジーズ執筆部」として参加してきました。
この記事では社内有志で技術書を作り上げるまでの道のりをご紹介します。</p>
<p>ちなみに今回作った新刊の紹介はこちらになります！無料なのでもしご興味があれば電子版のダウンロードよろしくお願いします。
<a href="https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement/">https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement/</a>
<a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6">https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6</a></p>
<h2>始まり</h2>
<p>私はこれまでに技術本の執筆をしたことがあり、久々に技術本を書きたいなという思いがありました。
文章を他人に読んでもらうだけなら技術ブログや登壇でも十分なのですが、やっぱり自分の書いた文章が本になるのは感慨深いものがあります。</p>
<p>弊社では社員有志でサークルを組んで技術書を書くということがなかったため、今回チャレンジしてみることにしました。</p>
<h2>仲間を作る</h2>
<h3>サポートしてくれそうな人を見つける</h3>
<p>技術書典へ参加する約半年前の6月くらいから動き出し始めました。
早めにサポートしてくれそうな人を見つけておくとタスクを進めておく上で非常に心強いです。</p>
<p>弊社には技術広報グループがあり社員の技術的な外部発信の相談に乗ってもらえます。
やり遂げることができたのも技術広報のおかげだなと思っています。本当に感謝しています。</p>
<h3>執筆に興味がありそうな人を見つける</h3>
<p>技術書典への参加は初めての試みということもあって、本当に参加者が集まるのかが不安なところでした。
いきなり全社に参加者募集の告知をして、参加者を集めるのはハードルが高いと感じていました。</p>
<p>そこで興味がありそうな人に声をかけてあらかじめ参加する確度の高い人を集めることにしました。
弊社ではSlack上でtimes(分報チャンネル)を活用する人も多く、気軽に自分の気持ちを表明できるのが良いところです。
技術書典への出展したい気持ちを匂わせることで、興味がありそうな人が誰か知ることにしました。</p>
<p>ここで5人くらい集まったので、最悪全社告知して集まらなくても企画ができそうだなと思い、安心して進めることができました。</p>
<h2>告知する</h2>
<p>全社で執筆者を募集できるよう簡単なスライドを作りました。
技術書典について、執筆に対するメリット等をまとめていて参加を訴求する内容となってます。
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/announce.webp" alt="告知スライド"></p>
<p>イベントの規模を紹介するのに公式から出ている協賛資料が参考になりました。
<a href="https://techbookfest.org/assets/tbf18/for-sponsors.pdf">https://techbookfest.org/assets/tbf18/for-sponsors.pdf</a></p>
<h2>印刷所の選定をする</h2>
<p>物理本を作るため印刷所の選定が必要になります。
技術書典ではバックアップ印刷所が設定されており、このバックアップ印刷所に入稿することで印刷から会場への納品までやっていただける非常に便利な仕組みとなっています。
バックアップ印刷所には日光企画・ねこのしっぽがあるのですが、今回は日光企画さんを選択しました。</p>
<h2>組版システムを選定する・設定を整える</h2>
<p>組版システムとは印刷向けの見た目に変換するものを言います。
技術書向けの組版システムとしてはRe:VIEWがよく使用されます。
独自の「Re:VIEWフォーマット」で記述されたテキストファイルをRe:VIEWに通すことによって、電子書籍としてよく用いられるEPUB、PDFなどに変換されます。
紙面上のスタイルの設定と内容が分離していることで、執筆者が見た目を意識せずどんな内容の文章を書くかという本質的な営みに集中できるのがメリットです。</p>
<p>今回は組版システム^[読み方は「くみはん」らしいです。毎回そばんと読んでしまいます。
<a href="https://ja.wikipedia.org/wiki/%E7%B5%84%E7%89%88%5D%E3%81%A8%E3%81%97%E3%81%A6%E3%80%8CVivliostyle%E3%80%8D%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%BE%E3%81%97%E3%81%9F%E3%80%82">https://ja.wikipedia.org/wiki/%E7%B5%84%E7%89%88]として「Vivliostyle」を使用しました。</a>
<a href="https://vivliostyle.org/ja/">https://vivliostyle.org/ja/</a></p>
<p>VivliostyleはCSSを使って見た目を変えられるのが特徴で、フロントエンド技術に詳しい方であれば難なく使いこなせるかと思います。</p>
<p>ゼロから設定を整えるのは大変なので、今回はゆめみさんが配布されているテンプレートを活用させていただきました。
このようなテンプレートを配布いただき大変感謝しております。ありがとうございました！</p>
<p><a href="https://github.com/yumemi-inc/daigirin-template">https://github.com/yumemi-inc/daigirin-template</a></p>
<p>上記のテンプレートを元に以下の修正を入れました</p>
<ul>
<li>タイトル・発行者・奥付けを今回の技術書向けの内容に修正</li>
<li>紙面サイズをB5へ変更^[ここでB5にはJIS B5とISO B5があることを知りました。同じB5でもサイズが異なるのでご注意を。]</li>
<li>目次の自動出力を有効化、うまく出力できていない箇所はテンプレートを調整</li>
</ul>
<h2>技術書典へサークル登録する</h2>
<p>色々準備がある中で忘れそうにはなりますが、サークル参加申込は忘れずに行いましょう。
カレンダーに予定を入れたりする方が良いかもですね。</p>
<h2>執筆者に書いてもらう</h2>
<p>執筆者が執筆に困らないようガイドラインを作成し、共有しました。</p>
<ul>
<li>全体的なスケジュールとそれぞれのフェーズでやること</li>
<li>執筆方法(Vivliostyle Flavored Markdown^[一般的なmarkdownを書籍特有の構造も表現できるよう拡張した記法。
<a href="https://vivliostyle.github.io/vfm/#/%5D%E3%81%AE%E8%A8%98%E6%B3%95">https://vivliostyle.github.io/vfm/#/]の記法</a>, PDFのプレビュー方法等)</li>
</ul>
<p><img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/guideline.webp" alt="ガイドライン"></p>
<h3>原稿管理</h3>
<p>原稿管理はGitHubで実施し、mainブランチからそれぞれ執筆者ごとにブランチを切ってもらい執筆作業を行ってもらいました。
執筆者ごとにPRを出しておくと進捗確認ができて便利です。</p>
<h3>Slackリストを活用する</h3>
<p>書籍内でテーマが被らないようテーマが決まったらSlackのリストにまとめてもらうようにお願いしました。
他にも巻末に入れる自己紹介文や執筆状況等、自分で把握しておきたい内容を入れておくと見通しが良いです。
Confluenceで表を使って管理しても良かったのですが、個人的にはSlackの方が見る頻度が高いのとNotionのデータベースっぽく使えるSlackリストで管理していました。
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/theme_list.webp" alt="テーマリスト"></p>
<h2>表紙を作成してもらう</h2>
<p>技術書の表紙を自分で作るか悩んでいたところ、参加者の一人から「クリエイティブ室のデザイナーさんに協力をお願いしてみては？」と提案がありました。
せっかくなら人を集めてワイワイしながら作っていこうということで、デザイナーが生成AIを駆使して表紙を作成する「生成AI × ライブペインティング」イベントの開催が決まりました。</p>
<p><img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/vibe_painting_hour.webp" alt="Vibe Painting Hour">
<em>今回の技術書を作るのに開催されたイベント「Vibe Painting Hour #01」</em></p>
<p>イベントでは5人のデザイナーの方に来ていただき、参加者が見守る中、1時間という制限時間で生成AIを駆使して表紙を作っていただきました。
「モビリティ感があって未来感のある表紙を作る」という、うえはらからの中身がふわふわした依頼にもかかわらず、最終的に上がってきた表紙の5案はどれもクオリティが高く1つに決めるのが勿体無いくらいでした！
参加者の多数決とうえはらの判断によって、今回の技術書の表紙が選ばれました。</p>
<p>![表紙](/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/cover.webp =300x)
<em>新書「TECH BOOK By KINTO Technologies Vol.01」の表紙。</em></p>
<h2>原稿の締切を設ける</h2>
<p>こちらは重要です。入稿期限は絶対的な締切なので、それに合わせて余裕を持って設定した方が良いです。
また一発で原稿が揃うのはなかなかないと思った方が良いでしょう。
締切は1個だけではなくバックアップのためさらに2、3個設け、それぞれの締切で達成してほしい最低ラインを設定した方が良いでしょう。</p>
<h2>執筆者間でレビューしてもらう</h2>
<p>執筆者間でレビューしてもらいました。
レビュー項目は社外への外部発信時のレビュー項目に準じつつ、最終的には物理本にすることを意識して項目を設定しておくと良いでしょう。
原稿が出揃わないとレビューすらできないので、締切は早めに設定しておくことをお勧めします(2回目)。</p>
<h2>校正</h2>
<p>全員分の原稿を取りまとめた上で、全体の紙面の見栄えを調整していきました。
文字サイズが小さいことに気づいたため少し大きくしたり、目次だけで10ページと多くなってしまったので、目次に出す項目を絞ったりしました。
ページ数を4の倍数に調整しておかないと入稿を受け付けてくれないので、うまいことページ数の調整もしていきました。
この影響で表示が崩れてしまった章もあったので、執筆者に修正をお願いしていました。
時間がない中での作業だったので、締め切りは早めに設定しておきましょう(3回目)。</p>
<h2>入稿</h2>
<p>入稿用のPDFができたら印刷所に入稿していきます。
日光企画さんでは先に入金しておき、入稿用の表紙と本文データをフォームで送信すればOKでした。
ここでデータの不備があれば指摘があり再提出することになりますが、今回は特に不備もなく無事入稿できました。</p>
<h2>ブース設営のシミュレーション</h2>
<p>ブースの設営に詳しい技術広報の方に協力してもらいながら、前日にシミュレーションを行いました。
事前にブースのイメージを膨らませておきつつ必要なものも洗い出せたので、当日ブースを見て何となく思ってたのと違う...みたいなことにはならなかったと思います。</p>
<h2>オフライン会場当日の様子</h2>
<p>オフライン会場で出来上がった本を初めてみた時、思ったより分厚いなと感じました。
この分厚い本を無料で頒布したので、参加者からは驚きの声も聞かれました。
約300部印刷したのですが全て頒布できたので、初回参加の結果としては上々なのではないかと思っています！</p>
<p><img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/booth.webp" alt="ブース"></p>
<p><a href="https://x.com/KintoTech_Dev/status/1989983294059168095">https://x.com/KintoTech_Dev/status/1989983294059168095</a></p>
<h2>国会図書館へ納入</h2>
<p>技術書典の翌日、国会図書館へ納本しました。日本国内で頒布を目的として発行した出版物は原則納本の対象になるらしく、今回のような技術同人誌でも納本できるようです。</p>
<p><a href="https://www.ndl.go.jp/jp/collect/deposit/deposit.html">https://www.ndl.go.jp/jp/collect/deposit/deposit.html</a></p>
<blockquote>
<p>日本国内で頒布を目的として発行された出版物は、原則として、すべて納本の対象となります（「納本制度の概要」参照）。
国立国会図書館に出版物を納本いただくと、その書誌データが「全国書誌」として国立国会図書館サーチで検索できるようになります。また、図書館資料として広く利用されるとともに、国民共有の文化的資産として永く保存され、日本国民の知的活動の記録として後世に継承されます。</p>
</blockquote>
<p>納本には以下の記事も参考にさせていただきました。
<a href="https://qiita.com/mitsuharu_e/items/850ae6688a94ea3d67f3">https://qiita.com/mitsuharu_e/items/850ae6688a94ea3d67f3</a></p>
<h3>いざ納本へ</h3>
<p>国の中枢ということもあり、銀座線、丸の内線、半蔵門線といった様々な路線が乗り入れています。
2番出口が国会図書館の最寄りのため、路線によっては多少歩くことになりそうです。
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/library_01.webp" alt="案内板">
<em>駅周辺の案内。出口2が一番最寄りです。私は銀座線で向かったため、歩く量が多かった。</em></p>
<p><img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/library_02.webp" alt="国立国会図書館の案内">
<em>国立国会図書館の案内。納本の方は通用口(西口)へという案内がある。</em></p>
<p>国立国会図書館の通用口(西口)から入って、建物の入り口にいる警備員さんに納本したい旨伝えると手続きをしてくれます。
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/library_03.webp" alt="国立国会図書館の通用口(西口)">
<em>国立国会図書館の通用口(西口)</em></p>
<p>納本の窓口に着いたら、納本する書籍を渡して書類に記入します。手続きが終わったら受領書を渡してくれます。
<img src="/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/library_04.webp" alt="資料受領書">
<em>資料受領書</em></p>
<p>納本すると以下のように国立国会図書館サーチの結果に表示されるようになり、また国会図書館で資料として読めるようになります。
<a href="https://ndlsearch.ndl.go.jp/books/R100000002-I034410569">https://ndlsearch.ndl.go.jp/books/R100000002-I034410569</a></p>
<h2>終わってみて感じたこと</h2>
<h3>一人で抱え込まず、助けを借りよう</h3>
<p>書籍一冊作るだけでも決めることややるべきタスクが多く、一人だけで全て捌き切るのはだいぶ大変です。
他の人でもできる、むしろ他の人の方がうまくやれるタスクはお願いしていった方が良いです。</p>
<h3>技術本によって繋がりが増えた</h3>
<p>技術書典のオフライン会場での参加者との交流はもちろん、技術本の制作を通して社内有志で繋がりが深まったと思っています。</p>
<h3>締切は多重に早めに入れておく</h3>
<p>余裕を持って締切を設定したつもりがやってみると意外と時間がないなという気持ちになったので、次はもう少し余裕を持たせていこうと思ってます。多重で締切を設定することは大事です。</p>
<h2>まとめ</h2>
<p>技術書典19への参加の道のりには大変なことも多かったですが、終わってみると得られるものも多く本当に楽しかったです。
もしこの記事を読んで興味が湧いた方はぜひ技術本制作にチャレンジしてみてください。
また今後の技術書典でパワーアップした姿を見せられるよう頑張っていきます。</p>
<p><a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6">https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uehara/2025-12-12-techbookfest19-report/thumbs.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[【Osaka Tech Lab】こだわりが爆発！新オフィスのコンセプトを見せます！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-11-KINTOテクノロジーズ-大阪オフィスツアー/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-11-KINTOテクノロジーズ-大阪オフィスツアー/</guid>
            <pubDate>Thu, 11 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[【Osaka Tech Lab】こだわりが爆発！新オフィスのコンセプトを見せます！]]></description>
            <content:encoded><![CDATA[<p>こんにちは！あるいは、まいど！ 人事企画G 労務・総務チームのつんつんです。今回は、ついに完成したOsaka Tech Labの新フロアをご紹介します。</p>
<p>正直に言います。以前のオフィスとはもう「全然違う」空間に仕上がりました。 </p>
<p>今回のコンセプト KINTOテクノロジーズらしく、車やモビリティを感じさせるデザインに全振りしました。</p>
<p>写真多めで、こだわりのポイントを余すことなくお届けします！</p>
<h1>エントランス</h1>
<p>まずエントランスでお出迎えするのは、KINTO Technologiesのロゴ。 これ、ただの看板じゃありません。後ろの骨組みが黒にし、あえてロゴを「白」にして、しかも光らせてます。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-01.jpg" alt=""></p>
<p>そして写真左側この植栽。 「これ本物？」ってよく聞かれますが、一部、フェイクもありますが、殆どリアル（本物）です。 水やり大変そうとか言わないでください。この空間の充実に命かけてるんで。</p>
<h1>受付からの廊下の風景</h1>
<p>廊下からも緑がいい具合に演出していて一石二鳥</p>
<p><img src="/assets/blog/authors/tsujimoto/o-02.jpg" alt="">
車愛が止まらない
今回のコンセプトの一部でもあり具体化した、「ガレージ」です。 </p>
<h1>① こだわりの会議室「ガレージ1・2・3」</h1>
<p>会議室エリアは、まさにガレージそのもの。全会議室の床に白いラインを引き駐車場をイメージ、</p>
<p>また核となる３つの会議室には天つりモニターにし、電動ブラインドを設置。</p>
<p>（大事な会議もこれで安心！目線にも気を配りました。）</p>
<p><img src="/assets/blog/authors/tsujimoto/o-03.jpg" alt=""></p>
<h1>Garage 1： 車の車庫をイメージ</h1>
<p>床に白い駐車ラインを引き、有効ボード（ペグボード）を設置。ここに工具とか飾りたい！</p>
<p>（予算なくてまだ何もかけてないけど）。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-04.jpg" alt=""></p>
<h1>Garage 2： 車の模型をおしゃれに配置。</h1>
<p>チップボード風の壁紙にし、無骨なガレージ感を演出。棚を配置しちょっとした小物を置けるようにしました。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-05.jpg" alt=""></p>
<h1>Garage 3：西海岸の風を感じる。</h1>
<p>ここはちょっとアメリカンな雰囲気。「アメリカンフェンス」というそのまんまな名前の商品を入れて、ヤシの木を導入しました。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-06.jpg" alt=""></p>
<h1>廊下もただの廊下じゃない</h1>
<p>廊下の天井には道路標識が埋め込まれています。 「大阪ジャンクションまで7m」。これ、適当な数字じゃありません。ちゃんとCADで測って、本当に7mあります。JCTの部屋は後ほど・・・</p>
<p><img src="/assets/blog/authors/tsujimoto/o-07.jpg" alt="">
こういう細かいこだわり、大好きです。</p>
<h1>会議室「PIT（ピット）」シリーズ</h1>
<p>ガレージの次はピットです。ここも部屋ごとに椅子が全部違います。全部変えました。こだわりすぎて記憶飛びそうですが。</p>
<p>写真左からPIT-A, PIT-B, PIT-C</p>
<table>
<thead>
<tr>
<th>PIT-A</th>
<th>PIT-B</th>
<th>PIT-C</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/tsujimoto/o-08.jpg" alt=""></td>
<td><img src="/assets/blog/authors/tsujimoto/o-09.jpg" alt=""></td>
<td><img src="/assets/blog/authors/tsujimoto/o-10.jpg" alt=""></td>
</tr>
<tr>
<td>受付と反対側にある４人部屋。受付との一部をガラスにすることにより、ここでも緑が大活躍。癒してくれます。</td>
<td>小会議室の４人部屋。PIT Aと色違いの椅子を入れることで雰囲気がだいぶ変わりました。</td>
<td>机がビンテージ風でかっこいいんですが、真ん中から配線が出せないのが玉に瑕。でも、デザイナーさんの熱意に負けて入れました。</td>
</tr>
</tbody></table>
<h1>PIT-D</h1>
<p>ここはプロジェクター投影専用の壁紙を採用。スクリーンを下ろす手間がありません。机は可動式で、スクール形式にも対応。コンセントも爆量で用意してるので、勉強会もやり放題です。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-11.jpg" alt="">
<img src="/assets/blog/authors/tsujimoto/o-12.jpg" alt=""></p>
<h1>MOTORPOOL</h1>
<p>８人用会議部屋。こちらは格式高く設計しており役員会議など行われる設定です。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-13.jpg" alt=""></p>
<h1>遊び心満載の「ジャンクション」エリア</h1>
<p>オフィスの中心にあるのが、みんなが集まる「ジャンクション」。</p>
<p>壁には大阪メンバーのクリエイティブによる巨大アートが！ 「この指とまれ」をコンセプトに、大阪らしくたこ焼き、カニ、くいだおれ人形が描かれています。</p>
<h1>この指とまれ案のストーリー</h1>
<p>技術交流を通じて、部署内や外部の関係者を巻き込みながら、自ら進んで物事を進める力。これが大阪の強み、気質。</p>
<p>この特徴をこの指とまれの指をモチーフに色々な予想外な業種、事、人、技術とコラボしていくことを壁に表現しております。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-14.jpg" alt="">
そしてここには、私がどうしても置きたかった業務用「冷凍庫付き冷蔵庫」があります。 </p>
<p><img src="/assets/blog/authors/tsujimoto/o-15.jpg" alt=""></p>
<h1>執務エリアからJCTを望む。</h1>
<p>コンセプト</p>
<blockquote>
<p>情熱のアルゴリズムコード</p>
<p>壁の「この指とまれ」を強調するガラスになっており、</p>
<p>情熱が生まれ広がっていく過程を視覚的にコードで表現してます。</p>
<p>情熱が自分から生まれ　→  外部要因と掛け合わさる　→　最終的に情熱が</p>
<p>波のように「うねり」になり、社会に大きな影響を与える様子を表現しています。</p>
</blockquote>
<p><img src="/assets/blog/authors/tsujimoto/o-16.jpg" alt=""></p>
<h1>土禁×リラックスゾーン “PARK”</h1>
<p>靴を脱いでくつろげるグリーンカーペットは、まるでオフィス内ピクニック場！
梅田の街並みを眺めながらランチやちょっとしたMTGもOK🍱☕</p>
<p><img src="/assets/blog/authors/tsujimoto/o-17.jpg" alt=""></p>
<h1>謎のこだわり「SUVタイヤ」と「低いホワイトボード」</h1>
<p>park部分には、なぜか巨大なタイヤが転がっています。 これ、中身が入った本物のSUVタイヤです。最初はノーマルタイヤだったんですが、「そんなんじゃダメだ！ゴツゴツしたやつがいい！」とわがままを言って変えてもらいました。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-18.jpg" alt=""></p>
<h1>こちらは木製モーターベンチ！</h1>
<p>足元には本物タイヤを装着！座るだけで車会社らしい遊び心を体感できるミニドライブ気分</p>
<p><img src="/assets/blog/authors/tsujimoto/o-19.jpg" alt=""></p>
<p> 2人乗っても大丈夫ですが、めちゃくちゃ重いです。</p>
<h1>執務エリアは「広さ」が命</h1>
<p>働くデスク周りも妥協してません。 移転前より通路幅、デスク幅を大きくしました。</p>
<p>（デスク幅は1400mm、前後の通路幅は1800mm確保。）</p>
<p>「狭い」とは言わせません。 ストレスフリーで働けます。</p>
<p>まとめ：とにかく「こだわり」がすごい
正直、まだまだ語りきれないこだわりが山ほどあります。</p>
<p> 全部書き切れないのですが一部ご紹介</p>
<h1>公園のベンチを忠実に再現した木目スラット。</h1>
<p>座るだけでほっこりピクニック気分♪</p>
<p><img src="/assets/blog/authors/tsujimoto/o-20.jpg" alt=""></p>
<h1>HUBエリア</h1>
<p>壁紙はマップ（地図）をイメージ　</p>
<p>ファミレス席は移転前の部屋名を踏襲しており歴史も大事にしてます。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-21.jpg" alt=""></p>
<h1>サボテンもあります。</h1>
<p><img src="/assets/blog/authors/tsujimoto/o-22.jpg" alt=""></p>
<h1>オフィスからの風景</h1>
<p>大阪駅の真上にあり電車マニアにはたまらない！真ん中より少し右側に線路が見えます。</p>
<p><img src="/assets/blog/authors/tsujimoto/o-23.jpg" alt=""></p>
<p>とにかく、Osaka Tech Labは <strong>「働きたくなるオフィス」</strong> に仕上がりました。 </p>
<p>ここまでお付き合い頂きありがとうございました。</p>
<p>現場からは以上です！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Our 2025 Efforts to Design User-Focused Products]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025-en/</guid>
            <pubDate>Thu, 11 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article summarizes our year-long efforts to promote user-first design that we focus on in 2025 and the evolving perceptions within the company.]]></description>
            <content:encoded><![CDATA[<p>Hello, I&#39;m Moriya (emim) from the Engineering Office.</p>
<p>This article is part of <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> for Day 11.</p>
<p>On Day 2 of the event, I posted an <a href="https://blog.kinto-technologies.com/posts/2025-12-02-SNSA11y/">article about accessibility</a>, while my primary job is product design. As a core function, I work as a designer within the <a href="https://blog.kinto-technologies.com/posts/2025-02-12-engineering-office/">Engineering Office</a>, analyzing the situation at KINTO Technologies (KTC) and implementing initiatives to strengthen organizational capabilities.</p>
<p>At KTC, our Executive Vice President Kageyama announced the following four key ideas which we focus on in 2025: AI first and release first that are directly related to our product development, as well as user first and organizational intensity to fundamentally support the former two ideas.</p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024-en/">https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024-en/</a></p>
<p>As a designer, I am involved in promoting user-first design among these ideas. Looking back to June 2025 when I joined KTC, user-focused activities have been already underway within the organization in various aspects since the beginning of 2025.</p>
<p>As a year-end summary for 2025, I will wrap up what we have accomplished.</p>
<p>##First Internal Survey Revealed Inconsistent Perception of Users Within KTC</p>
<p>Our first activity to promote user-first design was a survey to assess internal perception of users.</p>
<p>There are so many different types of users that we need to ensure that our members share a common understanding of them. The survey was designed to plan specific methods to promote user-first design through confirming whether everyone has the same understanding of &quot;users.&quot;</p>
<p>We gathered members from various divisions and roles across the company and conducted group discussions to understand the current circumstances. Group-work sessions were held in various formats, including brainstorming with online whiteboard tools and on-site workshops to map the user perception with sticky notes.</p>
<p><img src="/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img01.png" alt="4組のグループで行われたディスカッションの様子を表す付箋の俯瞰図"></p>
<p>I joined my current team after completing the survey, and we then organized the relationship between users at each level—from business model stakeholders to end users—and products, in a format similar to <a href="https://jpattonassociates.com/identify-your-products/">Jeff Patton&#39;s onion model</a>.</p>
<p>Through this process, we found a considerable gap between our products and users’ needs. Perhaps due to the gap or complexity of our businesses, some people seemed to perceive stakeholders (such as colleagues in different roles with hierarchical relationships, partners at related/group companies, or clients) as users.</p>
<p>![ビジネスモデルと照らし各階層のユーザーからエンドユーザーまでとシステムの関係性を表す図と、サービスとユーザーとの距離を表した図](/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img02.png =600x)</p>
<p>While the user-first design is raised as a discussion topic from the concern that we cannot see what users want, we found that our development organization structure is one of the factors contributing to addressing this. When developers are distant from our users, the next step will be how to involve stakeholders, raise their awareness, and draw out users’ needs.</p>
<p>We also shared the information with our Executive Vice President, who is responsible for this initiative, confirming alignment for seamless activity promotion.</p>
<h2>Current Statement Analysis Using Level of UX Maturity and Next Step of Sharing</h2>
<p>With organizational challenges clarified, we focused on two points:</p>
<ol>
<li>Raising developers’ awareness of the term user to remind them of a user-first mindset</li>
<li>Involving members to understand users</li>
</ol>
<p>The former may seem obvious for product/service development, but not all developers sufficiently consider users yet.</p>
<p>To measure our developers’ perception on users, we conducted a current state analysis referencing <a href="https://www.nngroup.com/articles/ux-maturity-model/">The 6 Levels of UX Maturity</a>. This document includes a quiz in the latter half that allows individuals to self-assess their organizational conditions.</p>
<p>Based on the content and quiz results, we are still at Stage 2: Limited. Clarifying our current position enables us to identify what we must do to advance to Stage 3: Emergent, the next level.</p>
<p><img src="/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img03.png" alt="UXの成熟度モデルを参考に、重要な内容を社内向けにサマライズした表のキャプチャー：弊社は「限定的（Limited）レベル」にいて、UXへのアプローチが不規則。専門役職、プロセス、予算が存在しない。と表されている"></p>
<p>Fortunately, we do not face the issue common in typical organizations: lack of the company’s understanding (no budget allocation). With problems identified through the top-down process, we find them easier to take action.</p>
<p>For organizational information sharing, regular meetings are scheduled to share information with other key topics. This enables relationships where, for example, we receive user-related consultations in release contexts, and we can also had discussions about AI utilization for future initiatives.</p>
<p>However, this alone limits information sharing to a specific scope. Therefore, we next conducted a company-wide information sharing session.</p>
<h2>Initiatives to Prevent UX from Being Isolated</h2>
<h3>Sharing Our Promotion Activities</h3>
<p>The first step was reporting activities at the monthly company-wide information sharing session.</p>
<p>We reconfirmed our current position, which we previously agreed upon with various parties, and mainly presented how developers can put a focus on user value.</p>
<p><img src="/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img04.png" alt="「ユーザーにとっての価値」を考え抜く、そして習慣化させるをテーマにした資料と、アジャイル開発の図と人間中心デザインの図の類似性を指摘した上でいずれも「ユーザー」が大事であることを示した資料、2枚分の資料のキャプチャー"></p>
<p>We explained development methods focusing on user value, citing similarities between agile development and human-centered design (HCD).</p>
<p>Even engineers familiar with agile diagrams reacted as if they heard about HCD for the first time. Through the presentation that specific methods are provided for each HCD process, this single presentation created a situation where engineers could easily have discussions on specific topics.</p>
<h3>Exploring Internal Case Studies (Conducting Study Sessions)</h3>
<p>Next, we organized study sessions to spotlight those who have been working on running the HCD process before and share their activities on a company-wide basis. Through these sessions, we aimed to give rise to participants’ expectation that they could do it too.</p>
<p>In these sessions, we had presenters share examples of implementing specific HCD processes within projects and cases where they attended events hosted by our sister company KINTO to understand how users use our services.</p>
<p>Regarding the latter, one of the session attendees previously wrote an article on the following blog.</p>
<p><a href="https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent/">https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent/</a></p>
<p>Furthermore, considering our company&#39;s large engineering population, we invited our frontend engineers who implemented the aforementioned initiatives and backend engineers whom we had interviews with to share detailed changes in their mindset through their efforts during the panel discussions in the session.</p>
<p><img src="/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img05.png" alt="エンジニアの投影資料「課題分析結果」の表示されたスライド写真と、パネルディスカッションに参加してもらった3人のエンジニア：左から金田（BE）、中原（FE）、岡野（FE）。背景にスライド「パネルディスカッション：ユーザーテスト／ユーザーインタビューの準備について」と表示されている写真が並列表記されている"></p>
<p>After this study session, we witnessed changes in members’ behavior; for example, they contacted us about specific methods for deeply understanding user needs that include user interviews and customer journey maps. Recently, we received requests for consultations from engineers at a pace of about one every two weeks.</p>
<h2>Our Future Activities</h2>
<p>Currently, this initiative is carried out by myself and one of my colleagues Okane-san from the Creative Group. We have roughly divided our roles: I, Moriya, handle internal promotion of user-first activities and standardization/horizontal deployment of various processes, while Okane-san joins projects to execute HCD processes. Of course, we are short-handed, though.</p>
<p>As mentioned earlier, we believe the most important thing is to increase the number if colleagues who can execute the processes, reach out to people in related areas for cooperation, and keep this activity going. Additionally, by presenting these activities again, we can spotlight activities that no longer gain traction in various areas once again to learn and leverage them as knowledge.</p>
<p>As introduced at the beginning, while this is one of 2025&#39;s focus points, thinking about users is the foundation of product design. To keep proceeding with this initiative in a sustainable manner, we plan to continue refining our efforts beyond next year.</p>
<p>Finally, on December 23, Okane-san, who works with me on this initiative, will publish an article about her own challenges in the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a>. Stay tuned!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_title.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年注力テーマ「ユーザーファースト」の取り組みについて]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-11-userfirst2025/</guid>
            <pubDate>Thu, 11 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[1年間に渡って2025年注力テーマであるユーザーファーストの取り組みを推進した活動記録と、社内の受け止めの変化についてまとめました。]]></description>
            <content:encoded><![CDATA[<p>こんにちは、Engineering Officeの守谷（emim）です。</p>
<p>この記事は<a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a>の11日目のものです。</p>
<p>2日目に、<a href="https://blog.kinto-technologies.com/posts/2025-12-02-SNSA11y/">アクセシビリティについての記事</a>を投稿しましたが、私の主領域はプロダクトデザインです。主軸では、<a href="https://blog.kinto-technologies.com/posts/2025-02-12-engineering-office/">Engineering Office</a>のメンバーとしてデザイナーの立場でKINTOテクノロジーズ（以下KTC）の状況をとらえ、組織力を上げるための取り組みを行っています。</p>
<p>KTCでは2025年の注力テーマとして、副社長の景山より、2つのテーマ「AIファースト」「リリースファースト」とともに、それを下支えする「ユーザーファースト」と「組織インテンシティ」が掲げられました。</p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/</a></p>
<p>私はこのうちデザイナーとして、「ユーザーファースト」推進活動に関わっています。私自身は6月入社ですが、このユーザーファーストについての活動は、今年のはじめから様々な方向での組織内の取り組みがおこなわれています。</p>
<p>今回は2025年の総括として、我々の実行した内容をまとめて行きます。</p>
<h2>はじめに着手した社内調査から、社内での「ユーザー」の認知にブレがあることがわかった</h2>
<p>まず初めに行われたのが、ユーザーファースト推進において主軸となる「ユーザー」についての組織内での認知調査です。</p>
<p>ひと口に「ユーザー」と言っても、全員が同じ対象をイメージできているのか？という確認からです。ユーザーが一般的な用語だからゆえの解釈の違いを明確にした後に、具体の推進方法を検討するための調査です。</p>
<p>様々な部署、様々なロールのメンバーを全社から招集し、グループディスカッションを通し現状を把握しました。オンラインホワイトボードツールでの実施や、オンサイトでの付箋でのワークなど、開催形態は様々です。</p>
<p><img src="/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img01.png" alt="4組のグループで行われたディスカッションの様子を表す付箋の俯瞰図"></p>
<p>調査が一通り終わったところで私が参画したこともあり、次に、ビジネスモデルと照らし各階層のユーザーからエンドユーザーまでと製品との関係性を、<a href="https://jpattonassociates.com/identify-your-products/">Jeff Patton氏の提唱するThe onion model</a>のような形での整理しました。</p>
<p>そこから見えてきたのは、我々の向かう製品とユーザーとの間にかなり距離があることでした。またその距離の遠さからか、事業の複雑さからか、ステークホルダー（チーム内で例えば上下関係のある別職域の人や、取り引きをしている関係会社／グループ会社の担当者、依頼主など）をユーザーと捉えている人も居そうなことがわかりました。</p>
<p>![ビジネスモデルと照らし各階層のユーザーからエンドユーザーまでとシステムの関係性を表す図と、サービスとユーザーとの距離を表した図](/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img02.png =600x)</p>
<p>「ユーザーのことが見えていない」という課題感からユーザーファーストというテーマが上がってきましたが、開発組織構造も関係していることがわかりました。開発者とユーザーに距離がある場合、どのようにステークホルダーを巻き込み意識付けを行い、ユーザーの要求事項を聞き出すのか、が次のステップになる、と考えました。</p>
<p>これらの情報は、この活動のオーナーである副社長にも共有し、齟齬なく活動を推進していけることの確認にも利用できました。</p>
<h2>「UXの成熟度モデル」を参考にした現状分析と推進の次の一手の「共有」</h2>
<p>組織課題が明らかになったところで、次に我々が着目したのが</p>
<ol>
<li>「ユーザー」というワードが開発の現場で出てくるための意識付け</li>
<li>「ユーザー理解」のためのメンバーの巻き込み</li>
</ol>
<p>という2点です。前者については、プロダクト／サービス開発をする以上当たり前のことだと感じるかもしれませんが、全開発者がユーザーに向き合えているかというとまだまだだ不十分という状況です。</p>
<p>このあたりの肌感を探る為、<a href="https://www.nngroup.com/articles/ux-maturity-model/">The 6 Levels of UX Maturity</a>（UXの成熟度モデル）を参考にしながら、現状分析を行いました。このドキュメントには後半に自身で組織の状態を評価（推定）のできるテストが用意されています。</p>
<p>内容及びテストの結果からして、まだ我々は「Stage 2: Limited（限定的）」にいます。現在地が明らかになることで、次レベルの「Stage 3: Emergent（新興）」へ向かうために何をすべきか、という観点の確認が可能となります。</p>
<p><img src="/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img03.png" alt="UXの成熟度モデルを参考に、重要な内容を社内向けにサマライズした表のキャプチャー：弊社は「限定的（Limited）レベル」にいて、UXへのアプローチが不規則。専門役職、プロセス、予算が存在しない。と表されている"></p>
<p>幸いなことに、一般的な組織で次に課題となる「会社側の理解がない（予算がつかない）」という状況にはありません。トップダウンで課題提起がなされている分、動きやすいと感じます。</p>
<p>組織の情報共有の仕組みとしては、他の注力テーマと定期的に情報共有をする機会（定例会議）が設定されています。それにより、例えばリリース分脈でユーザーについての相談をもらえるような関係にあったり、我々からも今後の取り組みとしてAI活用についての相談を上げたりしています。</p>
<p>ただしこれだけでは、限定的な範囲での情報共有となってしまいます。そこで次に、広く全社に向けての情報共有会を行いました。</p>
<h2>UXが孤立しないための取り組み</h2>
<h3>我々の推進活動の共有</h3>
<p>まず取り組んだのは、月に一度全社に向けて行われる情報共有会での活動報告です。</p>
<p>事前に各所と合意を得た「現在地」についての改めての報告と、開発者の多い現場でどうしたら「ユーザーの価値」に着目できるのか？という点に絞ってプレゼンを行いました。</p>
<p><img src="/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img04.png" alt="「ユーザーにとっての価値」を考え抜く、そして習慣化させるをテーマにした資料と、アジャイル開発の図と人間中心デザインの図の類似性を指摘した上でいずれも「ユーザー」が大事であることを示した資料、2枚分の資料のキャプチャー"></p>
<p>「ユーザーの価値」を中心に据えた開発手法として、アジャイル開発と人間中心デザイン（Human Centered Design 、以後HCD）の類似性を引用しながら説明を行いました。</p>
<p>アジャイルの図はよく目にするエンジニアでも、HCDについては初めて耳にしたというリアクションが出ていました。HCDの各プロセスには具体の手法が提供されていることも合わせて提示したことで、この1度の発表だけでエンジニアからでも具体の相談の上がりやすい状況に転換できました。</p>
<h3>社内の先行事例の発掘（勉強会の実施）</h3>
<p>次に着手したのが、先行事例として取り組まれている方たちをクローズアップし、改めて社内にその活動を共有する勉強会を開催しました。勉強会を通し、「自分達でもできそう」という感覚を持ってもらうことを目的としています。</p>
<p>この勉強会では、具体のHCDプロセスをプロジェクト内で回している事例と、兄弟会社のKINTOの主催のイベントに赴き、ユーザーのサービス利用の状況把握に努めた事例について、改めて発表をしてもらいました。</p>
<p>後者については、過去にこのブログでも本人が記事を起こしてくれています。</p>
<p><a href="https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent/">https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent/</a></p>
<p>さらに、エンジニアの多い会社であることをふまえ、前述の取り組みをしたフロントエンドのエンジニアや、インタビューを実行したバックエンドのエンジニアを招聘し、パネルディスカッションを通して、具体の心境の変化などを共有してもらいました。</p>
<p><img src="/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_img05.png" alt="エンジニアの投影資料「課題分析結果」の表示されたスライド写真と、パネルディスカッションに参加してもらった3人のエンジニア：左から金田（BE）、中原（FE）、岡野（FE）。背景にスライド「パネルディスカッション：ユーザーテスト／ユーザーインタビューの準備について」と表示されている写真が並列表記されている"></p>
<p>この勉強会以降、具体のユーザーインタビューやカスタマージャーニーマップなど、ユーザーニーズを掘り下げるような具体手法についての相談が上がってくる、という変化が起きました。最近は半月に1件くらいのペースで相談をいただいています。</p>
<h2>今後の我々の活動について</h2>
<p>現在、この取り組みは私ともう一人、クリエイティブGの大金とで行っています。暫定的に、ユーザーファースト推進活動の社内プロモーションや色々なプロセスの型化／横展開を行う守谷と、プロジェクト内に入ってHCDプロセスを実行していく大金と、という役割分担にしてはいますがもちろん手が足りてはいません。</p>
<p>前述のように、具体のプロセスを実行できる仲間を増やしたり、近い領域の人たちに声を掛け協力を仰いで、この活動を止めない事が最重要ではないかと考えています。また、今回こういった活動を改めて発表をおこなったことで、これまで各領域で埋もれていた活動（＝いわゆるサイロ化現象が起きていた）なども、情報として表面化するようになってきました。</p>
<p>冒頭紹介の通り「2025年の」注力テーマのひとつではありますが、ユーザーのことを考えるのはプロダクトデザインの基礎の基礎。せっかくのこの取り組みの火を絶やさないよう、来年以降も研鑽をし続けようと考えています。</p>
<p>最後に。12月23日には<a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a>にて、ともにこの活動を行う大金から彼女自身の挑戦についての記事が公開されま <del>す。お楽しみに！</del> した。併せてぜひ、ご覧ください！</p>
<p><a href="https://blog.kinto-technologies.com/posts/2025-12-23_hcd-expert/">https://blog.kinto-technologies.com/posts/2025-12-23_hcd-expert/</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/emim/2025-12-11-userfirst2025/2025-12-11-userfirst2025_title.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Stop Console Clicking: Managing Multi-Environment AWS Parameter Store with YAML and GitHub Actions]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-10-aws-parameter-store-automation-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-10-aws-parameter-store-automation-en/</guid>
            <pubDate>Wed, 10 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Is managing AWS Parameter Store across multiple environments becoming a hassle? We built a system that visualizes all parameters in YAML and syncs them with one click using GitHub Actions. Solve your Parameter Store management challenges as environments multiply.]]></description>
            <content:encoded><![CDATA[<blockquote>
<p><strong>Note:</strong> This article was translated from Japanese with the assistance of AI.</p>
</blockquote>
<p>This article is the Day 10 entry of the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a>🎅🎄</p>
<h2>Table of Contents</h2>
<p><a href="#introduction">Introduction</a>
<a href="#summary">Summary</a>
<a href="#what-we-built">What We Built</a>
<a href="#features">Features</a>
<a href="#prerequisites">Prerequisites</a>
<a href="#operational-best-practices">Operational Best Practices</a>
<a href="#architecture">Architecture</a>
<a href="#implementation-details">Implementation Details</a>
<a href="#usage">Usage</a>
<a href="#results">Results</a>
<a href="#conclusion">Conclusion</a></p>
<h2>Introduction</h2>
<p>Hi, I&#39;m Miyashita, an engineer in the Common Services Development Group at KINTO Technologies.</p>
<p>Modern development environments using AWS and similar platforms are easy to scale up and down, but this flexibility often leads to environment sprawl. Our development setup has grown to include dev, dev2, stg, stg2...stg5, ins, prod, and more.</p>
<p>As a result, the values managed in <a href="https://docs.aws.amazon.com/systems-manager/latest/userguide/systems-manager-parameter-store.html">AWS Systems Manager Parameter Store</a> have multiplied (number of environments × number of parameters), leading to frequent mistakes.</p>
<p>Here are some examples of errors we encountered:</p>
<ul>
<li>Updated Parameter Store for dev but forgot to update stg</li>
<li>KEY and VALUE were correct, but forgot to add tags</li>
<li>Deployment failed because we forgot to register a newly required parameter</li>
<li>When manually copying from dev to stg, accidentally copied values that should have been environment-specific</li>
</ul>
<p>Additionally, it was difficult to know the current state of Parameter Store values. Checking each environment through the browser was tedious and often got postponed, creating a vicious cycle of more careless mistakes.</p>
<p>To solve this, we built an automation system based on the principle of &quot;consolidating all environment parameters in a local YAML file and syncing that file with AWS.&quot; This article introduces that approach.</p>
<p>:::message
For simplicity, this article uses 3 representative environments: <code>dev / stg / prod</code>.
In practice, we have many more environments including dev2, stg2-stg5, inte, ins, prod, etc.
:::</p>
<h2>Summary</h2>
<ul>
<li>Automated AWS Parameter Store management for 10+ environments × 50+ parameters because manual management was too cumbersome</li>
<li>Visualize all parameters in YAML + one-click sync with GitHub Actions</li>
<li>Eliminated update oversights, careless mistakes, and tedious manual copy-paste work</li>
</ul>
<h2>What We Built</h2>
<p>We built this system combining these three components:</p>
<ol>
<li><strong>YAML file</strong> - Centralized management of Parameter Store values for all environments</li>
<li><strong>Python scripts</strong> - Sync processing between YAML and AWS</li>
<li><strong>GitHub Actions</strong> - One-click deployment to all environments</li>
</ol>
<h2>Features</h2>
<h3>1. YAML Visualization</h3>
<p>Previously, checking Parameter Store values for each environment required logging into the AWS console multiple times, switching accounts for each environment.</p>
<p>Now, opening a single YAML file in the repository shows all parameters for all environments at a glance.</p>
<pre><code class="language-yaml">parameters:
  # Parameters with different values per environment
  - key: api/endpoint
    description: &quot;API endpoint&quot;
    environment_values:
      dev:  &quot;https://dev-api.example.com&quot;
      stg:  &quot;https://stg-api.example.com&quot;
      prod: &quot;https://prod-api.example.com&quot;

  # Common value across all environments
  - key: app/timeout
    description: &quot;Timeout setting (seconds)&quot;
    default_value: &quot;30&quot;

  # Sensitive information (SecureString)
  # Value retrieved from GitHub Secrets via environment variables (e.g., SSM__DB__PASSWORD)
  - key: db/password
    description: &quot;Database password&quot;
    type: &quot;SecureString&quot;
</code></pre>
<p><strong>Benefits:</strong></p>
<ul>
<li>See at a glance which value is used in which environment</li>
<li>Git-managed, so change history is trackable</li>
<li>PR reviews can catch value errors before deployment</li>
</ul>
<h3>2. One-Click Sync</h3>
<p>Simply manually trigger the <a href="https://github.com/features/actions">GitHub Actions</a> workflow, and all environments&#39; Parameter Stores automatically sync with the YAML contents.</p>
<p>Using <a href="https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs">matrix strategy</a>, multiple environments (dev, stg, prod) run in parallel, so execution time stays nearly constant even as environments increase.</p>
<p>No more clicking around the AWS console switching between environments.</p>
<h2>Prerequisites</h2>
<p>This system works under the following conditions:</p>
<ul>
<li><a href="https://aws.amazon.com/cli/">AWS CLI</a> available on GitHub Actions runners</li>
<li>Permissions for Parameter Store: <code>ssm:GetParametersByPath</code>, <code>ssm:PutParameter</code>, <code>ssm:AddTagsToResource</code></li>
<li>GitHub Actions can access AWS (via Access Key or <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_oidc.html">OIDC</a>)</li>
<li>Python 3.11 + <code>pyyaml</code> available</li>
</ul>
<p>:::message
This article uses IAM user access keys for simplicity. For production use, we recommend <a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services">OIDC (OpenID Connect)</a> for keyless authentication to improve security.
:::</p>
<h2>Operational Best Practices</h2>
<p>While automation brings convenience, we must also consider the risk of mistakes. Our team implements these practices:</p>
<h3>Restrict SecureString Operation Permissions</h3>
<p>We limit who can edit GitHub Secrets, minimizing the number of people who can handle sensitive information.</p>
<h3>Separate Workflow with Approval for Production</h3>
<p>When updating Parameter Store for production, we use a dedicated production workflow that includes a Slack approval step. The approval request displays the list of parameters to be updated, so reviewers can confirm &quot;what will change&quot; before approving. This prevents accidental production updates.</p>
<h2>Architecture</h2>
<pre><code class="language-mermaid">graph TB
    YAML[&quot;aws-params.yml&quot;]
    Secrets[&quot;GitHub Secrets (per env)&quot;]

    subgraph GHA[&quot;GitHub Actions&quot;]
        Workflow[&quot;3 environments in parallel&quot;]
    end

    subgraph Scripts[&quot;Python&quot;]
        Update[&quot;update_aws_params.py&quot;]
    end

    subgraph AWS[&quot;AWS Parameter Store&quot;]
        dev[&quot;/dev/app/config/&quot;]
        stg[&quot;/stg/app/config/&quot;]
        prod[&quot;/prod/app/config/&quot;]
    end

    YAML --&gt; Workflow
    Secrets --&gt; Workflow
    Workflow --&gt; Update
    Update --&gt; dev
    Update --&gt; stg
    Update --&gt; prod

    style YAML fill:#e1f5ff
    style Secrets fill:#fff0f0
    style GHA fill:#fff4e1
    style Scripts fill:#f0ffe1
    style AWS fill:#ffe1e1
</code></pre>
<h2>Implementation Details</h2>
<h3>Directory Structure</h3>
<pre><code class="language-bash">$ tree .github
.github
├── aws-params.yml
├── scripts
│   ├── aws_param_common.py
│   └── update_aws_params.py
└── workflows
    └── sync-parameters.yml
</code></pre>
<h3>YAML Design</h3>
<p>All environment parameters are consolidated in <code>.github/aws-params.yml</code> (see the &quot;Features&quot; section for YAML examples).</p>
<h4>Handling SecureString</h4>
<p>Hardcoding sensitive information like DB passwords in YAML is a security risk.
Instead, we separated responsibilities: <strong>&quot;YAML contains only key definitions&quot;</strong> while <strong>&quot;actual values are in GitHub Secrets&quot;</strong>.</p>
<p>The Python script checks the YAML definition, and if <code>type: SecureString</code>, it reads from the corresponding environment variable.</p>
<p><strong>Naming Convention:</strong></p>
<pre><code class="language-text">YAML key: db/password
→ Environment variable: SSM__DB__PASSWORD
</code></pre>
<h4>Separating SecureString Values by Environment</h4>
<p>DB passwords often differ by environment. Using GitHub Actions <a href="https://docs.github.com/en/actions/deployment/targeting-different-environments/using-environments-for-deployment">Environments</a> feature, you can set different Secrets for each environment.</p>
<p><strong>Setup Steps:</strong></p>
<ol>
<li>Create environments in GitHub repository Settings → Environments (<code>dev</code>, <code>stg</code>, <code>prod</code>)</li>
<li>Register <code>SSM__DB__PASSWORD</code> etc. in each environment&#39;s Secrets (different values per environment)</li>
<li>Specify <code>environment: ${{ matrix.env }}</code> in the workflow</li>
</ol>
<p>This way, the dev environment automatically uses dev&#39;s DB password, and stg uses stg&#39;s.</p>
<p><img src="/assets/blog/authors/miyashita/actions_secrets_variables.png" alt="GitHub Actions Environment Variables">
<img src="/assets/blog/authors/miyashita/github_env_dev.png" alt="GitHub Actions dev Environment Variables"></p>
<h4>Note: Parameter Store vs Secrets Manager</h4>
<p>You might think &quot;Shouldn&#39;t sensitive information go in <a href="https://docs.aws.amazon.com/secretsmanager/latest/userguide/intro.html">Secrets Manager</a>?&quot; Here&#39;s a comparison:</p>
<table>
<thead>
<tr>
<th></th>
<th>Parameter Store (SecureString)</th>
<th>Secrets Manager</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Cost</strong></td>
<td>Standard parameters are free</td>
<td>$0.40/secret/month</td>
</tr>
<tr>
<td><strong>Rotation</strong></td>
<td>Manual</td>
<td>Automatic rotation available</td>
</tr>
<tr>
<td><strong>Best for</strong></td>
<td>API keys with low update frequency</td>
<td>When automatic DB password rotation is needed</td>
</tr>
</tbody></table>
<p>In most cases, Parameter Store (SecureString) is sufficient. Consider Secrets Manager when you need &quot;automatic RDS password rotation.&quot;</p>
<p>:::message
<strong>Note: You can manage Secrets Manager values in a similar way.</strong></p>
<pre><code class="language-python">cmd = [
    &quot;aws&quot;, &quot;secretsmanager&quot;, &quot;put-secret-value&quot;,
    &quot;--secret-id&quot;, secret_id,
    &quot;--secret-string&quot;, value
]
subprocess.run(cmd, check=True)
</code></pre>
<p>:::</p>
<h3>Python Script Structure</h3>
<h4><code>aws_param_common.py</code> - Common Functions</h4>
<pre><code class="language-python">#!/usr/bin/env python3
&quot;&quot;&quot;AWS Parameter Store Common Functions&quot;&quot;&quot;

import os
import sys
import json
import subprocess
from typing import Dict, Any, Tuple
import yaml


def get_env_name() -&gt; str:
    &quot;&quot;&quot;Get environment name&quot;&quot;&quot;
    env = os.environ.get(&quot;ENV_NAME&quot;)
    if not env:
        print(&quot;Error: ENV_NAME environment variable is not set&quot;)
        sys.exit(1)
    return env


def get_prefix(env: str) -&gt; str:
    &quot;&quot;&quot;Return prefix based on environment&quot;&quot;&quot;
    return f&quot;/{env}/app/config/&quot;


def load_yaml_config() -&gt; Tuple[Dict[str, Any], set]:
    &quot;&quot;&quot;Load YAML file&quot;&quot;&quot;
    yaml_path = os.path.join(os.path.dirname(__file__), &quot;..&quot;, &quot;aws-params.yml&quot;)
    with open(yaml_path, &quot;r&quot;, encoding=&quot;utf-8&quot;) as f:
        config = yaml.safe_load(f)
    yaml_keys = {param[&quot;key&quot;] for param in config.get(&quot;parameters&quot;, [])}
    return config, yaml_keys


def get_existing_params(env: str) -&gt; Dict[str, Dict[str, Any]]:
    &quot;&quot;&quot;Get existing parameters from AWS SSM (with pagination support)&quot;&quot;&quot;
    prefix = get_prefix(env)
    existing_params = {}
    next_token = None

    while True:
        cmd = [
            &quot;aws&quot;, &quot;ssm&quot;, &quot;get-parameters-by-path&quot;,
            &quot;--path&quot;, prefix,
            &quot;--recursive&quot;,
            &quot;--with-decryption&quot;,
            &quot;--output&quot;, &quot;json&quot;
        ]
        if next_token:
            cmd.extend([&quot;--next-token&quot;, next_token])

        try:
            result = subprocess.run(cmd, check=True, capture_output=True, text=True)
            data = json.loads(result.stdout)
            params_data = data.get(&quot;Parameters&quot;, [])
        except subprocess.CalledProcessError as e:
            print(f&quot;Warning: Failed to get parameters: {e.stderr}&quot;)
            return {}

        for param in params_data:
            key = param[&quot;Name&quot;].replace(prefix, &quot;&quot;)
            existing_params[key] = {
                &quot;value&quot;: param[&quot;Value&quot;],
                &quot;type&quot;: param[&quot;Type&quot;],
                &quot;version&quot;: param.get(&quot;Version&quot;, 1)
            }

        next_token = data.get(&quot;NextToken&quot;)
        if not next_token:
            break

    return existing_params


def get_param_value(param: Dict[str, Any], env: str) -&gt; str | None:
    &quot;&quot;&quot;Get parameter value (SecureString from env vars, others from YAML)&quot;&quot;&quot;
    # For SecureString, get from environment variable
    if param.get(&quot;type&quot;) == &quot;SecureString&quot;:
        env_var_name = &quot;SSM__&quot; + param[&quot;key&quot;].upper().replace(&quot;/&quot;, &quot;__&quot;)
        value = os.environ.get(env_var_name)
        if not value:
            print(f&quot;Warning: Environment variable {env_var_name} for SecureString {param[&#39;key&#39;]} is not set&quot;)
            return None
        return value

    # Environment-specific value
    env_values = param.get(&quot;environment_values&quot;, {})
    if env in env_values:
        return str(env_values[env])

    # Default value
    if &quot;default_value&quot; in param:
        return str(param[&quot;default_value&quot;])
    return None


def validate_param(param: Dict[str, Any], env: str) -&gt; Tuple[bool, str, Dict[str, Any] | None]:
    &quot;&quot;&quot;Validate parameter&quot;&quot;&quot;
    key = param.get(&quot;key&quot;)
    if not key:
        return False, &quot;key is not defined&quot;, None

    value = get_param_value(param, env)
    if value is None:
        return False, f&quot;{key}: value for environment {env} is not defined&quot;, None

    param_info = {
        &quot;key&quot;: key,
        &quot;value&quot;: value,
        &quot;type&quot;: param.get(&quot;type&quot;, &quot;String&quot;),
        &quot;description&quot;: param.get(&quot;description&quot;, &quot;&quot;)
    }
    return True, &quot;&quot;, param_info


def update_parameter(param_info: Dict[str, Any], env: str) -&gt; bool:
    &quot;&quot;&quot;Update parameter&quot;&quot;&quot;
    prefix = get_prefix(env)
    full_name = prefix + param_info[&quot;key&quot;]

    cmd = [
        &quot;aws&quot;, &quot;ssm&quot;, &quot;put-parameter&quot;,
        &quot;--name&quot;, full_name,
        &quot;--value&quot;, param_info[&quot;value&quot;],
        &quot;--type&quot;, param_info[&quot;type&quot;],
        &quot;--overwrite&quot;
    ]
    if param_info.get(&quot;description&quot;):
        cmd.extend([&quot;--description&quot;, param_info[&quot;description&quot;]])

    try:
        subprocess.run(cmd, check=True, capture_output=True, text=True)
        add_tags(full_name, env)  # Add tags
        return True
    except subprocess.CalledProcessError as e:
        print(f&quot;Error: Failed to update {param_info[&#39;key&#39;]}: {e.stderr}&quot;)
        return False


def add_tags(parameter_name: str, env: str) -&gt; bool:
    &quot;&quot;&quot;Add tags to parameter&quot;&quot;&quot;
    cmd = [
        &quot;aws&quot;, &quot;ssm&quot;, &quot;add-tags-to-resource&quot;,
        &quot;--resource-type&quot;, &quot;Parameter&quot;,
        &quot;--resource-id&quot;, parameter_name,
        &quot;--tags&quot;,
        f&quot;Key=Environment,Value={env}&quot;,
        &quot;Key=SID,Value=backend-api&quot;
    ]
    try:
        subprocess.run(cmd, check=True, capture_output=True, text=True)
        return True
    except subprocess.CalledProcessError as e:
        print(f&quot;Warning: Failed to add tags: {e.stderr}&quot;)
        return False
</code></pre>
<p><strong>Key Points:</strong></p>
<ul>
<li><code>get_existing_params</code>: Pagination support for 50+ parameters</li>
<li><code>get_param_value</code>: SecureString from environment variables, regular parameters from YAML</li>
<li><code>update_parameter</code>: Calls <code>add_tags</code> after parameter update to apply tags</li>
</ul>
<h4>About Tags</h4>
<p>Tags are automatically applied when creating parameters. Tags are useful for searching in the AWS console and cost management, and some systems require tags to read parameters.</p>
<table>
<thead>
<tr>
<th>Tag</th>
<th>Value</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><code>Environment</code></td>
<td><code>dev</code>, <code>stg</code>, <code>prod</code></td>
<td>Environment name automatically set at runtime</td>
</tr>
<tr>
<td><code>SID</code></td>
<td><code>backend-api</code></td>
<td>Service identifier (replace with your service name)</td>
</tr>
</tbody></table>
<h4><code>update_aws_params.py</code> - Update Script</h4>
<pre><code class="language-python">#!/usr/bin/env python3
&quot;&quot;&quot;AWS Parameter Store Update Script&quot;&quot;&quot;

import sys
import aws_param_common as common


def update_parameters():
    &quot;&quot;&quot;Update parameters and report results&quot;&quot;&quot;
    env = common.get_env_name()
    print(f&quot;=== Environment: {env} ===&quot;)
    print(f&quot;Prefix: {common.get_prefix(env)}&quot;)
    print()

    config, yaml_keys = common.load_yaml_config()
    existing_params = common.get_existing_params(env)
    print(f&quot;Existing parameters: {len(existing_params)}&quot;)
    print()

    updated_params = []
    skipped_params = []
    failed_params = []

    for param in config.get(&quot;parameters&quot;, []):
        is_valid, error_msg, param_info = common.validate_param(param, env)

        if not is_valid:
            print(f&quot;[Skip] {error_msg}&quot;)
            continue

        param_key = param_info[&quot;key&quot;]
        value = param_info[&quot;value&quot;]

        # Compare with existing value
        if param_key in existing_params:
            if existing_params[param_key][&quot;value&quot;] == value:
                print(f&quot;[Skip] {param_key}: no change&quot;)
                skipped_params.append(param_key)
                continue
            print(f&quot;[Update] {param_key}: updating value&quot;)
        else:
            print(f&quot;[New] {param_key}: adding new parameter&quot;)

        # Update parameter
        success = common.update_parameter(param_info, env)
        if success:
            updated_params.append(param_key)
            print(f&quot;  ✓ Done&quot;)
        else:
            failed_params.append(param_key)
            print(f&quot;  ✗ Failed&quot;)

    # Result summary
    print()
    print(&quot;=== Summary ===&quot;)
    print(f&quot;Updated: {len(updated_params)}&quot;)
    print(f&quot;Skipped (no change): {len(skipped_params)}&quot;)
    print(f&quot;Failed: {len(failed_params)}&quot;)

    if failed_params:
        print()
        print(&quot;Failed parameters:&quot;)
        for key in failed_params:
            print(f&quot;  - {key}&quot;)
        sys.exit(1)

    print()
    print(&quot;✓ Completed successfully&quot;)


if __name__ == &quot;__main__&quot;:
    update_parameters()
</code></pre>
<p><strong>Key Points:</strong></p>
<ul>
<li>Skip parameters with unchanged values (prevent unnecessary updates)</li>
<li>Output statistics as summary</li>
<li>Exit with code 1 on failure</li>
</ul>
<h3>GitHub Actions Workflow</h3>
<pre><code class="language-yaml">name: Sync AWS Parameter Store

on:
  workflow_dispatch:  # Manual trigger

jobs:
  sync-parameters:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        env: [dev, stg, prod]
    environment: ${{ matrix.env }}  # Use environment-specific Secrets

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: &#39;3.11&#39;

      - name: Install dependencies
        run: pip install pyyaml

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Sync Parameters
        env:
          ENV_NAME: ${{ matrix.env }}
          # For SecureString (from environment-specific GitHub Secrets)
          SSM__DB__PASSWORD: ${{ secrets.SSM__DB__PASSWORD }}
          SSM__API__SECRET_KEY: ${{ secrets.SSM__API__SECRET_KEY }}
        run: |
          cd .github/scripts
          python update_aws_params.py
</code></pre>
<p><strong>Key Points:</strong></p>
<ul>
<li><code>strategy.matrix</code> runs multiple environments in parallel</li>
<li><code>environment: ${{ matrix.env }}</code> uses environment-specific Secrets (different DB passwords for dev and prod)</li>
<li>SecureString values passed to script via environment variables</li>
<li>Parameters with unchanged values are automatically skipped</li>
</ul>
<h2>Usage</h2>
<h3>1. Adding/Changing Parameters</h3>
<p>Just edit <code>.github/aws-params.yml</code> and submit a PR.</p>
<pre><code class="language-yaml">parameters:
  # Add new parameter
  - key: feature/enable_payment_v2
    description: &quot;Enable new payment system&quot;
    environment_values:
      dev:  &quot;true&quot;
      stg:  &quot;false&quot;
      prod:  &quot;false&quot;
</code></pre>
<h3>2. Deploying to All Environments</h3>
<ol>
<li>Open GitHub Actions page</li>
<li>Select <code>Sync AWS Parameter Store</code></li>
<li>Click <code>Run workflow</code> button</li>
<li>Deploys to all environments in parallel</li>
</ol>
<h3>3. Adding SecureString Parameters</h3>
<ol>
<li>Add definition to YAML:</li>
</ol>
<pre><code class="language-yaml">- key: payment/api_key
  description: &quot;Payment API key&quot;
  type: &quot;SecureString&quot;
</code></pre>
<ol start="2">
<li>Register value in GitHub Environments:</li>
</ol>
<p>Settings → Environments → Register in each environment&#39;s Secrets (dev, stg, prod)</p>
<pre><code class="language-text">Secret name: SSM__PAYMENT__API_KEY
Value: (actual value, different per environment)
</code></pre>
<ol start="3">
<li>Add to workflow file&#39;s environment variables section:</li>
</ol>
<pre><code class="language-yaml">env:
  SSM__PAYMENT__API_KEY: ${{ secrets.SSM__PAYMENT__API_KEY }}
</code></pre>
<h3>4. Demo</h3>
<ol>
<li><p>Select and run the action we created from the GitHub Actions page.
<img src="/assets/blog/authors/miyashita/github_actions_choice.png" alt="Selecting and Running GitHub Actions"></p>
</li>
<li><p>Confirm the action completed successfully. You can see each environment ran in parallel.
<img src="/assets/blog/authors/miyashita/github_actions_result.png" alt="GitHub Actions Results"></p>
</li>
<li><p>Open the AWS Parameter Store console to verify. The parameters are registered. Success!
<img src="/assets/blog/authors/miyashita/aws_my_param.png" alt="Parameter Store Registration Results"></p>
</li>
</ol>
<h2>Results</h2>
<h3>Concrete Results</h3>
<ul>
<li><strong>Work time</strong>: Environments × 5 min → <strong>One click</strong> (50 min saved for 10 environments)</li>
<li><strong>Update oversights</strong>: Several per month → <strong>Zero</strong></li>
<li><strong>Verification</strong>: Open AWS console → <strong>Just look at YAML</strong></li>
</ul>
<h2>Conclusion</h2>
<p>The &quot;too many environments&quot; problem in the cloud era is a challenge many teams face.</p>
<p>The key points of this approach:</p>
<ol>
<li><strong>YAML visualization</strong> - Manage all environment parameters in one file</li>
<li><strong>One-click sync</strong> - Auto-deploy with GitHub Actions</li>
<li><strong>SecureString support</strong> - Securely manage sensitive information</li>
</ol>
<p>No special technology required—just <strong>GitHub Actions + Python + AWS CLI</strong>.</p>
<p>I hope this helps those struggling with Parameter Store management or dealing with environment sprawl.</p>
<p>Thank you for reading!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/miyashita/ssm_blog_title.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[【脱コンソールポチポチ】多環境AWS Parameter StoreをYAML×GitHub Actionsで一元管理する仕組みを作った]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-10-aws-parameter-store-automation/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-10-aws-parameter-store-automation/</guid>
            <pubDate>Wed, 10 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[複数環境のAWS Parameter Store管理が煩雑になっていませんか？YAMLで全環境のパラメータを可視化し、GitHub Actionsでワンクリック同期する仕組みを構築しました。環境増加に伴うParameter Store管理の課題を解決します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の10日目の記事です🎅🎄</p>
<h2>目次</h2>
<p><a href="#%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">はじめに</a>
<a href="#%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%AE%E8%A6%81%E7%B4%84">この記事の要約</a>
<a href="#%E4%BD%9C%E3%81%A3%E3%81%9F%E3%82%82%E3%81%AE">作ったもの</a>
<a href="#%E7%89%B9%E5%BE%B4">特徴</a>
<a href="#%E5%89%8D%E6%8F%90%E6%9D%A1%E4%BB%B6">前提条件</a>
<a href="#%E9%81%8B%E7%94%A8%E4%B8%8A%E3%81%AE%E5%B7%A5%E5%A4%AB">運用上の工夫</a>
<a href="#%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3">アーキテクチャ</a>
<a href="#%E5%AE%9F%E8%A3%85%E3%81%AE%E3%83%9D%E3%82%A4%E3%83%B3%E3%83%88">実装のポイント</a>
<a href="#%E4%BD%BF%E3%81%84%E6%96%B9">使い方</a>
<a href="#%E5%B0%8E%E5%85%A5%E5%8A%B9%E6%9E%9C">導入効果</a>
<a href="#%E3%81%BE%E3%81%A8%E3%82%81">まとめ</a></p>
<h2>はじめに</h2>
<p>こんにちは。KINTOテクノロジーズの共通サービス開発グループのエンジニア、宮下です。</p>
<p>AWSなどを使う現在の開発環境は、簡単に増やしたり減らしたりできる反面、環境の数が増えていきがちです。
我々の開発環境も、dev、dev2、stg、stg2...stg5、ins、prodのように、とても多くなってしまっています。</p>
<p>そのため、<a href="https://docs.aws.amazon.com/ja_jp/systems-manager/latest/userguide/systems-manager-parameter-store.html">AWS Systems Manager Parameter Store</a>で管理する値も、環境数 × パラメータ数の組み合わせでどんどん増えていき、ケアレスミスが多発していました。</p>
<p>例えば以下のようなミスです。</p>
<ul>
<li>devのParameter Storeは最新化したけれど、stgのパラメータを更新し忘れていた</li>
<li>KEY, VALUEは正しいけれど、タグを入れ忘れていた</li>
<li>デプロイに失敗するので調査したら、新規追加のパラメータを登録し忘れていたのが原因だった</li>
<li>devからstgへ手作業でコピペする際、環境ごとに変えるべき値をそのままコピペしてしまった</li>
</ul>
<p>また、現在のParameter Storeの値がどうなっているかも分かりづらく、ブラウザでいちいち各環境に入って確認するのが面倒で、つい後回しになりがちでした。その結果、よりケアレスミスが起きる悪循環に陥っていました。</p>
<p>そこで、「ローカルのYAMLファイルに各環境のパラメータを集約し、そのファイルとAWSを同期する」という方針で自動化の仕組みを作りました。今回はそのアイデアを紹介します。</p>
<p>:::message
本記事では説明を簡潔にするために、代表として <code>dev / stg / prod</code> の3環境を例に説明します。
実際の現場ではこの他に dev2, stg2〜stg5, inte, ins, prodなど、さらに多くの環境があります。
:::</p>
<h2>この記事の要約</h2>
<ul>
<li>10環境以上 × 50以上のパラメータ のAWS Parameter Store管理が煩雑すぎたので自動化した</li>
<li>YAMLで全パラメータを可視化 + GitHub Actionsでワンクリック同期</li>
<li>更新漏れやケアレスミスをゼロにし、面倒くさい機械的なコピペ作業を無くした</li>
</ul>
<h2>作ったもの</h2>
<p>以下の3つを組み合わせてこの仕組みを構築しました：</p>
<ol>
<li><strong>YAMLファイル</strong> - 全環境のParameter Storeの値を一元管理</li>
<li><strong>Pythonスクリプト</strong> - YAMLとAWS間の同期処理</li>
<li><strong>GitHub Actions</strong> - ワンクリックで全環境に反映</li>
</ol>
<h2>特徴</h2>
<h3>1. YAML可視化</h3>
<p>従来は各環境のParameter Storeの値を確認するために、AWSコンソールに各環境ごとにアカウントを切り替えて何度もログインする必要がありました。</p>
<p>今は、リポジトリにある1つのYAMLファイルを開けば、全環境の全パラメータが一覧できます。</p>
<pre><code class="language-yaml">parameters:
  # 環境ごとに値が異なるパラメータ
  - key: api/endpoint
    description: &quot;APIエンドポイント&quot;
    environment_values:
      dev:  &quot;https://dev-api.example.com&quot;
      stg:  &quot;https://stg-api.example.com&quot;
      prod: &quot;https://prod-api.example.com&quot;

  # 全環境で共通の値
  - key: app/timeout
    description: &quot;タイムアウト設定（秒）&quot;
    default_value: &quot;30&quot;

  # 機密情報（SecureString）
  # 値はGitHub Secretsから環境変数経由で取得（例: SSM__DB__PASSWORD）
  - key: db/password
    description: &quot;データベースパスワード&quot;
    type: &quot;SecureString&quot;
</code></pre>
<p><strong>メリット:</strong></p>
<ul>
<li>どの環境でどの値を使っているか一目瞭然</li>
<li>Git管理できるので変更履歴も追える</li>
<li>PRレビューで値の間違いを事前に防げる</li>
</ul>
<h3>2. ワンクリック同期</h3>
<p><a href="https://github.co.jp/features/actions">GitHub Actions</a>のワークフローを手動実行するだけで、全環境のParameter Storeが自動的にYAMLの内容と同期されます。</p>
<p><a href="https://docs.github.com/en/actions/using-jobs/using-a-matrix-for-your-jobs">マトリクス・ストラテジー</a>により、複数環境（dev, stg, prod）が並列で実行されるため、環境が増えても実行時間はほぼ変わりません。</p>
<p>AWSコンソールで環境を切り替えながらポチポチする必要がなくなりました。</p>
<h2>前提条件</h2>
<p>この仕組みは以下の前提で動作します。</p>
<ul>
<li><a href="https://aws.amazon.com/jp/cli/">AWS CLI</a>がGitHub Actionsランナー上で利用できること</li>
<li>Parameter Storeへの <code>ssm:GetParametersByPath</code>, <code>ssm:PutParameter</code>, <code>ssm:AddTagsToResource</code> 権限があること</li>
<li>GitHub ActionsからAWSにアクセスできること（Access Keyもしくは <a href="https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_roles_providers_oidc.html">OIDC</a>など）</li>
<li>Python 3.11 + <code>pyyaml</code> が利用できること</li>
</ul>
<p>:::message
本記事では簡略化のためにIAMユーザーのアクセスキーを使用していますが、本番運用ではセキュリティ向上のため <a href="https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services">OIDC (OpenID Connect)</a> を使用したキーレス認証を推奨します。
:::</p>
<h2>運用上の工夫</h2>
<p>自動化で便利になる一方、誤操作のリスクも考慮が必要です。我々のチームでは以下の工夫をしています。</p>
<h3>SecureStringの操作権限を絞る</h3>
<p>GitHub Secretsを編集できる人を限定し、機密情報を扱える人を最小限にしています。</p>
<h3>本番環境への反映はワークフローを分けて承認制に</h3>
<p>本番環境のParameter Store更新時は、本番環境専用のワークフローを使い、ワークフローの中でSlackで承認ステップを挟む運用にしています。承認依頼時には更新対象のパラメータ一覧がSlackに表示されるため、「何が変わるのか」を確認してから承認できます。これにより、うっかり本番を更新してしまう事故を防いでいます。</p>
<h2>アーキテクチャ</h2>
<pre><code class="language-mermaid">graph TB
    YAML[&quot;aws-params.yml&quot;]
    Secrets[&quot;GitHub Secrets (per env)&quot;]

    subgraph GHA[&quot;GitHub Actions&quot;]
        Workflow[&quot;3環境並列実行&quot;]
    end

    subgraph Scripts[&quot;Python&quot;]
        Update[&quot;update_aws_params.py&quot;]
    end

    subgraph AWS[&quot;AWS Parameter Store&quot;]
        dev[&quot;/dev/app/config/&quot;]
        stg[&quot;/stg/app/config/&quot;]
        prod[&quot;/prod/app/config/&quot;]
    end

    YAML --&gt; Workflow
    Secrets --&gt; Workflow
    Workflow --&gt; Update
    Update --&gt; dev
    Update --&gt; stg
    Update --&gt; prod

    style YAML fill:#e1f5ff
    style Secrets fill:#fff0f0
    style GHA fill:#fff4e1
    style Scripts fill:#f0ffe1
    style AWS fill:#ffe1e1
</code></pre>
<h2>実装のポイント</h2>
<h3>ディレクトリ構成</h3>
<pre><code class="language-bash">$ tree .github
.github
├── aws-params.yml
├── scripts
│   ├── aws_param_common.py
│   └── update_aws_params.py
└── workflows
    └── sync-parameters.yml
</code></pre>
<h3>YAML設計</h3>
<p>全環境のパラメータを <code>.github/aws-params.yml</code> に集約しています（YAMLの例は「特徴」セクションを参照）。</p>
<h4>SecureStringの扱い</h4>
<p>DBのパスワードなどの機密情報をYAMLにベタ書きするのはセキュリティ上NGです。
そこで、<strong>「YAMLにはキーの定義のみ」「実体（値）はGitHub Secrets」</strong> という役割分担を行いました。</p>
<p>Pythonスクリプト側で、YAMLの定義を見て <code>type: SecureString</code> ならば、対応する環境変数を読みに行く設計にしています。</p>
<p><strong>命名規則:</strong></p>
<pre><code class="language-text">YAMLのkey: db/password
→ 環境変数名: SSM__DB__PASSWORD
</code></pre>
<h4>環境ごとにSecureStringの値を分ける</h4>
<p>DBパスワードなどは環境ごとに異なる値を使うことが多いです。GitHub Actionsの
<a href="https://docs.github.com/en/actions/how-tos/deploy/configure-and-manage-deployments/manage-environments">Environments</a> 機能を使えば、環境ごとに異なるSecretsを設定できます。</p>
<p><strong>設定手順:</strong></p>
<ol>
<li>GitHubリポジトリの Settings → Environments で環境を作成（<code>dev</code>, <code>stg</code>, <code>prod</code>）</li>
<li>各環境のSecretsに <code>SSM__DB__PASSWORD</code> などを登録（値は環境ごとに異なる）</li>
<li>ワークフローで <code>environment: ${{ matrix.env }}</code> を指定</li>
</ol>
<p>これにより、DEV環境ではDEV用のDBパスワード、STG環境ではSTG用のDBパスワードが自動的に使われます。</p>
<p><img src="/assets/blog/authors/miyashita/actions_secrets_variables.png" alt="Github_Actions_の環境変数">
<img src="/assets/blog/authors/miyashita/github_env_dev.png" alt="Github_Actions_devの環境変数"></p>
<h4>補足：Parameter Store vs Secrets Manager</h4>
<p>「機密情報なら<a href="https://docs.aws.amazon.com/ja_jp/secretsmanager/latest/userguide/intro.html">Secrets Manager</a>では？」と思う方もいるかもしれません。使い分けの目安は以下の通りです：</p>
<table>
<thead>
<tr>
<th></th>
<th>Parameter Store (SecureString)</th>
<th>Secrets Manager</th>
</tr>
</thead>
<tbody><tr>
<td><strong>料金</strong></td>
<td>標準パラメータは無料</td>
<td>$0.40/シークレット/月</td>
</tr>
<tr>
<td><strong>ローテーション</strong></td>
<td>手動</td>
<td>自動ローテーション可能</td>
</tr>
<tr>
<td><strong>向いているケース</strong></td>
<td>APIキーなど更新頻度が低いもの</td>
<td>DBパスワードの自動ローテーションが必要な場合</td>
</tr>
</tbody></table>
<p>多くのケースではParameter Store（SecureString）で十分で、Secrets Managerは「RDSパスワードの自動ローテーション」が必要な場合に検討してください。</p>
<p>:::message
<strong>補足：Secrets Managerの値も同様に管理できます。</strong></p>
<pre><code class="language-python">cmd = [
    &quot;aws&quot;, &quot;secretsmanager&quot;, &quot;put-secret-value&quot;,
    &quot;--secret-id&quot;, secret_id,
    &quot;--secret-string&quot;, value
]
subprocess.run(cmd, check=True)
</code></pre>
<p>:::</p>
<h3>Pythonスクリプト構成</h3>
<h4><code>aws_param_common.py</code> - 共通機能</h4>
<pre><code class="language-python">#!/usr/bin/env python3
&quot;&quot;&quot;AWS Parameter Store 共通処理&quot;&quot;&quot;

import os
import sys
import json
import subprocess
from typing import Dict, Any, Tuple
import yaml


def get_env_name() -&gt; str:
    &quot;&quot;&quot;環境名を取得&quot;&quot;&quot;
    env = os.environ.get(&quot;ENV_NAME&quot;)
    if not env:
        print(&quot;エラー: ENV_NAME 環境変数が設定されていません&quot;)
        sys.exit(1)
    return env


def get_prefix(env: str) -&gt; str:
    &quot;&quot;&quot;環境に応じたプレフィックスを返す&quot;&quot;&quot;
    return f&quot;/{env}/app/config/&quot;


def load_yaml_config() -&gt; Tuple[Dict[str, Any], set]:
    &quot;&quot;&quot;YAMLファイルを読み込む&quot;&quot;&quot;
    yaml_path = os.path.join(os.path.dirname(__file__), &quot;..&quot;, &quot;aws-params.yml&quot;)
    with open(yaml_path, &quot;r&quot;, encoding=&quot;utf-8&quot;) as f:
        config = yaml.safe_load(f)
    yaml_keys = {param[&quot;key&quot;] for param in config.get(&quot;parameters&quot;, [])}
    return config, yaml_keys


def get_existing_params(env: str) -&gt; Dict[str, Dict[str, Any]]:
    &quot;&quot;&quot;AWS SSMから既存のパラメータを取得（ページネーション対応）&quot;&quot;&quot;
    prefix = get_prefix(env)
    existing_params = {}
    next_token = None

    while True:
        cmd = [
            &quot;aws&quot;, &quot;ssm&quot;, &quot;get-parameters-by-path&quot;,
            &quot;--path&quot;, prefix,
            &quot;--recursive&quot;,
            &quot;--with-decryption&quot;,
            &quot;--output&quot;, &quot;json&quot;
        ]
        if next_token:
            cmd.extend([&quot;--next-token&quot;, next_token])

        try:
            result = subprocess.run(cmd, check=True, capture_output=True, text=True)
            data = json.loads(result.stdout)
            params_data = data.get(&quot;Parameters&quot;, [])
        except subprocess.CalledProcessError as e:
            print(f&quot;警告: パラメータの取得に失敗しました: {e.stderr}&quot;)
            return {}

        for param in params_data:
            key = param[&quot;Name&quot;].replace(prefix, &quot;&quot;)
            existing_params[key] = {
                &quot;value&quot;: param[&quot;Value&quot;],
                &quot;type&quot;: param[&quot;Type&quot;],
                &quot;version&quot;: param.get(&quot;Version&quot;, 1)
            }

        next_token = data.get(&quot;NextToken&quot;)
        if not next_token:
            break

    return existing_params


def get_param_value(param: Dict[str, Any], env: str) -&gt; str | None:
    &quot;&quot;&quot;パラメータの値を取得（SecureStringは環境変数、それ以外はYAMLの値を使用）&quot;&quot;&quot;
    # SecureStringの場合は環境変数から取得
    if param.get(&quot;type&quot;) == &quot;SecureString&quot;:
        env_var_name = &quot;SSM__&quot; + param[&quot;key&quot;].upper().replace(&quot;/&quot;, &quot;__&quot;)
        value = os.environ.get(env_var_name)
        if not value:
            print(f&quot;警告: SecureString {param[&#39;key&#39;]} の環境変数 {env_var_name} が未設定&quot;)
            return None
        return value

    # 環境固有の値
    env_values = param.get(&quot;environment_values&quot;, {})
    if env in env_values:
        return str(env_values[env])

    # デフォルト値
    if &quot;default_value&quot; in param:
        return str(param[&quot;default_value&quot;])
    return None


def validate_param(param: Dict[str, Any], env: str) -&gt; Tuple[bool, str, Dict[str, Any] | None]:
    &quot;&quot;&quot;パラメータのバリデーション&quot;&quot;&quot;
    key = param.get(&quot;key&quot;)
    if not key:
        return False, &quot;keyが定義されていません&quot;, None

    value = get_param_value(param, env)
    if value is None:
        return False, f&quot;{key}: 環境 {env} の値が定義されていません&quot;, None

    param_info = {
        &quot;key&quot;: key,
        &quot;value&quot;: value,
        &quot;type&quot;: param.get(&quot;type&quot;, &quot;String&quot;),
        &quot;description&quot;: param.get(&quot;description&quot;, &quot;&quot;)
    }
    return True, &quot;&quot;, param_info


def update_parameter(param_info: Dict[str, Any], env: str) -&gt; bool:
    &quot;&quot;&quot;パラメータを更新&quot;&quot;&quot;
    prefix = get_prefix(env)
    full_name = prefix + param_info[&quot;key&quot;]

    cmd = [
        &quot;aws&quot;, &quot;ssm&quot;, &quot;put-parameter&quot;,
        &quot;--name&quot;, full_name,
        &quot;--value&quot;, param_info[&quot;value&quot;],
        &quot;--type&quot;, param_info[&quot;type&quot;],
        &quot;--overwrite&quot;
    ]
    if param_info.get(&quot;description&quot;):
        cmd.extend([&quot;--description&quot;, param_info[&quot;description&quot;]])

    try:
        subprocess.run(cmd, check=True, capture_output=True, text=True)
        add_tags(full_name, env)  # タグを追加
        return True
    except subprocess.CalledProcessError as e:
        print(f&quot;エラー: {param_info[&#39;key&#39;]} の更新に失敗: {e.stderr}&quot;)
        return False


def add_tags(parameter_name: str, env: str) -&gt; bool:
    &quot;&quot;&quot;パラメータにタグを追加&quot;&quot;&quot;
    cmd = [
        &quot;aws&quot;, &quot;ssm&quot;, &quot;add-tags-to-resource&quot;,
        &quot;--resource-type&quot;, &quot;Parameter&quot;,
        &quot;--resource-id&quot;, parameter_name,
        &quot;--tags&quot;,
        f&quot;Key=Environment,Value={env}&quot;,
        &quot;Key=SID,Value=backend-api&quot;
    ]
    try:
        subprocess.run(cmd, check=True, capture_output=True, text=True)
        return True
    except subprocess.CalledProcessError as e:
        print(f&quot;警告: タグの追加に失敗: {e.stderr}&quot;)
        return False
</code></pre>
<p><strong>ポイント:</strong></p>
<ul>
<li><code>get_existing_params</code>: ページネーション対応で50件以上のパラメータも取得可能</li>
<li><code>get_param_value</code>: SecureStringは環境変数から、通常パラメータはYAMLから値を取得</li>
<li><code>update_parameter</code>: パラメータ更新後に <code>add_tags</code> を呼び出してタグを付与</li>
</ul>
<h4>タグについて</h4>
<p>パラメータ作成時に、自動でタグを付与します。タグはAWSコンソールでの検索やコスト管理に便利なだけでなく、システムによってはタグがないとパラメータを読み込めない場合もあります。</p>
<table>
<thead>
<tr>
<th>タグ</th>
<th>値</th>
<th>説明</th>
</tr>
</thead>
<tbody><tr>
<td><code>Environment</code></td>
<td><code>dev</code>, <code>stg</code>, <code>prod</code></td>
<td>実行時の環境名が自動で入る</td>
</tr>
<tr>
<td><code>SID</code></td>
<td><code>backend-api</code></td>
<td>サービス識別子（自分のサービス名に置き換えて使用）</td>
</tr>
</tbody></table>
<h4><code>update_aws_params.py</code> - 更新スクリプト</h4>
<pre><code class="language-python">#!/usr/bin/env python3
&quot;&quot;&quot;AWS Parameter Store 更新スクリプト&quot;&quot;&quot;

import sys
import aws_param_common as common


def update_parameters():
    &quot;&quot;&quot;パラメータを更新し、結果をレポートする&quot;&quot;&quot;
    env = common.get_env_name()
    print(f&quot;=== 環境: {env} ===&quot;)
    print(f&quot;プレフィックス: {common.get_prefix(env)}&quot;)
    print()

    config, yaml_keys = common.load_yaml_config()
    existing_params = common.get_existing_params(env)
    print(f&quot;既存パラメータ数: {len(existing_params)}&quot;)
    print()

    updated_params = []
    skipped_params = []
    failed_params = []

    for param in config.get(&quot;parameters&quot;, []):
        is_valid, error_msg, param_info = common.validate_param(param, env)

        if not is_valid:
            print(f&quot;[スキップ] {error_msg}&quot;)
            continue

        param_key = param_info[&quot;key&quot;]
        value = param_info[&quot;value&quot;]

        # 既存の値と比較
        if param_key in existing_params:
            if existing_params[param_key][&quot;value&quot;] == value:
                print(f&quot;[スキップ] {param_key}: 値に変更なし&quot;)
                skipped_params.append(param_key)
                continue
            print(f&quot;[更新] {param_key}: 値を更新します&quot;)
        else:
            print(f&quot;[新規] {param_key}: 新規パラメータを追加します&quot;)

        # パラメータを更新
        success = common.update_parameter(param_info, env)
        if success:
            updated_params.append(param_key)
            print(f&quot;  ✓ 完了&quot;)
        else:
            failed_params.append(param_key)
            print(f&quot;  ✗ 失敗&quot;)

    # 結果サマリー
    print()
    print(&quot;=== 結果サマリー ===&quot;)
    print(f&quot;更新: {len(updated_params)} 件&quot;)
    print(f&quot;スキップ（変更なし）: {len(skipped_params)} 件&quot;)
    print(f&quot;失敗: {len(failed_params)} 件&quot;)

    if failed_params:
        print()
        print(&quot;失敗したパラメータ:&quot;)
        for key in failed_params:
            print(f&quot;  - {key}&quot;)
        sys.exit(1)

    print()
    print(&quot;✓ 正常終了&quot;)


if __name__ == &quot;__main__&quot;:
    update_parameters()
</code></pre>
<p><strong>ポイント:</strong></p>
<ul>
<li>値が変わっていないパラメータはスキップ（無駄な更新を防ぐ）</li>
<li>更新結果を統計情報として出力</li>
<li>失敗時は終了コード1で終了</li>
</ul>
<h3>GitHub Actionsワークフロー</h3>
<pre><code class="language-yaml">name: Sync AWS Parameter Store

on:
  workflow_dispatch:  # 手動実行

jobs:
  sync-parameters:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        env: [dev, stg, prod]
    environment: ${{ matrix.env }}  # 環境ごとのSecretsを使用

    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: &#39;3.11&#39;

      - name: Install dependencies
        run: pip install pyyaml

      - name: Configure AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      - name: Sync Parameters
        env:
          ENV_NAME: ${{ matrix.env }}
          # SecureString用（環境ごとのGitHub Secretsから取得）
          SSM__DB__PASSWORD: ${{ secrets.SSM__DB__PASSWORD }}
          SSM__API__SECRET_KEY: ${{ secrets.SSM__API__SECRET_KEY }}
        run: |
          cd .github/scripts
          python update_aws_params.py
</code></pre>
<p><strong>ポイント:</strong></p>
<ul>
<li><code>strategy.matrix</code> で複数環境を並列実行</li>
<li><code>environment: ${{ matrix.env }}</code> で環境ごとのSecretsを使用（devとprodで異なるDBパスワードなど）</li>
<li>SecureStringの値は環境変数経由でスクリプトに渡す</li>
<li>値に変更がないパラメータは自動的にスキップされる</li>
</ul>
<h2>使い方</h2>
<h3>1. パラメータの追加・変更</h3>
<p><code>.github/aws-params.yml</code> を編集してPRを出すだけです。</p>
<pre><code class="language-yaml">parameters:
  # 新しいパラメータを追加
  - key: feature/enable_payment_v2
    description: &quot;新決済システムの有効化&quot;
    environment_values:
      dev:  &quot;true&quot;
      stg:  &quot;false&quot;
      prod:  &quot;false&quot;
</code></pre>
<h3>2. 全環境への反映</h3>
<ol>
<li>GitHub Actionsページを開く</li>
<li><code>Sync AWS Parameter Store</code> を選択</li>
<li><code>Run workflow</code> ボタンをクリック</li>
<li>全環境に並列で反映される</li>
</ol>
<h3>3. SecureStringパラメータの追加</h3>
<ol>
<li>YAMLに定義を追加：</li>
</ol>
<pre><code class="language-yaml">- key: payment/api_key
  description: &quot;決済APIキー&quot;
  type: &quot;SecureString&quot;
</code></pre>
<ol start="2">
<li>GitHub Environmentsに値を登録：</li>
</ol>
<p>Settings → Environments → 各環境（dev, stg, prod）のSecretsに登録</p>
<pre><code class="language-text">Secret名: SSM__PAYMENT__API_KEY
値: （環境ごとに異なる実際の値）
</code></pre>
<ol start="3">
<li>ワークフローファイルの環境変数セクションに追加：</li>
</ol>
<pre><code class="language-yaml">env:
  SSM__PAYMENT__API_KEY: ${{ secrets.SSM__PAYMENT__API_KEY }}
</code></pre>
<h3>4. 実演</h3>
<ol>
<li><p>GitHub Actions画面から今回作ったアクションを選んで、起動します。
<img src="/assets/blog/authors/miyashita/github_actions_choice.png" alt="Github_Actionsの選択と実行"></p>
</li>
<li><p>アクションが正常終了したことを確認します。各環境が並列で動作した事がわかります。
<img src="/assets/blog/authors/miyashita/github_actions_result.png" alt="Github_Actionsの動作結果"></p>
</li>
<li><p>AWSのパラメータストア画面を開いて確認してみます。パラメータが登録されています。成功です。
<img src="/assets/blog/authors/miyashita/aws_my_param.png" alt="パラメータストアの登録結果"></p>
</li>
</ol>
<h2>導入効果</h2>
<h3>具体的な効果</h3>
<ul>
<li><strong>作業時間</strong>: 環境数 × 5分 → <strong>ワンクリック</strong>（10環境なら50分削減）</li>
<li><strong>更新漏れ</strong>: 月数回発生 → <strong>ゼロ</strong></li>
<li><strong>確認作業</strong>: AWSコンソールを開く → <strong>YAMLを見るだけ</strong></li>
</ul>
<h2>まとめ</h2>
<p>クラウド時代の「環境が増えすぎ問題」は、多くの現場で直面している課題だと思います。</p>
<p>今回紹介したアイデアのポイントは：</p>
<ol>
<li><strong>YAML可視化</strong> - 全環境のパラメータを1ファイルで管理</li>
<li><strong>ワンクリック同期</strong> - GitHub Actionsで自動反映</li>
<li><strong>SecureString対応</strong> - 機密情報も安全に管理</li>
</ol>
<p>特別な技術は使っておらず、<strong>GitHub Actions + Python + AWS CLI</strong> だけで実現できます。</p>
<p>Parameter Storeの管理で困っている方、環境が増えて運用が大変になっている方の参考になれば幸いです。</p>
<p>最後までお読みいただき、ありがとうございました🙇‍♂️</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/miyashita/ssm_blog_title.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Speaker, Booth Staff, and Attendee: Reflecting on CloudNative Days 2025 Winter]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-10-three-perspectives-event-our-cloud-native-days-2025-winter-experience-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-10-three-perspectives-event-our-cloud-native-days-2025-winter-experience-en/</guid>
            <pubDate>Wed, 10 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A participation report summarizing KINTO Technologies experience at CloudNative Days 2025 Winter as speakers, booth operators, and attendees, gaining insights that CloudNative is a culture rather than just technology, and that no organization is perfect, while sharing learnings across the entire team.]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<p>This article is Day 10 of the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> 🎅🎄</p>
<h2>Introduction</h2>
<p>I&#39;m Liu (YOU), an Infrastructure Architect in the Cloud Infrastructure Group (CIG) at KINTO Technologies. Starting in November, I&#39;ve also taken on responsibilities in the Developer Relations Group.</p>
<p>In November 2025, our company participated in &quot;<a href="https://event.cloudnativedays.jp/cndw2025">CloudNative Days 2025 Winter</a>&quot; as a sponsor. At this event, two of our colleagues gave presentations, the Cloud Infrastructure Group and the Developer Relations Group that I belong to operated the sponsor booth, and many members attended technical sessions, allowing the entire organization to be actively involved.</p>
<p>CloudNative Days is the largest event in Japan&#39;s cloud-native community. Through this participation, we not only deepened our technical knowledge but also had the opportunity to reconsider the very concept of cloud-native itself.</p>
<p>In this article, I&#39;ll share the event experience and learnings from the perspectives of speakers, booth operators, and general attendees.</p>
<h2>Speaker Interviews</h2>
<h3>Koshiro from Cloud Infrastructure Group: &quot;<a href="https://event.cloudnativedays.jp/cndw2025/talks/2760">Building the Cloud Foundation for the Future of Mobility Platforms</a>&quot;</h3>
<p><img src="/assets/blog/authors/you/04/koshiro.png" alt="Koshiro&#39;s profile"></p>
<h4>How I Came to Speak</h4>
<ul>
<li><strong>Koshiro</strong>: &quot;Having been with the company for less than a year, I didn&#39;t know the organization&#39;s history or the reasons behind our technology choices. Conveying things I hadn&#39;t experienced firsthand was the biggest challenge.&quot;</li>
</ul>
<p>Originally, another member was scheduled to speak, but circumstances led to him stepping in at short notice. Having joined in January 2025, this was a challenge for him without fully understanding the organization&#39;s history or the background of our technology decisions.</p>
<h4>Background on Topic Selection</h4>
<p>He struggled with cloud‑native as a topic. Looking into available resources, he found that many talks went deep into Kubernetes and container technologies. Since our company mainly uses ECS and only uses Kubernetes in limited areas, he couldn’t fully relate. Eventually, he realized that this struggle itself was worth sharing with others who are in the same situation.</p>
<ul>
<li><strong>Koshiro</strong>: &quot;Cloud‑native may feel like a high‑level concept and getting there requires organizational trial and error. Technical excellence is important but building a cloud‑native culture within the organization is more important. There is no single best practice.&quot;</li>
</ul>
<p>What I most wanted to convey was that <strong>cloud-native is a means, not an end</strong>. We select appropriate technologies while facing our business needs. By sharing our history of trial and error as it really happened, I hoped to give others facing similar challenges a chance to move forward.</p>
<h4>Audience Reactions on the Day</h4>
<p>Venue C, with a capacity of 66 people, was nearly full. Many attendees who had been watching other sessions online moved to this session midway through.</p>
<ul>
<li><strong>Koshiro</strong>: &quot;There were people nodding along as they listened, and some took photos of my slides to post on X. At an event where technology seems heavily emphasized, it was striking to see people show interest in my topic.&quot;</li>
</ul>
<p>Two people asked questions at the microphone, and three visited &quot;Ask the Speaker,&quot; giving me a strong sense of accomplishment for my first presentation. What stayed with me most were the questions about Terraform structure and the platform organization&#39;s decision-making. These are questions without definitive answers and I was reminded that <strong>everyone struggles with these things</strong>.</p>
<h4>Lessons Learned</h4>
<p>This was a substantial 40-minute presentation, and through the intensive preparation and practice, Koshiro-san realized how important regular output truly is.</p>
<ul>
<li><strong>Koshiro</strong>: &quot;I realized that if I had been giving a presentation regularly, creating slides would have been much easier. If I&#39;d been writing tech blogs, I could have adapted and utilized that content. I&#39;ve resolved to work harder on my output.&quot;</li>
</ul>
<p>He also gained an important insight about using AI. From his initial failed attempt to create a talk script with AI, he learned this lesson: &quot;First establish what message you want to convey, then ask AI how to express it. Never start by asking AI. It&#39;s a recipe for failure.&quot;</p>
<h4>Looking Ahead</h4>
<p>For next time, he wants to build experience with shorter 20-minute presentations and also challenge himself to present more technology-focused topics. Additionally, since it&#39;s a community event, he expressed a desire to deepen connections with participants through group work and mutual learning.</p>
<ul>
<li><strong>Koshiro</strong>: &quot;Next year, we&#39;ll be hosting a cloud-native conference. KINTO Technologies is one of the most advanced organizations within the Toyota Group when it comes to cloud adoption. While we don&#39;t have everything figured out yet, we&#39;ve reached a stage where we can build high-level solutions together. I&#39;d be happy to see more opportunities for collaboration and study sessions.&quot;</li>
</ul>
<h3>Lee-san from Platform G: &quot;<a href="https://event.cloudnativedays.jp/cndw2025/talks/2683">A New Era of Alert Response Pioneered by AI Agents</a>&quot;</h3>
<p><img src="/assets/blog/authors/you/04/lee.png" alt="李さんのプロフィール"></p>
<h4>The Decision to Submit Proposal to CFP</h4>
<p>Lee-san submitted a proposal to CFP with a &quot;let&#39;s just try&quot; attitude, thinking the chances of acceptance were low. The reasons for applying were clear: to enhance the company&#39;s external communication power, contribute to engineer recruitment within the group, and gain opportunities for external communication. Since the submission was completely separate from sponsor work, being accepted came as a surprise.</p>
<h4>Struggles with Material Preparation</h4>
<p>He struggled most with determining the right skill level for the audience when creating the materials.</p>
<ul>
<li><strong>Lee</strong>: &quot;With both experts and beginners in the audience, I struggled to decide what level to aim for. I wanted the content to be easy to understand even for those unfamiliar with the topic, but if I made it too light, it wouldn’t satisfy people looking for deeper knowledge. Striking that balance was the hardest part.&quot;</li>
</ul>
<p>I particularly focused on storytelling. I was conscious of the flow: &quot;there was this challenge → we responded this way → the result was this → now we&#39;re at this point,&quot; and reviewed the overall structure multiple times to make the narrative flow naturally. Material preparation took over a week, continuously improving the script and presentation.</p>
<h4>Unexpected Events</h4>
<p>On the day, his biggest concern was whether PowerPoint&#39;s script feature would be visible on the audience&#39;s screen. For a 40-minute presentation, going without notes would be tough, so during lunch he went to the venue myself to ask for a quick run‑through.</p>
<ul>
<li><strong>Lee</strong>: &quot;They provided basic information for speakers, such as arrival times and HDMI connection details, but there should have been more guidance on things like rehearsals and checking script display. For our members who present at future events, I’d recommend visiting the venue in advance to confirm these details themselves.&quot;</li>
</ul>
<h4>Audience Response</h4>
<p>The presentation was well-received overall, with fellow speaker Koshiro-san commenting that it was &quot;the most impressive session of the day.&quot; After the presentation, 4-5 people came with questions, and Lee-san said he could feel the high interest in AI Agents.</p>
<p>What stood out to him most was a question about context engineering: How do you distinguish between information needed for AI and information needed only by humans but not by AI? He shared the countermeasures currently under consideration for this question as well.</p>
<ul>
<li><strong>Lee</strong>: &quot;There&#39;s the issue of LLM model input limitations, and if the context gets too large, it exceeds the limits. We plan to address this with a single-agent configuration, verifying a method to summarize and compress history using LangChain&#39;s new features. I want to talk about these improvements at next year&#39;s conferences or in technical articles.&quot;</li>
</ul>
<h4>Lessons Learned</h4>
<p>For him, this was the first time speaking at a large-scale open community event, so there was a lot to gain.</p>
<ul>
<li><strong>Lee</strong>: &quot;I was nervous, but I found that there was no need to be as scared as I was. If there are people who want to try, I recommend giving it a shot. I was greatly inspired from other speakers and participants, and my technical motivation increased.&quot;</li>
</ul>
<p>In terms of technical takeaways, he found the presentations on incident‑management SaaS and CyberAgent’s Ōyama-san’s talk on Loki and Prometheus particularly insightful. He also gained a few dozen new followers on X, which helped broaden his connections within the community.</p>
<h4>Looking Ahead</h4>
<p>There are areas for improvement regarding time management. When he practiced his presentation, he noticed that he finished earlier than expected. However, during the actual presentation, he got nervous and couldn’t get the words out. He ended up running short on time and had to skip a page.</p>
<p>Next time, he is thinking of giving a talk on improving context engineering and methods for evaluating agents.</p>
<ul>
<li><strong>Lee</strong>: &quot;It’s difficult to evaluate the effectiveness of an agent because creating proper evaluation criteria is challenging. Even when alerts seem to be the same, the underlying causes can differ, which makes it hard to build accurate ground‑truth data. This is widely recognized as a common issue across the industry. Context engineering is a field that has recently been gaining attention, but best practices have yet to be established. I believe this is a topic many people are eager to learn about.&quot;</li>
</ul>
<p>His advice is clear.</p>
<ul>
<li><strong>Lee</strong>: &quot;What matters most is simply applying. Once you get accepted, your future self will figure it out. It boosts your motivation and inspiration, and it also helps raise your company’s presence, benefiting both sides.&quot;</li>
</ul>
<h3>Other Speakers</h3>
<p>Tsuji-san, the Manager of Cloud Infrastructure Group, also gave a talk about the Toyota Group&#39;s technical community!</p>
<h4>Tsuji-san from Cloud Infrastructure Group: &quot;<a href="https://event.cloudnativedays.jp/cndw2025/talks/2774">Introducing TURTLE, the Toyota Group Engineer Community!</a>&quot;</h4>
<p><img src="/assets/blog/authors/you/04/tsuji.jpg" alt="辻さんのプロフィール"></p>
<h2>Booth Operation Interview</h2>
<p>Many people from our company helped with this booth operation. With cooperation from various groups including Cloud Infrastructure Group, Platform Group, HR Group, and Developer Relations Group, we were able to run an exciting booth operation and complete it successfully.
For the booth operation interview, we proceeded in a discussion format, with participation from:</p>
<ul>
<li>Cloud Infrastructure Group (CIG): Koshiro, Shirai, Yasuda, Matsuo</li>
<li>Developer Relations Group: Murayama</li>
</ul>
<h3>Challenges in the Preparation Stage</h3>
<p>This was CIG&#39;s first time operating a booth, so we didn&#39;t know what to do, but our company has the Developer Relations Group that fully supports events like this from planning to reflection, enhancing both internal and external communication power.</p>
<p>This time too, Murayama-san from the Developer Relations Group provided support from the booth preparation stage. Her collaboration with Manager Tsuji-san was a key factor in successfully running the booth.</p>
<ul>
<li><p><strong>Murayama</strong>: &quot;I consulted with Manager Tsuji-san in advance and briefly communicated what information we wanted. Things progressed through CIG&#39;s regular meetings, and before I knew it, the shift schedule was ready, which was very helpful.&quot;</p>
</li>
<li><p><strong>Murayama</strong>: &quot;Also, at a conference we sponsored over the weekend before the event, we displayed a board featuring our system architecture diagram, which was very well received and helped us have many conversations with attendees. So, even though it was the day before the event, I suggested, &quot;Do you have any architecture diagrams we could display?&quot; and they immediately provided the perfect architecture diagram. We barely made it in time for printing, but we managed to have it at our booth. Since it served as a great conversation starter, I&#39;m really glad we were able to prepare it!&quot;</p>
</li>
</ul>
<p>I felt the advice was truly valuable, because understanding what attracts people to a booth is something you can’t really grasp without having run many booths yourself.</p>
<p><img src="/assets/blog/authors/you/04/IMG_6906.jpg" alt="運営ブース"></p>
<p>For Cloud Infrastructure Group, many people participated in booth operation for the first time, so most of it was about preparing ourselves to get into the right mindset.</p>
<ul>
<li><strong>Shirai</strong>: &quot;I made a conscious effort to stay cheerful and energetic so as not to give off any negative impression. I wanted visitors to have a good impression of our booth.&quot;</li>
</ul>
<p>As Shirai-san said, we put a lot of effort into presenting a positive attitude to the people who visited our booth.</p>
<h3>Observations and Insights on the Day</h3>
<p>Cloud Infrastructure Group had two main points they wanted to convey at CloudNative Days 2025 Winter. According to Koshiro-san, who spoke at the sponsor session:</p>
<ul>
<li><strong>Koshiro</strong>: &quot;First, we wanted people to know what the Cloud Infrastructure Group[^1] does. Second, it was about publicizing the activities of the Solutions Team within our group. Our company does various activities not only for our own services but for the entire Toyota Group. We wanted visitors to know a little about it.&quot;</li>
</ul>
<p><img src="/assets/blog/authors/you/04/1000013261.jpg" alt="質問ボード"></p>
<p>Specifically, we communicated with visitors through the question board prepared at the booth:</p>
<ul>
<li>Q3. Do you know KINTO Technologies?</li>
</ul>
<p>Through this question, we were able to broadly communicate our activities as Cloud Infrastructure Group and the company&#39;s direction while engaging in conversation.</p>
<p>There were also other important insights:</p>
<ul>
<li><strong>Koshiro</strong>: &quot;Not all event participants come to the booth, but those who do come and listen are at least interested in KINTO Technologies. Also, I noticed that a certain number of people aren&#39;t involved in cloud-native itself. This gap between the event title and the actual attendees stood out to me.&quot;</li>
</ul>
<p>Initially, we had the impression that many cloud-native engineers would participate since it was a cloud-native event, but we found that there were more people who want to pursue cloud-native in the future than engineers already doing cloud-native work. In fact, the venue had people from various backgrounds, including app developers, sales representatives, designers, reporters, and students.</p>
<ul>
<li><strong>Yasuda</strong>: &quot;I wanted more people to know about KINTO Technologies, but half of the visitors already knew about our company. It was amazing that many people knew about KINTO Technologies from recent events like the <a href="https://www.kinto-technologies.com/news/20250701/">Developer Productivity Conference</a> and <a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6?productVariantID=6A68KiYtH7WX6K7LA9XHNw">Gijutsushoten</a>.&quot;</li>
</ul>
<p>We also found that CloudNativeDays participants include very few infrastructure specialists. Many people said that app developers often also handle infrastructure responsibilities, and it&#39;s very challenging to write application code while also creating infrastructure resources and handling security. KINTO Technologies has dedicated infrastructure and SRE/security teams like ours, with clearly divided roles, so I strongly feel once again that we can focus on our respective areas of expertise.</p>
<p>There were also opinions that it was good to hear various people&#39;s perspectives through the question board. We adjusted our questions depending on visitors&#39; backgrounds, personalities, and points of interest.</p>
<ul>
<li><p>Q1. What&#39;s your cloud style? → Work style &amp; Development style</p>
</li>
<li><p>Q2. What is &quot;good infrastructure&quot; to you? → &quot;Good work&quot; or &quot;Good system&quot;</p>
</li>
<li><p><strong>Matsuo</strong>: &quot;I&#39;m glad we could go beyond the questions on the board and hear about people&#39;s perspectives and what they prioritize. What left an impression was that surprisingly many people work with cloud services other than AWS. Since I&#39;ve only used AWS, it made me want to try multi-cloud.&quot;</p>
</li>
</ul>
<p>Many people with various roles and a strong interest in technology participated:</p>
<ul>
<li><strong>Shirai</strong>: &quot;We had visitors with diverse roles, and it was difficult to have deeper conversations with people in roles outside my area of expertise. Going forward, I want to build my foundation (knowledge and communication skills) so I can talk with people in a wide range of roles.&quot;</li>
</ul>
<p>Even for CloudNative booth operations, I feel we need to prepare a system that can handle visitors of all roles, not just infrastructure specialist positions.</p>
<h3>Results and Improvements</h3>
<p>As one of the biggest achievements this time, we had this conversation:</p>
<ul>
<li><p><strong>Koshiro</strong>: &quot;Almost everyone in the Infrastructure Team and <em>Kaizen</em> Team participated in the event as our group&#39;s operators. It&#39;s quite a rare experience. I&#39;m grateful we could share the same event experience. While running the event, we were able to share learnings with each other like &#39;this is how it works&#39; or &#39;that&#39;s how it should be.&#39;&quot;</p>
</li>
<li><p><strong>Shirai</strong>: &quot;By holding an event with the whole team, the team&#39;s sense of unity increased even more. Also, by hearing about what other companies are practicing, we could gain new insights.&quot;</p>
</li>
</ul>
<p>Participating individually in events is good, but when participating as a team, you can establish the same mindset with each other. I believe we were able to foster team growth and raise awareness, and by working together, we could once again establish a path for continuous technical outreach.</p>
<p>On the other hand, there were also areas for improvement. This time, we also had the purpose of listening to sessions we were interested in as participants to broaden our knowledge, so we created a shift schedule within the group in advance.</p>
<ul>
<li><p><strong>Matsuo</strong>: &quot;We created a shift schedule, but when it was my turn and I headed to the booth, I was surprised that everyone was at the booth.&quot;</p>
</li>
<li><p><strong>Koshiro</strong>: &quot;Next time we run a booth, it would be better to have 3 people at the booth while others attend sessions for input. We need to think about ways to be more efficient, like finding blog topics at sessions.&quot;</p>
</li>
</ul>
<p>What we recognized as an issue wasn&#39;t the shift problem but time efficiency. Since CIG participated as both booth operators and attendees, I think it was difficult to balance between running the booth and gaining our own takeaways by attending sessions.</p>
<p>Still, by reflecting properly, we managed to bring clarity to the challenges each of us had in mind. Many visitors indicated on the question board that they already knew KINTO Technologies which is a clear result of our ongoing promotional efforts and proof that events aren&#39;t just one-time things. The Cloud Infrastructure Group will keep contributing to KINTO Technologies&#39; technical outreach, and this experience will be a key ingredient for our future endeavors.</p>
<h3>The Essence of Cloud-native</h3>
<p><img src="/assets/blog/authors/you/04/IMG_6895.jpg" alt="ブースの対応"></p>
<p>Finally, we explored in detail the results of the question board filled out by everyone.</p>
<ul>
<li><strong>Shirai</strong>: &quot;For the question &#39;What is good infrastructure?&#39;, many people answered, &#39;Something you can be passionate about.&#39; Exploring it further, understanding your own service to the point where you can love it might be one way to deepen the understanding of the essence of cloud native.&quot;</li>
</ul>
<p>Koshiro-san re-emphasized the message he delivered in his talk:</p>
<ul>
<li><strong>Koshiro</strong>: &quot;No organization has cloud-native perfectly figured out from the start. There are various gradations. It&#39;s necessary to build not just the technology, but also the culture and organization. Talking with various companies at the sponsor booths, I felt this anew.&quot;</li>
</ul>
<h2>Attendee Interview</h2>
<h3>Shimamura from Platform Group</h3>
<p>Shimamura-san planned to attend to see Lee&#39;s presentation from the same group, and also he had submitted a CFP himself. Additionally, his team had recently started working with Kubernetes and EKS, so the team&#39;s technical interest was growing.</p>
<h4>Sessions that stood out: <a href="https://event.cloudnativedays.jp/cndw2025/talks/2694">Nintendo&#39;s Case Study</a></h4>
<p>Shimamura-san was amzaed by Nintendo&#39;s session.</p>
<ul>
<li><strong>Shimamura</strong>: &quot;It was an on-premise case study and I wanted to incorporate this culture. They run full regression tests once a day, and I was surprised they use test results for purposes other than bug reporting.&quot;</li>
</ul>
<p>What was particularly interesting was the multi-purpose use of test results.</p>
<ul>
<li><strong>Shimamura</strong>: &quot;From a UI perspective, designers don&#39;t just look at surface-level design alone but use it as decision-making material like &#39;considering the build result images, this would (match the surrounding atmosphere) better.&#39; There&#39;s a flow of events from the past, and you can tell if it&#39;s natural within that flow. They also use it for voice actor dubbing. Using automated testing not just for finding bugs, but for other purposes too. It&#39;s a culture of proactively finding and improving points across multiple fronts using test results.&quot;</li>
</ul>
<p>And he also reflected on the differences between Nintendo&#39;s case and our company:</p>
<ul>
<li><strong>Shimamura</strong>: &quot;I felt that KINTO still lacks this culture. This mindset of making things possible, noticing issues, and fixing them doesn&#39;t come naturally to us yet. I felt it&#39;s necessary not only for improving websites that have become heavy, but also for delivering KINTO&#39;s services better.&quot;</li>
</ul>
<h4>Technical Gains</h4>
<p>Shimamura-san shared several technical gains:</p>
<ol>
<li><p><strong>Realizing the cause of Alloy log double-forwarding</strong>: When we recently did blue-green deployment, I noticed something similar to what I thought was the cause of Alloy logs being double-forwarded and used it as input for investigation.</p>
</li>
<li><p><strong>Docker build security</strong>: I learned there are frameworks for signatures and verification to check if built container images are contaminated with malware. Given recent circumstances, I shared this information with the security team.</p>
</li>
<li><p><strong>Reconfirming RCA&#39;s advantages</strong>: While incident management SaaS solutions only apply AI to incident management processes, our company has built an end-to-end solution with RCA. This reaffirmed the advantages and benefits of RCA: that everything must be unified and handled end-to-end.</p>
</li>
</ol>
<p>Conversely, he also shared some questions he felt about certain sessions:</p>
<ul>
<li>In some cases, cloud-native technology was becoming the purpose rather than a means to solve problems</li>
<li>I wanted to hear case-specific stories rather than general knowledge</li>
</ul>
<h4>Looking Ahead</h4>
<p>Shimamura-san shared improvement points and outlook for next time:</p>
<ul>
<li><strong>Shimamura</strong>: &quot;I want to register for event sessions early so I can make sure I attend the ones I’m interested in. Also, instead of keeping the information I gain to myself, I want to make it accessible to other team members. If people don’t know what happened or what examples I found, then the time I spent participating would feel wasted. I believe knowledge doesn’t mean much unless it’s shared and spread.&quot;</li>
</ul>
<p>He also showed interest in giving a talk:</p>
<ul>
<li><strong>Shimamura</strong>: &quot;My CFP submission was rejected twice, so I definitely want to give a talk next time. However, topics are the problem. I&#39;ve already talked about RCA, and culture theory at <a href="https://platformengineering.connpass.com/event/364328/">Platform Engineering Meetup</a>. I need to find new topics.&quot;</li>
</ul>
<h2>Conclusion</h2>
<p><img src="/assets/blog/authors/you/04/IMG_6905.jpg" alt="ブースの対応"></p>
<p>Through our participation in CloudNative Days 2025 Winter, we gained many learnings and insights.</p>
<h3>Technology and Culture as Two Wheels</h3>
<p>Speakers Koshiro-san and Lee-san each talked about cloud-native from different approaches. Koshiro-san from the perspective of organizational culture, Lee-san from the perspective of technical practice. However, what both had in common was the message that <strong>cloud-native is a means, not an end</strong>.</p>
<p>Through booth operation, we reconfirmed that &quot;no organization deploys cloud-native perfectly from the start.&quot; We need to build not just technology, but culture and organization as well.</p>
<p>As attendee Shimamura learned from Nintendo&#39;s case, cloud-native is a culture, not a technology. A culture of proactively finding and improving points, the idea of using test results for multiple purposes, and above all, understanding your own service to the point where you can &quot;love&quot; it. These might be the essence of cloud native.</p>
<h3>Team Unity</h3>
<p>One of the biggest achievements of attending this event was that the Infrastructure Team and <em>Kaizen</em> Team—almost everyone—could participate as group operators. Being able to share the same event experience and learnings together greatly increased the team&#39;s sense of unity. People from other groups also helped us, and we were able to have a very good time.</p>
<h3>The Importance of Continuous Output</h3>
<p>What both speakers commonly felt was the importance of daily output. Having experienced the difficulty of preparing a 40-minute presentation, they realized the need for a habit of continuous output through tech blogs and study session presentations.</p>
<p>Also, as Shimamura-san pointed out, it&#39;s important not to keep information gained at events to yourself, but to share it with the team and organization. We believe that spreading knowledge leads to organizational growth as a whole.</p>
<h3>Looking Ahead</h3>
<p>In 2026, the cloud-native conference will be held (a joint event of Platform Engineering Meetup, SRE Conference, and CloudNativeDays). We also plan to exhibit at JAWS Days.</p>
<p>To everyone struggling with cloud-native, here&#39;s a message from Koshiro-san:</p>
<p>&quot;Don&#39;t be driven by technology. Let&#39;s work together on culture-building and organization-building, striving together for business success and achieving the mission beyond business. Cloud-native is a means, not an end.&quot;</p>
<p>And don&#39;t forget Lee-san&#39;s advice:</p>
<p>&quot;It&#39;s important to submit an application first. Don&#39;t be afraid. Just submit papers. Once you&#39;re accepted, your future self will figure it out somehow.&quot;</p>
<p>We hope to apply the learnings and insights gained through this event participation to future organization-building, culture-building, and technical challenges. We look forward to growing together through continuous interaction with the community.</p>
<hr>
<p>[^1]: At the time of writing this article, Cloud Infrastructure Group consists of three teams: the Infrastructure Team, <em>Kaizen</em> Team, and Solutions Team.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/you/04/IMG_6930.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[登壇、ブース運営、参加。三つの視点で振り返る：CloudNative Days 2025 Winter]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-10-three-perspectives-event-our-cloud-native-days-2025-winter-experience/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-10-three-perspectives-event-our-cloud-native-days-2025-winter-experience/</guid>
            <pubDate>Wed, 10 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズがCloudNative Days 2025 Winterに登壇者・ブース運営者・参加者として参加し、 CloudNativeは技術ではなく文化であり、完璧な組織はないという気づきを得ながら、チーム全体で学びを共有 した経験をまとめた参加レポート。]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の10日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>KINTOテクノロジーズのCloud Infrastructure G(CIG)でInfrastructure Architectを担当している劉(YOU)です。11月から技術広報 Gも兼務することになりました。</p>
<p>2025年11月、弊社は「<a href="https://event.cloudnativedays.jp/cndw2025">CloudNative Days 2025 Winter</a>」に、スポンサー企業として参加しました。今回のイベントでは、弊社の社員2名が登壇し、私が所属しているCloud Infrastructure G・技術広報 Gがスポンサーブースを運営し、多くのメンバーが参加者として技術セッションに参加するなど、組織全体で積極的に関わることができました。</p>
<p>CloudNative Daysは、日本国内のクラウドネイティブコミュニティにおいて最大規模のイベントです。今回の参加を通じて、私たちは技術的な知見を深めるだけでなく、CloudNativeという概念そのものについて改めて考える機会を得ることができました。</p>
<p>本記事では、登壇者、ブース運営担当者、そして一般参加者、それぞれの視点から見たイベントの様子と、そこから得られた学びを共有します。</p>
<h2>登壇者インタビュー</h2>
<h3>Cloud Infrastructure G、古代さん：「<a href="https://event.cloudnativedays.jp/cndw2025/talks/2760">モビリティプラットフォームの未来を築くクラウド基盤</a>」</h3>
<p><img src="/assets/blog/authors/you/04/koshiro.png" alt="古代さんのプロフィール"></p>
<h4>登壇に至った経緯</h4>
<ul>
<li><strong>古代さん</strong>：「入社して1年経っていない状態で、組織の歴史を知らない、技術選定の理由を知らない。自分が経験していない、実体験がないことをどう伝えればいいのか、それが一番大変でした」</li>
</ul>
<p>当初は別のメンバーが登壇予定だったところ、都合により急遽登壇することになりました。2025年1月に入社したばかりで、組織の歴史や技術選定の背景を十分に把握していない状態でのチャレンジです。</p>
<h4>テーマ選定の背景</h4>
<p>古代さんはCloudNativeというテーマに対して悩みました。CloudNative関連の資料を探したら、多くの登壇者がKubernetesやコンテナなど、技術的に深い内容を発表する中、弊社は主にECSを利用していてKubernetesは限られた所だけ使っていません。しかし、その悩みこそが、同じような状況にいる多くの人々と共有できる価値だと気づきました。</p>
<ul>
<li><strong>古代さん</strong>：「CloudNativeはハイレベルに感じるかもしれませんが、そこに至るまでは組織としての試行錯誤が必要です。技術で尖ることも必要ですが、組織としてCloudNativeな文化を作ることが重要です。ベストプラクティスはありません」</li>
</ul>
<p>最も伝えたかったのは、<strong>CloudNativeは手段であり、目的ではない</strong>ということです。ビジネスに向き合いながら、適切な技術を選定していく。その試行錯誤の歴史をありのまま伝えることで、同じように悩んでいる人たちが前進できるきっかけになればと考えました。</p>
<h4>当日の反応</h4>
<p>会場Cの66名規模の会場は、ほぼ満席でした。オンラインで他のセッションを聞いていた方が、途中からこのセッションに移動してくるケースも多く見られました。</p>
<ul>
<li><strong>古代さん</strong>：「頷きながら聞いてくださる方がいて、スライドを写真に撮ってXに上げてくれる人もいました。技術がかなり重視されそうなイベントにおいて、自分のテーマに興味を持ってもらえたのは印象的でした」</li>
</ul>
<p>質問もマイクで2名、「Ask the Speaker」で3名が訪れ、初めての登壇としては大きな手応えを感じることができました。特に印象的だったのは、Terraformの作り方やプラットフォーム組織の意思決定についての質問でした。これらは正解のない問いですが、<strong>みんなそういうところで悩む</strong>ということを改めて実感します。</p>
<h4>得られた学び</h4>
<p>今回の発表は40分に至る長めのことでして、登壇に関する資料作成や発表練習に追われた古代さんは日々のアウトプットの重要性を痛感しました。</p>
<ul>
<li><strong>古代さん</strong>：「日々発表していれば、もっとスライド作りが簡単だったと実感しました。テックブログなどを書いていれば、それを応用して活用できるなと。アウトプットを頑張ろうと決意しました」</li>
</ul>
<p>また、AIの使い方についても重要な気づきがあります。最初にAIとスクリプトでトークスクリプトを作ろうとして失敗した経験から、「どういうメッセージを伝えたいのかを固めた上で、どう表現すればいいかをAIに聞くのが重要。最初からAIに聞くのは絶対にダメ」という教訓を得ました。</p>
<h4>次回への展望</h4>
<p>次回は20分程度の短い登壇で回数を重ね、技術を中心にしたネタの発表もチャレンジしたいと考えています。また、せっかくのコミュニティイベントなので、グループワークや相互学習を通じて、参加者とよりネットワークを深めたいという思いも語ってくれました。</p>
<ul>
<li><strong>古代さん</strong>：「来年はCloudNative会議が開催されます。KINTOテクノロジーズはトヨタグループの中でも先進的にクラウド活用を進めている組織です。ハイレベルなものがあるわけではありませんが、ハイレベルのものを一緒に作ることはできるフェーズになっています。一緒にコラボしたり、勉強会をしたりする機会が増えると嬉しいです」</li>
</ul>
<h3>Platform G、李さん：「<a href="https://event.cloudnativedays.jp/cndw2025/talks/2683">AI Agentで切り開くアラート対応の新時代</a>」</h3>
<p><img src="/assets/blog/authors/you/04/lee.png" alt="李さんのプロフィール"></p>
<h4>CFP応募の決断</h4>
<p>李さんは、採択される可能性は低いと考えながらも、「とりあえず出してみよう」という気持ちでCFPを応募しました。応募理由は明確です。会社の外部発信力を高め、グループ内でのエンジニア採用に貢献し、外部コミュニケーションの機会を得るためです。スポンサーワークとは完全に別枠での応募だったため、採択されたことに驚いたそうです。</p>
<h4>資料作成での苦労</h4>
<p>李さんが資料作成に苦労したのは、対象者のレベル設定でした。</p>
<ul>
<li><strong>李さん</strong>：「詳しい人もいれば、初心者もいる中で、どのレベルに合わせるか悩みました。知らない人が見ても理解しやすい内容にしたいが、軽すぎる内容では深い知識を求める人に物足りない。このバランスを取ることが最も難しかったです」</li>
</ul>
<p>特にストーリーテリングにも力を入れました。「こういう課題があって→こう対応して→結果こうなった→現在はこうなっている」という流れを意識し、話の流れが自然になるよう、全体構成を何度も見直しました。資料作成には約1週間以上、継続的に質を上げるために原稿やプレゼンを整えました。</p>
<h4>想定外の出来事</h4>
<p>当日、最も不安だったのは、PowerPointのスクリプト機能が聴衆側の画面に表示されるかどうかです。40分の発表で台本なしは厳しいため、昼休みに自ら会場に行って確認を依頼しました。</p>
<ul>
<li><strong>李さん</strong>：「登壇者向けの情報提供として、到着時間やHDMI接続などの基本情報はありましたが、リハーサルや台本表示確認などの詳細な案内が必要でした。次回登壇する社内メンバーには、事前に会場で確認することをアドバイスしたいです」</li>
</ul>
<h4>参加者の反応</h4>
<p>発表は全体的に好評で、同じ発表者である古代さんからも「今日のセッションで最も印象的だった」とのコメントをいただきました。発表終了後にも4〜5名が質問に来て、AI Agentに対する関心の高さを実感できたと話ました。</p>
<p>特に印象的だったのは、コンテキストエンジニアリングに関する質問です。AIに必要な情報と、人間にだけ必要でAIには不要な情報をどう区別しているか。この質問に対しても、現在検討中の対策を共有してくれました。</p>
<ul>
<li><strong>李さん</strong>：「LLMモデルのインプット制限の問題があり、コンテキストが大きくなりすぎると制限を超えてしまいます。シングルエージェント構成で対応予定で、LangChainの新機能を活用し、履歴を要約・圧縮する方式を検証中です。この改善内容を来年のカンファレンスや技術記事で発表したいと考えています」</li>
</ul>
<h4>得られた学び</h4>
<p>李さんにとって、大規模なオープンコミュニティでの登壇は初めてだったので得られた事が多かったそうです。</p>
<ul>
<li><strong>李さん</strong>：「緊張はしましたが、思ったほど恐れる必要はないと感じました。やってみたい人がいれば、積極的にトライすることをお勧めします。他の登壇者や参加者から大きな刺激を受け、技術的なモチベーションが向上しました」</li>
</ul>
<p>技術的な収穫としては、インシデント管理のSaaSやサイバーエージェント大山さんのLoki・Prometheusに関する発表が参考になったそうです。また、Xのフォロワーも数十人増加し、コミュニティでのつながりが広がりました。</p>
<h4>次回への展望</h4>
<p>時間管理については反省点があります。練習では台本を見ながら話すため早く終わりすぎないか心配でしたが、本番では緊張して言葉が出てこず、逆に時間が足りなくなり、1ページスキップする必要がありました。</p>
<p>次回は、コンテキストエンジニアリングの改善やAgentの評価方法について発表したいと考えています。</p>
<ul>
<li><strong>李さん</strong>：「Agentの効果をどう評価するか、評価基準の作成が難しい。同じアラートでも原因が異なる場合があり、正解データの作成が困難です。これは業界全体で共通の課題として認識されています。コンテキストエンジニアリングは最近注目されている分野で、ベストプラクティスがまだ確立されていません。多くの人が知りたがっている内容だと思います」</li>
</ul>
<p>李さんからのアドバイスは明確です。</p>
<ul>
<li><strong>李さん</strong>：「まずは応募してみることが重要。採択されてから、未来の自分が何とかしてくれます。個人的な刺激とモチベーション向上、会社のプレゼンス向上、双方にメリットがある活動です」</li>
</ul>
<h3>インタビュー外の登壇</h3>
<p>Cloud Infrastructure G マネージャーの辻さんもTOYOTAグループの技術コミュニティの紹介で登壇しました！</p>
<h4>Cloud Infrastructure G、辻さん：「<a href="https://event.cloudnativedays.jp/cndw2025/talks/2774">トヨタグループのエンジニアコミュニティ TURTLEの紹介！</a>」</h4>
<p><img src="/assets/blog/authors/you/04/tsuji.jpg" alt="辻さんのプロフィール"></p>
<h2>ブース運営インタビュー</h2>
<p>今回のブース運営には、弊社の色んな方々が手を貸してくれました。Cloud Infrastructure G・Platform G・人事 G・技術広報 G、社内の様々なGの協力の上、盛り上がったブース運営を遂行し、無事に終わらせることができました。
ブース運営のインタビューの場合、ディスカッション方式で進みまして、</p>
<ul>
<li>Cloud Infrastructure G(CIG)：古代さん、白井さん、安田さん、松尾さん</li>
<li>技術広報 G：村山さん</li>
</ul>
<p>が参加してくれました。</p>
<h3>準備段階での課題</h3>
<p>CIGでのブース運営は初めてで何をしたらいいのかわかりませんでしたが、弊社では技術広報 Gが社内外を問わず、発信力を高める組織として今回のようにイベントの企画から振り返りまで全力で手伝ってくれます。</p>
<p>今回も、技術広報 Gの村山さんがブース運営の準備段階からサポートをしてくれました。マネージャの辻さんと一緒に調整して成功的にブースの運営ができたキーポイントでした。</p>
<ul>
<li><p><strong>村山さん</strong>：「事前にマネージャーの辻さんと相談し、欲しい情報などを軽く伝えました。CIGの定例にて色々話を進めてくれて、シフトもいつの間にかできており大変助かりました。」</p>
</li>
<li><p><strong>村山さん</strong>：「また、週末にスポンサーをしたカンファレンスにてシステムアーキテクチャ図のボードを展示したら評判が良く、来場者と多くのコミュニケーションを取ることができていたので、開催日前日ながら『なにか掲載できるアーキテクチャ図とかありませんか？』と提案したところ、すぐにベストなアーキテクチャ図を提供してくれました。印刷もギリギリ間に合いブースに置けましたが、コミュニケーションのきっかけになっていたので用意できてよかったなと思いました！」</p>
</li>
</ul>
<p>どういう物がブースに惹かれることに役立つかは色んなブース運営経験なしでは出来ないことだったので、本当に貴重なアドバイスだと思います。</p>
<p><img src="/assets/blog/authors/you/04/IMG_6906.jpg" alt="運営ブース"></p>
<p>クラウドインフラGでは今回初めてブース運営参加した人も多く、心構えのような心理的な準備が大半でした。</p>
<ul>
<li><strong>白井さん</strong>:「明るく元気に、ネガティブな印象を抱かせないように意識しました。ブースに来てくれた人に対して良い印象を持ってもらうようにしてほしかったです。」</li>
</ul>
<p>白井さんが言うようにブースに訪ねてくれる方々にポジティブな姿を見せるために努力を注ぎました。</p>
<h3>当日の様子と気づき</h3>
<p>クラウドインフラGがCloudNative Days 2025 Winterで伝えたかったポイントは2つありました。スポンサーセッションに登壇した古代さんの話を聞くと、</p>
<ul>
<li><strong>古代さん</strong>：「1つ目は、クラウドインフラグループ[^1]が何をやっているかをちゃんと知ってもらうこと。2つ目は、グループの内にあるソリューションチームの活動内容について広報です。弊社は自社サービスだけでなく、トヨタグループ全体に対して多様な活動をやっている。そこを来場者のの皆さんが少しでも知ってもらいたかったです」</li>
</ul>
<p><img src="/assets/blog/authors/you/04/1000013261.jpg" alt="質問ボード"></p>
<p>具体的には来場者に対して、ブースで用意されていた質問ボードに記載されている、</p>
<ul>
<li>Q3.KINTOテクノロジーズを知っていますか？</li>
</ul>
<p>この質問を通してコミュニケーションをとりながら、クラウドインフラグループとしての活動と会社の方向性を広く認知してもらうことができました。</p>
<p>また、他にも重要な気づきがありまして、</p>
<ul>
<li><strong>古代さん</strong>：「イベント参加者全員がブースに来てくれるわけではありませんが、ブースに来て話を聞いてくれる人は、少なくともKINTOテクノロジーズに興味を持ってくれています。また、CloudNative自体に関わっていない人も一定数いることに気づきました。イベントタイトルに対してのギャップとして印象的でした」</li>
</ul>
<p>最初はCloudNativeのイベントなので、CloudNativeエンジニアが多く参加する印象がありましたが、既にCloudNativeな活躍を実施されているエンジニアより、CloudNativeをこれから目指したいと考えている方もたくさんいる事が分かりました。実際、会場ではアプリ開発者や営業・デザイナー・記者・学生などの様々なポジションの方が参加されていました。</p>
<ul>
<li><strong>安田さん</strong>：「もっとたくさんの人にKINTOテクノロジーズを知ってもらおうと思っていましたが、来場していただいた方の半数は既にKINTOテクノロジーズを知ってくれていました。直近実施したイベントの<a href="https://www.kinto-technologies.com/news/20250701/">開発生産性カンファレンス</a>や<a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6?productVariantID=6A68KiYtH7WX6K7LA9XHNw">技術書典</a>でKINTOテクノロジーズを知っていただいた方が多くて印象的でした」</li>
</ul>
<p>また、CloudNativeDaysの参加者の傾向としてインフラ担当者がかなり少ないことも分かりました。アプリ担当者がインフラも兼任していることが多く、アプリコードを書きながらインフラのリソース作成やセキュリティ対応を実施するなど非常に大変だと言っていました。KINTOテクノロジーズは我々のように専門のインフラ組織やSRE/セキュリティ組織が存在しており、役割が細分化されているので、各々の専門領域に注力できているのだなと改めて強く感じてます。</p>
<p>質問ボードを通じて、様々な人の考え方を聞けたことが良かったとの意見もありました。質問の内容についても来場者のバックグラウンドや性格・気になるポイントによって変更したりしました。</p>
<ul>
<li><p>Q1.あなたのクラウドスタイルは?　→　業務スタイル &amp; 開発スタイル</p>
</li>
<li><p>Q2.あなたにとって「良いインフラ」とは？　→　「良い仕事」　&amp; 「良いシステム」</p>
</li>
<li><p><strong>松尾さん</strong>：「ボードにある質問を深掘りして、人の考え方とか重要視していくことを聞けてよかったなと思ってます。印象に残っているのはAWS以外のクラウドサービスを触ってる人が意外と多くて、自分はAWSしか触ってないからマルチクラウドを少しでもやりたくなりました。」</p>
</li>
</ul>
<p>様々なロールの方や、技術に対して高い興味ある方が多く参加されており、</p>
<ul>
<li><strong>白井さん</strong>：「多種多様なロールの方に来場していただきましたが、やはり私の専門領域以外のロールの方と話を広げるのが難しかったです。今後は幅広いロールの方と話ができるように自分の土台（知識・話術）を作っていきたいと思います」</li>
</ul>
<p>CloudNativeだけどブース運営に関しては、単純にインフラの専門家のポジションではなくあらゆるロールの来場者を考慮して対応ができるように体制を整える必要があることを感じてます。</p>
<h3>成果と改善点</h3>
<p>今回の最大の成果として次のような会話があって、</p>
<ul>
<li><p><strong>古代さん</strong>：「インフラチームとカイゼンチーム、ほぼ全員がグループの運営側としてイベントに参加しました。なかなかない経験です。みんなで同じイベントの経験を共有できたのはありがたいです。イベント運営しながら『こうだよね、ああだよね』という学びを共有できました」</p>
</li>
<li><p><strong>白井さん</strong>：「チームのみんなでイベントを開催することによって、よりチームの一体感が増しました。また、他社の人たちが実践している内容を伺うことによって新たな気づき得ることもできました。」</p>
</li>
</ul>
<p>個人でイベントを参加することもいいことだと思いますが、チームとして参加してたらお互いが同じマインドセットを成立することができます。チームの成長と意識の向上、一緒にやることで連続性を持った技術発信の道を改めて整備ができたと思います。</p>
<p>一方で、改善点もありました。今回は参加者として興味あるセッションを聞いて知見を広める目的もありましたので、事前にグループ内でシフトを決めていました。</p>
<ul>
<li><p><strong>松尾さん</strong>：「シフト表を作成していましたが、自分の番になりブースに向かったらみんなブースにいたので『あれ？』と思いました」</p>
</li>
<li><p><strong>古代さん</strong>：「次回ブースをやるときは3人はブース内で、他の人はセッション聴講してインプットできるようにした方がいいかと。セッションでブログのネタを探すなど、効率化を考える必要があります」</p>
</li>
</ul>
<p>課題として認識したことはシフトの問題より、時間の効率化になります。CIGはブース運営者・参加者の二つの立場でイベントに参加したため、ブース運営とセッションを聞いて自らの収穫を得る間のバランス調整が難しかったと思います。</p>
<p>それでも、ちゃんと振り返りをしながら個々で考えていた課題を明確にすることができました。質問ボードでKINTOテクノロジーズを知っていたという答えが多かったことは、長く広報活動を実施した成果であり、イベントの運営は単発で終わることではない証でもあります。クラウドインフラGもKINTOテクノロジーズの一員として技術発信を継続していきますので、今回の参加は今後の活動のエッセンスになりました。</p>
<h3>CloudNativeの本質</h3>
<p><img src="/assets/blog/authors/you/04/IMG_6895.jpg" alt="ブースの対応"></p>
<p>最後に、みなさんに記載いただいた質問ボードの結果を深掘りしました</p>
<ul>
<li><strong>白井さん</strong>：「『良いインフラとは？』という質問に対して、『愛せること』と回答した人が多かったです。深掘りしてみると、自社サービスを愛せるまで理解することが、CloudNativeという言葉の本質の理解を深めることの1つなのかもしれません」</li>
</ul>
<p>古代さんからは、登壇でも発表したメッセージを改めて強調しました。</p>
<ul>
<li><strong>古代さん</strong>：「CloudNativeを最初から完璧にできている組織はありません。色々なグラデーションがあります。技術だけでなく、文化や組織も含めて作っていく必要があります。スポンサーブースで色々な企業と話しながら、改めて実感しました」</li>
</ul>
<h2>参加者インタビュー</h2>
<h3>Platform G、島村さん</h3>
<p>島村さんは、同じグループの李さんの登壇を見るため、そして自身もCFPを出していたため、どちらにしろ参加する予定でした。そして、最近チームでKubernetesやEKSを触り始めたこともあり、技術的な関心も高まっています。</p>
<h4>印象に残ったセッション：<a href="https://event.cloudnativedays.jp/cndw2025/talks/2694">任天堂の事例</a></h4>
<p>島村さんが最も印象に残ったのは、任天堂のセッションでした。</p>
<ul>
<li><strong>島村さん</strong>：「オンプレ環境の事例ですが、この文化を取り込みたいと思いました。真面目に毎日1回フルでリグレッションテストを回していて、バグ報告以外の目的でもテスト結果を使っていることに驚きました。」</li>
</ul>
<p>特に興味深かったのは、テスト結果の多目的活用です。</p>
<ul>
<li><strong>島村さん</strong>：「UI的な観点で、デザイナーが表面上のデザイン単体ではなく『ビルド結果の画像から考えると、こうした方が(周りなどの雰囲気と)マッチする』という判断材料となるそうです。過去からのイベントの流れがあり、その流れで自然かどうかも分かる。声優のアテレコにも使用しているそうです。自動テストをバグを見つけるだけのものではなく、他の用途にも使う。テストの結果を使ってプロアクティブに多方面で改善点を見つけて改善していく文化です。」</li>
</ul>
<p>そして、任天堂の事例と弊社との違いについても考察してまして、</p>
<ul>
<li><strong>島村さん</strong>：「KINTOにはこの文化がまだ足りないと思いました。できるようにして、気づいて、直していくという感覚が遠いです。重くなったウェブサイトは改善していく側面でも必要ですし、KINTOのサービスをより良く提供する面もあると感じました」</li>
</ul>
<h4>技術的な収穫</h4>
<p>島村さんは、いくつかの技術的な収穫を話してくれまして、</p>
<ol>
<li><p><strong>Alloyのログ二重転送の原因の気づき</strong>：最近ブルーグリーンデプロイをした時、Alloyのログが二重転送されていた原因だと思われる事象に似ていると気付いて、調査のインプットとしました。</p>
</li>
<li><p><strong>Dockerビルドのセキュリティ</strong>：ビルドしたコンテナイメージがマルウェアに汚染されていないか、署名や検証のフレームワークがあることを知りました。昨今の情勢もありましたので、セキュリティチームに情報を共有しました。</p>
</li>
<li><p><strong>RCAの優位点の再確認</strong>：インシデント管理のSaaSは、インシデント管理のやり方だけをAI化していますが、弊社では<a href="https://event.cloudnativedays.jp/cndw2025/talks/2683">RCA</a>で一気通貫で作っています。やはり全部統一して一気通貫しないといけないと、RCAの優位点・良かった点を見直せました。</p>
</li>
</ol>
<p>逆に、いくつかのセッションに対して疑問を感じたことも話してくれました。</p>
<ul>
<li>CloudNative技術が課題を解決する手段ではなく、CloudNative技術を使うための目的になっていることもあった</li>
<li>知識として得られるような内容ではなく、それぞれのケースに基づいた話を聞きたかった</li>
</ul>
<h4>次回への展望</h4>
<p>島村さんは、次回への改善点と展望を語りまして、</p>
<ul>
<li><strong>島村さん</strong>：「早めにイベントのセッション登録をして、自分が聞きたいセッションを確実に聞けるようにしたいです。また、参加して得た情報を個人の中に留めず、他のメンバーが見れるようにする。こういうことがある、事例があると知ってもらわないと、参加した時間がもったいない。知識は、広がらないとあまり意味がないと考えています。」</li>
</ul>
<p>登壇についても意欲を見せてくれました。</p>
<ul>
<li><strong>島村さん</strong>：「CFPを2回落ちているので、次は是非登壇したいです。ただ、ネタが問題です。RCAについては既に喋り、文化論も<a href="https://platformengineering.connpass.com/event/364328/">Platform Engineering Meetup</a>で喋っています。新しいネタを見つける必要があります」</li>
</ul>
<h2>まとめ</h2>
<p><img src="/assets/blog/authors/you/04/IMG_6905.jpg" alt="ブースの対応"></p>
<p>我々はCloudNative Days 2025 Winterへの参加を通じて、多くの学びと気づきを得ることができました。</p>
<h3>技術と文化の両輪</h3>
<p>登壇者の古代さんと李さんは、それぞれ異なるアプローチでCloudNativeを語りました。古代さんは組織文化の観点から、李さんは技術的な実践の観点からです。しかし、両者に共通していたのは、<strong>CloudNativeは手段であり、目的ではない</strong>というメッセージです。</p>
<p>ブース運営を通じて、私たちは「CloudNativeを最初から完璧にできている組織はない」ことを再確認しました。技術だけでなく、文化や組織も含めて作っていく必要があります。</p>
<p>参加者の島村さんが任天堂の事例から学んだように、CloudNativeは技術ではなく文化です。プロアクティブに改善点を見つけて改善していく文化、テスト結果を多目的に活用する発想、そして何より、自社サービスを「愛せる」まで理解すること。これらがCloudNativeの本質なのかもしれません。</p>
<h3>チームの一体感</h3>
<p>今回のイベントで最も大きな成果の一つは、インフラチームと改善チーム、ほぼ全員がグループの運営側としてイベントに参加できたことです。みんなで同じイベントの経験を共有し、学びを共有できたことは、チームの一体感を大きく高めました。他のグループの方も助けてくださり、とても良い時間を過ごす事ができました。</p>
<h3>継続的なアウトプットの重要性</h3>
<p>登壇者の両名が共通して感じたのは、日々のアウトプットの重要性でした。40分の発表を準備する大変さを経験し、普段からテックブログや勉強会での発表を通じて、継続的にアウトプットする習慣の必要性を実感しました。</p>
<p>また、島村さんが指摘したように、イベントで得た情報を個人の中に留めず、チーム・組織で共有することも重要です。知見を広げることで、組織全体の成長につながると信じてます。</p>
<h3>次回への展望</h3>
<p>2026年には、CloudNative会議（Platform Engineering Meetup、SRE会議、CloudNativeDaysの合同イベント）が開催されます。また、JAWS Daysへの出展も予定しています。</p>
<p>CloudNativeに悩む皆さんへ、古代さんからのメッセージをお伝えします。</p>
<p>「技術に振り回されないでください。ビジネスの成功、ビジネスの先にあるミッションの達成のために、一緒に悩みながら文化づくり、組織づくりを頑張りましょう。CloudNativeは手段であり、目的ではありません」</p>
<p>そして、李さんからのアドバイスも忘れないでください。</p>
<p>「まずは応募してみることが重要です。恐れずに、まずCFPを出してみる。採択されてから、未来の自分が何とかしてくれます」</p>
<p>今回のイベント参加を通じて得た学びと気づきを、今後の組織づくり、文化づくり、そして技術的な挑戦に活かしていきたいと思います。コミュニティの皆さんとの継続的な交流を通じて、共に成長していけることを楽しみにしています。</p>
<hr>
<p>[^1]: Cloud Infrastructure Gでは記事を作成する時点で、インフラチームとカイゼンチーム、ソリューションチームの三つのチームが存在します。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/you/04/IMG_6930.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[My Two-Month Paternity Leave: Preparation and Return]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-08-child-care-leave-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-08-child-care-leave-en/</guid>
            <pubDate>Tue, 09 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Reflections on stepping away from work for two months of paternity leave]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1>Introduction</h1>
<p>Hello, and thank you for reading!
I&#39;m Nakamoto, a frontend developer at <a href="https://factory.kinto-jp.com">KINTO FACTORY</a>.</p>
<p>This time, rather than discussing technical topics, I&#39;d like to share my experience taking paternity leave when my son was born in August this year. I&#39;ll cover the handover process at the team level and my thoughts after returning to work.</p>
<h2>Consulting with My Manager</h2>
<p>In April of this year, I was assigned as the team leader for the frontend team within the FACTORY E-commerce Development Group. At first, I felt a bit hesitant about being away from work for an extended period, but when I consulted with my manager, he wholeheartedly encouraged me to take paternity leave.</p>
<p>I was told that the first few months after birth are particularly demanding for mothers, both physically and mentally, so I should try to support my wife as much as possible.</p>
<p>So I applied for about two months of paternity leave, with the plan to reassess after one month whether I could return early.</p>
<h2>Handover Items</h2>
<p>Now, when it came to handing over my daily responsibilities, in addition to my regular frontend development work, as team leader I handled:</p>
<ul>
<li>Reviewing architecture for new projects, making directional decisions, and coordinating with related departments</li>
<li>1-on-1 meetings with each team member</li>
<li>Semi-annual reviews and evaluations with each team member</li>
<li>Recruiting activities</li>
</ul>
<p>Let me go into detail on each of these.</p>
<blockquote>
<p> Reviewing architecture for new projects, making directional decisions, and coordinating with related departments</p>
</blockquote>
<p>After regularly checking the roadmap with the PdMs and my manager for late August when my leave was scheduled to start, it turned out there weren&#39;t any major new projects coming up and most were already in progress.</p>
<p>So I started documenting the projects I had been handling in Confluence, making it a habit to briefly summarize things like &quot;who I discussed this with and what was decided&quot; and &quot;what I&#39;ve completed so far.&quot;</p>
<p><img src="/assets/blog/authors/k.nakamoto/childcare-leave/handover.jpg" alt="引き継ぎ資料の例"></p>
<blockquote>
<p>1-on-1 meetings with each team member
Semi-annual reviews and evaluations with each team member</p>
</blockquote>
<p>Next was the management area.
One of my missions as team leader was to review and evaluate each team member&#39;s progress on a semi-annual basis.</p>
<p>That review period was going to overlap directly with my leave period, so from the start of the April term, I had each team member set their goals in advance to make the reviews go smoothly.</p>
<p>In practice, each member would record their goals and write monthly updates in Confluence about &quot;what they accomplished and what they plan to do next month,&quot; which served as discussion topics for our 1-on-1s.
I also made notes in that same Confluence page about things I noticed and the efforts I appreciated, so I could provide feedback during reviews.</p>
<p>Since I was planning to take leave around the time of the birth, I figured that if my leave overlapped with the evaluation period, I could simply hand over that Confluence page to my manager, allowing him to quickly review each member&#39;s goals and achievements.</p>
<blockquote>
<p>Recruiting activities</p>
</blockquote>
<p>For this, I basically asked my manager to take over.</p>
<p>However, I wanted the development team members to get a sense of candidates&#39; technical skills and communication abilities from the interview stage, so I had them join interviews to check on communication and technical skills at the team level.</p>
<h1>During My Leave</h1>
<p>With most of the handover preparations in place, on August 24th, our healthy baby boy was born!</p>
<p><img src="/assets/blog/authors/k.nakamoto/childcare-leave/birth.jpg" alt="生まれたばかりの赤ちゃん"></p>
<p>I took three days of special leave, going back and forth between local government offices and the hospital. On the day before my paternity leave officially started, I went to the office just once to return my company equipment (PC, phone, employee badge, etc.).
From there, for about two months, I stepped back from work and fully committed to taking care of my baby!</p>
<h2>Childcare Is Tough</h2>
<p>The FACTORY E-commerce Development Group has plenty of experienced dads, and before the birth I&#39;d heard all kinds of stories, but as expected, the first month was really hard.
My wife and I split childcare and sleep into half-day shifts and I handled childcare from morning until night. My wife suggested this approach, saying it would help me maintain my sleep cycle for when I returned to work, so I was basically up during the day and sleeping at night.</p>
<p>With feeding, diaper changes, soothing the baby, and bath time coming at me almost by the minute, I barely had any time to think about myself. I don’t think work even crossed my mind at all during the first week or two. </p>
<h2>Still, the Service Is on My Mind</h2>
<p>About a month into my leave, I was able to talk with my manager and get updates on project progress and the team&#39;s status. After a month of hardly going out and barely talking to anyone, it felt like a huge mental reset.</p>
<p>Also, by this point I&#39;d gotten quite used to childcare, and my son was settling into longer sleep periods, so I remember occasionally checking on service updates.</p>
<p>KINTO FACTORY releases new products and features on Wednesdays, so every Wednesday I&#39;d visit the site to see &quot;what&#39;s new this month?&quot;</p>
<h2>The Return</h2>
<p>And so, after 65 days of paternity leave, I returned to work in November.
There weren&#39;t any major changes to the group members, and some ongoing projects had been pushed back. Overall things hadn&#39;t changed dramatically from before my leave, so I was able to return smoothly.</p>
<p>However, since my wife suddenly had to handle childcare solo during the day, I try to reduce her burden by going to work as early as possible in the morning and coming home as early as possible in the evening.
This is made possible by our full-flex system. As long as I coordinate meetings and get agreement from the team, I can freely adjust my working hours.</p>
<h1>Conclusion</h1>
<p>By dedicating myself to childcare during the first two months, I was able to witness my son’s growth up close, from his very first smiles to his little coos and all the small changes he made day by day. Being so involved during this period felt like an incredibly precious experience.</p>
<p>I was only able to have this experience because my manager and everyone in the group warmly sent me off on paternity leave. I&#39;d like to take this opportunity to express my gratitude.</p>
<p>At KINTO Technologies, I believe it&#39;s an environment where men can take paternity leave without difficulty. In fact, I&#39;ve heard that many male engineers across different divisions have taken paternity leave just this year alone (there are many kids the same age as my son in the company!).
I hope this is helpful for anyone preparing to take paternity leave or getting ready to return to work.</p>
<p>Also, the article below introduces a day in the life of an experienced dad in the same FACTORY E-commerce Development Group, so please check that out too!</p>
<p><a href="/posts/2025-09-08-factory-engineer-with-child/">A Must-Read for Parent Engineers! A Day in the Life of a KTC Dad Engineer</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/k.nakamoto/childcare-leave/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[福岡・新オフィスツアー！細部までこだわり抜いた「新オフィスの物語」]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-09-KINTOテクノロジーズ-福岡オフィスツアー/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-09-KINTOテクノロジーズ-福岡オフィスツアー/</guid>
            <pubDate>Tue, 09 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[福岡・新オフィスツアー！細部までこだわり抜いた「新オフィスの物語」]]></description>
            <content:encoded><![CDATA[<p>こんにちは！
人事企画G 労務・総務チームのつんつんです。 構想から約1年。何もないガランとした空間だった場所が、ついに「働きたくなるオフィス」へと生まれ変わりました。 今回は、写真や動画だけでは伝えきれない、私たちの「こだわり」と「熱量」を詰め込んだ福岡の新オフィスツアーへご案内します。壁紙一枚、椅子一脚にまでストーリーがあるんですよ。</p>
<h1>1. 幸せが溢れてくるエントランス</h1>
<p>まず皆様をお迎えするのは、ガラス張りの視界が良い緑豊かなエントランスです。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-01.jpg" alt=""></p>
<p> ただ鉢植えを置くだけでは面白くないですよね。そこで、あえて段差をつけて植物を配置し、空間に「枠」を作りました。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-02.jpg" alt=""></p>
<p>こうすることで、外からも中からも緑が映え、空間がキュッと締まって見えるんです。まさに「一石二鳥」のアイデア。  <strong>「幸せが溢れてくる入り口」</strong> ――手前味噌ですが、そんな表現がぴったりの空間になりました。</p>
<h1>2. 大人心をくすぐる「港」のラウンジとカウンター</h1>
<p>福岡といえば「港」。ラウンジエリアのテーマはずばり「Port（港）」です。 ここで絶対に見てほしいのが、壁面のデザイン。これ、住宅の外壁などに使われる**「ガルバリウム」**という本物の建材を使っているんです。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-15.jpg" alt="">
実はこれ、ただの飾りじゃありません。このコンテナロックはガラガラと動かすことができるんです。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-03.jpg" alt="">
またカウンター奥には ハンドドリップでコーヒーを淹れたり、業務終了後にはバーカウンターのように使ってイベントを行ったりすることも想定しています。</p>
<table>
<thead>
<tr>
<th>裏</th>
<th>表</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/tsujimoto/fukuoka-04.jpg" alt=""></td>
<td><img src="/assets/blog/authors/tsujimoto/fukuoka-05.jpg" alt=""></td>
</tr>
</tbody></table>
<p>また休憩エリアの照明には船舶風のマリンランプや、ガラスの浮き球を模した照明を吊るし、船内のような温かみのある雰囲気を演出しています。細部まで「港」を感じさせる、大人心をくすぐる空間です。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-06.jpg" alt=""></p>
<h1>3. 「WE BUILD BRIDGES」：絶景のラウンジエリア</h1>
<p>窓際は、このオフィス一番の特等席です。 目の前には天神の街と橋が広がり、開放感は抜群。この壁には大きく <strong>「WE BUILD BRIDGES」</strong> のアートワークを施しました。福岡で有名な荒津大橋からヒントを得て拠点長が各拠点がつながるようにという想いをこめて社内のクリエイティブが作成しました。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-07.jpg" alt="">
また港方面には椅子を置かず、「スタンディングエリア」にしています。 </p>
<p>海を眺めながら仕事するとアイデアが浮かんでくるかもですね。</p>
<table>
<thead>
<tr>
<th>夕方</th>
<th>夜</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/tsujimoto/fukuoka-08.jpg" alt=""></td>
<td><img src="/assets/blog/authors/tsujimoto/fukuoka-09.jpg" alt=""></td>
</tr>
</tbody></table>
<p>夜は夜景が綺麗でしてまた違った雰囲気を出しております〜</p>
<p>ふと視線を上げれば、窓の外に荒津大橋が見えます。（昼間の写真）</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-10.jpg" alt="">
 煮詰まった時にここに来て、外を眺めながら立って議論すれば、新しいアイデアの架け橋がかかるかもしれません。もちろん、電源も完備しているので集中作業もバッチリですよ。</p>
<h1>4. 会議室エリア</h1>
<p>執務・会議室エリアは、雰囲気をガラッと変えて大阪オフィスとはまた違う、落ち着いた大人の雰囲気です。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-11.jpg" alt=""></p>
<p>会議室の番号フォント、少し変わっていると思いませんか？ 実はこれ、ステンシルフォントのデザインフォントを採用しているんです。IT企業らしい遊び心をこっそり忍ばせています。</p>
<p>会議室４つの内３つはあえて「キャスター（タイヤ）がない」モデルを選びました。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-12.jpg" alt=""></p>
<p>不便そうですか？ いえいえ、キャスターがないと席を立った時に椅子が散らばらず、元に戻そうと心理が働き常に整然と元の位置に戻るんです。美しい空間を保つための、私たちの美学であり、こだわりポイントです。</p>
<p>また青色の壁はOsaka Tech Labと同じ青を使うことにより、西日本の一体感を創出してます。</p>
<h1>5. 見えない場所にこそ「愛」を</h1>
<p>最後に、こっそりお見せするのが、特注のベンチソファの下です。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-13.jpg" alt=""></p>
<p>座面をパカッと開けると、そこには災害用備蓄品が入っています。 おしゃれなデザインの裏側に、社員の安全を守る備えもしっかり隠しています。「何かあった時でも、ここなら安心」と思える場所にしたかったんです。</p>
<h1>おわりに：こだわり抜いた「柱」の話</h1>
<p>実はこのオフィスを作る際、柱のタイル装飾だけに予算をかけるか非常に悩みましたそれでも、「空間の空気感を変えるためには必要」と感じました。タイルを貼らなくても場所としては成立しますが、気持ちよく仕事をしてほしい・・・私たちのそんな想いが詰まっています。また本棚と一緒にすることによりスペースを有効活用しました。</p>
<p><img src="/assets/blog/authors/tsujimoto/fukuoka-14.jpg" alt=""></p>
<p>コーヒーの香り、コンテナ扉の重厚感、そして窓からの素晴らしい眺め。 ぜひ一度、遊びに来てください。このこだわりの場所で、皆様とお会いできるのを楽しみにしています！</p>
<p>今後もオフィスの改善活動を続けていきますので、Tech Blogでの報告を楽しみにしていてくださいね。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Why I Joined KINTO Technologies as a Person with Visual Impairment]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-09-What-I-want-to-achieve-as-blind-person-at-mobile-company-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-09-What-I-want-to-achieve-as-blind-person-at-mobile-company-en/</guid>
            <pubDate>Tue, 09 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[My journey from the digital accessibility industry to a mobility company, and my vision for the future]]></description>
            <content:encoded><![CDATA[<p>This article is for Day 9 of the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> 🎅🎄</p>
<h2>Introduction</h2>
<p>Hello. I&#39;m Katsutoshi Tsuji, and I joined KINTO Technologies in November.<br>I&#39;m an engineer who has worked in the digital accessibility field for over 20 years, and I have been completely blind since birth.</p>
<p>In this article, I&#39;ll share why I, a visually impaired person, chose to join KINTO Technologies, a company specializing in mobility, and what kind of future I&#39;m working toward. This might sound like a futuristic dream, but please stick with me to the end.</p>
<h2>The Challenges of Mobility for Visually Impaired People</h2>
<p>I was born in Sasebo City, Nagasaki Prefecture. Nagasaki is known as a city of hills. Buses were more common than trains, but my home was far from the bus stop, making it unrealistic for a visually impaired person to live independently.</p>
<p>When I was a child, every household had a car, and transportation centered around driving. The only way I could get around was to have my family drive me.</p>
<p>When I moved to Tokyo about 30 years ago, I remember being moved by the realization that once I memorized the route to the station, I could travel freely by train. That&#39;s how much my life in the countryside lacked freedom of movement.</p>
<p>The place where I currently live is also far from the station, which is inconvenient for visually impaired people. However, working remotely while listening to the birds singing outside my window is a very pleasant lifestyle. Some of my visually impaired acquaintances worry that it might be inconvenient, but after living here for three years, I haven&#39;t experienced any major difficulties in daily life.</p>
<h2>My Dream of Autonomous Driving</h2>
<p>How interested are you in autonomous driving?<br>Since I moved here, I have found myself increasingly fascinated by autonomous driving and the possibility it offers: the freedom to move on my own terms. For me, as a visually impaired person, autonomous driving is more than just technology. It is a dream of reclaiming freedom of mobility. </p>
<h2>A Video of an Autonomous Taxi Sparked My Interest</h2>
<p>The first time I saw a visually impaired person riding in an autonomous vehicle was in a video released by Google in 2012. At the time, I was skeptical, wondering if it could really become a reality.</p>
<p><a href="https://www.youtube.com/watch?v=cdgQpa1pUUE">https://www.youtube.com/watch?v=cdgQpa1pUUE</a></p>
<p>However, in 2024, I was amazed when I saw a video of a visually impaired person riding in an autonomous taxi. The system allows passengers to operate their smartphone and locate the vehicle by the sound of its horn. What&#39;s more, it was actually running on public roads in the United States.<br>At that moment, I strongly felt that I wanted to try it myself.</p>
<p><a href="https://www.youtube.com/watch?v=4OPiC-zXtJk&list=PLzaPe49p32aZ7SPFDhvgJV-cOpskQ5VYu&index=7">https://www.youtube.com/watch?v=4OPiC-zXtJk&amp;list=PLzaPe49p32aZ7SPFDhvgJV-cOpskQ5VYu&amp;index=7</a></p>
<p>I thought, at KINTO Technologies, I might be able to get involved with autonomous driving technology. Someday, I want to work on projects related to autonomous driving.
This is why I decided to work at a mobility company. I want to leverage the accessibility knowledge I&#39;ve built over the years to contribute to mobility for visually impaired people.</p>
<h2>What I Aim to Achieve at KINTO Technologies</h2>
<p>Even if we aim for a future where autonomous vehicles become commonplace for visually impaired people, it won&#39;t happen immediately. For most people, visually impaired person and car are probably the most unlikely combination.</p>
<p>As I mentioned earlier, you can probably imagine a visually impaired person being driven somewhere by someone else, but it&#39;s harder to imagine them getting into a car alone and traveling to their destination, right?
However, this need definitely exists, and especially visually impaired people living in rural areas dream of a future where they can go wherever they want, whenever they want, by themselves.
While public transportation can get you close to your destination, finding and navigating to the actual location from there is not easy.
For example, even if you use pedestrian GPS navigation to travel, as you approach your destination, the navigation ends with a message like &quot;You are approaching your destination.&quot;
Those few meters from there can be a huge hurdle for visually impaired people who cannot see.</p>
<p>In this way, if we replace &quot;car&quot; with &quot;mobility&quot; in the context of visually impaired individuals, everyone can easily understand that the freedom to get around is essential for achieving one&#39;s goals. To create a future where autonomous vehicles become a key means of mobility, I will work on the following.</p>
<h3>1. Making Accessibility the Norm in the Organization</h3>
<p>In the mobility industry, the importance of accessibility is not yet fully recognized. I will engage in careful dialogue within the company to explore together:</p>
<p>   Why accessibility is necessary
   What teams should start with
   What goals we should aim for  </p>
<p>By working alongside everyone and showing them the needs of users who may not have been considered before, what inconveniences they experience, and how they solve problems, my goal for the first year is to build an organization where people don&#39;t see accessibility as someone else&#39;s problem.
As a result, I hope that the accessibility of various initiatives undertaken by KINTO Technologies will improve, reach new users, eventually become a value for the organization, and lead to efforts that change society.</p>
<h3>2. Creating an Organization Where People with Disabilities Want to Continue Working</h3>
<p>Companies with 50 or more employees are required to hire people with disabilities, but in reality, some say, I don&#39;t know how to interact with them, or I can&#39;t imagine what kind of work to assign them.
There are many cases where employees are only given standardized tasks and become isolated within the company.</p>
<p>I am not the only employee with a disability at KINTO Technologies. My role is to demonstrate how people with disabilities contribute to the organization, and to foster an environment where we can work together as colleagues toward our company&#39;s goals.
And I want to create an organization where we can truly say, &quot;I want to keep working here.&quot;</p>
<p>I will maximize the value that can only be changed from inside the organization and contribute to the growth of KINTO Technologies.</p>
<h2>Conclusion</h2>
<p>For visually impaired people, freedom of movement has the power to change lives. At KINTO Technologies, I will take on the challenge of bringing that future closer to reality.<br>If anyone resonates with this initiative, I would love to think about the future of mobility together.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[視覚障害者の私がKINTOテクノロジーズに入社した理由]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-09-What-I-want-to-achieve-as-blind-person-at-mobile-company/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-09-What-I-want-to-achieve-as-blind-person-at-mobile-company/</guid>
            <pubDate>Tue, 09 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[デジタルアクセシビリティ業界からモビリティカンパニーに転職した経緯と今後の展望]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の9日目の記事です🎅🎄</p>
<h2>初めに</h2>
<p>こんにちは。11月に KINTOテクノロジーズ に入社した辻勝利です。<br>私は20年以上、デジタルアクセシビリティの分野で働いてきたエンジニアで、生まれたときから全盲の視覚障害者です。</p>
<p>この記事では、なぜ視覚障害者の私がモビリティを専門とするKINTOテクノロジーズに転職したのか、そして今後どんな未来を目指しているのかをお話しします。少し未来を見据えた、夢のような話になるかもしれませんが、ぜひ最後までお付き合いください。</p>
<h2>視覚障害者と「移動」の課題</h2>
<p>私は長崎県佐世保市で生まれました。長崎といえば坂の町。電車よりもバスが普及していましたが、私の家はバス停からも距離があり、視覚障害者が独力で日常生活を送るのは現実的ではありませんでした。</p>
<p>子供のころ、各家庭には自家用車があり、移動は車が中心。私が移動するには、家族に車を運転してもらうしかありませんでした。</p>
<p>30年ほど前に上京したとき、駅までの道を覚えれば電車で自由に移動できることに感動したのを覚えています。それほど、地方での生活は私にとって「移動の自由」がないものでした。</p>
<p>今住んでいる場所も駅から遠く、視覚障害者には不便な環境です。しかし、リモートワークで仕事をしながら、窓から聞こえる鳥の声に耳を傾ける生活はとても心地よいものです。視覚障害者の知人からは「不便ではないか」と心配されますが、3年住んでみて、日常生活に大きな不便は感じていません。</p>
<h2>自動運転への憧れ</h2>
<p>皆さんは「車の自動運転」にどれくらい興味がありますか？<br>私はこの場所に引っ越してから、自分の意思で自由に移動できる可能性を秘めた自動運転に強く惹かれるようになりました。視覚障害者である私にとって、自動運転は単なる技術ではなく、「移動の自由」を取り戻す夢です。</p>
<h2>きっかけは自動運転タクシーの動画</h2>
<p>初めて視覚障害者が自動運転車に乗る様子を見たのは、2012年にGoogleが公開した動画でした。当時は「本当に実現できるのだろうか？」と半信半疑でした。</p>
<p><a href="https://www.youtube.com/watch?v=cdgQpa1pUUE">https://www.youtube.com/watch?v=cdgQpa1pUUE</a></p>
<p>しかし2024年、自動運転タクシーに視覚障害者が乗車する動画を見て驚きました。スマートフォンを操作し、クラクションの音で車の位置を確認して乗車する仕組み。しかも、アメリカの公道で実際に走っているのです。<br>この瞬間、「自分も試してみたい」と強く思いました。</p>
<p><a href="https://www.youtube.com/watch?v=4OPiC-zXtJk&list=PLzaPe49p32aZ7SPFDhvgJV-cOpskQ5VYu&index=7">https://www.youtube.com/watch?v=4OPiC-zXtJk&amp;list=PLzaPe49p32aZ7SPFDhvgJV-cOpskQ5VYu&amp;index=7</a></p>
<p>「KINTOテクノロジーズであれば、自動運転の技術にかかわることができるかもしれない。いつかは自分も自動運転にかかわれるような仕事がしたい。」
これが、私がモビリティカンパニーで働きたいと考えたきっかけです。長年培ったアクセシビリティの知識を活かし、視覚障害者の移動に貢献したいと考えています。</p>
<h2>KINTOテクノロジーズで目指すこと</h2>
<p>自動運転車が視覚障害者にとって当たり前になる未来を目指すとしても、すぐに実現できるわけではありません。多くの人にとって「視覚障害者」と「車」は最も縁遠い組み合わせでしょう。</p>
<p>前述のように、視覚障害者が誰かに車を運転してもらって目的地に行くことは想像できますが、単独で車に乗車して目的地まで行くことはあまり想像できないのではないでしょうか？
しかし、このようなニーズは確かにあって、特に地方で暮らす視覚障害者は自分だけで行きたいところにいつでも出かけられるような未来を夢見ています。
公共交通機関を利用すれば目的地の近くまで移動することはできますが、そこから目的地を探して移動することは容易ではありません。
例えば、歩行者用のGPSナビゲーションを使って移動したとしても、目的地が近づくと「まもなく目的地付近です」という案内とともにナビゲーションは終了してしまいます。
そこからのほんの数メートルが、見ることのできない視覚障害者にとっては大きなハードルとなることがあるのです。</p>
<p>このように、「視覚障害者」と「移動」に置き換えれば、目的を達成するために移動することは誰もが容易に理解できるかと思います。その手段が「自動運転車」になる未来を作るために、私は次のことに取り組みます。</p>
<h3>1. アクセシビリティを組織の当たり前にする</h3>
<p>モビリティ業界では、アクセシビリティの重要性はまだ十分に認識されていません。私は社内で丁寧に対話し、</p>
<p>   なぜアクセシビリティが必要なのか
   チームで何から始めるべきか
   どんなゴールを目指すのか<br>    を一緒に考えていきます。</p>
<p>皆さんとともに活動し、これまで想定されていなかったかもしれないユーザーのニーズや、どんなことに不便を感じたり、どんなふうに課題を解決しているのかを見ていただくことで、「アクセシビリティは自分たちには関係ない」と思われない組織にすること。それが最初の1年の目標です。
その結果、KINTOテクノロジーズが取り組む様々な活動のアクセシビリティが向上し、新たなユーザーにリーチできて、ゆくゆくはそれが組織の価値になり、社会を変えるような取り組みになればいいなと考えています。</p>
<h3>2. 障害者が働き続けたい組織を作る</h3>
<p>50人以上の企業には障害者雇用が義務付けられていますが、実際には「どう接すればいいかわからない」「どんな仕事を任せればいいのか想像できない」という声もあります。
定型化された仕事だけを任され、企業の中で孤立してしまうケースも少なくありません。</p>
<p>KINTOテクノロジーズには私のほかにも障害のある社員がいます。私の役割は、私たち障害者が組織で働く姿を見てもらい、同僚として共に会社の目標に向かって進める環境を作ることです。
そして、私たち自身が「ここで長く働き続けたい」といえるような組織を作っていきたいと思っています。</p>
<p>「組織の内側からしか変えられない価値」を最大化し、KINTOテクノロジーズの発展に貢献していきます。</p>
<h2>最後に</h2>
<p>視覚障害者にとって「移動の自由」は人生を変える力を持っています。私はKINTOテクノロジーズで、その未来を現実に近づけるために挑戦します。<br>この取り組みに共感していただける方がいれば、ぜひ一緒に「移動の未来」を考えていきましょう。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Jetpack Compose Animation Techniques: Enhance App Impressions with Minimal Code Changes]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-08-Jetpack-Compose-Animation-Easy-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-08-Jetpack-Compose-Animation-Easy-en/</guid>
            <pubDate>Mon, 08 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Four effective Jetpack Compose techniques for adding UI animations with minimal code modifications]]></description>
            <content:encoded><![CDATA[<p>This article is for Day 8 of the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a>.</p>
<p>I&#39;m <a href="https://qiita.com/yamadacsa">Tsuyoshi Yamada</a>, an Android engineer at KINTO Technologies.
In this article, I&#39;ll introduce a collection of examples for adding animations to Jetpack Compose UIs with minimal code additions and changes to enhance the impression of animations.</p>
<h2>1. Introduction</h2>
<p>Animation is one of the key elements that significantly affects the impression of a smartphone app. Well-placed, thoughtful animations provide visual feedback for user actions, make app behavior easier to understand, enhance the app&#39;s impression, and increase user trust in quality. Jetpack Compose leverages the characteristics of declarative UI to enable highly productive animation implementation with shorter, more concise code than the traditional View system. This article introduces practical techniques for easily adding animations to existing source code with minimal additions and modifications.</p>
<p>This article verifies source code using <a href="https://developer.android.com/develop/ui/compose/bom/bom-mapping">Jetpack Compose Libraries BOM</a> 2025.12.00, which includes the latest stable version <a href="https://developer.android.com/jetpack/androidx/releases/compose-animation#1.10.0">1.10.0</a> of <a href="https://developer.android.com/jetpack/androidx/releases/compose-animation">Compose Animation</a> at the time of writing.</p>
<h2>2. Animation for UI Components with Coordinate Specifications</h2>
<p>The following code implements a simple puzzle game (15 Puzzle) in concise code:</p>
<pre><code class="language-kotlin:Puzzle15.kt">@Composable
fun Puzzle15(modifier: Modifier = Modifier) {
    var puzzleState by remember {
        mutableStateOf(PuzzleState.generate())
    }
    var moves by remember { mutableIntStateOf(puzzleState.moves) }
    val solved = puzzleState.isSolved()

    val titleStyle = MaterialTheme.typography.headlineLarge.merge(fontWeight = FontWeight.W600)
    val movesStyle = MaterialTheme.typography.titleMedium
    val solvedStyle =
        MaterialTheme.typography.titleLarge.merge(color = Color.Green, fontWeight = FontWeight.W600)
    val buttonStyle = MaterialTheme.typography.titleMedium.merge(fontWeight = FontWeight.W600)

    Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
        Column(
            modifier = modifier
                .fillMaxSize()
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = &quot;15 Puzzle&quot;,
                style = titleStyle,
                modifier = Modifier.padding(bottom = 16.dp)
            )

            Text(text = &quot;Moves: $moves&quot;, style = movesStyle)

            PuzzleGrid(
                puzzleState = puzzleState,
                modifier = Modifier.padding(vertical = 24.dp)
            ) { index -&gt;
                if (solved) return@PuzzleGrid
                puzzleState.moveTile(index)
                moves = puzzleState.moves
            }

            Button(onClick = {
                puzzleState = PuzzleState.generate()
                moves = 0
            }) {
                Text(&quot;New Game&quot;, style = buttonStyle)
            }
        }

        if (solved) {
            Text(text = &quot;🎉 Solved! 🎉&quot;, style = solvedStyle)
        }
    }
}
</code></pre>
<p>The 15 tiles drawn in a 4x4 grid are displayed with the following code:</p>
<pre><code class="language-kotlin:Puzzle15.kt">@Composable
private fun PuzzleGrid(
    puzzleState: PuzzleState,
    modifier: Modifier = Modifier,
    onTileClick: (Int) -&gt; Unit
) {
    BoxWithConstraints(
        modifier = modifier
            .fillMaxWidth()
            .aspectRatio(1F)
    ) {
        val gridSize = maxWidth
        val tileSize = (gridSize - 12.dp) / 4 // 3 gaps of 4dp

        for (position in 0.until(PuzzleState.GRID_COUNT)) {
            val value = puzzleState.tileAt(position)
            if (value == 0) continue
            val targetOffset = DpOffset(
                x = (tileSize + 4.dp) * (position % 4),
                y = (tileSize + 4.dp) * (position / 4)
            )

            PuzzleTile(
                value = value,
                onClick = { onTileClick(position) },
                modifier = Modifier
                    .offset(x = targetOffset.x, y = targetOffset.y)
                    .size(tileSize)
            )
        }
    }
}
</code></pre>
<p>Users tap movable tiles to move them to empty squares, repeating this operation to complete the puzzle. Up to 3 tiles can be moved simultaneously in a single move. While this is enjoyable enough as a simple game, it would be nice to have the physical sensation of moving tiles like in a real-world puzzle game. We aim to express this through animation.</p>
<p>The class representing the logical puzzle state is as follows:</p>
<pre><code class="language-kotlin">class PuzzleState private constructor(private val tiles: IntArray) {
    var moves = 0
        private set

    fun isSolved(): Boolean = tiles.all { tiles[it] == it + 1 || it == 15 }

    private fun getMoveOffsetOrZero(index: Int): Int {
        val emptyIndex = tiles.indexOf(0)
        val row = index / 4
        val col = index % 4
        val emptyRow = emptyIndex / 4
        val emptyCol = emptyIndex % 4

        return when {
            row == emptyRow -&gt; {
                if (col &lt; emptyCol) 1 else -1
            }

            col == emptyCol -&gt; {
                if (row &lt; emptyRow) 4 else -4
            }

            else -&gt; 0
        }
    }

    fun moveTile(index: Int) {
        val offset = getMoveOffsetOrZero(index)
        if (offset == 0) return

        var position = index
        do {
            position += offset
        } while (position &gt;= 0 &amp;&amp; position &lt; tiles.size &amp;&amp; tiles[position] != 0)
        do {
            val next = position - offset
            tiles[position] = tiles[next]
            position = next
        } while (position != index)
        tiles[index] = 0
        moves += 1
    }

    fun tileAt(index: Int) = tiles[index]

    companion object {
        const val GRID_COUNT = 16

        fun generate(): PuzzleState {
            val tiles = IntArray(GRID_COUNT) { it }
            // Shuffle (generate only solvable configurations)
            do {
                tiles.shuffle(Random.Default)
            } while (!isSolvable(tiles))
            return PuzzleState(tiles)
        }

        private fun isSolvable(tiles: IntArray): Boolean {
            val inversions = (0..15).sumOf { idx -&gt;
                (idx + 1 until tiles.size).count {
                    tiles[idx] != 0 &amp;&amp; tiles[it] != 0 &amp;&amp; tiles[idx] &gt; tiles[it]
                }
            }
            // Solvable if blank is on odd row (from bottom) and inversions are even, or blank is on even row and inversions are odd
            return (3 - tiles.indexOf(0) / 4) % 2 == inversions % 2
        }
    }
}
</code></pre>
<h3>2.1. Define a State that Holds Coordinate (Offset) State</h3>
<p>In the following code, we convert <code>targetOffset</code> from the original <code>PuzzleGrid(...)</code> using <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/core/package-summary#animateValueAsState(kotlin.Any,androidx.compose.animation.core.TwoWayConverter,androidx.compose.animation.core.AnimationSpec,kotlin.Any,kotlin.String,kotlin.Function1)">animateValueAsState(...)</a> to define a new <code>animatedOffset</code>, and replace the arguments of <code>Modifier.offset(...)</code> in <code>PuzzleTile(...)</code>. <code>animatedOffset</code> represents the same offset as <code>targetOffset</code> while having the capability to smoothly express changes in offset from before to after tile movement.</p>
<p>However, in this example, these 12 lines of code alone don&#39;t animate; we needed to use the variable <code>tilePositions</code> that sorts the display order, as commented with <code>// Track current position of each tile (by value)</code>. This sorting ensures that the order of tiles in the <code>for</code> loop within <code>PuzzleGrid(...)</code> is always kept in order of the numbers on the tiles. By doing this, under Jetpack Compose&#39;s declarative UI concept, we ensure expression continuity by always identifying tiles 1-15 as the same before and after movement, enabling animation display (we hoped the <code>label</code> argument of <code>animateValueAsState</code> would identify composable identity, but this effect was not confirmed in androidx.compose.animation library 1.10.0. Also, using <a href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#key(kotlin.Array,kotlin.Function0)">key(...)</a> to identify composables was considered, but this also doesn&#39;t seem to work):</p>
<pre><code class="language-kotlin:Puzzle15.kt">@Composable
private fun PuzzleGrid(
    puzzleState: PuzzleState,
    modifier: Modifier = Modifier,
    onTileClick: (Int) -&gt; Unit
) {
    // Track current position of each tile (by value)
    val tilePositions = IntArray(16) { -1 }
    repeat(PuzzleState.GRID_COUNT) { index -&gt;
        tilePositions[puzzleState.tileAt(index)] = index
    }

    BoxWithConstraints(
        modifier = modifier
            .fillMaxWidth()
            .aspectRatio(1F)
    ) {
        val gridSize = maxWidth
        val tileSize = (gridSize - 12.dp) / 4 // 3 gaps of 4dp

        // Draw tiles except blank (for each value 1-15)
        for (value in 1.until(PuzzleState.GRID_COUNT)) {
            val position = tilePositions[value]
            if (position == -1) continue
            val targetOffset = DpOffset(
                x = (tileSize + 4.dp) * (position % 4),
                y = (tileSize + 4.dp) * (position / 4)
            )

            val animatedOffset by animateValueAsState(
                targetValue = targetOffset,
                typeConverter = TwoWayConverter(
                    convertToVector = { AnimationVector2D(it.x.value, it.y.value) },
                    convertFromVector = { DpOffset(Dp(it.v1), Dp(it.v2)) }
                ),
                animationSpec = spring(
                    dampingRatio = Spring.DampingRatioNoBouncy,
                    stiffness = Spring.StiffnessMedium
                ),
                label = &quot;tile_$value&quot;
            )

            PuzzleTile(
                value = value,
                onClick = { onTileClick(position) },
                modifier = Modifier
                    .offset(x = animatedOffset.x, y = animatedOffset.y)
                    .size(tileSize)
            )
        }
    }
}
</code></pre>
<p>Nonetheless, with the addition of about a dozen lines of code and a few changes, we were able to express animation while keeping the code structure mostly intact. To customize this animation, try changing the <code>typeConverter</code>, <code>animationSpec</code>, and other arguments of <code>animateValueAsState(...)</code>. I&#39;ll continue introducing techniques for efficiently adding animations in the following sections.</p>
<p>![パズルのアニメーション中](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_puzzle.webp =256x)
<em>Fig. 1: Puzzle animating</em></p>
<h2>3. Adding Animation for Show/Hide Transitions</h2>
<p>In the puzzle game above, the display when solving the puzzle is rather plain, so we&#39;d like to add some flair. That said, since it&#39;s a mini-game, something modest will do. For now, let&#39;s consider an effect where something pops out when the puzzle is solved.</p>
<p>There&#39;s a simple method prepared for this.</p>
<h3>3.1. Wrap with AnimatedVisibility(...) and Add Modifier.animateEnterExit(...) Inside</h3>
<p>Simply changing <code>if (solved) { ... }</code> to <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#AnimatedVisibility(kotlin.Boolean,androidx.compose.ui.Modifier,androidx.compose.animation.EnterTransition,androidx.compose.animation.ExitTransition,kotlin.String,kotlin.Function1)">AnimatedVisibility</a><code>(solved) { ... }</code> enables default animations when transitioning from hidden to visible and from visible to hidden. To change the default animation, add <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedVisibilityScope#(androidx.compose.ui.Modifier).animateEnterExit(androidx.compose.animation.EnterTransition,androidx.compose.animation.ExitTransition,kotlin.String)">animateEnterExit(...)</a> to the <code>Modifier</code> of each composable wrapped by <code>AnimatedVisibility(solved) { ... }</code>:</p>
<pre><code class="language-kotlin:Puzzle15.kt">@Composable
fun Puzzle15(modifier: Modifier = Modifier) {

    // ...

    Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {

        // ...

        AnimatedVisibility(solved) {
            Text(
                text = &quot;🎉 Solved! 🎉&quot;,
                modifier = Modifier.animateEnterExit(
                    enter = scaleIn(
                        animationSpec = spring(
                            dampingRatio = Spring.DampingRatioHighBouncy,
                            stiffness = Spring.StiffnessMedium
                        )
                    ),
                    exit = None
                ),
                style = solvedStyle
            )
        }
    }
}
</code></pre>
<p>The <code>enter</code> and <code>exit</code> of <code>Modifier.animateEnterExit(...)</code> set the animations for transitioning from hidden to visible and from visible to hidden, respectively. The default values are set to <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#fadeIn(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Float)">fadeIn()</a> and <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#fadeOut(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Float)">fadeOut()</a>, respectively. In this case, since we only want to set animation when appearing, we set <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/EnterTransition#None()">None</a> for <code>exit</code> so it disappears without animation.</p>
<p>Wrapping a composable with an upper-level composable to create a scope (<a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedVisibilityScope">AnimatedVisibilityScope</a>) and adding individual settings to the <code>Modifier</code> of wrapped individual composables is a frequently used technique in Jetpack Compose. Similar techniques will appear later.</p>
<h2>4. Expressing Animation Where Old Screen Disappears and New Screen Appears on Screen Update</h2>
<p>The following code represents the action of moving on a 2D integer coordinate plane by tapping arrow buttons for up, down, left, and right:</p>
<pre><code class="language-kotlin:FlatField.kt">@Composable
fun FlatField(modifier: Modifier = Modifier) {
    var xy by remember { mutableStateOf(Coordinates2D(0, 0)) }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(xy.background)
            .safeContentPadding()
    ) {
        IconButton(
            modifier = modifier.align(Alignment.CenterStart),
            onClick = { xy = xy.goLeft() }
        ) {
            Icon(
                modifier = Modifier.size(64.dp),
                imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                contentDescription = &quot;goLeft&quot;,
                tint = xy.foreground
            )
        }

        IconButton(
            modifier = Modifier.align(Alignment.TopCenter),
            onClick = { xy = xy.goUp() }
        ) {
            Icon(
                modifier = Modifier
                    .size(64.dp)
                    .rotate(90F),
                imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                contentDescription = &quot;goUp&quot;,
                tint = xy.foreground
            )
        }

        IconButton(
            modifier = Modifier.align(Alignment.CenterEnd),
            onClick = { xy = xy.goRight() }
        ) {
            Icon(
                modifier = Modifier.size(64.dp),
                imageVector = Icons.AutoMirrored.Filled.ArrowForward,
                contentDescription = &quot;goRight&quot;,
                tint = xy.foreground
            )
        }

        IconButton(
            modifier = Modifier.align(Alignment.BottomCenter),
            onClick = { xy = xy.goDown() }
        ) {
            Icon(
                modifier = Modifier
                    .size(64.dp)
                    .rotate(90F),
                imageVector = Icons.AutoMirrored.Filled.ArrowForward,
                contentDescription = &quot;goDown&quot;,
                tint = xy.foreground
            )
        }

        Text(
            text = xy.coordinateString,
            modifier = Modifier.align(Alignment.Center),
            color = xy.foreground,
            fontSize = 64.sp
        )
    }
}
</code></pre>
<p>You can repeat operations like moving from the initial white location to the red location via the down button, then to the blue location via the right button, and so on, but without motion, it&#39;s difficult to grasp the sense of movement. This is exactly when adding animation is effective.</p>
<p>The class representing the state during movement is as follows:</p>
<pre><code class="language-kotlin">data class Coordinates2D(val x: Int, val y: Int) {
    val background: Color
    val foreground: Color
    val coordinateString: String

    init {
        val index = nonNegativeRemainder()
        background = backgroundColors[index]
        foreground = foregroundColors[index]
        coordinateString = coordinateString(x, y)
    }

    private fun nonNegativeRemainder(): Int = ((x + y) % 3).let { if (it &lt; 0) it + 3 else it }

    fun goLeft() = Coordinates2D(x - 1, y)

    fun goUp() = Coordinates2D(x, y - 1)

    fun goRight() = Coordinates2D(x + 1, y)

    fun goDown() = Coordinates2D(x, y + 1)

    companion object Companion {
        private val backgroundColors =
            arrayOf(Color.White, Color(0xED, 0x29, 0x39), Color(0x00, 0x23, 0x95))
        private val foregroundColors = arrayOf(Color.Black, Color.White, Color.White)

        private fun coordinateString(x: Int, y: Int) = if (x == 0 &amp;&amp; y == 0) &quot;O&quot; else &quot;(${x}, ${y})&quot;
    }
}
</code></pre>
<h3>4.1. Wrap with AnimatedContent(...) and Add Animation for Transitioning from Old Screen to New Screen</h3>
<p>The following wraps the <code>Box(...)</code> inside <code>FlatField(Modifier)</code> with <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#(androidx.compose.animation.core.Transition).AnimatedContent(androidx.compose.ui.Modifier,kotlin.Function1,androidx.compose.ui.Alignment,kotlin.Function1,kotlin.Function2)">AnimatedContent(...)</a>, but since the animation definition requires information about the size of <code>Box(...)</code>, we change <code>Box(...)</code> to <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#BoxWithConstraints(androidx.compose.ui.Modifier,androidx.compose.ui.Alignment,kotlin.Boolean,kotlin.Function1)">BoxWithConstraints(...)</a>, reference <code>constraints</code>, and use the values of <code>maxWidth</code> and <code>maxHeight</code> as arguments to <code>AnimatedContent(...)</code>:</p>
<pre><code class="language-kotlin:FlatField.kt">@Composable
fun FlatField(modifier: Modifier = Modifier) {
    var xy by remember { mutableStateOf(Coordinates2D(0, 0)) }

    var width by remember { mutableIntStateOf(0) }
    var height by remember { mutableIntStateOf(0) }

    AnimatedContent(
        modifier = modifier.fillMaxSize(),
        targetState = xy,
        transitionSpec = {
            val deltaX = targetState.x - initialState.x
            val deltaY = targetState.y - initialState.y

            slideIn { IntOffset(x = deltaX * width, y = deltaY * height) } togetherWith
                    slideOut { IntOffset(x = -deltaX * width, y = -deltaY * height) }
        },
        label = &quot;coordinates2D&quot;
    ) { targetXy -&gt;
        BoxWithConstraints(
            modifier = Modifier
                .fillMaxSize()
                .background(targetXy.background)
                .safeContentPadding()
        ) {
            width = constraints.maxWidth
            height = constraints.maxHeight

            IconButton(
                modifier = Modifier.align(Alignment.CenterStart),
                onClick = { xy = targetXy.goLeft() }
            ) {
                Icon(
                    modifier = Modifier.size(64.dp),
                    imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                    contentDescription = &quot;goLeft&quot;,
                    tint = targetXy.foreground
                )
            }

            IconButton(
                modifier = Modifier.align(Alignment.TopCenter),
                onClick = { xy = targetXy.goUp() }
            ) {
                Icon(
                    modifier = Modifier
                        .size(64.dp)
                        .rotate(90F),
                    imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                    contentDescription = &quot;goUp&quot;,
                    tint = targetXy.foreground
                )
            }

            IconButton(
                modifier = Modifier.align(Alignment.CenterEnd),
                onClick = { xy = targetXy.goRight() }
            ) {
                Icon(
                    modifier = Modifier.size(64.dp),
                    imageVector = Icons.AutoMirrored.Filled.ArrowForward,
                    contentDescription = &quot;goRight&quot;,
                    tint = targetXy.foreground
                )
            }

            IconButton(
                modifier = Modifier.align(Alignment.BottomCenter),
                onClick = { xy = targetXy.goDown() }
            ) {
                Icon(
                    modifier = Modifier
                        .size(64.dp)
                        .rotate(90F),
                    imageVector = Icons.AutoMirrored.Filled.ArrowForward,
                    contentDescription = &quot;goDown&quot;,
                    tint = targetXy.foreground
                )
            }

            Text(
                text = targetXy.coordinateString,
                modifier = Modifier.align(Alignment.Center),
                color = targetXy.foreground,
                fontSize = 64.sp
            )
        }
    }
}
</code></pre>
<p>In the <code>transitionSpec</code> argument of <code>AnimatedContent(...)</code>, we define the animation by merging <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#slideOut(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Function1)">slideOut { ... }</a> that pushes out the old screen and <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#slideIn(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Function1)">slideIn { ... }</a> that brings in the new screen using <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/EnterTransition#(androidx.compose.animation.EnterTransition).togetherWith(androidx.compose.animation.ExitTransition)">infix EnterTransition.togetherWith(ExitTransition)</a>. The argument (<code>targetXy</code>) of the lambda expression given to the <code>content</code> argument of <code>AnimatedContent(...)</code> holds the state of the screen being pushed out, and the state is updated by assigning the new state obtained from <code>targetXy</code> to <code>xy</code> in the <code>onClick</code> of each <code>IconButton(...)</code>.</p>
<p>![アニメーションによる疑似スクロール](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_pseudo-scroll_1.webp =256x)
![アニメーションによる疑似スクロール](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_pseudo-scroll_2.webp =256x)
<em>Fig. 2-1, 2-2: Pseudo-scroll by animation</em></p>
<p>With this code, if you (forcibly build while ignoring compiler warnings by) writing <code>xy = xy.goLeft()</code> instead of using <code>targetXy</code>, the old screen being pushed out during animation won&#39;t display correctly. This writing style shows that <code>AnimatedContent(...)</code> correctly manages the state during animation. To customize this animation, try changing the <code>transitionSpec</code> argument of <code>AnimatedContent(...)</code>, etc.</p>
<p>Adding animation makes it much easier to grasp the sense of position movement. Looking at this motion, it seems this could also be used as a two-dimensional pager. Jetpack Compose provides <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/pager/package-summary#HorizontalPager(androidx.compose.foundation.pager.PagerState,androidx.compose.ui.Modifier,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.pager.PageSize,kotlin.Int,androidx.compose.ui.unit.Dp,androidx.compose.ui.Alignment.Vertical,androidx.compose.foundation.gestures.TargetedFlingBehavior,kotlin.Boolean,kotlin.Boolean,kotlin.Function1,androidx.compose.ui.input.nestedscroll.NestedScrollConnection,androidx.compose.foundation.gestures.snapping.SnapPosition,androidx.compose.foundation.OverscrollEffect,kotlin.Function2)">HorizontalPager</a> for horizontal scrolling and <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/pager/package-summary#VerticalPager(androidx.compose.foundation.pager.PagerState,androidx.compose.ui.Modifier,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.pager.PageSize,kotlin.Int,androidx.compose.ui.unit.Dp,androidx.compose.ui.Alignment.Horizontal,androidx.compose.foundation.gestures.TargetedFlingBehavior,kotlin.Boolean,kotlin.Boolean,kotlin.Function1,androidx.compose.ui.input.nestedscroll.NestedScrollConnection,androidx.compose.foundation.gestures.snapping.SnapPosition,androidx.compose.foundation.OverscrollEffect,kotlin.Function2)">VerticalPager</a> for vertical scrolling, but there&#39;s no standard two-dimensional pager. However, you can create a pager-like UI just by adding animation. Of course, since we&#39;ve only added animation, you can&#39;t swipe to see part of an adjacent page, but with just about a dozen lines of additions and some changes, you can significantly change the app&#39;s impression.</p>
<h2>5. Animation Connecting Before and After Screen Transitions in NavHost(...)</h2>
<p>Starting from <a href="https://developer.android.com/jetpack/androidx/releases/compose-animation">Compose Animation</a> <a href="https://developer.android.com/jetpack/androidx/releases/compose-animation#1.10.0">version 1.10.0</a>, <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#SharedTransitionLayout(androidx.compose.ui.Modifier,kotlin.Function1)">SharedTransitionLayout</a> became stable. This defines <strong>shared elements</strong> for screens created with composables. What are shared elements? Check the short video in <a href="https://developer.android.com/develop/ui/compose/animation/shared-elements">Shared element transitions in Compose</a>.
Here&#39;s a code example when using <code>NavHost(...)</code>:</p>
<pre><code class="language-kotlin:GridTransform.kt">private val colorMap = mapOf(
    &quot;赤&quot; to Color.Red,
    &quot;緑&quot; to Color.Green,
    &quot;青&quot; to Color.Blue,
    &quot;シアン&quot; to Color.Cyan,
    &quot;マゼンタ&quot; to Color.Magenta,
    &quot;黄&quot; to Color.Yellow,
    &quot;茶&quot; to Color(132, 74, 43),
    &quot;群青&quot; to Color(76, 108, 179),
    &quot;カーキー&quot; to Color(197, 160, 90)
)

@Composable
fun GridTransform(modifier: Modifier = Modifier) {
    val navController = rememberNavController()
    NavHost(
        modifier = modifier
            .safeContentPadding()
            .fillMaxSize(),
        navController = navController,
        startDestination = ROUTE_SMALL_SQUARE
    ) {
        composable(route = ROUTE_SMALL_SQUARE) {
            val onClick: (String) -&gt; Unit = {
                navController.navigate(&quot;$ROUTE_LARGE_SQUARE?$ARG_SHARED=$it&quot;)
            }
            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                Column(
                    modifier = Modifier
                        .aspectRatio(1f)
                        .padding(8.dp)
                        .fillMaxSize()
                        .background(
                            color = MaterialTheme.colorScheme.primaryContainer,
                            shape = RoundedCornerShape(16.dp)
                        )
                ) {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        ColorButton(
                            &quot;赤&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;緑&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;青&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )
                    }

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        ColorButton(
                            &quot;シアン&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;マゼンタ&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;黄&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )
                    }

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        ColorButton(
                            &quot;茶&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;群青&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;カーキー&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )
                    }
                }
            }
        }

        composable(
            route = &quot;$ROUTE_LARGE_SQUARE?$ARG_SHARED={sharedKey}&quot;,
            arguments = listOf(navArgument(&quot;sharedKey&quot;) { type = NavType.StringType })
        ) { entry -&gt;
            val colorName = entry.arguments?.getString(&quot;sharedKey&quot;) ?: &quot;&quot;
            LargeSquare(colorName) {
                navController.popBackStack()
            }
        }
    }
}

@Composable
fun ColorButton(
    colorName: String,
    modifier: Modifier = Modifier,
    onClick: (String) -&gt; Unit
) {
    TextButton(
        modifier = modifier
            .padding(16.dp)
            .aspectRatio(1f)
            .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)),
        onClick = { onClick(colorName) }
    ) {
        Text(colorName, color = Color.White)
    }
}

@Composable
private fun LargeSquare(
    colorName: String,
    modifier: Modifier = Modifier,
    onBack: () -&gt; Unit
) {
    Box(
        modifier = modifier
            .padding(16.dp)
            .fillMaxSize()
            .aspectRatio(1f)
            .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp))
            .clickable { onBack() }
    ) {
        Text(text = colorName, modifier = Modifier.padding(8.dp), color = Color.White)
    }
}
</code></pre>
<p><em>Note: This code omits countermeasures against rapid tapping of the back button and 9-color buttons for simplicity. Please test while avoiding rapid tapping in short intervals.</em></p>
<p>On the initial screen, 9 color buttons are displayed, and tapping a button shows a large square in the tapped button&#39;s color. When the large square is displayed, pressing the back button (or performing a back gesture) returns to the 9-color button screen. Here, you can see fade-in and fade-out animations when returning to the large square screen by button tap and when returning to the 9-color button screen by back button. This is specified by the default values of the <code>enterTransition</code>, <code>exitTransition</code>, <code>popEnterTransition</code>, <code>popExitTransition</code>, <code>sizeTransform</code> arguments of <a href="https://developer.android.com/reference/kotlin/androidx/navigation/NavHost">NavHost(...)</a>. You can override this setting with animation that associates <strong>shared elements</strong> before and after specific screen transitions by adding code and making modifications as shown below for specific screen transitions.</p>
<p>For implementing <strong>shared element</strong> animation in screen transitions that don&#39;t involve back stack changes via <code>NavHost(...)</code>, refer to <a href="https://developer.android.com/develop/ui/compose/animation/shared-elements">Shared element transitions in Compose</a>. During screen transitions from left to right and right to left in the table below, the animation visually expresses that the square on the right screen that appeared from the button tapped on the left screen (bottom right) is exactly the target content.</p>
<table>
<thead>
<tr>
<th align="center">9-color button screen</th>
<th align="center">Large square screen</th>
</tr>
</thead>
<tbody><tr>
<td align="center">![9色のボタンの画面](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_9-buttons.webp =256x)<em>Fig. 3-1</em></td>
<td align="center">![Large square screen](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_large-square.webp =256x)<em>Fig. 3-2</em></td>
</tr>
</tbody></table>
<h3>5.1. Wrap with SharedTransitionLayout { ... } and Share Screen Elements</h3>
<p>Here we introduce the implementation of <strong>shared element</strong> animation before and after screen transitions via <code>NavHost(...)</code>:</p>
<pre><code class="language-kotlin:GridTransform.kt">@Composable
fun GridTransform(modifier: Modifier = Modifier) {
    val navController = rememberNavController()
    SharedTransitionLayout {
        NavHost(
            modifier = modifier
                .safeContentPadding()
                .fillMaxSize(),
            navController = navController,
            startDestination = ROUTE_SMALL_SQUARE
        ) {
            composable(route = ROUTE_SMALL_SQUARE) {
                val onClick: (String) -&gt; Unit = {
                    navController.navigate(&quot;$ROUTE_LARGE_SQUARE?$ARG_SHARED=$it&quot;)
                }
                Box(
                    modifier = Modifier.fillMaxSize(),
                    contentAlignment = Alignment.Center
                ) {
                    Column(
                        modifier = Modifier
                            .aspectRatio(1f)
                            .padding(8.dp)
                            .fillMaxSize()
                            .background(
                                color = MaterialTheme.colorScheme.primaryContainer,
                                shape = RoundedCornerShape(16.dp)
                            )
                    ) {
                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.spacedBy(8.dp)
                        ) {
                            ColorButton(
                                this@composable,
                                &quot;赤&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;緑&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;青&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )
                        }

                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.spacedBy(8.dp)
                        ) {
                            ColorButton(
                                this@composable,
                                &quot;シアン&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;マゼンタ&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;黄&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )
                        }

                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.spacedBy(8.dp)
                        ) {
                            ColorButton(
                                this@composable,
                                &quot;茶&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;群青&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;カーキー&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )
                        }
                    }
                }
            }

            composable(
                route = &quot;$ROUTE_LARGE_SQUARE?$ARG_SHARED={sharedKey}&quot;,
                arguments = listOf(navArgument(&quot;sharedKey&quot;) { type = NavType.StringType })
            ) { entry -&gt;
                val colorName = entry.arguments?.getString(&quot;sharedKey&quot;) ?: &quot;&quot;
                LargeSquare(this, colorName) {
                    navController.popBackStack()
                }
            }
        }
    }
}

@Composable
fun SharedTransitionScope.ColorButton(
    animatedContentScope: AnimatedVisibilityScope,
    colorName: String,
    modifier: Modifier = Modifier,
    onClick: (String) -&gt; Unit
) {
    TextButton(
        modifier = modifier
            .padding(16.dp)
            .aspectRatio(1f)
            .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp))
            .sharedBounds(
                sharedContentState = rememberSharedContentState(colorName),
                animatedVisibilityScope = animatedContentScope,
                boundsTransform = { _, _ -&gt;
                    tween(durationMillis = 500)
                },
                enter = fadeIn(),
                exit = fadeOut(),
                resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
            ),
        onClick = { onClick(colorName) }
    ) {
        Text(colorName, color = Color.White)
    }
}

@Composable
private fun SharedTransitionScope.LargeSquare(
    animatedContentScope: AnimatedVisibilityScope,
    colorName: String,
    modifier: Modifier = Modifier,
    onBack: () -&gt; Unit
) {
    Box(
        modifier = modifier
            .padding(16.dp)
            .fillMaxSize()
            .aspectRatio(1f)
            .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp))
            .sharedBounds(
                sharedContentState = rememberSharedContentState(colorName),
                animatedVisibilityScope = animatedContentScope,
                boundsTransform = { _, _ -&gt;
                    tween(durationMillis = 500)
                },
                enter = fadeIn(),
                exit = fadeOut(),
                resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
            )
            .clickable { onBack() }
    ) {
        Text(text = colorName, modifier = Modifier.padding(8.dp), color = Color.White)
    }
}
</code></pre>
<p>Wrapping the outer layer with <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#SharedTransitionLayout(androidx.compose.ui.Modifier,kotlin.Function1)">SharedTransitionLayout { ... }</a> and using <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/SharedTransitionScope#(androidx.compose.ui.Modifier).sharedBounds(androidx.compose.animation.SharedTransitionScope.SharedContentState,androidx.compose.animation.AnimatedVisibilityScope,androidx.compose.animation.EnterTransition,androidx.compose.animation.ExitTransition,androidx.compose.animation.BoundsTransform,androidx.compose.animation.SharedTransitionScope.ResizeMode,androidx.compose.animation.SharedTransitionScope.PlaceholderSize,kotlin.Boolean,kotlin.Float,androidx.compose.animation.SharedTransitionScope.OverlayClip)">Modifier.sharedBounds(...)</a> within <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/SharedTransitionScope">SharedTransitionScope</a> to associate <strong>shared elements</strong> is the same as for screen transitions that don&#39;t use <code>NavHost(...)</code>. For the <code>animatedVisibilityScope</code> argument of <code>Modifier.sharedBounds(...)</code>, we use <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedContentScope">AnimatedContentScope</a> (<code>this@composable</code>) derived from <a href="https://developer.android.com/reference/kotlin/androidx/navigation/NavGraphBuilder#(androidx.navigation.NavGraphBuilder).composable(kotlin.String,kotlin.collections.List,kotlin.collections.List,kotlin.Function1,kotlin.Function1,kotlin.Function1,kotlin.Function1,kotlin.Function1,kotlin.Function2)">NavGraphBuilder.composable(...)</a>. This enables displaying animation between shared elements during screen transitions via <code>NavHost(...)</code>. In <code>SharedTransitionScope.ColorButton(...)</code> and <code>SharedTransitionScope.LargeSquare(...)</code>, verify that screen elements are shared before and after screen transitions by using the button&#39;s color name as the <code>key</code> argument of <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/SharedTransitionScope#rememberSharedContentState(kotlin.Any)">rememberSharedContentState(Any)</a>.</p>
<p>The new coding required to set up shared element animation is:</p>
<ol>
<li>Wrap with <code>SharedTransitionLayout { ... }</code></li>
<li>Set <code>sharedBounds(...)</code> (or <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/SharedTransitionScope#(androidx.compose.ui.Modifier).sharedElement(androidx.compose.animation.SharedTransitionScope.SharedContentState,androidx.compose.animation.AnimatedVisibilityScope,androidx.compose.animation.BoundsTransform,androidx.compose.animation.SharedTransitionScope.PlaceholderSize,kotlin.Boolean,kotlin.Float,androidx.compose.animation.SharedTransitionScope.OverlayClip)">sharedElement(...)</a>) to define shared elements and match the <code>key</code> between shared elements</li>
<li>Pass the two scopes <code>SharedTransitionScope</code> and <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedVisibilityScope">AnimatedVisibilityScope</a> required for 2. to the composable</li>
</ol>
<p>These can be implemented without significantly changing the structure of the code before animation settings. If it&#39;s difficult to apply with minimal changes to existing code, try refactoring to a structure that&#39;s easier to modify.</p>
<p>Shared element animation is beautiful when it fits well, but animation that perfectly matches the screen design image may be difficult. In that case, try adjusting the <code>enter</code>, <code>exit</code>, <code>boundsTransform</code>, <code>resizeMode</code> arguments of <code>Modifier.sharedBounds(...)</code>, etc.</p>
<h3>5.2. Relationship with Predictive Back</h3>
<p>Shared element animation overrides the default screen transition animation of <code>NavGraphBuilder.composable(...)</code>. Additionally, when predictive back animation is enabled, that is, when <code>android:enableOnBackInvokedCallback=&quot;true&quot;</code> is specified in AndroidManifest.xml for API levels 33-35, or when <code>android:enableOnBackInvokedCallback=&quot;false&quot;</code> is not specified in AndroidManifest.xml for API level 36 or higher, it also overrides the back animation via <code>NavHost(...)</code>. Build the app with predictive back animation enabled, set the device to gesture navigation mode, and slowly perform the back gesture on the large square screen above, and you can easily confirm that the shared element animation slowly reverses. Also, on API level 36 or higher and Android OS 16 or higher, setting to button navigation mode and long-pressing the back button should show the shared element reverse animation.</p>
<p>Thus, the back animation of <code>NavHost(...)</code> affects the predictive back animation settings. With this in mind, you need to decide whether to enable predictive back animation settings. As of API level 36, you can disable predictive back animation by specifying <code>android:enableOnBackInvokedCallback=&quot;false&quot;</code>.</p>
<h2>6. Conclusion</h2>
<p>This article introduced 4 examples of techniques for implementing practical animations in Jetpack Compose with minimal changes. Animation within apps is not essential functionality in most cases, so implementation tends to be omitted especially in development projects with tight schedules. But depending on usage, it has the potential to bring significant usability improvements and greatly enhance the app&#39;s impression. If there are abundant means to achieve this with minimal effort, you can easily try implementation. Many APIs in Compose Animation are designed to be <em>declarative</em>, meaning you can add animations with the feeling of declaring that you&#39;ll animate UI elements, making it easy to avoid the complexity of having to define behavior through detailed procedural descriptions. I hope these can be utilized to improve the quality of many apps.</p>
<h2>7. References</h2>
<ul>
<li><a href="https://developer.android.com/reference">Android API reference</a></li>
<li><a href="https://developer.android.com/develop/ui/compose/animation/quick-guide">Quick guide to Animations in Compose</a></li>
<li><a href="https://developer.android.com/develop/ui/compose/animation/composables-modifiers">Animation modifiers and composables</a></li>
<li><a href="https://developer.android.com/guide/navigation/custom-back/support-animations">Add support for predictive back animations</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_top.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[小規模なコード修正で効果的にアプリの印象を改善する、Jetpack Composeのアニメーション追加のテクニック]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-08-Jetpack-Compose-Animation-Easy/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-08-Jetpack-Compose-Animation-Easy/</guid>
            <pubDate>Mon, 08 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[小規模なコード修正でUIにアニメーションを追加できるJetpack Composeの効果的なテクニック4例を紹介]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の 8 日目の記事です🎅🎄</p>
<p>KINTOテクノロジーズのAndroidエンジニア <a href="https://qiita.com/yamadacsa">山田 剛</a> です。
本記事では、少しのコード追加・変更でJetpack Composeを利用したUIにアニメーションを追加し、アニメーションの印象を向上させるための事例集を紹介します。</p>
<h2>1. はじめに</h2>
<p>スマートフォンアプリの印象を大きく左右する要素の一つがアニメーションです。適所に用意された気の利いたアニメーションは、ユーザーの操作に対する視覚的なフィードバックを提供し、アプリの動作の意味を理解しやすくするとともに、アプリの印象を向上させ、品質に対するユーザーの信頼感を増します。Jetpack Composeでは、「宣言的UI」の特性を活かした、従来のViewシステムよりも短く簡潔なコードで、生産性の高いアニメーションの実装が可能になっています。本記事では、その技術の中から、小規模なコードの追加・修正で既存のソースに容易にアニメーションを追加できる実用的なテクニックを紹介します。</p>
<p>本記事では、執筆時点での <a href="https://developer.android.com/jetpack/androidx/releases/compose-animation">Compose Animation</a> の最新の安定版 <a href="https://developer.android.com/jetpack/androidx/releases/compose-animation#1.10.0">1.10.0</a>を含んだ <a href="https://developer.android.com/develop/ui/compose/bom/bom-mapping">Jetpack Compose Libraries BOM</a> 2025.12.00 でソースコードを検証しています。</p>
<h2>2. 座標を指定するタイプのUI部品のアニメーション</h2>
<p>以下は簡単なパズルゲーム（15パズル）を短いコードで書いています:</p>
<pre><code class="language-kotlin:Puzzle15.kt">@Composable
fun Puzzle15(modifier: Modifier = Modifier) {
    var puzzleState by remember {
        mutableStateOf(PuzzleState.generate())
    }
    var moves by remember { mutableIntStateOf(puzzleState.moves) }
    val solved = puzzleState.isSolved()

    val titleStyle = MaterialTheme.typography.headlineLarge.merge(fontWeight = FontWeight.W600)
    val movesStyle = MaterialTheme.typography.titleMedium
    val solvedStyle =
        MaterialTheme.typography.titleLarge.merge(color = Color.Green, fontWeight = FontWeight.W600)
    val buttonStyle = MaterialTheme.typography.titleMedium.merge(fontWeight = FontWeight.W600)

    Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
        Column(
            modifier = modifier
                .fillMaxSize()
                .padding(16.dp),
            horizontalAlignment = Alignment.CenterHorizontally,
            verticalArrangement = Arrangement.Center
        ) {
            Text(
                text = &quot;15 Puzzle&quot;,
                style = titleStyle,
                modifier = Modifier.padding(bottom = 16.dp)
            )

            Text(text = &quot;Moves: $moves&quot;, style = movesStyle)

            PuzzleGrid(
                puzzleState = puzzleState,
                modifier = Modifier.padding(vertical = 24.dp)
            ) { index -&gt;
                if (solved) return@PuzzleGrid
                puzzleState.moveTile(index)
                moves = puzzleState.moves
            }

            Button(onClick = {
                puzzleState = PuzzleState.generate()
                moves = 0
            }) {
                Text(&quot;New Game&quot;, style = buttonStyle)
            }
        }

        if (solved) {
            Text(text = &quot;🎉 Solved! 🎉&quot;, style = solvedStyle)
        }
    }
}
</code></pre>
<p>4x4 の升目に描く15枚のタイルは、以下のコードで表示しています:</p>
<pre><code class="language-kotlin:Puzzle15.kt">@Composable
private fun PuzzleGrid(
    puzzleState: PuzzleState,
    modifier: Modifier = Modifier,
    onTileClick: (Int) -&gt; Unit
) {
    BoxWithConstraints(
        modifier = modifier
            .fillMaxWidth()
            .aspectRatio(1F)
    ) {
        val gridSize = maxWidth
        val tileSize = (gridSize - 12.dp) / 4 // 3 gaps of 4dp

        for (position in 0.until(PuzzleState.GRID_COUNT)) {
            val value = puzzleState.tileAt(position)
            if (value == 0) continue
            val targetOffset = DpOffset(
                x = (tileSize + 4.dp) * (position % 4),
                y = (tileSize + 4.dp) * (position / 4)
            )

            PuzzleTile(
                value = value,
                onClick = { onTileClick(position) },
                modifier = Modifier
                    .offset(x = targetOffset.x, y = targetOffset.y)
                    .size(tileSize)
            )
        }
    }
}
</code></pre>
<p>ユーザーは動かせるタイルをタップすることで空いた升目にタイルを移動させる操作を繰り返し、パズルの完成を目指します。タイルは1手で最大3枚同時に動かせます。これだけでもシンプルゲームとして充分楽しめますが、リアル世界のパズルゲームのように物理的にタイルを動かしている感触がほしいところです。それをアニメーションで表現することを目指します。</p>
<p>なお、論理的なパズルの状態を表すクラス PuzzleState は以下のようになっています:</p>
<pre><code class="language-kotlin">class PuzzleState private constructor(private val tiles: IntArray) {
    var moves = 0
        private set

    fun isSolved(): Boolean = tiles.all { tiles[it] == it + 1 || it == 15 }

    private fun getMoveOffsetOrZero(index: Int): Int {
        val emptyIndex = tiles.indexOf(0)
        val row = index / 4
        val col = index % 4
        val emptyRow = emptyIndex / 4
        val emptyCol = emptyIndex % 4

        return when {
            row == emptyRow -&gt; {
                if (col &lt; emptyCol) 1 else -1
            }

            col == emptyCol -&gt; {
                if (row &lt; emptyRow) 4 else -4
            }

            else -&gt; 0
        }
    }

    fun moveTile(index: Int) {
        val offset = getMoveOffsetOrZero(index)
        if (offset == 0) return

        var position = index
        do {
            position += offset
        } while (position &gt;= 0 &amp;&amp; position &lt; tiles.size &amp;&amp; tiles[position] != 0)
        do {
            val next = position - offset
            tiles[position] = tiles[next]
            position = next
        } while (position != index)
        tiles[index] = 0
        moves += 1
    }

    fun tileAt(index: Int) = tiles[index]

    companion object {
        const val GRID_COUNT = 16

        fun generate(): PuzzleState {
            val tiles = IntArray(GRID_COUNT) { it }
            // シャッフル（解ける配置のみ生成）
            do {
                tiles.shuffle(Random.Default)
            } while (!isSolvable(tiles))
            return PuzzleState(tiles)
        }

        private fun isSolvable(tiles: IntArray): Boolean {
            val inversions = (0..15).sumOf { idx -&gt;
                (idx + 1 until tiles.size).count {
                    tiles[idx] != 0 &amp;&amp; tiles[it] != 0 &amp;&amp; tiles[idx] &gt; tiles[it]
                }
            }
            // 空白が奇数行（下から）にあり、転置数が偶数の場合、または空白が偶数行にあり、転置数が奇数の場合は解ける
            return (3 - tiles.indexOf(0) / 4) % 2 == inversions % 2
        }
    }
}
</code></pre>
<h3>2.1. 座標（オフセット）の状態を保持する State を定義する</h3>
<p>以下のコードでは、修正前の <code>PuzzleGrid(...)</code> の <code>targetOffset</code> を <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/core/package-summary#animateValueAsState(kotlin.Any,androidx.compose.animation.core.TwoWayConverter,androidx.compose.animation.core.AnimationSpec,kotlin.Any,kotlin.String,kotlin.Function1)">animateValueAsState(...)</a> で変換して新たに <code>animatedOffset</code> を定義し、 <code>PuzzleTile(...)</code> の <code>Modifier.offset(...)</code> の引数を置き換えています。 <code>animatedOffset</code> は <code>targetOffset</code> と同じオフセットを表現しつつ、タイルを移動させるときの移動前から移動後のオフセットの変化をなめらかに表現する機能を持っています。</p>
<p>ただし、この例の場合はこの12行のコード追加だけではアニメーションせず、 <code>// 各タイル（値）の現在位置を追跡</code> とコメントされた部分の表示順のソートを行った変数 <code>tilePositions</code> を使う必要がありました。これは、 <code>PuzzleGrid(...)</code> における <code>for</code> ループ内でのタイルの順番を常にタイルに描かれた数字の順に保つための並べ替えを行っています。このようにすることで、Jetpack Composeの「宣言的UI」の考え方のもと、移動前と移動後の1〜15のタイルをそれぞれ常に同一視できるようにして表現の連続性を確保し、アニメーションの表示を可能にしています（<code>animateValueAsState</code> の <code>label</code> 引数によって composable の同一性を同定してほしいところでしたが、 androidx.compose.animation ライブラリ 1.10.0 ではそのような効果は確認できませんでした。また、 <a href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#key(kotlin.Array,kotlin.Function0)">key(...)</a> を用いて composable を同定させる方法も考えられますが、これもうまくいかないようです）:</p>
<pre><code class="language-kotlin:Puzzle15.kt">@Composable
private fun PuzzleGrid(
    puzzleState: PuzzleState,
    modifier: Modifier = Modifier,
    onTileClick: (Int) -&gt; Unit
) {
    // 各タイル（値）の現在位置を追跡
    val tilePositions = IntArray(16) { -1 }
    repeat(PuzzleState.GRID_COUNT) { index -&gt;
        tilePositions[puzzleState.tileAt(index)] = index
    }

    BoxWithConstraints(
        modifier = modifier
            .fillMaxWidth()
            .aspectRatio(1F)
    ) {
        val gridSize = maxWidth
        val tileSize = (gridSize - 12.dp) / 4 // 3 gaps of 4dp

        // 空白以外のタイルを描画（1-15の値ごとに）
        for (value in 1.until(PuzzleState.GRID_COUNT)) {
            val position = tilePositions[value]
            if (position == -1) continue
            val targetOffset = DpOffset(
                x = (tileSize + 4.dp) * (position % 4),
                y = (tileSize + 4.dp) * (position / 4)
            )

            val animatedOffset by animateValueAsState(
                targetValue = targetOffset,
                typeConverter = TwoWayConverter(
                    convertToVector = { AnimationVector2D(it.x.value, it.y.value) },
                    convertFromVector = { DpOffset(Dp(it.v1), Dp(it.v2)) }
                ),
                animationSpec = spring(
                    dampingRatio = Spring.DampingRatioNoBouncy,
                    stiffness = Spring.StiffnessMedium
                ),
                label = &quot;tile_$value&quot;
            )

            PuzzleTile(
                value = value,
                onClick = { onTileClick(position) },
                modifier = Modifier
                    .offset(x = animatedOffset.x, y = animatedOffset.y)
                    .size(tileSize)
            )
        }
    }
}
</code></pre>
<p>ともあれ、十数行のコードの追加と数行の変更で、コードの構成をほぼそのままにしたままアニメーションを表現できました。このアニメーションをカスタマイズしたい場合には、 <code>animateValueAsState(...)</code> の引数 <code>typeConverter</code>, <code>animationSpec</code> などを変更してみてください。このように、要領よくアニメーションを追加していくテクニックを以下の項でも紹介していきます。</p>
<p>![パズルのアニメーション中](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_puzzle.webp =256x)
<em>Fig. 1: パズルのアニメーション中</em></p>
<h2>3. 表示・非表示の切り替え時のアニメーションを追加する</h2>
<p>先ほどのパズルゲームで、パズルを解いたときの表示があっさりしているので、少し凝ってみたいところです。凝る、といってもミニゲームですので、ささやかなものでよいでしょう。ひとまず、パズルが解けたときに飛び出してくるような演出を考えてみましょう。</p>
<p>これには簡単な方法が用意されています。</p>
<h3>3.1. AnimatedVisibility(...) で囲み、内側で Modifier.animateEnterExit(...) を追加する</h3>
<p><code>if (solved) { ... }</code> を <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#AnimatedVisibility(kotlin.Boolean,androidx.compose.ui.Modifier,androidx.compose.animation.EnterTransition,androidx.compose.animation.ExitTransition,kotlin.String,kotlin.Function1)">AnimatedVisibility</a><code>(solved) { ... }</code> に替えるだけで、非表示から表示へ、および、表示から非表示へ変わるときにデフォルトのアニメーションが起こるようになります。デフォルトのアニメーションを変更するには、 <code>AnimatedVisibility(solved) { ... }</code> に囲まれた各composableの <code>Modifier</code> に対して <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedVisibilityScope#(androidx.compose.ui.Modifier).animateEnterExit(androidx.compose.animation.EnterTransition,androidx.compose.animation.ExitTransition,kotlin.String)">animateEnterExit(...)</a> を追加します:</p>
<pre><code class="language-kotlin:Puzzle15.kt">@Composable
fun Puzzle15(modifier: Modifier = Modifier) {

    // ...

    Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {

        // ...

        AnimatedVisibility(solved) {
            Text(
                text = &quot;🎉 Solved! 🎉&quot;,
                modifier = Modifier.animateEnterExit(
                    enter = scaleIn(
                        animationSpec = spring(
                            dampingRatio = Spring.DampingRatioHighBouncy,
                            stiffness = Spring.StiffnessMedium
                        )
                    ),
                    exit = None
                ),
                style = solvedStyle
            )
        }
    }
}
</code></pre>
<p><code>Modifier.animateEnterExit(...)</code> の <code>enter</code> と <code>exit</code> にはそれぞれ非表示から表示へ、表示から非表示へ変わるときのアニメーションを設定します。デフォルト値にはそれぞれ <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#fadeIn(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Float)">fadeIn()</a>, <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#fadeOut(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Float)">fadeOut()</a> が設定されています。この場合は表示時のみアニメーションを設定したいので、 <code>exit</code> のときはアニメーションせずに消えるように <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/EnterTransition#None()">None</a> を設定しています。</p>
<p>composableを上位のcomposableで囲ってスコープ (<a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedVisibilityScope">AnimatedVisibilityScope</a>) を作り、囲われた個別のcomposableの <code>Modifier</code> に個別の設定を追加する、というコーディングは、Jetpack Compose の頻出テクニックですね。このあとにも同様のテクニックが登場します。</p>
<h2>4. 画面更新時に古い画面が消えて新しい画面が現れるアニメーションを表現する</h2>
<p>以下のコードは、上下左右の矢印ボタンをタップして2次元の整数座標平面の上を移動する様子を表現したものです:</p>
<pre><code class="language-kotlin:FlatField.kt">@Composable
fun FlatField(modifier: Modifier = Modifier) {
    var xy by remember { mutableStateOf(Coordinates2D(0, 0)) }

    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(xy.background)
            .safeContentPadding()
    ) {
        IconButton(
            modifier = modifier.align(Alignment.CenterStart),
            onClick = { xy = xy.goLeft() }
        ) {
            Icon(
                modifier = Modifier.size(64.dp),
                imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                contentDescription = &quot;goLeft&quot;,
                tint = xy.foreground
            )
        }

        IconButton(
            modifier = Modifier.align(Alignment.TopCenter),
            onClick = { xy = xy.goUp() }
        ) {
            Icon(
                modifier = Modifier
                    .size(64.dp)
                    .rotate(90F),
                imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                contentDescription = &quot;goUp&quot;,
                tint = xy.foreground
            )
        }

        IconButton(
            modifier = Modifier.align(Alignment.CenterEnd),
            onClick = { xy = xy.goRight() }
        ) {
            Icon(
                modifier = Modifier.size(64.dp),
                imageVector = Icons.AutoMirrored.Filled.ArrowForward,
                contentDescription = &quot;goRight&quot;,
                tint = xy.foreground
            )
        }

        IconButton(
            modifier = Modifier.align(Alignment.BottomCenter),
            onClick = { xy = xy.goDown() }
        ) {
            Icon(
                modifier = Modifier
                    .size(64.dp)
                    .rotate(90F),
                imageVector = Icons.AutoMirrored.Filled.ArrowForward,
                contentDescription = &quot;goDown&quot;,
                tint = xy.foreground
            )
        }

        Text(
            text = xy.coordinateString,
            modifier = Modifier.align(Alignment.Center),
            color = xy.foreground,
            fontSize = 64.sp
        )
    }
}
</code></pre>
<p>初期画面の白い場所から↓ボタンの赤い場所に移動し、続いて→ボタンで青い場所に移動し…という操作を繰り返せますが、動きがないと移動している感覚をつかみにくいように感じます。こういう時こそ、アニメーションの追加が効果を発揮します。</p>
<p>移動中の状態を表すクラスは以下のようになります:</p>
<pre><code class="language-kotlin">data class Coordinates2D(val x: Int, val y: Int) {
    val background: Color
    val foreground: Color
    val coordinateString: String

    init {
        val index = nonNegativeRemainder()
        background = backgroundColors[index]
        foreground = foregroundColors[index]
        coordinateString = coordinateString(x, y)
    }

    private fun nonNegativeRemainder(): Int = ((x + y) % 3).let { if (it &lt; 0) it + 3 else it }

    fun goLeft() = Coordinates2D(x - 1, y)

    fun goUp() = Coordinates2D(x, y - 1)

    fun goRight() = Coordinates2D(x + 1, y)

    fun goDown() = Coordinates2D(x, y + 1)

    companion object Companion {
        private val backgroundColors =
            arrayOf(Color.White, Color(0xED, 0x29, 0x39), Color(0x00, 0x23, 0x95))
        private val foregroundColors = arrayOf(Color.Black, Color.White, Color.White)

        private fun coordinateString(x: Int, y: Int) = if (x == 0 &amp;&amp; y == 0) &quot;O&quot; else &quot;(${x}, ${y})&quot;
    }
}
</code></pre>
<h3>4.1. AnimatedContent(...) で囲み、古い画面から新しい画面へと切り替わるアニメーションを追加する</h3>
<p>以下は <code>FlatField(Modifier)</code> の中の <code>Box(...)</code> を <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#(androidx.compose.animation.core.Transition).AnimatedContent(androidx.compose.ui.Modifier,kotlin.Function1,androidx.compose.ui.Alignment,kotlin.Function1,kotlin.Function2)">AnimatedContent(...)</a> で囲ったものですが、アニメーションの定義に <code>Box(...)</code> のサイズの情報が必要なため <code>Box(...)</code> を <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#BoxWithConstraints(androidx.compose.ui.Modifier,androidx.compose.ui.Alignment,kotlin.Boolean,kotlin.Function1)">BoxWithConstraints(...)</a> に変え、 <code>constraints</code> を参照し <code>maxWidth</code> と <code>maxHeight</code> の値を使って <code>AnimatedContent(...)</code> の引数に与えています:</p>
<pre><code class="language-kotlin:FlatField.kt">@Composable
fun FlatField(modifier: Modifier = Modifier) {
    var xy by remember { mutableStateOf(Coordinates2D(0, 0)) }

    var width by remember { mutableIntStateOf(0) }
    var height by remember { mutableIntStateOf(0) }

    AnimatedContent(
        modifier = modifier.fillMaxSize(),
        targetState = xy,
        transitionSpec = {
            val deltaX = targetState.x - initialState.x
            val deltaY = targetState.y - initialState.y

            slideIn { IntOffset(x = deltaX * width, y = deltaY * height) } togetherWith
                    slideOut { IntOffset(x = -deltaX * width, y = -deltaY * height) }
        },
        label = &quot;coordinates2D&quot;
    ) { targetXy -&gt;
        BoxWithConstraints(
            modifier = Modifier
                .fillMaxSize()
                .background(targetXy.background)
                .safeContentPadding()
        ) {
            width = constraints.maxWidth
            height = constraints.maxHeight

            IconButton(
                modifier = Modifier.align(Alignment.CenterStart),
                onClick = { xy = targetXy.goLeft() }
            ) {
                Icon(
                    modifier = Modifier.size(64.dp),
                    imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                    contentDescription = &quot;goLeft&quot;,
                    tint = targetXy.foreground
                )
            }

            IconButton(
                modifier = Modifier.align(Alignment.TopCenter),
                onClick = { xy = targetXy.goUp() }
            ) {
                Icon(
                    modifier = Modifier
                        .size(64.dp)
                        .rotate(90F),
                    imageVector = Icons.AutoMirrored.Filled.ArrowBack,
                    contentDescription = &quot;goUp&quot;,
                    tint = targetXy.foreground
                )
            }

            IconButton(
                modifier = Modifier.align(Alignment.CenterEnd),
                onClick = { xy = targetXy.goRight() }
            ) {
                Icon(
                    modifier = Modifier.size(64.dp),
                    imageVector = Icons.AutoMirrored.Filled.ArrowForward,
                    contentDescription = &quot;goRight&quot;,
                    tint = targetXy.foreground
                )
            }

            IconButton(
                modifier = Modifier.align(Alignment.BottomCenter),
                onClick = { xy = targetXy.goDown() }
            ) {
                Icon(
                    modifier = Modifier
                        .size(64.dp)
                        .rotate(90F),
                    imageVector = Icons.AutoMirrored.Filled.ArrowForward,
                    contentDescription = &quot;goDown&quot;,
                    tint = targetXy.foreground
                )
            }

            Text(
                text = targetXy.coordinateString,
                modifier = Modifier.align(Alignment.Center),
                color = targetXy.foreground,
                fontSize = 64.sp
            )
        }
    }
}
</code></pre>
<p><code>AnimatedContent(...)</code> の引数 <code>transitionSpec</code> で、古い画面を追い出す <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#slideOut(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Function1)">slideOut { ... }</a> と 新しい画面を引き入れる <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#slideIn(androidx.compose.animation.core.FiniteAnimationSpec,kotlin.Function1)">slideIn { ... }</a> を <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/EnterTransition#(androidx.compose.animation.EnterTransition).togetherWith(androidx.compose.animation.ExitTransition)">infix EnterTransition.togetherWith(ExitTransition)</a> で合併してアニメーションを定義しています。 <code>AnimatedContent(...)</code> の引数 <code>content</code> に与えるラムダ式の引数 (<code>targetXy</code>) が追い出される画面の状態を保持しており、各 <code>IconButton(...)</code> の <code>onClick</code> で <code>targetXy</code> から得た新しい状態を <code>xy</code> に代入することで状態を更新します。</p>
<p>![アニメーションによる疑似スクロール](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_pseudo-scroll_1.webp =256x)
![アニメーションによる疑似スクロール](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_pseudo-scroll_2.webp =256x)
<em>Fig. 2-1, 2-2: アニメーションによる疑似スクロール</em></p>
<p>このコードで、仮に（<code>xy = xy.goLeft()</code> などと記述して） <code>targetXy</code> を使わないで（コンパイラの警告を無視して無理やり）ビルドすると、アニメーション中で追い出されていく古い画面が正しく表示されません。この書き方で、 <code>AnimatedContent(...)</code> がアニメーション中の状態を正しく管理してくれていることがわかります。このアニメーションをカスタマイズしたい場合には、 <code>AnimatedContent(...)</code> の引数 <code>transitionSpec</code> を変更するなどしてみてください。</p>
<p>アニメーションの追加で、位置を移動している感覚が一気につかみやすくなりました。この動きを確認していると、これは縦横2次元のページャーのようにも使えそうです。Jetpack Composeには、水平方向にスクロールする <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/pager/package-summary#HorizontalPager(androidx.compose.foundation.pager.PagerState,androidx.compose.ui.Modifier,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.pager.PageSize,kotlin.Int,androidx.compose.ui.unit.Dp,androidx.compose.ui.Alignment.Vertical,androidx.compose.foundation.gestures.TargetedFlingBehavior,kotlin.Boolean,kotlin.Boolean,kotlin.Function1,androidx.compose.ui.input.nestedscroll.NestedScrollConnection,androidx.compose.foundation.gestures.snapping.SnapPosition,androidx.compose.foundation.OverscrollEffect,kotlin.Function2)">HorizontalPager</a>、垂直方向にスクロールする <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/pager/package-summary#VerticalPager(androidx.compose.foundation.pager.PagerState,androidx.compose.ui.Modifier,androidx.compose.foundation.layout.PaddingValues,androidx.compose.foundation.pager.PageSize,kotlin.Int,androidx.compose.ui.unit.Dp,androidx.compose.ui.Alignment.Horizontal,androidx.compose.foundation.gestures.TargetedFlingBehavior,kotlin.Boolean,kotlin.Boolean,kotlin.Function1,androidx.compose.ui.input.nestedscroll.NestedScrollConnection,androidx.compose.foundation.gestures.snapping.SnapPosition,androidx.compose.foundation.OverscrollEffect,kotlin.Function2)">VerticalPager</a> が用意されていますが、2次元のページャーは標準にはありません。それがアニメーションの追加だけでページャー風のUIを作れます。もちろんアニメーションを追加しただけですので、スワイプして隣のページの一部だけを見るような操作はできませんが、十数行の追加と若干の変更だけで、アプリの印象を大きく変えられます。</p>
<h2>5. NavHost(...) での画面遷移の前後をつなぐアニメーション</h2>
<p><a href="https://developer.android.com/jetpack/androidx/releases/compose-animation">Compose Animation</a> <a href="https://developer.android.com/jetpack/androidx/releases/compose-animation#1.10.0">バージョン 1.10.0</a> から、 <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#SharedTransitionLayout(androidx.compose.ui.Modifier,kotlin.Function1)">SharedTransitionLayout</a> が安定版になりました。これはcomposableで作成した画面の<strong>共有要素</strong>を定義するものです。共有要素とは何か？ それは、<a href="https://developer.android.com/develop/ui/compose/animation/shared-elements?hl=ja">Compose での共有要素の遷移</a>の中の短い動画で確認してください。
<code>NavHost(...)</code> を利用している場合のコード例を以下に示します:</p>
<pre><code class="language-kotlin:GridTransform.kt">private val colorMap = mapOf(
    &quot;赤&quot; to Color.Red,
    &quot;緑&quot; to Color.Green,
    &quot;青&quot; to Color.Blue,
    &quot;シアン&quot; to Color.Cyan,
    &quot;マゼンタ&quot; to Color.Magenta,
    &quot;黄&quot; to Color.Yellow,
    &quot;茶&quot; to Color(132, 74, 43),
    &quot;群青&quot; to Color(76, 108, 179),
    &quot;カーキー&quot; to Color(197, 160, 90)
)

@Composable
fun GridTransform(modifier: Modifier = Modifier) {
    val navController = rememberNavController()
    NavHost(
        modifier = modifier
            .safeContentPadding()
            .fillMaxSize(),
        navController = navController,
        startDestination = ROUTE_SMALL_SQUARE
    ) {
        composable(route = ROUTE_SMALL_SQUARE) {
            val onClick: (String) -&gt; Unit = {
                navController.navigate(&quot;$ROUTE_LARGE_SQUARE?$ARG_SHARED=$it&quot;)
            }
            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.Center
            ) {
                Column(
                    modifier = Modifier
                        .aspectRatio(1f)
                        .padding(8.dp)
                        .fillMaxSize()
                        .background(
                            color = MaterialTheme.colorScheme.primaryContainer,
                            shape = RoundedCornerShape(16.dp)
                        )
                ) {
                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        ColorButton(
                            &quot;赤&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;緑&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;青&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )
                    }

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        ColorButton(
                            &quot;シアン&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;マゼンタ&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;黄&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )
                    }

                    Row(
                        modifier = Modifier.fillMaxWidth(),
                        horizontalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        ColorButton(
                            &quot;茶&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;群青&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )

                        ColorButton(
                            &quot;カーキー&quot;,
                            modifier = Modifier.weight(1f),
                            onClick = onClick
                        )
                    }
                }
            }
        }

        composable(
            route = &quot;$ROUTE_LARGE_SQUARE?$ARG_SHARED={sharedKey}&quot;,
            arguments = listOf(navArgument(&quot;sharedKey&quot;) { type = NavType.StringType })
        ) { entry -&gt;
            val colorName = entry.arguments?.getString(&quot;sharedKey&quot;) ?: &quot;&quot;
            LargeSquare(colorName) {
                navController.popBackStack()
            }
        }
    }
}

@Composable
fun ColorButton(
    colorName: String,
    modifier: Modifier = Modifier,
    onClick: (String) -&gt; Unit
) {
    TextButton(
        modifier = modifier
            .padding(16.dp)
            .aspectRatio(1f)
            .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp)),
        onClick = { onClick(colorName) }
    ) {
        Text(colorName, color = Color.White)
    }
}

@Composable
private fun LargeSquare(
    colorName: String,
    modifier: Modifier = Modifier,
    onBack: () -&gt; Unit
) {
    Box(
        modifier = modifier
            .padding(16.dp)
            .fillMaxSize()
            .aspectRatio(1f)
            .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp))
            .clickable { onBack() }
    ) {
        Text(text = colorName, modifier = Modifier.padding(8.dp), color = Color.White)
    }
}
</code></pre>
<p><em>注意: このコードは、単純化のためバックボタンと9色のボタンの連打などの対策を省略しています。短時間での連打を避けてテストしてみてください。</em></p>
<p>初期画面で9色のボタンが表示され、ボタンをタップするとタップしたボタンの色の大きな正方形が現れます。大きな正方形が表示された状態でバックボタンを押す（またはバックジェスチャを行う）と9色のボタンの画面に戻ります。ここで、ボタンタップで大きな正方形の画面に戻るときと、バックボタンで9色のボタンの画面に戻るときにフェイドインとフェイドアウトのアニメーションが見られます。これは <a href="https://developer.android.com/reference/kotlin/androidx/navigation/NavHost">NavHost(...)</a> の引数 <code>enterTransition</code>, <code>exitTransition</code>, <code>popEnterTransition</code>, <code>popExitTransition</code>, <code>sizeTransform</code> のデフォルト値で規定されています。この設定を、特定の画面遷移において以下のようなコードの追加と修正を加えることにより、特定の画面遷移前後の<strong>共有要素</strong>を関連づけるアニメーションで上書きすることができます。</p>
<p><code>NavHost(...)</code> によるバックスタックの変化を伴わない画面遷移における<strong>共有要素</strong>アニメーションの実装は、<a href="https://developer.android.com/develop/ui/compose/animation/shared-elements?hl=ja">Compose での共有要素の遷移</a>を参照してください。下表の左から右、および右から左への画面遷移時に、左の画面でタップしたボタン（右下）によって現れた右の画面の正方形がまさに目的のコンテンツであったことがアニメーションによって視覚的に表現されます。</p>
<table>
<thead>
<tr>
<th align="center">9色のボタンの画面</th>
<th align="center">大きい正方形の画面</th>
</tr>
</thead>
<tbody><tr>
<td align="center">![9色のボタンの画面](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_9-buttons.webp =256x)<em>Fig. 3-1</em></td>
<td align="center">![大きい正方形の画面](/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_large-square.webp =256x)<em>Fig. 3-2</em></td>
</tr>
</tbody></table>
<h3>5.1. SharedTransitionLayout { ... } で囲み、画面要素を共有する</h3>
<p>ここでは、 <code>NavHost(...)</code> による画面遷移の前後での<strong>共有要素</strong>アニメーションの実装を紹介します:</p>
<pre><code class="language-kotlin:GridTransform.kt">@Composable
fun GridTransform(modifier: Modifier = Modifier) {
    val navController = rememberNavController()
    SharedTransitionLayout {
        NavHost(
            modifier = modifier
                .safeContentPadding()
                .fillMaxSize(),
            navController = navController,
            startDestination = ROUTE_SMALL_SQUARE
        ) {
            composable(route = ROUTE_SMALL_SQUARE) {
                val onClick: (String) -&gt; Unit = {
                    navController.navigate(&quot;$ROUTE_LARGE_SQUARE?$ARG_SHARED=$it&quot;)
                }
                Box(
                    modifier = Modifier.fillMaxSize(),
                    contentAlignment = Alignment.Center
                ) {
                    Column(
                        modifier = Modifier
                            .aspectRatio(1f)
                            .padding(8.dp)
                            .fillMaxSize()
                            .background(
                                color = MaterialTheme.colorScheme.primaryContainer,
                                shape = RoundedCornerShape(16.dp)
                            )
                    ) {
                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.spacedBy(8.dp)
                        ) {
                            ColorButton(
                                this@composable,
                                &quot;赤&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;緑&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;青&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )
                        }

                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.spacedBy(8.dp)
                        ) {
                            ColorButton(
                                this@composable,
                                &quot;シアン&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;マゼンタ&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;黄&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )
                        }

                        Row(
                            modifier = Modifier.fillMaxWidth(),
                            horizontalArrangement = Arrangement.spacedBy(8.dp)
                        ) {
                            ColorButton(
                                this@composable,
                                &quot;茶&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;群青&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )

                            ColorButton(
                                this@composable,
                                &quot;カーキー&quot;,
                                modifier = Modifier.weight(1f),
                                onClick = onClick
                            )
                        }
                    }
                }
            }

            composable(
                route = &quot;$ROUTE_LARGE_SQUARE?$ARG_SHARED={sharedKey}&quot;,
                arguments = listOf(navArgument(&quot;sharedKey&quot;) { type = NavType.StringType })
            ) { entry -&gt;
                val colorName = entry.arguments?.getString(&quot;sharedKey&quot;) ?: &quot;&quot;
                LargeSquare(this, colorName) {
                    navController.popBackStack()
                }
            }
        }
    }
}

@Composable
fun SharedTransitionScope.ColorButton(
    animatedContentScope: AnimatedVisibilityScope,
    colorName: String,
    modifier: Modifier = Modifier,
    onClick: (String) -&gt; Unit
) {
    TextButton(
        modifier = modifier
            .padding(16.dp)
            .aspectRatio(1f)
            .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp))
            .sharedBounds(
                sharedContentState = rememberSharedContentState(colorName),
                animatedVisibilityScope = animatedContentScope,
                boundsTransform = { _, _ -&gt;
                    tween(durationMillis = 500)
                },
                enter = fadeIn(),
                exit = fadeOut(),
                resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
            ),
        onClick = { onClick(colorName) }
    ) {
        Text(colorName, color = Color.White)
    }
}

@Composable
private fun SharedTransitionScope.LargeSquare(
    animatedContentScope: AnimatedVisibilityScope,
    colorName: String,
    modifier: Modifier = Modifier,
    onBack: () -&gt; Unit
) {
    Box(
        modifier = modifier
            .padding(16.dp)
            .fillMaxSize()
            .aspectRatio(1f)
            .background(color = colorMap[colorName]!!, shape = RoundedCornerShape(16.dp))
            .sharedBounds(
                sharedContentState = rememberSharedContentState(colorName),
                animatedVisibilityScope = animatedContentScope,
                boundsTransform = { _, _ -&gt;
                    tween(durationMillis = 500)
                },
                enter = fadeIn(),
                exit = fadeOut(),
                resizeMode = SharedTransitionScope.ResizeMode.scaleToBounds()
            )
            .clickable { onBack() }
    ) {
        Text(text = colorName, modifier = Modifier.padding(8.dp), color = Color.White)
    }
}
</code></pre>
<p><a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/package-summary#SharedTransitionLayout(androidx.compose.ui.Modifier,kotlin.Function1)">SharedTransitionLayout { ... }</a> で大外を囲み、 <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/SharedTransitionScope">SharedTransitionScope</a> の中で <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/SharedTransitionScope#(androidx.compose.ui.Modifier).sharedBounds(androidx.compose.animation.SharedTransitionScope.SharedContentState,androidx.compose.animation.AnimatedVisibilityScope,androidx.compose.animation.EnterTransition,androidx.compose.animation.ExitTransition,androidx.compose.animation.BoundsTransform,androidx.compose.animation.SharedTransitionScope.ResizeMode,androidx.compose.animation.SharedTransitionScope.PlaceholderSize,kotlin.Boolean,kotlin.Float,androidx.compose.animation.SharedTransitionScope.OverlayClip)">Modifier.sharedBounds(...)</a> を使って<strong>共有要素</strong>の対応づけを行うのは <code>NavHost(...)</code> を使わない画面遷移の場合と同じです。 <code>Modifier.sharedBounds(...)</code> の引数 <code>animatedVisibilityScope</code> には <a href="https://developer.android.com/reference/kotlin/androidx/navigation/NavGraphBuilder#(androidx.navigation.NavGraphBuilder).composable(kotlin.String,kotlin.collections.List,kotlin.collections.List,kotlin.Function1,kotlin.Function1,kotlin.Function1,kotlin.Function1,kotlin.Function1,kotlin.Function2)">NavGraphBuilder.composable(...)</a> に由来する <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedContentScope">AnimatedContentScope</a> (<code>this@composable</code>) をあてます。これで <code>NavHost(...)</code> による画面遷移時に共有要素間のアニメーションを表示できるようになります。 <code>SharedTransitionScope.ColorButton(...)</code> と <code>SharedTransitionScope.LargeSquare(...)</code> で、 <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/SharedTransitionScope#rememberSharedContentState(kotlin.Any)">rememberSharedContentState(Any)</a> の引数 <code>key</code> にボタンの色の名前をあてることで、画面遷移前と遷移後の画面要素を共有していることを確かめてみてください。</p>
<p>共有要素アニメーションの設定のために新たに追加が必要なコーディングは、</p>
<ol>
<li><code>SharedTransitionLayout { ... }</code> で囲む</li>
<li>共有要素を定義するための <code>sharedBounds(...)</code> (or <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/SharedTransitionScope#(androidx.compose.ui.Modifier).sharedElement(androidx.compose.animation.SharedTransitionScope.SharedContentState,androidx.compose.animation.AnimatedVisibilityScope,androidx.compose.animation.BoundsTransform,androidx.compose.animation.SharedTransitionScope.PlaceholderSize,kotlin.Boolean,kotlin.Float,androidx.compose.animation.SharedTransitionScope.OverlayClip)">sharedElement(...)</a>) を設定し共有要素間で <code>key</code> を一致させる</li>
<li><ol start="2">
<li>のために必要な <code>SharedTransitionScope</code>, <a href="https://developer.android.com/reference/kotlin/androidx/compose/animation/AnimatedVisibilityScope">AnimatedVisibilityScope</a> の2つのスコープを composable に渡す</li>
</ol>
</li>
</ol>
<p>です。これらはアニメーション設定前のコードの構成を大きく変えることなく実装可能でしょう。もし、既存のコードに対して少ない変更での適用が難しいようでしたら、変更が容易な構成になるようリファクタリングを試みてみてください。</p>
<p>共有要素アニメーションはうまくはまれば美しいですが、画面設計のイメージとぴったり合うアニメーションは難しいかもしれません。その場合は <code>Modifier.sharedBounds(...)</code> の引数 <code>enter</code>, <code>exit</code>, <code>boundsTransform</code>, <code>resizeMode</code> をいろいろと調整するなどしてみてください。</p>
<h3>5.2. 予測型「戻る」との関係</h3>
<p>共有要素アニメーションは、 <code>NavGraphBuilder.composable(...)</code> のデフォルトの画面遷移アニメーションを上書きします。加えて、予測型「戻る」アニメーションの有効化、すなわち、API level 33〜35 の AndroidManifest.xml において <code>android:enableOnBackInvokedCallback=&quot;true&quot;</code> を指定している場合、または API level 36 以上の AndroidManifest.xml において <code>android:enableOnBackInvokedCallback=&quot;false&quot;</code> を指定していない場合において、 <code>NavHost(...)</code> による戻るアニメーションも上書きします。予測型「戻る」アニメーションを有効にした状態でアプリをビルドし、端末をジェスチャーナビゲーションモードに設定して上記の大きな正方形の画面で「戻る」ジェスチャをゆっくりと実行すると、共有要素アニメーションがゆっくりと逆戻りしていくことが容易に確かめられます。また、API level 36 以上、かつ Android OS 16 以上でボタンナビゲーションモードに設定してバックボタンを長押しすると、共有要素の逆戻りアニメーションが見られるはずです。</p>
<p>このように <code>NavHost(...)</code> の「戻る」アニメーションは予測型「戻る」アニメーションの設定に影響を与えます。そのことに留意して、予測型「戻る」アニメーションの有効化設定を行うか否か決定する必要があります。API level 36 の時点では、 <code>android:enableOnBackInvokedCallback=&quot;false&quot;</code> を指定することによって予測型「戻る」アニメーションを無効にできます。</p>
<h2>6. まとめ</h2>
<p>本記事では、小規模の変更で Jetpack Compose における実用的なアニメーションを実装できるテクニックを4例ほど紹介しました。アプリ内のアニメーションは、ほとんどの場合必須の機能ではないため、特にスケジュールに余裕のない開発プロジェクトでは実装が省略されがちですが、使いどころによっては小さくない使い勝手の向上をもたらし、アプリの印象を大きく向上させる力を秘めています。それらを少ない工数で可能にする手段が豊富にあれば、気軽に実装を試すことができます。Compose Animation のAPIの多くは、<em>宣言的</em>、すなわち、UI要素をアニメーションさせるよ、と宣言するような感覚でアニメーションを追加できるように工夫されており、細かい手続き的記述の中で動作を定義しなければならないような複雑さを回避しやすい設計になっています。それらを活用し、多くのアプリの品質向上に役立てられれば幸いです。</p>
<h2>7. 参考文献</h2>
<ul>
<li><a href="https://developer.android.com/reference">Android API reference</a></li>
<li><a href="https://developer.android.com/develop/ui/compose/animation/quick-guide">Quick guide to Animations in Compose</a></li>
<li><a href="https://developer.android.com/develop/ui/compose/animation/composables-modifiers">Animation modifiers and composables</a></li>
<li><a href="https://developer.android.com/guide/navigation/custom-back/support-animations">Add support for predictive back animations</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/tsuyoshi_yamada/tech-blog_compose-animation_top.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[２ヶ月間の育児休暇　〜事前準備と復帰まで〜]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-08-child-care-leave/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-08-child-care-leave/</guid>
            <pubDate>Mon, 08 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[２ヶ月間育児休暇で現場を離れた感想です]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->
<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の8日目の記事です🎅🎄</p>
<h1>はじめに</h1>
<p>こんにちは。ご覧いただきありがとうございます！
<a href="https://factory.kinto-jp.com">KINTO FACTORY</a>にて、フロントエンド開発している中本です。</p>
<p>今回は、技術寄りのお話ではなく今年８月に、息子が誕生した際に取得した育児休暇について、現場レベルでの引き継ぎや復帰してからの感想など、紹介させていただきます。</p>
<h2>上司への相談</h2>
<p>今年の４月より、FACTORY開発グループ内のフロントエンドチームのチームリーダーにアサイン頂いたので、最初は長期間で休むことに少し抵抗も感じましたが、マネージャーへ相談した際には快く育児休暇の取得を後押し頂きました。</p>
<p>特に産まれてからの最初の数カ月は、奥さんも心身ともに大変な時期になると思うので、なるべくサポートしてあげてください、と前向きな言葉をかけて頂きました。</p>
<p>そこで、ひとまず２ヶ月ほどの育児休暇を申請し、１ヶ月経った頃に復帰できそうかどうかを判断させていただく、というやり方にさせて頂きました。</p>
<h2>引き継ぎ事項</h2>
<p>さて、日々の業務を引き継ぎしていくにあたり、普段やっているフロントエンド開発に加え、チームリーダーとして、</p>
<ul>
<li>新規案件のアーキテクチャ確認・方向性決定・関係部署との連携</li>
<li>各メンバーとの 1-on-1</li>
<li>各メンバーと半期ごとの振り返りと評価</li>
<li>採用活動</li>
</ul>
<p>などがありました。</p>
<p>一つずつ、どのようにしていったか深堀ります。</p>
<blockquote>
<p> 新規案件のアーキテクチャ確認・方向性決定・関係部署との連携</p>
</blockquote>
<p>休暇取得が始まりそうな、８月後半〜にかけてのロードマップをPdMやマネージャーと日々確認したところ、そこまで大きな新規案件は無さそうで、現在進行中のものが大半でした。</p>
<p>そこで、今まで自分の方で担当していた案件は、極力 Confluence の方にもまとめることを始め、「誰とどのような話をしてここまで決まっている」や「自分の中でここまではやった」などを簡単にまとめるクセを付けておきました。</p>
<p><img src="/assets/blog/authors/k.nakamoto/childcare-leave/handover.jpg" alt="引き継ぎ資料の例"></p>
<blockquote>
<p>各メンバーとの 1-on-1
各メンバーと半期ごとの振り返りと評価</p>
</blockquote>
<p>次に、マネジメントエリアです。
チームリーダーの１つのミッションとして、半期ごとに各メンバーの振り返りを確認し評価する必要があります。</p>
<p>その振り返り期間が、ちょうど休暇を取得する期間とモロ被りしそうだったので、あらかじめ４月の期が始まったタイミングから、各自に目標を設定してもらい振り返りをスムーズにできるようにしておきました。</p>
<p>実際には、各メンバーが決めた目標と１ヶ月ごとに「何を達成した、来月は何をする」をConfluence へ記入してもらい、1-on-1の話のネタにしておりました。
自分の方でも、気付いた部分・頑張ってくれた点は上記 Confluence へメモしておき、振り返りの際にフィードバックできるようにしておきました。</p>
<p>産まれるタイミングで休暇を取得する予定だったため、もし評価期間と被ってしまった場合は、上記 Confluence をそのままマネージャーへお渡しすれば、各メンバーの目標と成果をすぐ確認できると考えておりました。</p>
<blockquote>
<p>採用活動</p>
</blockquote>
<p>こちらは、基本的にマネージャーへ代わりをお願いさせて頂きました。</p>
<p>ただ、開発メンバーにも面接時から応募者の技術力やコミュニケーション力などを感じて欲しいので、面接に同行してもらい、現場レベルでのコミュニケーションやテックスキルなどを確認してもらいました。</p>
<h1>おやすみ中</h1>
<p>さて、そのように大体の引き継ぎ目処が経った8月24日、無事に元気な男の子が産まれました！</p>
<p><img src="/assets/blog/authors/k.nakamoto/childcare-leave/birth.jpg" alt="生まれたばかりの赤ちゃん"></p>
<p>そこから３日間の特別休暇を頂き、役所関係や病院との往復を繰り返し、育児休暇開始前日に会社からの貸与品（PC、スマホ、社員証）などを返却しに１日だけ出社しました。
ここから約２ヶ月弱の間、業務からは一歩引いて、育児にフルコミットすることとします！</p>
<h2>育児は大変</h2>
<p>FACTORY開発グループには、現役の子育てパパがいっぱいいるので、色々なお話を産前から伺ってましたが、例にも漏れずやはり最初の１ヶ月は大変でした...
我が家は、妻と自分で半日ずつの交代制で育児と睡眠を分け、自分は朝起きてから夜寝るまでの育児を担当しました。妻が、そのほうが育児休暇明けも睡眠サイクルを崩さず復帰できるのでは？と言ってくれたので、基本的に日中は起きて・夜間に就寝できるようになりました。</p>
<p>ミルク・おむつ替え・あやす・沐浴など、やることが分単位でやってくるので、自分のことを考える余裕もなく、最初の1-2週間は業務のことは全く頭になかったかと思います (´ω`；)</p>
<h2>でもサービスも気になる</h2>
<p>休暇に入って、１ヶ月ほど経ったくらいで、マネージャーとお話しでき、案件の進捗状況やチームの状態など共有いただくことができました。１ヶ月間、外出も最低限で、他人とお話するのも久しぶりだったので、精神的にもすごく解放された感じがしました。</p>
<p>また、この頃からだいぶ育児にも慣れて来ており、息子も落ち着いて寝る時間が増えてきたので、ちょこちょこサービスの更新状況を見に行っていた記憶があります。</p>
<p>KINTO FACTORYは水曜日に新商品の発売開始や、新機能の提供が始まるので、水曜日はサイトに訪れ「今月は何がでたかな？」と確認していました。</p>
<h2>帰還</h2>
<p>そしてこの度、65日間の育児休暇を終え11月より現場に復帰しました。
グループのメンバーなどにも大きな変化はなく、進行中だった案件が後ろ倒しになっていたりと、思っていたより休暇に入る前からそこまで大幅な変化もなく、すんなりと復帰することができた印象です。</p>
<p>ただ、いきなり妻が日中帯でのワンオペ育児となるので、なるべく負荷を低減してもらうために、できるだけ朝早い時間に出社し夕方もなるべく早く帰宅するように心がけています。
このあたりも、フルフレックス制度によりMTGの調整やチーム内での合意を取れれば、自由に稼働時間を調整することが可能となっております。</p>
<h1>さいごに</h1>
<p>最初の２ヶ月間育児に専念することで、息子が初めて見せた笑顔だったり、あーうーと唸りだしたりと、日に日に成長する姿を間近に見ることができ、この時期に育児に関わることで貴重な体験をすることができたと思います。</p>
<p>このような体験ができたのも、育児休暇を取得するにあたって、快く送り出して頂いたマネージャー、そしてグループの皆さんにこの場を借りて感謝させて頂きたいです。</p>
<p>KINTO テクノロジーズでは、このように男性でも育児休暇を取得しやすい環境かと思います。実際に、今年だけでも他部署含め多くの男性エンジニアが育児休暇を取得しているお話を聞いています（社内に息子の同級生も多い！）。
育児休暇を取得する準備や、復帰について少しでも参考になれば幸いです。</p>
<p>また、下記記事では同じFACTORY開発グループ内の先輩パパの１日が紹介されていますので、そちらもぜひご覧頂ければと思います！</p>
<p><a href="/posts/2025-09-08-factory-engineer-with-child/">パパママエンジニア必見！KTCパパエンジニアの１日</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/k.nakamoto/childcare-leave/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[セキュリティはビジネスの推進力である - 当社が目指す“Speed × Quality”を支えるセキュリティ組織とは]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-07-KTC_SecurityReport/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-07-KTC_SecurityReport/</guid>
            <pubDate>Sun, 07 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[セキュリティを「ビジネスの推進力」と考え、自社の取り組みや哲学を通じて、モビリティと金融を融合させたサービスにおけるスピードと品質を保ちながら、効果的なセキュリティを実現する方法について説明する。]]></description>
            <content:encoded><![CDATA[<p>これは <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の7日目の記事です🎄</p>
<h3><strong>1.はじめに</strong></h3>
<p>KINTOテクノロジーズ、セキュリティ・プライバシー部のKa-Saiです。</p>
<p>私たちはトヨタグループの一員として金融を含むモビリティサービスを提供するにあたり、お客様やパートナー企業からお預かりした個人情報や機密情報、そしてそれらを支えるシステムといった「情報資産」を守ることを最重要テーマの一つとして位置づけ、次のミッションとビジョンを掲げて活動しています。</p>
<h5><strong>ミッション</strong></h5>
<ul>
<li><strong>お客様に安心、安全なサービスを提供する。</strong></li>
<li><strong>私たちのミッションはブレーキを踏むことではなく、どうしたら安心・安全にアクセルを踏めるのかを考えることである。</strong></li>
</ul>
<h5><strong>ビジョン</strong></h5>
<ul>
<li><strong>セキュリティ・バイ・デザイン、プライバシー・バイ・デザインを浸透させ、安心、安全なサービスが当たり前の世の中を作る。</strong></li>
</ul>
<p>・・・・・</p>
<p>さて、セキュリティという言葉からは「スピードを落とすもの」「新しい挑戦を止めるもの」というイメージが先行しがちです。しかし私たちは<strong>セキュリティはビジネスの“ブレーキ”ではなく、“推進力”になりうる</strong>と考えており、このエントリーでは、</p>
<ul>
<li>なぜセキュリティがブロッカーに見えがちなのか</li>
<li>その構造的な課題に対し、当社がどのような哲学と設計で向き合っているか</li>
<li>具体的な組織・仕組み・取り組み</li>
<li>そして、私たちが見据える未来</li>
</ul>
<p>についてご紹介したいと思います。</p>
<h3><strong>2. 課題意識：従来型セキュリティのジレンマ</strong></h3>
<h4><strong>2-1. セキュリティはなぜ“遅くするもの”に見えるのか</strong></h4>
<p>多くの組織で、セキュリティは次のように捉えられがちです。</p>
<ul>
<li>手続きが重く、レビューや承認でスピードが落ちる</li>
<li>新しいツール、クラウドサービス、生成AIなどにストップがかかる</li>
<li>リリース直前に差し戻しが発生し、手戻りが大きくなる</li>
<li>コストセンターとして扱われ、価値が見えにくい</li>
</ul>
<p>これらは、「後付けのセキュリティ」「人に依存した個別判定」「都度相談ベース」といった構造から生じる課題です。そしてもう一つ、私たちが強く意識しているのが、</p>
<p><strong>「ルールは存在するだけで、暗黙のブロッカーになりうる」</strong></p>
<p>という現実です。これは、ルールが存在するだけで、「なんとなく怖いからやめておこう」「ここに触れるのはやめておこう」という自己検閲が働きます。その結果、チャレンジもイノベーションも生まれにくくなります。</p>
<h4><strong>2-2. 飛躍成長に“スケールする”セキュリティへ</strong></h4>
<p>当社は、モビリティ×金融という高いレギュレーションと複雑なビジネス要件が絡み合う領域で、事業の飛躍的成長を目指しています。<br>新しい市場・国・サービス形態への展開にあたっては、セキュリティも同じスピードとスケール感で成長しなければなりません。</p>
<p>ここで重要になるのが、</p>
<p><strong>「セキュリティもリスクだが、ビジネスの減速もまた大きなリスクである」</strong></p>
<p>という視点です。</p>
<p>攻撃を受けるリスクだけでなく、<strong>成長の推進力を失うリスク</strong>も、経営にとっては無視できません。</p>
<p>そこで私たちは、「いかに早く“セキュアなプロダクト”を世の中に出せるか」を重要視しています。<br>つまり、最初の段階から一定水準のセキュリティとコンプライアンスを満たした状態で、素早くマーケットに到達できるかという観点です。</p>
<ul>
<li>新しいビジネス・機能を早く世に出す</li>
<li>同時に、金融・モビリティ領域に求められる厳しい安全基準を満たす</li>
</ul>
<p>この両方を満たすためには、「ビジネスゴールに直接貢献する目標」と「セキュリティの目標」を切り離さず、同じテーブルで設計するアプローチが必要です。</p>
<h4><strong>2-3. 従業員の負担をどう減らすか</strong></h4>
<p>セキュリティ・プライバシー部は全知全能ではありません。すべてのコード、すべての設定、すべてのデータフローを当部だけで確認することは現実的ではなく、従業員一人ひとりの協力が不可欠です。</p>
<p>しかし従業員にはそれぞれ本来のミッションがあります。プロダクト開発・運用・ビジネス側のタスクも山積みです。</p>
<ul>
<li>セキュリティ対応が「追加の仕事」として乗ってくる</li>
<li>手動のチェックや証跡作業が増える</li>
<li>要件理解に時間がかかり、本来の仕事が圧迫される</li>
</ul>
<p>結果として、過剰または非対称な（かけた時間に対してインパクトが小さい）セキュリティ作業が発生しがちです。だからこそ、当社では</p>
<ul>
<li>CISOからのトップダウンな支援</li>
<li>現場への権限移譲と自律的なボトムアップの行動</li>
</ul>
<p>この両方のバランスを取り、従業員の負荷を増やさずにセキュリティレベルを上げる設計を重視しています。</p>
<h3><strong>3. 当社のセキュリティ哲学：Security as Momentum</strong></h3>
<p>上述の課題を踏まえ、当部がたどり着いた結論はシンプルです。</p>
<p><strong>セキュリティは、正しくデザインすれば、事業を加速させる推進力になる。</strong></p>
<p>この哲学のもと、私たちは次のような考え方を採用しています。</p>
<h4><strong>3-1. “プラットフォーム”としてのセキュリティ</strong></h4>
<p>人が一つひとつチェックするのではなく、<strong>仕組みやプラットフォームにセキュリティを埋め込む</strong>ことで、摩擦を最小化します。</p>
<ul>
<li>安全なクラウド設計・設定をガイドラインとテンプレートに落とし込む</li>
<li>CI/CD パイプラインに自動チェックを組み込む</li>
<li>セキュアなデフォルト値を提供し、「何もしなければ安全」な状態をつくる</li>
</ul>
<h4><strong>3-2. “止める人”ではなく“実現する人”</strong></h4>
<p>セキュリティの役割は「No」を突きつけることではなく、「どうしたら安全にYesと言えるか」を一緒に考えることです。</p>
<ul>
<li>企画段階からの相談を歓迎し、実現可能な選択肢を提示する</li>
<li>“やってはいけないこと”だけではなく、“こうすればできる”を示す</li>
</ul>
<h4><strong>3-3. 開発者体験としてのセキュリティ</strong></h4>
<p>“セキュアな開発者の体験” を整えることは、結果的に開発組織全体の生産性向上につながるため、開発者にとっての「安心してアクセルを踏める環境づくり」になっているかを常に意識しています。</p>
<ul>
<li>「このテンプレートを使えばOK」という明確なガードレール</li>
<li>面倒なセキュリティ評価の証跡取得作業を自動化</li>
<li>相談しやすいチャネルと、わかりやすいドキュメント</li>
</ul>
<h3><strong>4. セキュリティ管理部署の組織体制：ビジネス成長と速度を支えるための構造</strong></h3>
<p>当社はトヨタ自動車の子会社であるトヨタファイナンシャルサービス（TFS）のグループ会社に属しています。TFSグループではサイバーセキュリティのリスクを最も重要な経営課題の一つとして位置づけ、グループ全体で対策を推進しており、この方針のもと、当社における情報セキュリティ管理は、TFSグループのセキュリティプログラムを基盤として運用されています。</p>
<h4><strong>4-1. GIS Standard：グローバル基準に基づくセキュリティフレームワーク</strong></h4>
<p>そしてTFSグループでは、NISTやISO27001などの国際標準をベースに、金融サービスに最適化された独自のセキュリティ要件「GIS Standard（Global Information Security Standard）」を策定し、グループ全体で運用しています。</p>
<p><img src="/assets/blog/authors/ka_sai/20251201_sec.png" alt="GIS Standardに含まれるセキュリティ要件の項目名を画像で可視化したもの"></p>
<p>このGIS Standardには</p>
<ul>
<li>ガバナンス</li>
<li>オペレーション</li>
<li>テクニカルコントロール</li>
<li>クラウドセキュリティ</li>
<li>生成AI などの新領域</li>
</ul>
<p>といった多岐にわたるドメインをカバーする300以上の管理項目が含まれており、当社はこのGIS Standardを自社の標準として採用し、<strong>グローバル水準の安全性と信頼性を前提条件として事業を進める</strong>ことを選択しています。</p>
<h4><strong>4-2. セキュリティ・プライバシー部の3つの専門グループ</strong></h4>
<p>このGIS Standardというセキュリティフレームワークを現場で機能させるため、セキュリティ・プライバシー部は以下の専門グループで構成されています。</p>
<h5><strong>1.クラウドセキュリティG</strong></h5>
<ul>
<li>クラウドにおける設計・設定標準化（ガイドライン・ガードレール）</li>
<li>CNAPP / CSPM / CIEM などの導入・運用</li>
<li>マルチクラウド環境のリスク監視</li>
</ul>
<h5><strong>2.サイバーセキュリティG</strong></h5>
<ul>
<li>SOC業務（ログ収集・分析・インシデント対応）</li>
<li>脆弱性管理・診断（Red Team / Blue Team）</li>
<li>シャドーIT対策</li>
</ul>
<h5><strong>3.インフォメーションセキュリティG</strong></h5>
<ul>
<li>GIS StandardアセスメントのDX化</li>
<li>規程整備・リスク評価</li>
<li>情報セキュリティ教育の高度化</li>
<li>プライバシー影響評価（PIA）</li>
<li>AI利用におけるデータ保護ガイドライン策定</li>
<li>知的財産権管理の仕組化</li>
</ul>
<p>これらのグループが連携しながら、プロダクトチーム・コーポレート部門・グローバルの関連会社と伴走する体制を形作っています。</p>
<h3><strong>5. 当社の具体的な取り組み：セキュリティを推進力へ変える実装構造</strong></h3>
<h4><strong>5-1. プラットフォームセキュリティ：仕組みに埋め込むセキュリティ</strong></h4>
<h5>(1) クラウドセキュリティの標準化</h5>
<ul>
<li>AWS / Azure / GCPごとのセキュリティガイドライン</li>
<li>ガードレールの「カイゼンガイド」</li>
<li>AIセキュリティガイドライン</li>
</ul>
<p>を整備し、「<strong>これに沿って設計・実装すればGIS Standardやベストプラクティスを自然に満たせる</strong>」状態を目指しています。</p>
<h5>(2) CNAPP(Cloud-Native Application Protection Platform)の導入</h5>
<ul>
<li>クラウド設定の継続的評価</li>
<li>IAM権限の過剰状態の検知と是正</li>
<li>クラウド上のワークロード保護</li>
<li>ログ収集・分析による脅威検知</li>
</ul>
<p>を自動化。「人が都度チェックする」から、「システムが常時監視し、必要に応じてアラートを上げる」体制へと進化しつつあります。</p>
<h4><strong>5-2. サイバーディフェンス：攻めと守りの両輪</strong></h4>
<h5>(1) Red Team（攻め）</h5>
<ul>
<li>新規プロジェクトや診断未実施プロダクトへの積極的な脆弱性診断</li>
<li>SBOM・Secret管理・EOLなど、ソフトウェアサプライチェーンの観点も含めた検証</li>
</ul>
<h5>(2) Blue Team（守り）</h5>
<ul>
<li>SIEM・EDR・Proxy・DNSなどを用いたログ監視・分析</li>
<li>インシデント対応計画の整備と演習</li>
<li>検知ルールの継続的なチューニング</li>
</ul>
<p>これらの取り組みにより、「攻撃を受けてから慌てて対応する」のではなく、  <strong>事前の備えと継続的なモニタリングで、組織全体の“余白”を生み出す</strong>ことを目指しています。</p>
<h4><strong>5-3. セキュリティ・プライバシーアセスメントDX：コンプライアンスを“副産物”にする</strong></h4>
<h5>(1) セキュリティガバナンス</h5>
<ul>
<li>GIS Standardへの適合評価（アセスメント）の自動化</li>
<li>エビデンス収集のダッシュボード化</li>
<li>リスクベースアプローチによるアセスメント対象の整理</li>
<li>セキュリティ問い合わせの自動応答化</li>
</ul>
<p>いわゆる”セキュリティガバナンス”を「DX（デジタルトランスフォーメーション）」の対象として捉え、<strong>「監査対応のために証跡をかき集める」ような後ろ向きの作業</strong>を減らし、「普段の運用がそのまま証跡になる」状態へと近づけています。</p>
<h5>(2) プライバシーガバナンス / 知的財産の管理</h5>
<ul>
<li>プライバシー影響評価（PIA）の導入・運用</li>
<li>AI利用におけるデータ保護ガイドラインの策定・浸透</li>
<li>ライフサイクルに連動した網羅的な知財調査とログ管理</li>
</ul>
<p>これにより、<strong>「安心して使えるサービス」であることが、ビジネスの選ばれる理由になる状態</strong>を目指しています。</p>
<h3><strong>6. おわりに：セキュリティを職人仕事から“標準化”、そして“推進力”へ</strong></h3>
<p>セキュリティ・プライバシー部のミッションは、お客様に安心・安全なサービスを提供することです。<br>その達成のため、セキュリティは単なる「リスクの最小化」ではなく、<strong>ビジネスの成長と推進力醸成にどう貢献できるか</strong>を常にSpeedとQualityの両面から考えています。</p>
<p>一方で、現実には
　</p>
<ul>
<li>急速なビジネス成長への対応</li>
<li>従業員の負担軽減</li>
<li>法令要件への効率的な準拠</li>
</ul>
<p>といったテーマのバランスをとることが、大きなチャレンジであることも事実です。私たちは、このチャレンジに対して</p>
<h5><strong>「職人仕事」から「標準化」へ</strong></h5>
<p>   一部のセキュリティエキスパートだけに依存した属人的なセキュリティから、  誰もが同じ品質を出せる標準化された仕組みへ</p>
<h5><strong>従業員中心設計（Employee-Centered Design）</strong></h5>
<p>   セキュリティのために人を酷使するのではなく、  従業員が無理なく・誇りを持って取り組めるように設計する</p>
<h5><strong>自動化中心設計（Automation-First Design）</strong></h5>
<p>   トイル（自動化可能な業務）を減らし、クリエイティブな仕事に集中できるようにする</p>
<h5><strong>アジャイルガバナンス</strong></h5>
<p>   一度作ったルールも、“壊しながら”作り直し続ける姿勢を持つ</p>
<p>という観点で取り組みを進めており、</p>
<p><strong>私たちはQualityを高めるセキュリティを提供するだけでなく、Speedにも寄与し、事業を加速させる形でセキュリティを推進できる</strong></p>
<p>と信じてこれからもビジネス、開発、そしてお客様とともに歩んでまいります！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Protecting Workloads on Azure Container Apps: A Practical Approach with Sysdig Serverless Agent]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-07-enabling-workload-protection-for-azure-container-apps-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-07-enabling-workload-protection-for-azure-container-apps-en/</guid>
            <pubDate>Sun, 07 Dec 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for day 7 in the KINTO Technologies Advent Calendar 2025 🎅🎄</p>
<h1>Introduction</h1>
<p>Hello, I&#39;m Tada from the <strong>Cloud Security</strong> Group at KINTO Technologies. I usually work at the <a href="https://blog.kinto-technologies.com/posts/2025-07-09-office-access-osaka/">Osaka Tech Lab</a>.</p>
<p>Many of the services we develop are built on Amazon Web Services. However, with the growing adoption of generative AI, we&#39;re increasingly using Microsoft Azure alongside OpenAI.</p>
<p>In this article, I&#39;ll explain the need for workload protection in Azure Container Apps, the limitations of Microsoft Defender for Cloud, and a practical protection approach using Sysdig Serverless Agent.</p>
<h1>What Is Azure Container Apps?</h1>
<p>Azure Container Apps (ACA) is a serverless container execution environment provided by Microsoft. For more details about ACA, please refer to Microsoft&#39;s <a href="https://learn.microsoft.com/en-us/azure/container-apps/overview">documentation</a>.</p>
<p>From a security perspective, ACA is a Kubernetes-based managed service where Microsoft is responsible for securing the infrastructure layer (nodes, network, OS). However, security at the application layer (container workloads) is the user&#39;s responsibility.</p>
<p>This article focuses on how to achieve workload protection at the application layer within this shared responsibility model.</p>
<h1>Why Is Workload Protection Necessary?</h1>
<p>Even in serverless environments, the following security risks still exist:</p>
<ul>
<li>Vulnerabilities in container images</li>
<li>Runtime threats</li>
<li>Misconfigurations and excessive permissions</li>
</ul>
<p>In particular, for runtime threats, you need to detect and respond to threats such as:</p>
<ul>
<li>Detecting and preventing cryptocurrency mining</li>
<li>Preventing container drift (unauthorized changes)</li>
<li>Detecting and preventing unauthorized network communications</li>
<li>Blocking reverse shell execution</li>
<li>Detecting fileless execution</li>
</ul>
<p>To address these threats, <strong>continuous monitoring and threat detection at runtime are essential.</strong></p>
<p>For more information about ACA&#39;s security features, please refer to <a href="https://learn.microsoft.com/en-us/azure/container-apps/security">this documentation</a>.</p>
<h1>Can Microsoft Defender for Cloud Provide Workload Protection?</h1>
<p>The short answer is: <strong>As of November 2025, Defender for Containers does not support ACA.</strong></p>
<p>Microsoft Defender for Containers supports Azure Kubernetes Service, Azure Container Registry, AWS EKS, Google GKE, and more. For detailed support coverage, please refer to <a href="https://learn.microsoft.com/en-us/azure/defender-for-cloud/support-matrix-defender-for-containers">this page</a>.</p>
<p>This means that <strong>to achieve workload protection at the application layer, adopting third-party products is recommended.</strong></p>
<h1>Workload Protection with Sysdig Serverless Agent</h1>
<p>At KINTO Technologies, we use <a href="https://www.sysdig.com/">Sysdig Secure</a> for cloud security operations, including detective guardrails (CSPM).
We&#39;ve published several blog posts about how we use Sysdig Secure, so feel free to search for them. The most recent one is <a href="https://blog.kinto-technologies.com/posts/2025-06-16-aispm/">AI-SPM Initiatives for Securing LLM Applications</a>.</p>
<p>We&#39;re also working on protecting ACA workloads using the Serverless Agent provided by Sysdig Secure.</p>
<h1>What Is Sysdig Serverless Agent?</h1>
<p>Sysdig Serverless Agent is a runtime security agent designed for serverless environments. It can monitor container workloads and detect threats.</p>
<p>For installation and configuration of Serverless Agent on ACA, please refer to <a href="https://www.scsk.jp/sp/sysdig/blog/scsk/sysdigserverless_agentazure_container_apps.html">SCSK Corporation&#39;s blog</a>, which provides a detailed explanation. It will give you a good understanding of the Serverless Agent.</p>
<p>The Serverless Agent performs <strong>user-space level</strong> monitoring in serverless environments where it cannot access the host kernel.</p>
<p>For detailed architecture information, please refer to Sysdig&#39;s <a href="https://docs.sysdig.com/en/sysdig-secure/understand-serverless-agent-drivers/">documentation</a>.</p>
<p>An important point about this mechanism is that only the process tree started by the container&#39;s <code>ENTRYPOINT</code> is monitored. Therefore, shells or child processes spawned via <code>docker exec</code> are created outside this monitored tree and <strong>are not detected</strong> at the system call level.</p>
<h1>Blind Spots in Serverless Agent Detection</h1>
<p>As mentioned above, Serverless Agent monitors the process tree started by <code>ENTRYPOINT</code>. If an attacker gains access to the Azure console or CLI through some method (vulnerability exploitation, credential theft, etc.), they can access the container interior with the following command:</p>
<pre><code>az containerapp exec --name hogehoge-containerapp --resource-group hogehoge-resourcegroup --exec-command &quot;/bin/bash&quot;
</code></pre>
<p>Once inside the container, unauthorized activities can be performed without being detected by the Serverless Agent.</p>
<h1>How Do We Detect Attacks via exec?</h1>
<p>Since attacks via <code>exec</code> cannot be detected by the Serverless Agent, we detect signs of attacks by monitoring the <code>exec</code> operations themselves recorded in Azure Activity Log (audit logs).</p>
<p>Sysdig Secure has a feature called <a href="https://www.sysdig.com/solutions/cloud-detection-and-response-cdr">Cloud Detection and Response</a> that can monitor audit logs in real time.</p>
<p>Sysdig&#39;s Cloud Detection and Response performs threat detection using <a href="https://www.cncf.io/projects/falco/">Falco</a>. When the <code>az containerapp exec</code> command is executed or <code>exec</code> is performed via the Azure console, the following event is recorded in Azure Activity Log:</p>
<pre><code>Microsoft.App/containerApps/getAuthToken/action
</code></pre>
<p>By detecting this event with a Falco rule, you can detect <code>exec</code> operations in real time. The Falco rule is as follows, and since it can detect <code>exec</code> via both the Azure console and CLI, you can respond by implementing operational procedures such as checking for unauthorized <code>exec</code> operations.</p>
<pre><code>rules:
  - rule: Detect Azure ContainerApp AuthToken Succeeded
    desc: Detect when Azure Activity Log shows Microsoft.App/containerApps/getAuthToken/action with status Succeeded
    condition: &gt;
      evt.type = &quot;open&quot; and
      json.value[&quot;operationName.value&quot;] = &quot;Microsoft.App/containerApps/getAuthToken/action&quot; and
      json.value[&quot;status.value&quot;] = &quot;Succeeded&quot;
    output: &gt;
      Azure ContainerApp AuthToken request succeeded
      (operation=%json.value[&quot;operationName.value&quot;], status=%json.value[&quot;status.value&quot;], caller=%json.value[&quot;caller&quot;])
    priority: WARNING
    source: json
    tags: [azure, containerapp, auth, security]
</code></pre>
<h1>Summary</h1>
<p>In this article, I explained a practical approach to workload protection in Azure Container Apps.</p>
<ul>
<li>Defender for Cloud does not support Azure Container Apps</li>
<li>Workload protection with third-party products like Sysdig Serverless Agent is effective</li>
<li>It&#39;s important to understand that Serverless Agent may have detection blind spots due to its architecture</li>
<li>For blind spots, it&#39;s crucial to detect threats through defense in depth and increase coverage</li>
</ul>
<p>This article covered workload protection in Azure Container Apps.</p>
<p>Our Cloud Security Group practices security in multi-cloud environments on a daily basis. We will continue to share our group&#39;s initiatives in the future.</p>
<p>Thank you for reading to the end.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Azure Container Apps のワークロード保護を実現する ― Sysdig Serverless Agent による実践的アプローチ]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-07-enabling-workload-protection-for-azure-container-apps/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-07-enabling-workload-protection-for-azure-container-apps/</guid>
            <pubDate>Sun, 07 Dec 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の7日目の記事です🎅🎄</p>
<h1>はじめに</h1>
<p>こんにちは、KINTO テクノロジーズ <strong>Cloud Security</strong> グループの多田です。普段は<a href="https://blog.kinto-technologies.com/posts/2025-07-09-office-access-osaka/">大阪 (Osaka Tech Lab)</a> で勤務しています。</p>
<p>我々が開発する多くのサービスは、Amazon Web Services 上で開発していますが、昨今は生成 AI の活用も盛んで、OpenAI の利用に伴い、Microsoft Azure での開発も増えてきました。</p>
<p>本記事では、Azure Container Apps におけるワークロード保護の必要性から、Microsoft Defender for Cloud の限界、そして Sysdig Serverless Agent を用いた実践的な保護手法について、解説します。</p>
<h1>Azure Container Apps とは？</h1>
<p>Azure Container Apps（以下、ACA）は、Microsoft が提供するサーバーレス型のコンテナ実行環境です。ACA の詳細については、Microsoft の<a href="https://learn.microsoft.com/ja-jp/azure/container-apps/overview">ドキュメント</a>を参照してください。</p>
<p>セキュリティ上の特徴としては、Kubernetes ベースのマネージドサービスであり、基盤レイヤー（ノード、ネットワーク、OS）のセキュリティは Microsoft の責任範囲となることです。一方で、アプリケーション層（コンテナワークロード）のセキュリティはユーザー側の責任となります。</p>
<p>この「共有責任モデル」においてアプリケーション層のワークロード保護をどう実現するかが、本記事の内容となります。</p>
<h1>なぜワークロード保護が必要なのか？</h1>
<p>サーバレスな環境であっても、以下のようなセキュリティリスクは依然として存在します。</p>
<ul>
<li>コンテナイメージの脆弱性</li>
<li>ランタイムの脅威</li>
<li>設定ミスや過剰な権限</li>
</ul>
<p>特に、ランタイムの脅威については、以下のような脅威を検知し対処する必要があります。</p>
<ul>
<li>暗号通貨マイニングの検知・防止</li>
<li>コンテナドリフト（不正な変更）の防止</li>
<li>不正なネットワーク通信の検知・防止</li>
<li>リバースシェル実行のブロック</li>
<li>ファイルレス実行の検知</li>
</ul>
<p>これらの脅威に対処するため、<strong>ランタイムでの継続的な監視と脅威検知が不可欠となります。</strong></p>
<p>ちなみに、ACA のセキュリティ機能については、<a href="https://learn.microsoft.com/ja-jp/azure/container-apps/security">こちら</a>を参照してください。</p>
<h1>Microsoft Defender for Cloud ではワークロード保護ができるのか？</h1>
<p>結論から言うと、<strong>2025年11月時点では、Defender for Containers は ACA をサポートしていません。</strong></p>
<p>Microsoft Defender for Containers は、Azure Kubernetes Service や Azure Container Registry、AWS EKS、Google GKE などに対応しています。詳細なサポート範囲については、<a href="https://learn.microsoft.com/ja-jp/azure/defender-for-cloud/support-matrix-defender-for-containers">こちら</a>を参照してください。</p>
<p>つまり、<strong>アプリケーション層のワークロード保護を実現するには、サードパーティ製品の導入が推奨される</strong> ということになります。</p>
<h1>Sysdig Serverless Agent によるワークロード保護</h1>
<p>KINTO テクノロジーズでは、発見的ガードレール（CSPM）など、クラウドセキュリティの運用に <a href="https://www.sysdig.com/jp">Sysdig Secure</a> を利用しています。
Sysdig Secure をどのように活用しているかは、過去にいくつかブログを投稿していますので、よろしければ検索してみてください。一番新しいものだと、「<a href="https://blog.kinto-technologies.com/posts/2025-06-16-aispm/">LLM アプリケーションのセキュリティを保護する AI-SPM の取組み</a>」を投稿しています。</p>
<p>ACA のワークロード保護についても、Sysdig Secure が提供する Serverless Agent を利用して保護する取組みを進めています。</p>
<h1>Sysdig Serverless Agent とは？</h1>
<p>Sysdig Serverless Agent は、サーバレス環境向けに設計されたランタイムセキュリティエージェントです。コンテナワークロードの監視、脅威検知等を実施することができます。</p>
<p>ACA への Serverless Agent のインストールや設定については、<a href="https://www.scsk.jp/sp/sysdig/blog/scsk/sysdigserverless_agentazure_container_apps.html">SCSK 株式会社さんのブログ</a>で丁寧に解説されていますので、そちらを参照してください。Serverless Agent についてイメージがつくと思います。</p>
<p>Serverless Agent は、ホストのカーネルにアクセスできないサーバレス環境において、<strong>ユーザスペースレベル</strong>の監視を行います。</p>
<p>詳細なアーキテクチャについては、Sysdig の<a href="https://docs.sysdig.com/en/sysdig-secure/understand-serverless-agent-drivers/">ドキュメント</a>を参照してください。</p>
<p>この仕組みの重要なポイントは、コンテナの <code>ENTRYPOINT</code> で起動されたプロセスツリーのみが監視対象となる点です。そのため、<code>docker exec</code> 経由で生成されたシェルや子プロセスはこの監視対象ツリーの外部で生成されるため、システムコールレベルの<strong>挙動として検知されません。</strong></p>
<h1>Serverless Agent の検知の盲点</h1>
<p>前述の通り、Serverless Agent は <code>ENTRYPOINT</code> で起動されたプロセスツリーを監視対象としています。攻撃者が何らかの方法（脆弱性悪用、認証情報搾取等）で、Azure コンソールや CLI へのアクセス権を取得した場合、以下のコマンドでコンテナ内部にアクセスできます。</p>
<pre><code>az containerapp exec --name hogehoge-containerapp --resource-group hogehoge-resourcegroup --exec-command &quot;/bin/bash&quot;
</code></pre>
<p>このように、コンテナ内部にアクセスできれば、Serverless Agent に検知されることなく、不正なアクティビティが可能となります。</p>
<h1>では、exec 経由の攻撃をどう検知するか？</h1>
<p><code>exec</code> 経由の攻撃は、Serverless Agent では検知できないため、Azure Activity Log（監査ログ）に記録される <code>exec</code> 操作そのものを検知することで、攻撃の予兆を検知します。</p>
<p>Sysdig Secure には、<a href="https://www.sysdig.com/jp/solutions/cloud-detection-and-response-cdr">Cloud Detection and Response</a> と呼ばれる機能があり、監査ログをリアルタイムに監視することができます。</p>
<p>Sysdig の Cloud Detection and Response は、<a href="https://www.cncf.io/projects/falco/">Falco</a> による脅威検知を実施しています。<code>az containerapp exec</code> コマンドの実行や、Azure コンソール経由での <code>exec</code> を実施すると、Azure Activity Log には、以下のイベントが記録されます。</p>
<pre><code>Microsoft.App/containerApps/getAuthToken/action
</code></pre>
<p>このイベントを Falco ルールで検知することで、<code>exec</code> 操作をリアルタイムに検知できます。Falco ルールは以下となり、Azure コンソール及び CLI 経由での <code>exec</code> を検知できるので、無許可の <code>exec</code> 操作があれば確認するなどの運用を実施することで対応が可能となります。</p>
<pre><code>rules:
  - rule: Detect Azure ContainerApp AuthToken Succeeded
    desc: Detect when Azure Activity Log shows Microsoft.App/containerApps/getAuthToken/action with status Succeeded
    condition: &gt;
      evt.type = &quot;open&quot; and
      json.value[&quot;operationName.value&quot;] = &quot;Microsoft.App/containerApps/getAuthToken/action&quot; and
      json.value[&quot;status.value&quot;] = &quot;Succeeded&quot;
    output: &gt;
      Azure ContainerApp AuthToken request succeeded
      (operation=%json.value[&quot;operationName.value&quot;], status=%json.value[&quot;status.value&quot;], caller=%json.value[&quot;caller&quot;])
    priority: WARNING
    source: json
    tags: [azure, containerapp, auth, security]
</code></pre>
<h1>まとめ</h1>
<p>本記事では、Azure Container Apps におけるワークロード保護の実践的なアプローチを解説しました。</p>
<ul>
<li>Defender for Cloud は、Azure Container Apps に未対応</li>
<li>Sysdig Serverless Agent のようなサードパーティ製品でのワークロード保護が有効</li>
<li>Serverless Agent には、仕組み上、検知の盲点が存在する場合があるため、その理解が重要</li>
<li>盲点については、多層防御で脅威を検知し、カバレッジを高めることが大切</li>
</ul>
<p>今回は、Azure Container Apps　におけるワークロード保護について記載しました。</p>
<p>我々、Cloud Security グループは、マルチクラウド環境におけるセキュリティについて、日々実践しています。今後も当グループの取り組みをご紹介していきたいと思います。</p>
<p>最後までお読みいただきありがとうございました。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Security as a Business Accelerator: How Our Security Organization Enables Speed × Quality]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-07-security-as-business-accelerator-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-07-security-as-business-accelerator-en/</guid>
            <pubDate>Sun, 07 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Viewing security as a business accelerator, this article explains how we achieve effective security while maintaining speed and quality in services that combine mobility and finance, through our initiatives and philosophy.]]></description>
            <content:encoded><![CDATA[<p>This is the Day 7 article of <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> 🎄</p>
<h3><strong>1. Introduction</strong></h3>
<p>I&#39;m Ka-Sai from the Security and Privacy Division at KINTO Technologies.</p>
<p>As a member of the Toyota Group, we provide mobility services including financial services. We consider protecting information assets such as personal information and confidential data entrusted to us by customers and partner companies, as well as the systems that support them, as one of our most important priorities. We operate under the following mission and vision:</p>
<h5><strong>Mission</strong></h5>
<ul>
<li><strong>Provide customers with safe and secure services.</strong></li>
<li><strong>Our mission is not to apply the brakes, but to think about how we can safely and securely step on the accelerator.</strong></li>
</ul>
<h5><strong>Vision</strong></h5>
<ul>
<li><strong>Establish Security by Design and Privacy by Design to create a world where safe and secure services are the norm.</strong></li>
</ul>
<p>・・・・・</p>
<p>The word security often brings to mind images of slowing things down or blocking new attempts. However, we believe that <strong>security is not a brake on business but can become a driving force</strong>. In this entry, I would like to discuss:</p>
<ul>
<li>Why security tends to appear as a blocker</li>
<li>How our company addresses these structural challenges through our philosophy and design</li>
<li>Our specific organizational structure, systems, and initiatives</li>
<li>The future we envision</li>
</ul>
<h3><strong>2. Problem Awareness: The Dilemma of Traditional Security</strong></h3>
<h4><strong>2-1. Why Does Security Appear to Slow Things Down?</strong></h4>
<p>In many organizations, security is often seen as:</p>
<ul>
<li><p>Procedures are cumbersome, and reviews and approvals slow things down</p>
</li>
<li><p>New tools, cloud services, and generative AI get blocked</p>
</li>
<li><p>Rollbacks occur just before release, causing significant rework</p>
</li>
<li><p>Security is treated as a cost center, making its value hard to see</p>
</li>
</ul>
<p>These challenges arise from structures such as security being added after the fact, individual judgments dependent on specific people, and case-by-case consultation-based approaches. There&#39;s another reality we are keenly aware of:</p>
<p><strong>Rules can become implicit blockers simply by existing.</strong></p>
<p>When rules exist, people tend to self-censor, thinking I&#39;d better not do that just to be safe or I should avoid touching this area. As a result, both new initiatives and innovation become less likely to emerge.</p>
<h4><strong>2-2. Toward Security That Scales for Breakthrough Growth</strong></h4>
<p>Our company aims for breakthrough growth in a domain where mobility and finance intersect, a field characterized by strict regulations and complex business requirements.
As we expand into new markets, countries, and service models, our security must grow at the same speed and scale.</p>
<p>What becomes important here is the perspective that:</p>
<p><strong>Security is a risk, but slowing down business is also a significant risk.</strong></p>
<p>Not only the risk of being attacked, but also <strong>the risk of losing momentum for growth</strong> cannot be ignored from a management perspective.</p>
<p>Therefore, we place great importance on how quickly we can deliver secure products to the world.
In other words, from the initial stage, we focus on how quickly we can reach the market while meeting a certain level of security and compliance.</p>
<ul>
<li>Deliver new businesses and features to the world quickly</li>
<li>At the same time, meet the strict safety standards required in the finance and mobility sectors</li>
</ul>
<p>To satisfy both requirements, we need an approach that designs business goals that directly contribute to business success and security goals at the same table, rather than separating them.</p>
<h4><strong>2-3. How to Reduce the Burden on Employees</strong></h4>
<p>The Security and Privacy Division is not all‑knowing or all‑powerful. It is not realistic for our team alone to review every piece of code, every configuration, and every data flow, and the cooperation of each and every employee is essential.</p>
<p>However, employees each have their own missions. They are also swamped with product development, operations, and business-side tasks.</p>
<ul>
<li>Security work piles on as additional work</li>
<li>Manual checks and evidence collection increase</li>
<li>Understanding the requirements takes time, which puts pressure on the time they have for their regular work.</li>
</ul>
<p>As a result, we often end up with excessive or disproportionate security work whose impact is small compared to the time invested. That&#39;s why at our company, we emphasize:</p>
<ul>
<li>Top-down support from the CISO</li>
<li>Delegation of authority to the front lines and autonomous bottom-up actions</li>
</ul>
<p>We focus on designing systems that raise security levels without increasing the burden on employees by balancing both of these approaches.</p>
<h3><strong>3. Our Security Philosophy: Security as Momentum</strong></h3>
<p>Based on the challenges described above, the conclusion our division reached is simple:</p>
<p><strong>When properly designed, security becomes a driving force that accelerates business.</strong></p>
<p>Under this philosophy, we have adopted the following approaches:</p>
<h4><strong>3-1. Security as a Platform</strong></h4>
<p>Rather than having people check things one by one, we <strong>embed security into systems and platforms</strong> to minimize friction.</p>
<ul>
<li>Incorporate secure cloud design and configuration into guidelines and templates</li>
<li>Build automated checks into CI/CD pipelines</li>
<li>Provide secure baseline settings and ensure the system is safe without any extra steps.</li>
</ul>
<h4><strong>3-2. Not Someone Who Stops, But Someone Who Enables</strong></h4>
<p>The role of security is not to say &quot;No&quot; but to think together about how we can safely say &quot;Yes&quot;.</p>
<ul>
<li>Welcome consultations from the planning stage and present feasible options</li>
<li>Show not only what you shouldn&#39;t do but also how you can do it</li>
</ul>
<h4><strong>3-3. Security as Developer Experience</strong></h4>
<p>Building a secure developer experience ultimately leads to improved productivity across the entire development organization, so we constantly keep in mind whether we are creating an environment where developers can confidently step on the accelerator.</p>
<ul>
<li>Clear guardrails that say, &quot;You’re good to go if you use this template.&quot;</li>
<li>Automate tedious security assessment evidence collection work</li>
<li>Easy-to-consult channels and clear documentation</li>
</ul>
<h3><strong>4. Security Management Organization Structure: A Structure to Support Business Growth and Speed</strong></h3>
<p>Our company belongs to the Toyota Financial Services (TFS) group, a subsidiary of Toyota Motor Corporation. The TFS Group positions cybersecurity risk as one of the most important management issues and promotes countermeasures across the entire group. Under this policy, information security management at our company operates based on the TFS Group&#39;s security program.</p>
<h4><strong>4-1. GIS Standard: A Security Framework Based on Global Standards</strong></h4>
<p>The TFS Group has developed its own security requirements called the GIS Standard (Global Information Security Standard), optimized for financial services based on international standards such as NIST and ISO27001, and operates it across the entire group.</p>
<p><img src="/assets/blog/authors/ka_sai/20251201_sec.png" alt="Visualization of security requirement items included in the GIS Standard"></p>
<p>The GIS Standard includes over 300 control items covering a wide range of domains such as:</p>
<ul>
<li>Governance</li>
<li>Operations</li>
<li>Technical controls</li>
<li>Cloud security</li>
<li>New areas such as generative AI</li>
</ul>
<p>Our company has adopted this GIS Standard as our own standard, choosing to <strong>conduct our business with global-standard safety and reliability as fundamental prerequisites</strong>.</p>
<h4><strong>4-2. Three Specialized Groups in the Security and Privacy Division</strong></h4>
<p>To make this GIS Standard security framework in practice, the Security and Privacy Division consists of the following specialized groups:</p>
<h5><strong>1. Cloud Security Group</strong></h5>
<ul>
<li>Standardization of cloud design and configuration (guidelines and guardrails)</li>
<li>Introduction and operation of CNAPP / CSPM / CIEM</li>
<li>Risk monitoring of multi-cloud environments</li>
</ul>
<h5><strong>2. Cyber Security Group</strong></h5>
<ul>
<li>SOC operations (log collection, analysis, incident response)</li>
<li>Vulnerability management and assessment (Red Team / Blue Team)</li>
<li>Shadow IT countermeasures</li>
</ul>
<h5><strong>3. Information Security Group</strong></h5>
<ul>
<li>Digital transformation of GIS Standard assessments</li>
<li>Policy development and risk assessment</li>
<li>Enhancement of information security education</li>
<li>Privacy Impact Assessment (PIA)</li>
<li>Development of data protection guidelines for AI use</li>
<li>Systematization of intellectual property rights management</li>
</ul>
<p>These groups work in coordination to form a structure that partners with product teams, corporate divisions, and related global companies.</p>
<h3><strong>5. Our Specific Initiatives: Implementation Structure to Transform Security into a Driving Force</strong></h3>
<h4><strong>5-1. Platform Security: Embedding Security into Systems</strong></h4>
<h5>(1) Standardization of Cloud Security</h5>
<p>We have developed:</p>
<ul>
<li>Security guidelines for each of AWS / Azure / GCP</li>
<li>Kaizen Guides for guardrails</li>
<li>AI security guidelines</li>
</ul>
<p>We aim for a state where <strong>designing and implementing according to these naturally meets GIS Standard and best practices</strong>.</p>
<h5>(2) Introduction of CNAPP (Cloud-Native Application Protection Platform)</h5>
<p>We are automating:</p>
<ul>
<li>Continuous evaluation of cloud configurations</li>
<li>Detection and remediation of excessive IAM permissions</li>
<li>Protection of workloads on the cloud</li>
<li>Threat detection through log collection and analysis</li>
</ul>
<p>We are evolving from having people check things case by case to a system that constantly monitors and raises alerts as needed.</p>
<h4><strong>5-2. Cyber Defense: Both Offense and Defense</strong></h4>
<h5>(1) Red Team (Offense)</h5>
<ul>
<li>Proactive vulnerability assessments for new projects and products that haven&#39;t been assessed</li>
<li>Verification including software supply chain perspectives such as SBOM, secret management, and EOL</li>
</ul>
<h5>(2) Blue Team (Defense)</h5>
<ul>
<li>Log monitoring and analysis using SIEM, EDR, Proxy, DNS, etc.</li>
<li>Development and exercises of incident response plans</li>
<li>Continuous tuning of detection rules</li>
</ul>
<p>Through these initiatives, rather than scrambling to respond after being attacked, we aim to <strong>create organizational capacity through advance preparation and continuous monitoring</strong>.</p>
<h4><strong>5-3. Security and Privacy Assessment Digital Transformation: Making Compliance a Byproduct</strong></h4>
<h5>(1) Security Governance</h5>
<ul>
<li>Automation of compliance assessments with GIS Standard</li>
<li>Dashboarding of evidence collection</li>
<li>Organization of assessment targets through a risk-based approach</li>
<li>Automation of security inquiry responses</li>
</ul>
<p>We treat security governance as a key focus of our Digital Transformation efforts, reducing <strong>the reactive burden of scrambling to collect audit evidence after the fact</strong> and moving toward a state where daily operations naturally serve as audit trails.</p>
<h5>(2) Privacy Governance / Intellectual Property Management</h5>
<ul>
<li>Introduction and operation of Privacy Impact Assessment (PIA)</li>
<li>Development and dissemination of data protection guidelines for AI use</li>
<li>Comprehensive intellectual property investigation and log management linked to the lifecycle</li>
</ul>
<p>Through this, we aim to make <strong>being a service that customers can use with peace of mind one of the reasons why they choose us</strong>.</p>
<h3><strong>6. Conclusion: From Craftsman&#39;s Work to Standardization, and Then to a Driving Force</strong></h3>
<p>The mission of the Security and Privacy Division is to provide customers with safe and secure services.
To achieve this, we constantly think about <strong>how security can contribute to business growth and momentum building</strong> from both Speed and Quality perspectives, rather than merely minimizing risk.</p>
<p>On the other hand, balancing competing priorities such as:</p>
<ul>
<li>Responding to rapid business growth</li>
<li>Reducing the burden on employees</li>
<li>Ensuring efficient compliance with legal requirements</li>
</ul>
<p>is, in reality, a significant challenge. We are addressing this challenge through:</p>
<h5><strong>From Craftsman&#39;s Work to Standardization</strong></h5>
<p>   From security dependent on specific security experts to standardized systems where anyone can produce the same quality</p>
<h5><strong>Employee-Centered Design</strong></h5>
<p>   Rather than overworking people for security, design so that employees can engage without strain and with pride</p>
<h5><strong>Automation-First Design</strong></h5>
<p>   Reduce toil (work that can be automated) so people can focus on creative work</p>
<h5><strong>Agile Governance</strong></h5>
<p>   Maintain an attitude of continuously rebuilding while breaking down existing rules</p>
<p>We believe that:</p>
<p><strong>We can not only provide security that enhances Quality, but also contribute to Speed and promote security in a way that accelerates business.</strong></p>
<p>We will continue to move forward with our business teams, our developers, and customers!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How We Reviewed and Restructured Our AWS Security Governance]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-06-aws-security-governance-review-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-06-aws-security-governance-review-en/</guid>
            <pubDate>Sat, 06 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article introduces the process of reviewing and restructuring our AWS security governance.]]></description>
            <content:encoded><![CDATA[<p>This article is the entry for Day 6 in the KINTO Technologies <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">Advent Calendar 2025</a>.</p>
<h2>Introduction</h2>
<p>Hello. I&#39;m Watanabe from the Cloud Security Group, Security and Privacy Division at KINTO Technologies.</p>
<p>Our company has been operating a multi-account environment using AWS Organizations and Control Tower. However, after years of changes, we found it necessary to revisit some of our design decisions. We therefore decided to reorganize our security governance design based on the existing environment.</p>
<p>In this project, rather than rebuilding everything from scratch, we adopted an approach of prioritizing the affected areas due to the rebuild and addressing them gradually. While prioritizing safety, we also incorporated some changes that will lead to future operational improvements, such as creating new OUs and leveraging AWS managed features.</p>
<p>The project is still in the design phase, and the final outcomes are yet to come. In this article, I&#39;ll share the decision-making process and background up to this point.</p>
<p><strong>Note: This article is intended for readers with a certain level of understanding of AWS Organizations, AWS Control Tower, Security Hub CSPM, SCP, and OU design.</strong>
<strong>Note: The content is based on the design phase, which is the first half of the project and may change during the implementation phase.</strong></p>
<hr>
<h2>Design Element Review and Analysis</h2>
<p>First, we organized the security governance elements of our existing environment and prioritized the following five areas for review and analysis based on their impact and improvement potential.</p>
<p>The first three areas have a broad scope of impact and involve other areas that affect teams beyond cloud security. The latter two areas require consideration in conjunction with the former ones.</p>
<ol>
<li>OU design
-&gt; OU structure directly affects guardrail design</li>
<li>Preventive guardrails
-&gt; This is a mechanism to proactively block undesirable operations and affects existing accounts</li>
<li>Detective guardrails
-&gt; This is a mechanism to detect undesirable configurations early and complements preventive guardrails</li>
<li>Configuration automation tool
-&gt; An internal tool that automates initial setup for new accounts</li>
<li>Account issuance Flow
-&gt; The process for provisioning new accounts</li>
</ol>
<p>In this article, I&#39;ll introduce the issues and background of decision-making processes that emerged in reviewing and analyzing each of these areas.</p>
<hr>
<h2>Organizing the AWS Security Governance Structure</h2>
<p>In this chapter, I&#39;ll organize how our company&#39;s AWS security governance is structured across different layers.</p>
<p>The following diagram shows:</p>
<ul>
<li>Settings applied in the management account (AWS Organizations / AWS Control Tower / AWS CloudFormation)</li>
<li>Settings additionally applied to the management account and user accounts by the configuration automation tool</li>
</ul>
<p>And how these settings work for the user account side as:</p>
<ul>
<li>Three-tier preventive guardrails (Tier 1: Control Tower standard, Tier 2: additional guardrails, Tier 3: custom SCPs)</li>
<li>Two detection methods (using Security Hub CSPM or Control Tower)</li>
<li>Pre-optimization of monitored/protected resources (initial settings by the configuration automation tool)</li>
</ul>
<p><img src="/assets/blog/authors/watanabe/governance-overview.png" alt=" この図は、当社の AWS セキュリティガバナンスが「予防 ( 3 段構成) 」「検出 ( 2 系統 ) 」「初期設定 (事前適正化)」という複数レイヤで構成されていることを示しています。"></p>
<p>As shown in the diagram, our environment consists of three tiers of preventive controls: Control Tower&#39;s standard guardrails, additional guardrails supplemented by the configuration automation tool, and custom SCPs.</p>
<p>Additionally, we have two detection layers using Security Hub CSPM and Control Tower as well as pre-optimization of resources by the configuration automation tool. The combination of these tools establishes our overall governance structure.</p>
<p>Since they were introduced at different times and for different purposes, there are some variations in settings between OUs and accounts.</p>
<p>The main roles in this diagram can be organized into the following five categories:</p>
<ul>
<li>Control Tower standard guardrails
 -&gt; Baseline for preventive and detective controls provided by AWS</li>
<li>Additional guardrails (enabled by the configuration automation tool)
 -&gt; Preventive and detective guardrails to supplement constraints not covered by the standard</li>
<li>Custom SCPs (configured via the configuration automation tool)
 -&gt; Company-specific restrictions for exceptional requirements</li>
<li>Security Hub CSPM Stream (detection layer on the CSPM side)
 -&gt; A service that integrates with AWS Config to continuously detect misconfigurations and best practice violations</li>
<li>Detection using Control Tower (detection layer on the Control Tower side)
 -&gt; Implements detective controls linked to Control Tower guardrails in coordination with AWS Config</li>
</ul>
<p>While this multi-layer structure is not uncommon in AWS governance, the review and analysis process required clearly organizing which layer provides which controls and whether each setting belongs to Control Tower, Security Hub CSPM, or the configuration automation tool. This involved a certain level of complexity.</p>
<p>For this effort, we focused on bringing Control Tower&#39;s additional guardrails—especially the high-impact preventive controls—up to date. We plant to optimize overlaps and role allocation between layers as an improvement topic into the future to be addressed gradually.</p>
<hr>
<h2>Review and Analysis of OU Design</h2>
<p>OU design is closely related to Control Tower&#39;s behavior, making it particularly difficult to assess the scope of impact in changing designs.</p>
<p>The following three points have specifically large impacts and made the assessment difficult:</p>
<ol>
<li>Some operations are not documented, making us hard to fully predict behavior in advance
-&gt; For example, detailed specifications about what happens to a landing zone are not shared at the timing of the zone reset required after the change in an OU name</li>
<li>In addition to differences between OUs, past implementations and iterated operations have a large impact, requiring individual verification of which settings are reapplied and how the reapplication is performed when moving accounts within an OU to a different OU</li>
<li>Since accounts in our production environment and Control Tower record cannot be accurately reproduced in a testing environment, safety cannot be fully ensured through testing alone
-&gt; For example, in terms of accounts in the production environment, there are cases where resources necessary for Control Tower operations have been modified or deleted for some reason during past operations</li>
</ol>
<p>Based on the above, we compared the following three patterns for where to place newly created accounts.</p>
<p>The three patterns are as follows:</p>
<ul>
<li>Existing OU (with its current state maintained): Continue placing new accounts in the current OU structure and guardrails without changes.</li>
<li>Existing OU (applied to new guardrails): Apply new guardrails to the existing OU group and place new accounts there, aligning the state of the accounts with that of the existing ones all at once.</li>
<li>New OU group: Maintain the existing OU group as it is while creating a new OU group with new guardrails applied, and place new accounts in that OU.</li>
</ul>
<table>
<thead>
<tr>
<th align="center">No</th>
<th align="left">Placement</th>
<th align="left">Impact of Change</th>
<th align="left">Long-term soundness</th>
<th align="left">Ease of Introduction</th>
<th align="left">Comment</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1</td>
<td align="left">Existing OU (with its current state maintained)</td>
<td align="left">Excellent: No impact on existing environment</td>
<td align="left">Poor: Technical debt remains</td>
<td align="left">Excellent: Easy to implement with the current state maintained</td>
<td align="left">Temporarily safe but increases long-term debt</td>
</tr>
<tr>
<td align="center">2</td>
<td align="left">Existing OU (applied to new guardrails)</td>
<td align="left">Poor: Major impact on all existing environment</td>
<td align="left">Excellent: High soundness</td>
<td align="left">Poor: High verification burden</td>
<td align="left">Ideal but heavy impact, which requires gradual migration</td>
</tr>
<tr>
<td align="center">3</td>
<td align="left">New OU group</td>
<td align="left">Excellent: No impact on existing environment</td>
<td align="left">Fair: Risk of OU separation</td>
<td align="left">Excellent: Easy to implement and verify</td>
<td align="left">Need to resolve OU separation in subsequent phases</td>
</tr>
</tbody></table>
<p>This time, we adopted Option 3, which avoids impact on the existing environment while making it easy to introduce new frameworks.</p>
<p>That said, this is not a permanent solution but the first step to verify new guardrail configurations and operational models without immediately reorganizing existing Ous all at once.</p>
<p>After gaining operational experiences and verification results with the new OU group, we plan to gradually migrate existing accounts with smaller impact, such as Sandbox OUs, and ultimately unify the OU structure and guardrails as much as possible as our medium- to long-term policy.</p>
<hr>
<h2>Review and Analysis of Preventive Guardrails</h2>
<p>Preventive guardrails are frameworks to proactively prevent undesirable operations and configurations. In our environment, along with AWS Control Tower&#39;s standard guardrails, we enable additional guardrail and use custom SCPs through the configuration automation tool to supplement any gaps.</p>
<table>
<thead>
<tr>
<th>Tier</th>
<th>Name</th>
<th>Major tool</th>
<th>Role</th>
</tr>
</thead>
<tbody><tr>
<td>Tier 1</td>
<td>Standard guardrails</td>
<td>AWS Control Tower</td>
<td>AWS standard preventive controls</td>
</tr>
<tr>
<td>Tier 2</td>
<td>Additional guardrails</td>
<td>Configuration automation tool (CDK)</td>
<td>Supplements gaps not covered by standard guardrails</td>
</tr>
<tr>
<td>Tier 3</td>
<td>Custom guardrails</td>
<td>Configuration automation tool (CDK)</td>
<td>SCPs tailored to our company’s specific requirements</td>
</tr>
</tbody></table>
<p>Since we are going to place new accounts in newly created OUs, adding guardrails to new OUs does not affect the existing environment. Therefore, we examined if we should enable new preventive controls added to Control Tower after initial deployment.</p>
<p>However, only in terms of the severity level provided by AWS, we could not find why a particular evaluation was given or what operational impact might occur, as illustrated by the following three examples.</p>
<p>Examples of where severity is high with the adoption undetermined:</p>
<ul>
<li>(1) [CT.EC2.PV.4] Require that Amazon EBS direct APIs are not called</li>
<li>(2) [CT.S3.PV.2] Require all requests to Amazon S3 resources use authentication based on an Authorization header</li>
</ul>
<p>Examples where severity is medium but seemingly worth adopting:</p>
<ul>
<li>(3) [CT.EC2.PV.11] Disallow public sharing of Amazon Machine Images (AMIs)</li>
</ul>
<p>Therefore, we used Amazon Q Developer Pro* for evaluating each control&#39;s SCP from the perspectives of operational impact, recommendation level, and adoption process. For the above items from (1) to (3), we gained the following insights:</p>
<ol>
<li>Caution is needed due to potential impacts on backups using AWS Backup partner products that are applied to EBS direct APIs(CT.EC2.PV.4)</li>
<li>Presigned URLs will no longer work (CT.S3.PV.2)</li>
<li>Adoption is appropriate, but the behavior of a DECLARATIVE_POLICY differs from that of an SCP (CT.EC2.PV.11)</li>
</ol>
<p>Through this organizing process, we decided to actively enable controls in new OUs whose activation are recommended with its lower impacts.</p>
<p>*We adopted Amazon Q Developer Pro because it appears to reference official AWS documentation for each response. This gives us a sense of security at a certain level even in areas with frequent specification changes, while the service response speed is slower compared to other AI tools.</p>
<hr>
<h2>Review and Analysis of Detective Guardrails</h2>
<p>In our environment, we use Security Hub CSPM as the main pillar of security auditing from the perspectives of centralized management of security standards and automatic updates.</p>
<p>Control Tower&#39;s detective controls are a set of checks that include perspectives on guardrails and common infrastructure configurations provided by Control Tower. We set this as a framework that complements Security Hub CSPM.</p>
<p>In this review process, we examined Control Tower&#39;s detective controls added after operations began to determine whether we should enable the controls by targeting those with its severity critical or its guidance strongly recommended.</p>
<p>As a result, we confirmed that controls, such as the ones listed below, can be audited under Security Hub CSPM standards, deciding not to enable them redundantly on the Control Tower side:</p>
<ul>
<li>[CONFIG.KMS.DT.1] Checks if AWS Key Management Service (AWS KMS) keys are not scheduled for deletion in AWS KMS</li>
<li>[CONFIG.KMS.DT.2] Checks if the AWS KMS key policy allows public access</li>
</ul>
<p>On the other hand, for some controls that handle service-specific settings like the following, we decided to individually examine their necessity based on our actual usage and future plans:</p>
<ul>
<li>[CONFIG.EMR.DT.1] Checks if an account with Amazon EMR has block public access settings enabled</li>
</ul>
<hr>
<h2>Review and Analysis of the Configuration Automation Tool</h2>
<p>Our company has developed and operates a configuration automation tool (based on CDK) to automate initial setup for new accounts. For many years, this tool has greatly contributed to standardizing new accounts and preventing omissions in initial settings.</p>
<p>In this review and analysis, we assessed how much we can simplify this framework, considering the current account scale, team structure, and the expansion of AWS managed features.</p>
<p>In general, many cases expand the scope of automation, but we focused on maintainability to decide to narrow the scope down to necessary parts.</p>
<p>The configuration automation tool currently handles the following:</p>
<ol>
<li>Applying additional preventive guardrails</li>
<li>Adding custom SCPs</li>
<li>Suppressing duplicate/unnecessary Security Hub CSPM alarms</li>
<li>Setting services focused on Security Hub CSPM compliance (e.g., default encryption and activation of public access blocks)</li>
<li>Automatically applying other security settings</li>
</ol>
<p>Currently, we are going to proceed with a three-tier approach:</p>
<ul>
<li>Shift to the automation of as many processes that can be handled by AWS managed features as possible</li>
<li>For parts irreplaceable by managed features, prioritize management with declarative IaC (CloudFormation, CloudFormation StackSets, Terraform, etc.)</li>
<li>Leave exceptional processing that cannot be shifted to IaC as minimal code, with specific implementation methods to be determined in subsequent phases</li>
</ul>
<p>This doesn’t simply aim to reduce code but to record limited responsibilities that the organization should maintain long-term in code.</p>
<p>For the above item 4 in particular, through a migration to <a href="https://docs.aws.amazon.com/securityhub/latest/userguide/central-configuration-intro.html">Security Hub CSPM central configuration</a>, some settings may be able to leverage AWS managed features. In terms of existing OUs, individual differences in control states remain, so the review and analysis process is required for their application, but we plan to gradually utilize OUs from new ones.</p>
<p>Additionally, for the S3 Block Public Access settings currently implemented as part of item 5, migration to the recently released <a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_s3.html">S3 policies</a> may similarly allow us to leverage AWS managed features. We plan to examine if we can adopt it, going forward.</p>
<p>Note that the configuration automation tool has unique strengths, such as CDK-based conditional branching, which may become decision-making points in this review. We plan to continue carefully verifying whether the above policy is appropriate.</p>
<hr>
<h2>Review and Analysis of the Account Provisioning Flow</h2>
<p>Our company has previously operated with MFA enabled for root users when issuing new accounts. However, this involves physical work, requiring a certain amount of effort each time an account was issued.</p>
<p>With <a href="https://docs.aws.amazon.com/IAM/latest/UserGuide/id_root-enable-root-access.html">centralized root access management for member accounts</a> released at the end of 2024, there is potential to update this operation itself.</p>
<p>In conjunction with the current redesign, we are considering organizing the account issuance flow into a form that is as simple and secure as possible.</p>
<hr>
<h2>Conclusion</h2>
<p>In this review and analysis process, we organized what to change and what to maintain from both extensibility and maintainability perspectives, based on settings and frameworks repeatedly updated over years of operation.</p>
<p>It is not easy to rebuild our existing Organizations environment into an ideal configuration while maintaining it. This is because multiple layers—OU design, guardrails, and initial setup automation tools—are interdependent, and updating any one of them can affect other layers.</p>
<p>Therefore, rather than completely overhauling everything, we proceeded with this organization under the policy of gradually reducing technical debt while continuing improvements without stopping production. In particular, since we were not able to fully understand Control Tower&#39;s behavior and the scope of impact of some guardrails, according to public information alone, I feel that it is essential to take a step-by-step verification approach diversifying risks.</p>
<p>Going forward, we plan to actively leverage AWS managed features while focusing on areas to update in line with the existing environment and continuing gradual improvements.</p>
<p>For those redesigning governance based on existing environments, I hope this article provides some materials helping their decision-making processes and perspectives.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AWS セキュリティガバナンスの棚卸しと見直し方針を整理した話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-06-aws-security-governance-update/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-06-aws-security-governance-update/</guid>
            <pubDate>Sat, 06 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[AWS セキュリティガバナンスの棚卸しと見直し方針を整理したプロセスを紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は KINTOテクノロジーズ <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">Advent Calendar 2025</a> の 6 日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>こんにちは。KINTO テクノロジーズ Cloud Security グループの渡邉です。</p>
<p>当社では AWS Organizations と Control Tower を利用したマルチアカウント運用を続けていますが、長年の積み重ねにより、一部の設計を見直す必要が出てきました。そこで、既存環境を前提にセキュリティガバナンス設計の再整理を進めることにしました。</p>
<p>本プロジェクトでは、いきなり全体を作り直すのではなく、影響の大きい領域から優先順位を付けて段階的に整理していく方針を取っています。そのうえで、安全性の判断を優先しつつ、新規 OU や AWS のマネージド機能の活用など、将来の運用改善につながる変更も一部取り入れています。</p>
<p>プロジェクトはまだ設計フェーズの段階であり、最終成果はこれからですが、本記事では現時点での意思決定の流れと背景を共有します。</p>
<p>※ <strong>本記事は AWS Organizations / AWS Control Tower / Security Hub CSPM / SCP / OU 設計に一定の理解がある読者を対象としています。</strong>
※ プロジェクト前半である設計フェーズ時点の内容をベースにしており、今後の実装フェーズで変わる可能性があります。</p>
<hr>
<h2>設計項目の棚卸し</h2>
<p>まずは既存環境のセキュリティガバナンス要素を整理し、影響度と改善効果の観点から、以下の 5 項目を優先的に棚卸ししました。</p>
<p>最初の 3 項目は影響範囲が広く、クラウドセキュリティ以外のチームにも関係する領域です。後半の 2 項目は、それらに付随して検討が必要となる領域です。</p>
<ol>
<li>OU 設計<br>└ OU 構造がそのままガードレール設計に影響するため</li>
<li>予防的ガードレール<br>└ 望ましくない操作を事前にブロックする仕組みであり、既存アカウントに影響するため</li>
<li>発見的ガードレール<br>└ 望ましくない設定を早期に検出する仕組みであり、予防的ガードレールと相互補完の関係にあるため</li>
<li>設定自動化ツール<br>└ 新規アカウントの初期設定を自動化する社内ツール</li>
<li>アカウント発行フロー<br>└ 新規アカウント払い出しのプロセス</li>
</ol>
<p>本記事では、それぞれの棚卸し過程で見えてきた課題と判断の背景を紹介します。</p>
<hr>
<h2>AWS セキュリティガバナンス構成の整理</h2>
<p>この章では、当社の AWS セキュリティガバナンスがどのようなレイヤで構成されているかを整理します。</p>
<p>次の図は、</p>
<ul>
<li>管理アカウントで適用される設定 ( AWS Organizations / AWS Control Tower / AWS CloudFormation )</li>
<li>それとは別に、設定自動化ツールによって管理アカウントおよびユーザアカウントに追加される設定</li>
</ul>
<p>が、ユーザアカウント側で</p>
<ul>
<li>3 段階の予防ガードレール ( 1 段目 : Control Tower 標準、2 段目 : 追加ガードレール、3 段目 : カスタムSCP )</li>
<li>2 系統の検出 ( Security Hub CSPM 系統、Control Tower 系統)</li>
<li>監視・予防対象リソースの事前適正化 (設定自動化ツールによる初期設定)</li>
</ul>
<p>として作用する全体構造を示したものです。</p>
<p><img src="/assets/blog/authors/watanabe/governance-overview.png" alt="この図は、当社の AWS セキュリティガバナンスが「予防 ( 3 段構成) 」「検出 ( 2 系統 ) 」「初期設定 (事前適正化)」という複数レイヤで構成されていることを示しています。"></p>
<p>図のとおり、当社環境では Control Tower の標準ガードレールに加えて、設定自動化ツールで補完した追加ガードレールやカスタム SCP により、3 段階の予防コントロールを構成しています。</p>
<p>また、Security Hub CSPM 系統と、Control Tower 系統という 2 系統の検出レイヤ、そして設定自動化ツールによるリソースの事前適正化も組み合わせることで、ガバナンス全体を構築しています。</p>
<p>これらは導入時期や目的が異なるため、OU や アカウント間で設定のばらつきが生じている部分もあります。</p>
<p>この図における主要な役割分担は、次の 5 つに整理できます。</p>
<ul>
<li>Control Tower 標準ガードレール<br> └ AWS が提供する予防・検出のベースライン</li>
<li>追加ガードレール (設定自動化ツールで有効化)<br> └ 標準だけではカバーできない制約を追加するための予防・検出ガードレール</li>
<li>カスタムSCP (設定自動化ツールから設定)<br> └ 例外的な要件に対応するための当社独自の制限</li>
<li>Security Hub CSPM 系統 ( CSPM 側の検出レイヤ)<br> └ AWS Config と連携し、設定ミスやベストプラクティス違反を継続的に検出するサービス</li>
<li>Control Tower 系統 ( Control Tower 側の検出レイヤ)<br> └ AWS Config と連携し、Control Tower のガードレールに紐づく検出コントロールを実現</li>
</ul>
<p>こうした多層構造は AWS ガバナンスでは珍しくない構成ですが、棚卸しを進めるうえでは、どのレイヤがどのコントロールを提供し、各設定が Control Tower / Security Hub CSPM / 設定自動化ツールのどこに属しているのかを明確に整理する必要があり、一定の複雑さを伴いました。</p>
<p>今回は、まず Control Tower の追加ガードレール、特に影響の大きい予防コントロールを最新の状態へ追従させることを優先しました。レイヤ間の重複や役割分担の最適化については、今後の改善テーマとして段階的に進めていく方針としています。</p>
<hr>
<h2>OU 設計の棚卸し</h2>
<p>OU 設計は Control Tower の動作と密接に関係するため、設計変更時の影響範囲を見極めるのが特に難しい領域です。</p>
<p>特に影響が大きく、判断を難しくしたのは以下の 3 点です。</p>
<ol>
<li>一部の動作がドキュメント化されておらず、事前に挙動を読み切れない場面がある点
└ 例えば、OU 名の変更後に必要となるランディングゾーンのリセット時に、何が行われるのかに関する詳細仕様が公開されていない点など</li>
<li>OU ごとの差異に加え、過去の実装や運用の積み重ねが影響しており、OU 内のアカウントを別 OU に移動する際に、どの設定がどう再適用されるかを個別確認する必要がある点</li>
<li>本番アカウントや Control Tower の履歴を含む状態を検証環境で正確に再現できないため、検証だけでは安全性を保証しきれない点
└ 例えば、本番アカウントにおいて、Control Tower の運用に必要なリソースが、過去の運用の中で何らかの理由により変更・削除されているケースなど</li>
</ol>
<p>上記を踏まえ、今後作成されるアカウントの収容先として、次の 3 パターンを比較しました。</p>
<p>ここでの 3 パターンは次のようなイメージです。</p>
<ul>
<li>既存 OU (現状維持) : いま使っている OU 構造やガードレールを変えずに、新規アカウントを収容していく。</li>
<li>既存 OU (新ガードレール適用) : いま使っている OU 群に新しいガードレールを適用し、新規アカウントを収容していくことで、既存アカウントも含めて一気に状態を揃える。</li>
<li>新規 OU 群 : 既存 OU 群は現状維持として、新しいガードレールを適用した OU 群を新たに作成し、その OU に新規アカウントを収容していく。</li>
</ul>
<table>
<thead>
<tr>
<th align="center">No</th>
<th align="left">収容先</th>
<th align="left">変更の影響</th>
<th align="left">長期の健全性</th>
<th align="left">導入容易性</th>
<th align="left">コメント</th>
</tr>
</thead>
<tbody><tr>
<td align="center">1</td>
<td align="left">既存 OU (現状維持)</td>
<td align="left">◎ 既存への影響なし</td>
<td align="left">× 負債が残る</td>
<td align="left">◎ 現状維持で容易</td>
<td align="left">一時的には安全だが長期負債が増える</td>
</tr>
<tr>
<td align="center">2</td>
<td align="left">既存 OU (新ガードレール適用)</td>
<td align="left">× 既存全体へ影響大</td>
<td align="left">◎ 健全性が高い</td>
<td align="left">× 検証負荷が大きい</td>
<td align="left">理想的だが影響が重く段階移行が必要</td>
</tr>
<tr>
<td align="center">3</td>
<td align="left">新規 OU 群</td>
<td align="left">◎ 既存への影響なし</td>
<td align="left">△ OU 分散リスク</td>
<td align="left">◎ 導入・検証が容易</td>
<td align="left">OU 分散を後続フェーズで解消する必要あり</td>
</tr>
</tbody></table>
<p>今回は、既存環境への影響を避けつつも新たな仕組みの導入が容易な案 3 を採用しました。</p>
<p>ただし、これは恒久的な解ではなく、既存 OU をいきなり組み替えずに新しいガードレール構成と運用モデルを検証するための最初の一歩という位置付けです。</p>
<p>新規 OU 群で運用実績と検証結果を蓄積したうえで、Sandbox OU など影響の小さい既存アカウントから段階的に移設し、最終的には OU 構成とガードレールを可能な限り統一していくことを中長期の方針としています。</p>
<hr>
<h2>予防的ガードレールの棚卸し</h2>
<p>予防的ガードレールは、望ましくない操作や構成を未然に防ぐための仕組みです。当社環境では、AWS Control Tower の標準ガードレールに加え、その不足分を補完するため、設定自動化ツールで追加のガードレール有効化・カスタム SCP を適用しています。</p>
<table>
<thead>
<tr>
<th>段</th>
<th>名称</th>
<th>主体</th>
<th>役割</th>
</tr>
</thead>
<tbody><tr>
<td>1 段目</td>
<td>標準ガードレール</td>
<td>AWS Control Tower</td>
<td>AWS 標準の予防コントロール</td>
</tr>
<tr>
<td>2 段目</td>
<td>追加ガードレール</td>
<td>設定自動化ツール ( CDK )</td>
<td>標準ガードレールでカバーできない不足分の補完</td>
</tr>
<tr>
<td>3 段目</td>
<td>カスタムガードレール</td>
<td>設定自動化ツール ( CDK )</td>
<td>当社固有の要件に合わせた独自の SCP</td>
</tr>
</tbody></table>
<p>新規アカウントを新設 OU に収容する方針としたため、新規 OU に設定するガードレール追加に伴う既存環境への影響はありません。そのため、初期導入後に追加された Control Tower の新しい予防コントロールを有効化すべきかどうかを検討しました。</p>
<p>しかしながら、AWS が提供する「重大度」だけでは、以下に挙げる 3 つの例のように、なぜその評価になっているのか、実運用でどのような影響が出るのかまでは見えませんでした。</p>
<p>重大度が高だが、導入判断がつかないものの例</p>
<ul>
<li>(1) [CT.EC2.PV.4] Require that Amazon EBS direct APIs are not called</li>
<li>(2) [CT.S3.PV.2] Require all requests to Amazon S3 resources use authentication based on an Authorization header</li>
</ul>
<p>重大度が中だが、導入しても良いと思われるものの例</p>
<ul>
<li>(3) [CT.EC2.PV.11] Disallow public sharing of Amazon Machine Images ( AMIs )</li>
</ul>
<p>そこで、Amazon Q Developer Pro ※ を用いて、各コントロールの SCP を、運用影響・推奨度・導入プロセスの観点で評価しました。上記 1～3 については、次のような気付きがありました。 </p>
<ol>
<li>EBS direct APIs を利用する AWS Backup パートナー製品などのバックアップに影響する可能性があるため注意が必要 ( CT.EC2.PV.4 )</li>
<li>Presigned URL が使えなくなる ( CT.S3.PV.2 )</li>
<li>導入は妥当であるが、SCP ではなく DECLARATIVE_POLICY なので挙動が異なる点に注意が必要 ( CT.EC2.PV.11 )</li>
</ol>
<p>こうした整理を通じて、有効化が推奨され、影響が小さいものは、新規 OU で積極的に有効化する方針としました。</p>
<p>※ Amazon Q Developer Pro を利用した理由は、他の AI と比較すると応答速度は遅いものの、AWS の公式ドキュメントを都度参照したうえで回答していると推察でき、仕様変更の多い領域でも一定の安心感があったためです。</p>
<hr>
<h2>発見的ガードレールの棚卸し</h2>
<p>当社環境では、セキュリティ基準の一元管理と自動更新性の観点から Security Hub CSPM をセキュリティ監査の主軸としています。</p>
<p>Control Tower の検出コントロールは、Control Tower が提供するガードレールや共通基盤の構成に関する観点を含むチェック群であり、当社では Security Hub CSPM を補完する仕組みとして位置付けています。</p>
<p>今回の見直しでは、運用開始後に追加された Control Tower の検出コントロールについて、「重大度：重大」または「ガイダンス：強く推奨」に該当するものを対象に、有効化の要否を確認しました。</p>
<p>その結果、例えば以下のようなコントロールは、Security Hub CSPM の基準にて既に監査可能であることを確認できたため、Control Tower 側では重複して有効化しない方針としました。</p>
<ul>
<li>[CONFIG.KMS.DT.1] Checks if AWS Key Management Service ( AWS KMS ) keys are not scheduled for deletion in AWS KMS</li>
<li>[CONFIG.KMS.DT.2] Checks if the AWS KMS key policy allows public access</li>
</ul>
<p>一方、以下のように、サービス固有の設定を扱う一部のコントロールについては、当社での実際の利用状況や将来の利用計画に応じて、必要性を個別に検討する方針としました。</p>
<ul>
<li>[CONFIG.EMR.DT.1] Checks if an account with Amazon EMR has block public access settings enabled</li>
</ul>
<hr>
<h2>設定自動化ツールの棚卸し</h2>
<p>当社では、新規アカウントの初期設定を自動化するため、設定自動化ツール ( CDK ベース) を開発・運用しています。長年にわたり、このツールが新規アカウントの標準化や初期設定の抜け漏れ防止に大きく寄与してきました。</p>
<p>今回の棚卸しでは、現在のアカウント規模やチーム構成、AWS のマネージド機能の拡充状況を踏まえ、この仕組みをどこまでシンプルにできるかを整理しています。</p>
<p>一般的には自動化範囲を広げる事例も多いですが、当社では維持しやすさを優先し、必要な部分に絞る方向性としました。</p>
<p>設定自動化ツールで実施している内容は以下の通りです。</p>
<ol>
<li>追加の予防的ガードレール適用</li>
<li>カスタム SCP の追加</li>
<li>Security Hub CSPM の重複・不要なアラーム抑制</li>
<li>Security Hub CSPM 準拠を目的としたサービス設定 (例：デフォルト暗号化やパブリックアクセスブロックの有効化など)</li>
<li>その他のセキュリティ設定の自動適用</li>
</ol>
<p>現時点の方針としては、以下の 3 段構えで進める想定です。</p>
<ul>
<li>AWS のマネージド機能に任せられる部分は、できる限り移行する</li>
<li>マネージド機能では置き換えられない部分は、宣言的 IaC ( CloudFormation、CloudFormation StackSets、Terraform など) での管理を優先する</li>
<li>IaC にも寄せられない例外的な処理は、最小限のコードとして残し、具体的な実装方式は後続フェーズで判断する</li>
</ul>
<p>単にコードを減らすことが目的ではなく、組織として長期的に持ち続けるべき責務だけをコードに残すことを狙いとしています。</p>
<p>特に項目 4 については、<a href="https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/central-configuration-intro.html">Security Hub CSPM の中央設定</a> に移行することで、一部の設定を AWS のマネージド機能に寄せられる可能性があります。既存 OU ではコントロールの状態に個別差分が残っており、適用には棚卸しが必要ですが、新規 OU から段階的に活用する予定です。</p>
<p>また、5 の中で実施している S3 ブロックパブリックアクセス有効化の設定についても、先日公開された <a href="https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_s3.html">S3 ポリシー</a> への移行により、同様に AWS のマネージド機能に寄せられる可能性があります。こちらも今後、導入可否についての調査を進めていく予定です。</p>
<p>なお、設定自動化ツールには CDK による条件分岐など独自の強みがあり、これが見直しにおける判断ポイントとなる可能性があります。引き続き、上記方針が適切かどうかを慎重に検証しながら進めていく予定です。</p>
<hr>
<h2>アカウント発行フローの棚卸し</h2>
<p>当社では、これまで新規アカウント発行時にルートユーザの MFA を有効化する運用を行っていましたが、物理作業が伴うため、発行の都度一定の稼働が発生していました。</p>
<p>2024年末に<a href="https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_root-enable-root-access.html">メンバーアカウントのルートアクセス一元管理</a>により、この運用自体を見直せる可能性があります。</p>
<p>今回の再設計と併せて、アカウント発行フローもできる限りシンプルかつ安全な形に整理することを検討しています。</p>
<hr>
<h2>まとめ</h2>
<p>今回の棚卸しでは、長年の運用で積み上がった設定や仕組みを前提に、将来の拡張性と保守性の両面から、どこを変え、どこを維持すべきかを整理しました。</p>
<p>既存 Organizations を維持したまま理想的な構成へ作り替えることは簡単ではありません。OU 設計、ガードレール、初期設定の自動化ツールといった複数レイヤが相互に依存しているため、どれか一つを更新すると他のレイヤにも影響が波及するからです。</p>
<p>そのため今回は、すべてを全面刷新するのではなく、本番を止めずに少しずつ負債を減らしながら改善を進めるという方針で整理を進めました。特に Control Tower の挙動や一部のガードレールの影響範囲は、公開情報だけでは読み切れない部分もあるため、リスクを分割しながら段階的に検証する進め方が不可欠だと感じています。</p>
<p>今後は、AWS が提供するマネージド機能を積極的に活用しつつ、既存環境との整合を取りながら変更箇所を局所化し、段階的に改善を続けていく方針です。</p>
<p>本記事が、既存環境を前提にガバナンスを再設計する方々にとって、少しでも判断材料や視点のヒントになれば幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Hosting Frontend Conference Kansai 2025]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-06-frontend-conf-kansai-2025-eng/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-06-frontend-conf-kansai-2025-eng/</guid>
            <pubDate>Sat, 06 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[As a founding member of Frontend Conference Kansai 2025, revived after a 6-year hiatus, I look back on the journey to making this event happen.]]></description>
            <content:encoded><![CDATA[<p>This article is for Day 6 of the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> and Day 6 of the <a href="https://adventar.org/calendars/11711">Tech Event &amp; Conference Management Know-how Advent Calendar 2025</a>🎅🎄</p>
<h2><strong>Introduction</strong></h2>
<p>Hello!
I&#39;m high-g (<a href="https://x.com/high_g_engineer">@high_g_engineer</a>) from the Master Maintenance Tool Development Team, KINTO Backend Development Group, KINTO Development Division, also working with the Developer Relations Group, and based at Osaka Tech Lab. I work as a frontend engineer.</p>
<p>Frontend Conference Kansai 2025 (hereafter referred to as FEC Kansai 2025) was held on Sunday, November 30, 2025.
I participated as a founding member of this conference, serving as the leader of the Speaker Team, which handled CfP, various speaker-related matters, timetable creation, and session management on the day of the event.
In this article, I&#39;ll share the story behind how FEC Kansai 2025 came to be and the significance of hosting a conference in a regional area, while reflecting on my own experiences with tech community activities.</p>
<h2><strong>Results of FEC Kansai 2025</strong></h2>
<p>First, regarding FEC Kansai 2025, we had over 200 participants on the day. The keynote had standing room only, networking party tickets sold out, and sponsor booths were a huge success. While we had some challenges to address, as an inaugural event, I believe we achieved a successful launch without any major issues.</p>
<p><img src="/assets/blog/authors/high-g/20251206/img1.jpg" alt="基調講演の様子"></p>
<p><img src="/assets/blog/authors/high-g/20251206/img2.jpg" alt="スポンサールームの様子"></p>
<p><img src="/assets/blog/authors/high-g/20251206/img3.jpg" alt="懇親会の様子"></p>
<p>KINTO Technologies was also a sponsor!!</p>
<p><img src="/assets/blog/authors/high-g/20251206/img_ktc.jpg" alt="KINTOテクノロジーズのスポンサーブース写真"></p>
<h2><strong>Frontend Conference and Me</strong></h2>
<p>2025 was a great year for Frontend Conferences, with events held not only in Kansai but also in Hokkaido and Tokyo.
I also attended Frontend Conference Hokkaido 2025, where I learned from the presentations and enjoyed meeting people from the Hokkaido community at the networking party.</p>
<p>Now, about Frontend Conference; it&#39;s not something that just started recently. Before the COVID-19 pandemic, it was held annually in Kansai as well, serving as a yearly festival where attendees could catch up on the latest frontend trends and gain practical knowledge about new frameworks and libraries.</p>
<p><img src="/assets/blog/authors/high-g/20251206/img4.png" alt="コロナ禍前の関西のフロントエンドカンファレンスのロゴ"></p>
<p>I can honestly say that Frontend Conference played a significant role in launching my career as a frontend engineer.
However, after the 2019 event, the Kansai Frontend Conference went on a 6-year hiatus.</p>
<p>As someone who loves frontend development, the absence of this annual festival left me feeling like something was missing.</p>
<h2><strong>How FEC Kansai 2025 Got Started</strong></h2>
<p>Last year, TSKaigi Kansai 2024, a Kansai regional version of TSKaigi, Japan&#39;s largest TypeScript conference, was held in Kyoto. That was my debut as a conference staff member.</p>
<p><a href="https://note.com/highgrenade/n/nde9f7e059e2e">https://note.com/highgrenade/n/nde9f7e059e2e</a></p>
<p>Riding that momentum, I organized the Kansai Frontend Year-End Party 2024 at our company.</p>
<p><a href="https://kinto-technologies.connpass.com/event/337002/">https://kinto-technologies.connpass.com/event/337002/</a></p>
<p>The event was a hit, and afterward, I went out for drinks with some TSKaigi Kansai 2024 staff members and Vue Fes Japan staff who live in Kansai.
There, we discovered we all shared the same desire to bring back Frontend Conference, and FEC Kansai officially kicked off in 2025.</p>
<p><a href="https://fec-kansai.connpass.com/event/339864/">https://fec-kansai.connpass.com/event/339864/</a></p>
<h2><strong>The Journey to FEC Kansai 2025</strong></h2>
<p>Of course, since this was a first-time event, we started from scratch with no rules, no budget, no staff, and no name recognition.
Naturally, we had to handle everything ourselves: recruiting staff, securing sponsors, booking the venue, managing the call for proposals, building the website, creating merchandise, and placing orders.</p>
<p>I optimistically assumed that securing staff and sponsors would be easy thanks to the name recognition of past Frontend Conferences, but in reality, it wasn’t nearly that simple.
Since this was our first event, we focused on ensuring a smooth and successful launch. We avoided aiming too high, yet still made sure not to lose sight of delivering a satisfying experience for participants.</p>
<p>Despite these circumstances, I&#39;m incredibly grateful to all the staff who joined us and the companies who sponsored us.</p>
<p>To help shape our vision for the conference, I personally attended various other conferences:</p>
<ul>
<li>February 1: BuriKaigi 2025 (Toyama)</li>
<li>May 23-24: TSKaigi 2025 (Tokyo) *Participated as staff</li>
<li>July 19: PHP Conference Kansai 2025 (Kobe)</li>
<li>July 26: Kinoko Conference in Kansai (Kyoto) *Not a conference per se, but memorable enough to mention</li>
<li>September 6: Frontend Conference Hokkaido (Sapporo)</li>
<li>September 17: Developers Summit 2025 KANSAI (Osaka)</li>
</ul>
<h2><strong>The Significance of Hosting Conferences in Regional Areas</strong></h2>
<p>What struck me when attending regional conferences like BuriKaigi and Frontend Conference Hokkaido was the extraordinary feeling of gathering in places I would rarely visit otherwise, surrounded by people who share the same purpose, and experiencing talks with a live energy you can only feel on-site, an excitement shared exclusively among those who came together on that day. It felt like giving up my day off was completely worth it, and to go even further, it was the kind of experience that made me genuinely grateful to be an engineer.</p>
<p>It&#39;s similar to the feeling of attending a music festival and experiencing moments of pure joy that only exist in that time and place. (For anyone who has never been to a music festival, just imagine everyone sharing that same excitement while enjoying incredibly delicious yakiniku together.)</p>
<p>Reflecting on it, I realized that back when I attended Frontend Conference before the pandemic, I wasn&#39;t just going to learn—I was going to share in that collective experience.</p>
<p>I believe that if regional conferences can consistently create this kind of excitement, they can truly energize the local engineering community.</p>
<p>I want to enjoy this myself, and I want others to experience this excitement too, so we can all have fun with technology while working together.
If we can create something truly great through collective knowledge, strengthen the local pool of engineers, and generate a positive cycle in the hiring market, I don’t think there could be anything better.</p>
<h2><strong>Closing Thoughts</strong></h2>
<p>Going forward, I want to continue expanding my knowledge, engaging with more conferences and tech communities, and refining FEC Kansai. I hope to help attendees feel that engineering is fun! and frontend is amazing!! while contributing to the Kansai tech community in any way I can.</p>
<p>I really do love frontend development!!</p>
<p>Thank you for reading to the end.</p>
<p><img src="/assets/blog/authors/high-g/20251206/img5.jpg" alt="フロカン関西2025のスタッフ写真"></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/high-g/20251206/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[フロントエンドカンファレンス関西2025を主催してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-06-frontend-conf-kansai-2025/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-06-frontend-conf-kansai-2025/</guid>
            <pubDate>Sat, 06 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[6年ぶりに復活したフロカン関西2025の立ち上げメンバーとして参加した筆者が、開催までの道のりを振り返った記事です。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の 6 日目と <a href="https://adventar.org/calendars/11711">技術イベント・カンファレンス運営のノウハウ Advent Calendar 2025</a> の 6 日目 の記事です🎅🎄</p>
<h2><strong>はじめに</strong></h2>
<p>こんにちは！<br>KINTO開発部 KINTOバックエンド開発G マスターメンテナンスツール開発チーム、技術広報G兼務、Osaka Tech Lab 所属の high-g（<a href="https://x.com/high_g_engineer">@high_g_engineer</a>）です。フロントエンドエンジニアをやっています。</p>
<p>2025年11月30日(日)に開催されたフロントエンドカンファレンス関西2025（以下、フロカン関西2025）。<br>自分はこのカンファレンスに立ち上げメンバーとして参加し、CfP、登壇者関連の諸々、タイムテーブル作成、当日のセッション進行を行うスピーカーチームのリーダーを担当していました。
本記事では、フロカン関西2025の開催に至った経緯や、地方でカンファレンスを開催する意義について、これまでの自身の技術コミュニティ活動を振り返りながらお伝えします。</p>
<h2><strong>フロカン関西2025の開催結果</strong></h2>
<p>まず、フロカン関西2025についてですが、当日の参加者は200人を超えており、基調講演では立ち見が出たり、懇親会チケットが売り切れたり、スポンサーブースも大盛況だったりといった形で、いくつか課題を残しつつも大きな問題なく、立ち上げ初回開催としては無事成功を収めたのではないかと思います。</p>
<p><img src="/assets/blog/authors/high-g/20251206/img1.jpg" alt="基調講演の様子"></p>
<p><img src="/assets/blog/authors/high-g/20251206/img2.jpg" alt="スポンサールームの様子"></p>
<p><img src="/assets/blog/authors/high-g/20251206/img3.jpg" alt="懇親会の様子"></p>
<p>KINTOテクノロジーズも協賛しました！！</p>
<p><img src="/assets/blog/authors/high-g/20251206/img_ktc.jpg" alt="KINTOテクノロジーズのスポンサーブース写真"></p>
<h2><strong>フロントエンドカンファレンスと自分</strong></h2>
<p>2025年はフロントエンドカンファレンスが豊作な年で、関西以外でも北海道や東京でも開催されました。
自分もフロントエンドカンファレンス北海道2025に参加し、カンファレンスの発表で学んだり、懇親会で北海道のコミュニティの方々と挨拶をしたりして、しっかりと満喫してきました。</p>
<p>さて、フロントエンドカンファレンスについてですが、実は最近になって開催され始めたわけではなく、コロナ禍前は関西でも毎年のように開催されており、その年のフロントエンドのトレンドや新しいフレームワークやライブラリなどの実践知識を確認するための一年に一度のお祭り的な存在でした。</p>
<p><img src="/assets/blog/authors/high-g/20251206/img4.png" alt="コロナ禍前の関西のフロントエンドカンファレンスのロゴ"></p>
<p>自分自身がフロントエンドエンジニアとしてキャリアをスタートしたきっかけもフロントエンドカンファレンスのおかげと言っても過言ではありません。
そんな毎年、当たり前のように開催されていた関西のフロントエンドカンファレンスですが、2019年の開催を最後に6年間開催されない状況が続きました。</p>
<p>フロントエンドが好きな自分としては、年に一度のお祭りがなくなったことで、心にぽっかりと穴が空いたような感覚がありました。</p>
<h2><strong>フロカン関西2025の立ち上げの経緯</strong></h2>
<p>去年、TSKaigi Kansai 2024 というTypeScriptの国内最大規模カンファレンスである TSKaigi の関西ローカル版が京都で開催され、自分はそこでカンファレンススタッフとしてデビューしました。</p>
<p><a href="https://note.com/highgrenade/n/nde9f7e059e2e">https://note.com/highgrenade/n/nde9f7e059e2e</a></p>
<p>その勢いのまま、関西フロントエンド忘年会2024というイベントを弊社で開催しました。</p>
<p><a href="https://kinto-technologies.connpass.com/event/337002/">https://kinto-technologies.connpass.com/event/337002/</a></p>
<p>イベントは盛り上がり、そこに参加していた TSKaigi Kansai 2024 のスタッフや Vue Fes Japan の関西在住スタッフとイベント後に飲みに行くことになりました。<br>そこでフロントエンドカンファレンスをやりたいという気持ちが一致し、2025年に入ってからフロカン関西が始動しました。</p>
<p><a href="https://fec-kansai.connpass.com/event/339864/">https://fec-kansai.connpass.com/event/339864/</a></p>
<h2><strong>フロカン関西2025の当日までの歩み</strong></h2>
<p>もちろん、初回開催のカンファレンスなので、ルールもない、お金もない、スタッフもいない、知名度もないという状況からスタートしました。<br>当たり前ですが、スタッフの確保、スポンサー様の確保、イベント会場の確保、プロポーザル募集、サイト制作、ノベルティ制作、発注作業などをすべて自分たちでやらないといけません。</p>
<p>スタッフやスポンサー様の確保は、過去のフロントエンドカンファレンスの知名度にあやかれるだろうと楽観的に考えていましたが、実際はそう甘くはありませんでした。<br>とにかく初回なので、無事に開催を成功させることを念頭に、高望みせず、かと言って参加者を満足させることは忘れないように進めてまいりました。</p>
<p>こんな状態にもかかわらず参加いただいたスタッフのみなさんや協賛いただいた企業様には感謝しかありません。</p>
<p>また、どんなカンファレンスにしたいかイメージを固めるため、個人的にいろんなカンファレンスに参加しました。</p>
<ul>
<li>2/1     BuriKaigi 2025（富山）</li>
<li>5/23-24 TSKaigi 2025（東京）※スタッフとして参加</li>
<li>7/19    PHPカンファレンス関西2025（神戸）</li>
<li>7/26    きのこカンファレンス in 関西（京都）※カンファレンスではないですが、印象に残ったため記載</li>
<li>9/6     フロントエンドカンファレンス北海道（札幌）</li>
<li>9/17    Developers Summit 2025 KANSAI（大阪）</li>
</ul>
<h2><strong>地方でカンファレンスを開催する意義</strong></h2>
<p>BuriKaigi やフロントエンドカンファレンス北海道などの地方カンファレンスに参加して思ったのは、普段であればほとんど行く機会のないような場所に、共通の目的を持った人たちが集まって、そこでしか聞けないようなライブ感のある登壇の感動をその日に集った人たちだけでシェアする非日常的な感覚がとても刺激的だったということです。休日を返上して来たかいがあったと思えるし、もっと言うと、エンジニアをやっていてよかったなあと感動を覚えるレベルでした。</p>
<p>例えば、音楽フェスに行って、その瞬間だけでしか味わえないような感動を覚える感覚に近いです。（音楽フェスに行ったことのない人は、めちゃくちゃ美味しい焼肉をみんなで食べながら感動をシェアしているイメージを持ってもらえればと）</p>
<p>コロナ禍以前の当時のフロントエンドカンファレンスに対して、自分は勉強しに行くというよりかはこういった感覚をシェアしに行っていたのかもしれないなと、ふと感じるようになったんですよね。</p>
<p>こういう感動を地方カンファレンスで安定して生み出せるようになれば、その地域のエンジニアコミュニティが活性化すると自分は信じています。</p>
<p>自分自身が楽しみたいし、この感動を他の人にも味わってもらって、全員で技術を楽しみながら仕事をしたい。<br>本当に良いものを集合知で作っていけるようになったり、その地域のエンジニアの層を厚くして、採用市場にも良い循環を生み出せたら、こんなに良いことはないと思います。</p>
<h2><strong>おわりに</strong></h2>
<p>今後はさらに自分自身の知識を増やし、今よりも多くのカンファレンスや技術コミュニティに触れて、フロカン関西を磨き上げていきたいと思っています。参加者のみなさまに「エンジニアリングっておもろい！」「フロントエンド最高やん！！」と思ってもらえるように、また、関西の技術コミュニティに少しでも貢献できたらうれしいです。</p>
<p>やっぱり自分はフロントエンドが好きだー！！</p>
<p>最後まで読んでいただきありがとうございました。</p>
<p><img src="/assets/blog/authors/high-g/20251206/img5.jpg" alt="フロカン関西2025のスタッフ写真"></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/high-g/20251206/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Smart Task Management with Teams × Power Platform]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-05-Teams-Power-Platform-smart-task-management-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-05-Teams-Power-Platform-smart-task-management-en/</guid>
            <pubDate>Fri, 05 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[By combining Microsoft Teams with Power Platform (Power Automate/Power Apps/Power BI), we were able to create a task management system that enables easy automation and visualization.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello, I&#39;m Angela Wang from the Group Core Systems Division at KINTO Technologies.</p>
<p>Recently, I had the opportunity to explore <strong>Microsoft Power Automate</strong> and was impressed by how easily systems can be built using low-code. I&#39;d like to introduce one solution example.</p>
<p>Do you ever face these challenges in your daily work?</p>
<ul>
<li>Task instructions scattered across chat messages</li>
<li>Progress reporting is person-dependent and difficult to track</li>
<li>Unable to get an overview of the situation at a glance</li>
</ul>
<p>These challenges can be solved by combining <strong>Microsoft Teams with Power Platform (Power Automate/Power Apps/Power BI)</strong> to create a <strong>task management system that anyone can easily automate and visualize</strong>.</p>
<h2>1. Architecture Overview</h2>
<p>Here is the basic concept of this solution:</p>
<blockquote>
<p><strong>Achieve end-to-end task management from creation to progress tracking and report analysis, all centered around Teams.</strong></p>
</blockquote>
<p>![Architecture](/assets/blog/authors/angela.wang/architecture.png =600x688)</p>
<h3>Components</h3>
<table>
<thead>
<tr>
<th>Component</th>
<th>Role</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Microsoft Teams</strong></td>
<td>Entry point &amp; notification hub</td>
<td>Execute task creation, updates, and notifications within channels</td>
</tr>
<tr>
<td><strong>Power Apps</strong></td>
<td>Task management app</td>
<td>Register and update tasks with an intuitive UI</td>
</tr>
<tr>
<td><strong>Power Automate</strong></td>
<td>Automation workflow</td>
<td>Automate notifications, reminders, and report generation</td>
</tr>
<tr>
<td><strong>Power BI</strong></td>
<td>Visualization &amp; analysis</td>
<td>Visualize task completion rates and delay trends in real-time</td>
</tr>
<tr>
<td><strong>SharePoint List</strong></td>
<td>Data storage</td>
<td>Store task data</td>
</tr>
</tbody></table>
<h2>2. Feature Design</h2>
<h3>Task Creation and Assignment (Power Apps)</h3>
<p>Create a form in Power Apps to input the following information:</p>
<ul>
<li>Task name</li>
<li>Assignee (MS user)</li>
<li>Status</li>
<li>Priority</li>
<li>Due date</li>
<li>Created by</li>
<li>Completion date</li>
<li>Details</li>
</ul>
<p>![feature1](/assets/blog/authors/angela.wang/feature1.png =431x681)</p>
<p>After creation, the data is recorded in SharePoint List.<br>Screens for viewing and editing the task list and task details are also created.</p>
<p>![feature2](/assets/blog/authors/angela.wang/feature2.png =431x681)</p>
<p>![feature3](/assets/blog/authors/angela.wang/feature3.png =431x681)</p>
<h3>Process Automation (Power Automate)</h3>
<p>Here are typical automation scenarios:</p>
<table>
<thead>
<tr>
<th><strong>Scenario</strong></th>
<th><strong>Processing</strong></th>
<th><strong>Implementation</strong></th>
<th><strong>Comments</strong></th>
</tr>
</thead>
<tbody><tr>
<td>Task creation</td>
<td>Notify assignee via Teams</td>
<td>Trigger on &quot;When an item is created&quot; and<br>send to assignee via Teams</td>
<td>Implemented</td>
</tr>
<tr>
<td>Status change</td>
<td>Update log</td>
<td>Update event to SharePoint</td>
<td>Example</td>
</tr>
<tr>
<td>Past due</td>
<td>Remind assignee and supervisor</td>
<td>Conditional branching + Teams message</td>
<td>Example</td>
</tr>
<tr>
<td>Weekly report</td>
<td>Send task summary to Teams</td>
<td>Scheduled trigger</td>
<td>Example</td>
</tr>
</tbody></table>
<h3>Data Visualization (Power BI)</h3>
<p>In Power BI, create reports such as:</p>
<ul>
<li>Task status and progress rate</li>
<li>Due date distribution</li>
<li>Task count by assignee (*)</li>
</ul>
<p>*: Retrieving assignee information requires editing in Power BI Desktop, but this was not implemented this time.</p>
<h2>3. Integrated Experience Through Teams Integration</h2>
<p>By centering operations around Teams, the following experience can be achieved:</p>
<ul>
<li>Display Power Apps as a tab within Teams channels</li>
<li>Automatic notifications via Power Automate Bot</li>
<li>Direct viewing of Power BI dashboards</li>
</ul>
<p>In other words,</p>
<blockquote>
<p><strong>Users can complete task management without ever leaving Teams.</strong></p>
</blockquote>
<p>This is the true strength of Microsoft 365.<br>*Adding Power Apps and Power BI dashboards as tabs to Teams channels requires permissions, but this was not implemented this time.</p>
<h2>4. Data Structure (SharePoint List)</h2>
<table>
<thead>
<tr>
<th>Column Name</th>
<th>Data Type</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>ID</td>
<td>Auto number</td>
<td>Unique task ID</td>
</tr>
<tr>
<td>Title</td>
<td>Text</td>
<td>Task name</td>
</tr>
<tr>
<td>Description</td>
<td>Multi-line text</td>
<td>Details</td>
</tr>
<tr>
<td>Assignee</td>
<td>User</td>
<td>Assignee</td>
</tr>
<tr>
<td>Status</td>
<td>Choice</td>
<td>Status: Not Started / In Progress / Completed / Delayed</td>
</tr>
<tr>
<td>Priority</td>
<td>Choice</td>
<td>Priority: High / Medium / Low</td>
</tr>
<tr>
<td>DueDate</td>
<td>Date</td>
<td>Due date</td>
</tr>
<tr>
<td>CreatedBy</td>
<td>User</td>
<td>Created by</td>
</tr>
<tr>
<td>CompletedDate</td>
<td>Date</td>
<td>Completion date</td>
</tr>
</tbody></table>
<h2>5. Implementation Steps</h2>
<ol>
<li><strong>Prepare SharePoint List (Task List)</strong><br>Create &quot;TaskList&quot; in SharePoint.</li>
</ol>
<p>![list](/assets/blog/authors/angela.wang/list.png =760x440)</p>
<ol start="2">
<li><strong>Build Power Apps (Task Management App)</strong><br>Use the convenient &quot;Start with an app template&quot; feature to quickly build the app.</li>
</ol>
<p>![powerapps1](/assets/blog/authors/angela.wang/powerapps1.png =760x238)</p>
<p>![powerapps2](/assets/blog/authors/angela.wang/powerapps2.png =736x318)</p>
<ol start="3">
<li><strong>Build Power Automate (Task Management Flow)</strong><br>Implement notifications to be sent to the assignee&#39;s Teams when a new task is created.</li>
</ol>
<p>![powerplatform](/assets/blog/authors/angela.wang/powerplatform.png =760x600)</p>
<ol start="4">
<li><strong>Create Power BI (Task Management Dashboard)</strong><br>Create visuals for &quot;Task Progress&quot; and &quot;Due Date Distribution&quot; on the dashboard.</li>
</ol>
<p>![powerbi](/assets/blog/authors/angela.wang/powerbi.png =760x328)</p>
<ol start="5">
<li><strong>Teams Integration Settings</strong><br>Add Power Apps and Power BI tabs (requires permissions, but this was not implemented this time).</li>
</ol>
<h2>6. Conclusion</h2>
<p>By leveraging Power Platform, you can create a task management solution that <strong>anyone can build, is immediately usable, and seamlessly fits into your team</strong>. Please consider implementing this in your work.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/angela.wang/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Teams × Power Platformで実現するスマートなタスク管理]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-05-Teams-Power-Platform-smart-task-management/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-05-Teams-Power-Platform-smart-task-management/</guid>
            <pubDate>Fri, 05 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Microsoft TeamsとPower Platform（Power Automate／Power Apps／Power BI）を組み合わせることで、簡単に自動化・可視化できるタスク管理システムを実現できました。]]></description>
            <content:encoded><![CDATA[<h2>✨ はじめに</h2>
<p>こんにちは、KINTOテクノロジーズグループコアシステム部のAngela Wangです。</p>
<p>最近、<strong>Microsoft Power Automate</strong> に触れる機会があり、ローコードで簡単にシステムを構築できる点に魅力を感じました。そこで、一つのソリューション例を通してご紹介させていただきます。</p>
<p>日々の業務の中で、こんな悩みはありませんか？</p>
<ul>
<li>タスクの指示がチャット内で散乱している</li>
<li>進捗報告が属人的で、追跡が難しい</li>
<li>状況を一覧で把握できない</li>
</ul>
<p>これらの課題は、<strong>Microsoft Teams と Power Platform（Power Automate／Power Apps／Power BI）</strong> を組み合わせることで、<strong>誰でも簡単に自動化・可視化できるタスク管理システム</strong>として解決できます。</p>
<h2>🧩 1. 全体構成の概要</h2>
<p>下記がこのソリューションの基本コンセプトです。</p>
<blockquote>
<p><strong>「Teams を中心に、タスク作成から進捗追跡、レポート分析までをワンストップで実現する」</strong></p>
</blockquote>
<p>![Architecture](/assets/blog/authors/angela.wang/architecture.png =600x688)</p>
<h3>構成要素</h3>
<table>
<thead>
<tr>
<th>コンポーネント</th>
<th>役割</th>
<th>説明</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Microsoft Teams</strong></td>
<td>操作の入口・通知ハブ</td>
<td>タスクの作成、更新、通知をチャネル上で実行</td>
</tr>
<tr>
<td><strong>Power Apps</strong></td>
<td>タスク管理アプリ</td>
<td>直感的な UI でタスクを登録・更新</td>
</tr>
<tr>
<td><strong>Power Automate</strong></td>
<td>自動化ワークフロー</td>
<td>通知、リマインド、レポート生成を自動化</td>
</tr>
<tr>
<td><strong>Power BI</strong></td>
<td>可視化・分析</td>
<td>タスク完了率・遅延傾向をリアルタイムで可視化</td>
</tr>
<tr>
<td><strong>SharePoint List</strong></td>
<td>データ保存</td>
<td>タスクデータの保存</td>
</tr>
</tbody></table>
<h2>⚙️ 2. 機能設計</h2>
<h3>① タスクの作成と担当割り当て（Power Apps）</h3>
<p>Power Apps 上で、以下の情報を入力できるフォームを作成します。</p>
<ul>
<li>タスク名</li>
<li>担当者（MSユーザー）</li>
<li>ステータス</li>
<li>優先度</li>
<li>期限日</li>
<li>作成者</li>
<li>完了日</li>
<li>詳細内容</li>
</ul>
<p>![feature1](/assets/blog/authors/angela.wang/feature1.png =431x681)</p>
<p>作成後、SharePoint Listに記録します。<br>また、タスク一覧やタスク詳細を確認・編集できる画面も作成します。</p>
<p>![feature2](/assets/blog/authors/angela.wang/feature2.png =431x681)</p>
<p>![feature3](/assets/blog/authors/angela.wang/feature3.png =431x681)</p>
<h3>② 処理の自動化（Power Automate）</h3>
<p>代表的な自動化シナリオは次の通りです。</p>
<table>
<thead>
<tr>
<th><strong>シナリオ</strong></th>
<th><strong>処理内容</strong></th>
<th><strong>実装方法</strong></th>
<th><strong>コメント</strong></th>
</tr>
</thead>
<tbody><tr>
<td>タスク作成時</td>
<td>担当者にTeamsに通知</td>
<td>「項目が作成された時」トリガーし、Teamsに<br>担当者へ送信</td>
<td>実現済</td>
</tr>
<tr>
<td>ステータス変更</td>
<td>ログ更新</td>
<td>SharePointへ更新イベント</td>
<td>例</td>
</tr>
<tr>
<td>期限超過</td>
<td>担当者と上長へリマインド</td>
<td>条件分岐 + Teams メッセージ</td>
<td>例</td>
</tr>
<tr>
<td>週次レポート</td>
<td>タスク集計をTeamsに送信</td>
<td>スケジュールトリガー</td>
<td>例</td>
</tr>
</tbody></table>
<h3>③ データの可視化（Power BI）</h3>
<p>Power BI では、以下のようなレポートを作成します：</p>
<ul>
<li>✅ タスクステータス・進捗率</li>
<li>🏷 期限日分布</li>
<li>👤 担当者別数（※）</li>
</ul>
<p>※：担当者情報を取得するためには Power BI Desktop で編集する必要がありますが、今回は実施しないことにしました。</p>
<h2>💬 3. Teams 連携による「一体型」体験</h2>
<p>Teams での操作を中心にすることで、以下の体験を実現できます。</p>
<ul>
<li>Teams チャネル内で Power Apps アプリをタブ表示</li>
<li>Power Automate Bot による自動通知</li>
<li>Power BI ダッシュボードの直接閲覧</li>
</ul>
<p>つまり、</p>
<blockquote>
<p><strong>ユーザーは Teams から一歩も出ずに、タスク管理を完結できます。</strong></p>
</blockquote>
<p>これこそが「Microsoft 365 の真の強み」です。<br>※Power Apps アプリや Power BI ダッシュボードを Teams チャンネルにタブ追加するには権限が必要ですが、今回は実施しないことにしました。</p>
<h2>🧱 4. データ構造（SharePoint List）</h2>
<table>
<thead>
<tr>
<th>列名</th>
<th>データ型</th>
<th>説明</th>
</tr>
</thead>
<tbody><tr>
<td>ID</td>
<td>自動番号</td>
<td>一意のタスクID</td>
</tr>
<tr>
<td>Title</td>
<td>テキスト</td>
<td>タスク名</td>
</tr>
<tr>
<td>Description</td>
<td>複数行テキスト</td>
<td>詳細内容</td>
</tr>
<tr>
<td>Assignee</td>
<td>ユーザー</td>
<td>担当者</td>
</tr>
<tr>
<td>Status</td>
<td>選択肢</td>
<td>ステータス：未開始 / 進行中 / 完了 / 遅延</td>
</tr>
<tr>
<td>Priority</td>
<td>選択肢</td>
<td>優先度：高 / 中 / 低</td>
</tr>
<tr>
<td>DueDate</td>
<td>日付</td>
<td>期限日</td>
</tr>
<tr>
<td>CreatedBy</td>
<td>ユーザー</td>
<td>作成者</td>
</tr>
<tr>
<td>CompletedDate</td>
<td>日付</td>
<td>完了日</td>
</tr>
</tbody></table>
<h2>🚀 5. 実装ステップ</h2>
<ol>
<li><strong>SharePoint List（タスクリスト）の準備</strong><br>SharePoint に 「TaskList」 を作成します。</li>
</ol>
<p>![list](/assets/blog/authors/angela.wang/list.png =760x440)</p>
<ol start="2">
<li><strong>Power Apps（タスク管理アプリ）の構築</strong><br>「アプリ テンプレートで開始する」という便利な機能を使い、迅速にアプリを構築します。</li>
</ol>
<p>![powerapps1](/assets/blog/authors/angela.wang/powerapps1.png =760x238)</p>
<p>![powerapps2](/assets/blog/authors/angela.wang/powerapps2.png =736x318)</p>
<ol start="3">
<li><strong>Power Automate（タスク管理フロー）の構築</strong><br>新規タスク作成時に担当者の Teams へ通知が送信されるように実装します。</li>
</ol>
<p>![powerplatform](/assets/blog/authors/angela.wang/powerplatform.png =760x600)</p>
<ol start="4">
<li><strong>Power BI（タスク管理ダッシュボード）の作成</strong><br>ダッシュボードに「タスク進捗」や「期限日分布」のビジュアルを作成します。</li>
</ol>
<p>![powerbi](/assets/blog/authors/angela.wang/powerbi.png =760x328)</p>
<ol start="5">
<li><strong>Teamsの統合設定</strong><br>Power Apps・Power BI のタブを追加します（権限が必要ですが、今回は実施しないことにしました）。</li>
</ol>
<h2>🏁 6. まとめ</h2>
<p>Power Platform を活用すれば、<strong>「誰でも作れる・すぐ使える・チームに馴染む」</strong> タスク管理ソリューションが実現できます。ぜひ業務の中でご検討ください。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/angela.wang/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Claude Code: Fully Automating from Jira Ticket to PR — Commit and PR Without Thinking]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-05-claude-code-jira-automation-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-05-claude-code-jira-automation-en/</guid>
            <pubDate>Fri, 05 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article introduces a case study of fully automating the development flow from Jira tickets to pull request creation using Claude Code and Atlassian MCP.]]></description>
            <content:encoded><![CDATA[<p>This article is the Day 5 entry of the KINTO Technologies Advent Calendar 2025🎅🎄</p>
<h2>Introduction</h2>
<p>The KINTO Development Division Frontend Team handles frontend development using React/Next.js.
We use Jira for task management and have adopted a ticket-based development flow.</p>
<p>As the team has grown, we felt that <strong>standardizing development flow conventions and reducing cognitive overhead</strong> — such as branch naming conventions, commit message formats, and PR template selection — had become a challenge.</p>
<p>This article introduces how we combined Claude Code with Atlassian MCP to automate these things you don&#39;t have to think about, creating an environment where developers can focus on solving real problems.</p>
<h2>Technology Stack</h2>
<ul>
<li><strong>Claude Code</strong>: AI-driven development assistant</li>
<li><strong>Atlassian MCP</strong>: API integration with Jira/Confluence (automatic ticket information retrieval)</li>
<li><strong>GitHub CLI (gh)</strong>: PR operation automation</li>
<li><strong>CLAUDE.md</strong>: Project-specific rule definitions</li>
</ul>
<h2>Background and Challenge: The Cognitive Load Problem in Development Flows</h2>
<p>What developers should really focus on is writing code.
However, in actual development, cognitive resources were being consumed by <strong>non-essential tasks</strong> like these:</p>
<ul>
<li>&quot;What was that ticket number again? Let me open Jira to check...&quot;</li>
<li>&quot;What should the branch name be? What goes after <code>feature/JIRAKEY-1234/</code>?&quot;</li>
<li>&quot;Should I branch from develop? Or from the project branch?&quot;</li>
<li>&quot;Which emoji was it for the commit message, <code>:sparkles:</code> or <code>:wrench:</code>?&quot;</li>
<li>&quot;Do I add <code>:m:</code> to the PR title or not?&quot;</li>
<li>&quot;Which PR template should I use? <code>for_dev.md</code>? The default one?&quot;</li>
</ul>
<p>These may seem trivial, but they are <strong>decisions that occur multiple times a day</strong>. When accumulated, they significantly drain developers&#39; focus.</p>
<h2>Solution: Achieving a &quot;No-Thinking&quot; Development Flow</h2>
<p><strong>&quot;Just provide the ticket number, and everything else is automated.&quot;</strong></p>
<p>To achieve this, we combined Claude Code with Atlassian MCP.</p>
<h3>What the Developer Does</h3>
<pre><code>Developer: &quot;Create a branch for JIRAKEY-1234&quot;
</code></pre>
<h3>What Claude Code Does (Automatically)</h3>
<ol>
<li>✅ Retrieves ticket information via Jira API</li>
<li>✅ Checks project affiliation through epic determination</li>
<li>✅ Automatically generates the appropriate branch name (<code>feature/JIRAKEY-1234/update_claude_docs</code>)</li>
<li>✅ Automatically determines the appropriate base branch (develop or project branch)</li>
<li>✅ Creates the branch</li>
</ol>
<hr>
<h3>What the Developer Does</h3>
<pre><code>Developer: &quot;Commit&quot;
</code></pre>
<h3>What Claude Code Does (Automatically)</h3>
<ol>
<li>✅ Analyzes the changes</li>
<li>✅ Selects the appropriate emoji shortcode (<code>:pencil:</code>, <code>:bug:</code>, <code>:sparkles:</code>, etc.)</li>
<li>✅ Executes the commit</li>
</ol>
<hr>
<h3>What the Developer Does</h3>
<pre><code>Developer: &quot;Create a PR&quot;
</code></pre>
<h3>What Claude Code Does (Automatically)</h3>
<ol>
<li>✅ Extracts the ticket number from the branch name</li>
<li>✅ Retrieves the ticket title via Jira API</li>
<li>✅ Automatically generates the PR title (<code>JIRAKEY-1234: Standardizing Claude Code Operation Rules</code>)</li>
<li>✅ Automatically determines the base branch (develop or project branch)</li>
<li>✅ Automatically selects the appropriate PR template</li>
<li>✅ Executes PR creation</li>
</ol>
<p><strong>The developer only needs to give three instructions. Branch creation, commit execution, and PR creation are all handled automatically by Claude Code.</strong></p>
<h2>Implementation: CLAUDE.md — A &quot;Rulebook for AI to Read&quot;</h2>
<p>All automation is achieved through rules written in a document called <strong>CLAUDE.md</strong>.</p>
<pre><code class="language-markdown">### Branch Naming Conventions
Project base branch: `feature/project-name`
  - Example: `feature/simulation`
  - Base branch: `develop`

Feature branch (under project): `feature/JIRAKEY-ticket-number/description`
  - Example: `feature/JIRAKEY-1234/add_simulation_list`
  - Base branch: `feature/project-name`

Regular feature branch (outside project): `feature/JIRAKEY-ticket-number/description`
  - Example: `feature/JIRAKEY-1234/fix_bug`
  - Base branch: `develop`

For parent-child tickets:
  - Parent branch: `feature/JIRAKEY-parent-ticket-number/develop`
  - Child branch: `feature/JIRAKEY-parent-ticket-number/JIRAKEY-child-ticket-number/description`

Relationship Between Epics and Project Base Branches

- Epic determination: If `parent.fields.issuetype.name` of a Jira ticket is &quot;Epic&quot;, that ticket belongs to a project
- Important: When creating branches for tasks under an epic, always confirm the project base branch name with the user

### Commit Message Format

- Required format: `:emoji: JIRAKEY-ticket-number: subject`
- Refer to `.commit_template` for emoji shortcodes

- Commit examples:
:bug: JIRAKEY-1234: Fix crash during login
:sparkles: JIRAKEY-2345: Add user profile image upload feature
:robot: JIRAKEY-3456: Add tests for login component

### PR Creation Rules

- Title format:
  - Project base branch → develop: `:m: JIRAKEY-ticket-number: ticket-title`
  - Parent branch → develop: `:m: JIRAKEY-parent-ticket-number: ticket-title`
  - Regular feature branch → develop: `JIRAKEY-ticket-number: ticket-title`
  - Other PRs: `JIRAKEY-ticket-number: ticket-title`

- Template usage:
  - PRs with `:m:`: `.github/for_dev_template.md`
  - PRs without `:m:`: `.github/pull_request_template.md`
</code></pre>
<p><strong>That&#39;s it.</strong> No code changes whatsoever.</p>
<h2>Actual Operation Flow</h2>
<pre><code class="language-mermaid">sequenceDiagram
    actor Developer
    participant Claude Code
    participant Jira API
    participant git
    participant gh

    Note over Developer,gh: Branch Creation Flow
    Developer-&gt;&gt;Claude Code: &quot;Create a branch for JIRAKEY-1234&quot;
    Claude Code-&gt;&gt;Jira API: Retrieve ticket information
    Jira API--&gt;&gt;Claude Code: Title, epic information, etc.
    Claude Code-&gt;&gt;Claude Code: Generate branch name&lt;br/&gt;(feature/JIRAKEY-1234/update_claude_docs)
    Claude Code-&gt;&gt;git: Execute git checkout -b
    Claude Code--&gt;&gt;Developer: Branch creation complete

    Note over Developer,gh: Commit Flow
    Developer-&gt;&gt;Claude Code: After code changes, &quot;Commit&quot;
    Claude Code-&gt;&gt;Claude Code: Analyze changes
    Claude Code-&gt;&gt;Claude Code: Auto-generate message in :pencil: format
    Claude Code-&gt;&gt;git: Execute git commit
    Claude Code--&gt;&gt;Developer: Commit complete

    Note over Developer,gh: PR Creation Flow
    Developer-&gt;&gt;Claude Code: &quot;Create a PR&quot;
    Claude Code-&gt;&gt;Claude Code: Determine base branch (develop)
    Claude Code-&gt;&gt;Claude Code: Generate PR title&lt;br/&gt;(JIRAKEY-1234: ticket-title)
    Claude Code-&gt;&gt;Claude Code: Select template&lt;br/&gt;(.github/pull_request_template.md)
    Claude Code-&gt;&gt;gh: Execute gh pr create
    gh--&gt;&gt;Claude Code: PR URL
    Claude Code--&gt;&gt;Developer: PR creation complete (with URL)
</code></pre>
<h2>Impact: The Value Gained from &quot;No Thinking&quot;</h2>
<h3>Dramatic Reduction in Cognitive Load</h3>
<ul>
<li>✅ Creating branch names</li>
<li>✅ Checking the base branch</li>
<li>✅ Remembering commit message formats</li>
<li>✅ Copying and pasting PR titles from tickets</li>
<li>✅ Selecting PR templates</li>
</ul>
<p>→ <strong>Everything is completed by &quot;just providing the ticket number&quot;</strong></p>
<h3>Ensuring Consistency</h3>
<ul>
<li>Branch names, commit messages, and PR titles are 100% compliant with project rules</li>
<li>The hassle of reviewers pointing out &quot;this doesn&#39;t follow the naming convention&quot; has disappeared</li>
</ul>
<h3>Reduced Onboarding Time</h3>
<ul>
<li>New team members don’t need to worry about memorizing the branch naming rules.&quot;</li>
<li>Instead of &quot;look at CLAUDE.md&quot;, it&#39;s just &quot;ask Claude Code&quot;</li>
</ul>
<h3>Improved Development Speed</h3>
<ul>
<li>The back-and-forth of &quot;opening Jira and copying the ticket title&quot; has disappeared</li>
<li>Fewer decisions make it <strong>easier to maintain flow state</strong></li>
</ul>
<h2>Future Outlook</h2>
<h3>Context Window Optimization Through Sub-agent Utilization</h3>
<p>In the current implementation, there is an issue where using Atlassian MCP consumes the context window. As a solution, we are considering leveraging <strong>sub-agents</strong> for hierarchical task distribution.</p>
<h4>What Are Sub-agents?</h4>
<p>Claude Code sub-agents are AI assistants specialized for specific tasks, with <strong>independent context windows</strong>.
This enables:</p>
<ul>
<li>✅ Not polluting the main agent&#39;s context</li>
<li>✅ Efficient processing of specialized tasks</li>
<li>✅ Separating bulk information retrieval and processing</li>
</ul>
<h4>Implementation Plan: Three Specialized Sub-agents</h4>
<h5>1. <strong>Jira Information Retrieval Sub-agent</strong> (<code>jira-researcher</code>)</h5>
<pre><code class="language-markdown">**Role**:
- Retrieve ticket information via Atlassian MCP
- Extract only necessary information (ticket number, title, epic, status)
</code></pre>
<h5>2. <strong>Branch Strategy Determination Sub-agent</strong> (<code>branch-strategist</code>)</h5>
<pre><code class="language-markdown">**Role**:
- Generate branch names from ticket information
- Determine parent-child ticket relationships
- Decide base branch from branching patterns
</code></pre>
<h5>3. <strong>PR Creation Sub-agent</strong> (<code>pr-creator</code>)</h5>
<pre><code class="language-markdown">**Role**:
- Execute PR creation branching logic
- Select appropriate templates
</code></pre>
<h2>Conclusion: AI Assistants Enable &quot;No-Thinking Development&quot;</h2>
<p><strong>&quot;Just provide the ticket number, and branch creation, commits, and PR creation are all completed.&quot;</strong></p>
<p>This was achieved with just CLAUDE.md — a &quot;document that AI can read&quot; — and MCP integration. Zero code changes. Zero impact on existing systems.</p>
<p>The key point is that we <strong>clearly identified what developers don&#39;t have to think about and delegated it to AI</strong>.</p>
<p>By evolving AI assistants from &quot;code completion tools&quot; to &quot;partners for the entire development flow&quot;, we can realize a world where developers can <strong>focus solely on solving real problems</strong>.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Claude Code で JiraチケットからPRまでを完全自動化した話 〜考えないでコミット・PRできる世界〜]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-05-claude-code-jira-automation/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-05-claude-code-jira-automation/</guid>
            <pubDate>Fri, 05 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Claude CodeとAtlassian MCPを活用し、Jiraチケットからプルリクエスト作成までの開発フローを完全自動化した事例を紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の5日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>KINTO開発部 FEチーム では、React/Next.jsを用いたフロントエンド開発を行っています。
開発タスクの管理にはJiraを使用しており、チケットベースでの開発フローを採用しています。</p>
<p>チーム規模が拡大する中で、「ブランチ名の命名規則」「コミットメッセージの形式」「PRテンプレートの選択」といった <strong>開発フロー上の統一と認知コスト</strong> が課題と感じていました。</p>
<p>この記事では、Claude Code と Atlassian MCP を組み合わせることで、これらの「考えなくてもいいこと」を自動化し、開発者が本質的な問題解決に集中できる環境をどう構築したかを紹介します。</p>
<h2>技術スタック</h2>
<ul>
<li><strong>Claude Code</strong>: AI駆動の開発アシスタント</li>
<li><strong>Atlassian MCP</strong>: Jira/ConfluenceとのAPI連携（チケット情報の自動取得）</li>
<li><strong>GitHub CLI (gh)</strong>: PR操作の自動化</li>
<li><strong>CLAUDE.md</strong>: プロジェクト固有のルール定義</li>
</ul>
<h2>背景・課題：開発フローの「認知負荷」問題</h2>
<p>開発者が本来集中すべきはコードの実装です。
しかし、実際の開発では以下のような <strong>「本質的でない作業」</strong> に認知リソースが奪われていました:</p>
<ul>
<li>「このチケット番号なんだっけ？Jira開いて確認するか...」</li>
<li>「ブランチ名どうするんだっけ？<code>feature/JIRAKEY-1234/</code>の後は何を書けばいい？」</li>
<li>「このブランチ、developから切るんだっけ？プロジェクトブランチから切るんだっけ？」</li>
<li>「コミットメッセージの絵文字、<code>:sparkles:</code>と<code>:wrench:</code>どっちだっけ？」</li>
<li>「PRのタイトルは<code>:m:</code>付けるんだっけ？付けないんだっけ？」</li>
<li>「PRテンプレートどっち使うんだっけ？<code>for_dev.md</code>？デフォルト？」</li>
</ul>
<p>これらは些細に見えますが、 <strong>1日に何度も発生する意思決定</strong> です。積み重なると開発者の集中力を大きく削ぎます。</p>
<h2>解決策：「考えない」開発フローの実現</h2>
<p><strong>「チケット番号を伝えるだけで、あとは全部自動化する」</strong></p>
<p>これを実現するために、Claude Code と Atlassian MCP を組み合わせました。</p>
<h3>開発者がやること</h3>
<pre><code>開発者: 「JIRAKEY-1234のブランチを作成して」
</code></pre>
<h3>Claude Codeがやること（自動）</h3>
<ol>
<li>✅ Jira APIでチケット情報を取得</li>
<li>✅ エピック判定でプロジェクト所属を確認</li>
<li>✅ 適切なブランチ名を自動生成（<code>feature/JIRAKEY-1234/update_claude_docs</code>）</li>
<li>✅ 適切なベースブランチを自動判定（develop or プロジェクトブランチ）</li>
<li>✅ ブランチ作成</li>
</ol>
<hr>
<h3>開発者がやること</h3>
<pre><code>開発者: 「コミットして」
</code></pre>
<h3>Claude Codeがやること（自動）</h3>
<ol>
<li>✅ 変更内容を解析</li>
<li>✅ 適切な絵文字ショートコードを選択（<code>:pencil:</code>, <code>:bug:</code>, <code>:sparkles:</code>など）</li>
<li>✅ コミット実行</li>
</ol>
<hr>
<h3>開発者がやること</h3>
<pre><code>開発者: 「PRを作成して」
</code></pre>
<h3>Claude Codeがやること（自動）</h3>
<ol>
<li>✅ ブランチ名からチケット番号を抽出</li>
<li>✅ Jira APIでチケット件名を取得</li>
<li>✅ PRタイトルを自動生成（<code>JIRAKEY-1234: Claude Codeの運用ルールの標準化</code>）</li>
<li>✅ ベースブランチを自動判定（develop or プロジェクトブランチ）</li>
<li>✅ 適切なPRテンプレートを自動選択</li>
<li>✅ PR作成実行</li>
</ol>
<p><strong>開発者がやることは3つの指示を出すだけ。ブランチ作成、コミット実行、PR作成実行は、すべてClaude Codeが自動で行います。</strong></p>
<h2>実装方法：CLAUDE.mdという「AIが読むルールブック」</h2>
<p>すべての自動化は <strong>CLAUDE.md</strong> というドキュメントに記述されたルールで実現されています。</p>
<pre><code class="language-markdown">### ブランチ命名規則
プロジェクトベースブランチ: `feature/プロジェクト名`
  - 例: `feature/simulation`
  - ベースブランチ: `develop`

機能ブランチ（プロジェクト配下）: `feature/JIRAKEY-チケット番号/説明`
  - 例: `feature/JIRAKEY-1234/add_simulation_list`
  - ベースブランチ: `feature/プロジェクト名`

通常の機能ブランチ（プロジェクト外）: `feature/JIRAKEY-チケット番号/説明`
  - 例: `feature/JIRAKEY-1234/fix_bug`
  - ベースブランチ: `develop`

親子チケットの場合:
  - 親ブランチ: `feature/JIRAKEY-親チケット番号/develop`
  - 子ブランチ: `feature/JIRAKEY-親チケット番号/JIRAKEY-子チケット番号/説明`

エピックとプロジェクトベースブランチの関係

- エピック判定: Jiraチケットの `parent.fields.issuetype.name` が「エピック」の場合、そのチケットはプロジェクトに属する
- 重要: エピック配下のタスクのブランチ作成時は、必ずユーザーにプロジェクトベースブランチ名を確認すること

### コミットメッセージフォーマット

- 必須形式: `:emoji: JIRAKEY-チケット番号: サブジェクト`
- 絵文字ショートコードは`.commit_template`を参照

- コミット例:
:bug: JIRAKEY-1234: ログイン時のクラッシュを修正
:sparkles: JIRAKEY-2345: ユーザープロフィール画像アップロード機能を追加
:robot: JIRAKEY-3456: ログインコンポーネントのテストを追加

### PR作成ルール

- タイトル形式:
  - プロジェクトベースブランチ → develop: `:m: JIRAKEY-チケット番号: チケット件名`
  - 親ブランチ → develop: `:m: JIRAKEY-親チケット番号: チケット件名`
  - 通常の機能ブランチ → develop: `JIRAKEY-チケット番号: チケット件名`
  - その他の PR: `JIRAKEY-チケット番号: チケット件名`

- テンプレート使用:
  - `:m:` 付きPR: `.github/for_dev_template.md`
  - `:m:` なしPR: `.github/pull_request_template.md`
</code></pre>
<p><strong>これだけ</strong> です。コード変更は一切ありません。</p>
<h2>実際の動作フロー</h2>
<pre><code class="language-mermaid">sequenceDiagram
    actor 開発者
    participant Claude Code
    participant Jira API
    participant git
    participant gh

    Note over 開発者,gh: ブランチ作成フロー
    開発者-&gt;&gt;Claude Code: 「JIRAKEY-1234のブランチを作成して」
    Claude Code-&gt;&gt;Jira API: チケット情報取得
    Jira API--&gt;&gt;Claude Code: 件名、エピック情報など
    Claude Code-&gt;&gt;Claude Code: ブランチ名生成&lt;br/&gt;(feature/JIRAKEY-1234/update_claude_docs)
    Claude Code-&gt;&gt;git: git checkout -b 実行
    Claude Code--&gt;&gt;開発者: ブランチ作成完了

    Note over 開発者,gh: コミットフロー
    開発者-&gt;&gt;Claude Code: コード変更後「コミットして」
    Claude Code-&gt;&gt;Claude Code: 変更内容を分析
    Claude Code-&gt;&gt;Claude Code: :pencil:形式でメッセージ自動生成
    Claude Code-&gt;&gt;git: git commit実行
    Claude Code--&gt;&gt;開発者: コミット完了

    Note over 開発者,gh: PR作成フロー
    開発者-&gt;&gt;Claude Code: 「PRを作成して」
    Claude Code-&gt;&gt;Claude Code: ベースブランチ判定(develop)
    Claude Code-&gt;&gt;Claude Code: PRタイトル生成&lt;br/&gt;(JIRAKEY-1234: チケット件名)
    Claude Code-&gt;&gt;Claude Code: テンプレート選択&lt;br/&gt;(.github/pull_request_template.md)
    Claude Code-&gt;&gt;gh: gh pr create実行
    gh--&gt;&gt;Claude Code: PR URL
    Claude Code--&gt;&gt;開発者: PR作成完了(URL付き)
</code></pre>
<h2>導入効果：「考えない」ことで得られた価値</h2>
<h3>認知負荷の劇的な削減</h3>
<ul>
<li>✅ ブランチ名を考える</li>
<li>✅ ベースブランチを確認する</li>
<li>✅ コミットメッセージの形式を思い出す</li>
<li>✅ PRタイトルをチケットからコピペする</li>
<li>✅ PRテンプレートを選択する</li>
</ul>
<p>→ <strong>「チケット番号を伝えるだけ」で完結</strong></p>
<h3>一貫性の担保</h3>
<ul>
<li>ブランチ名・コミットメッセージ・PRタイトルがプロジェクトルールに100%準拠</li>
<li>レビュワーが「これ命名規則違う」と指摘する手間が消滅</li>
</ul>
<h3>オンボーディング時間の短縮</h3>
<ul>
<li>新規メンバーが「ブランチ名のルール覚えなきゃ...」と悩む必要がない</li>
<li>「CLAUDE.mdを見て」ではなく「Claude Codeに聞いて」でOK</li>
</ul>
<h3>開発速度の向上</h3>
<ul>
<li>「Jira開いてチケット件名コピー」という往復が消滅</li>
<li>意思決定の回数が減ることで<strong>フロー状態を維持しやすい</strong></li>
</ul>
<h2>今後の展望</h2>
<h3>サブエージェント活用によるコンテキストウィンドウ最適化</h3>
<p>現在の実装では、Atlassian MCPを使用するとコンテキストウィンドウが圧迫される課題があります。この解決策として、 <strong>サブエージェント</strong> を活用した階層的なタスク分散を検討しています。</p>
<h4>サブエージェントとは?</h4>
<p>Claude Code のサブエージェントは、特定のタスクに特化したAIアシスタントで、<strong>独立したコンテキストウィンドウ</strong> を持ちます。
これにより</p>
<ul>
<li>✅ メインエージェントのコンテキストを汚染しない</li>
<li>✅ 専門的なタスクを効率的に処理</li>
<li>✅ 大量の情報取得と処理を分離できる</li>
</ul>
<h4>実装計画: 3つの専門サブエージェント</h4>
<h5>1. <strong>Jira情報取得サブエージェント</strong> (<code>jira-researcher</code>)</h5>
<pre><code class="language-markdown">**役割**:
- Atlassian MCPでチケット情報を取得
- 必要な情報のみを抽出(チケット番号、件名、エピック、ステータス)
</code></pre>
<h5>2. <strong>ブランチ戦略判定サブエージェント</strong> (<code>branch-strategist</code>)</h5>
<pre><code class="language-markdown">**役割**:
- チケット情報からブランチ名を生成
- 親子チケット関係を判定
- 分岐パターンからベースブランチを決定
</code></pre>
<h5>3. <strong>PR作成サブエージェント</strong> (<code>pr-creator</code>)</h5>
<pre><code class="language-markdown">**役割**:
- PR作成の分岐判定を実行
- 適切なテンプレート選択
</code></pre>
<h2>まとめ：AIアシスタントは「考えない開発」を実現する</h2>
<p><strong>「チケット番号を伝えるだけで、ブランチ作成・コミット・PR作成が完了する」</strong></p>
<p>これを実現したのは、CLAUDE.mdという「AIが読めるドキュメント」とMCP連携だけです。コード変更はゼロ。既存システムへの影響もゼロ。</p>
<p>重要なのは、 <strong>開発者が「考えなくていいこと」を明確にし、AIに任せた</strong> 点です。</p>
<p>AIアシスタントを「コード補完ツール」から「開発フロー全体のパートナー」に進化させることで、開発者は <strong>本質的な問題解決にだけ集中できる</strong> 世界が実現できます。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Building an AI Agent with GitHub Copilot]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-04-Building_AI_Agent_with_GitHub_Copilot-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-04-Building_AI_Agent_with_GitHub_Copilot-en/</guid>
            <pubDate>Thu, 04 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Building an AI Agent with GitHub Copilot]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello. I&#39;m Yamada, and I work on developing and operating internal tools at the Platform Engineering Team, Platform Group of KINTO Technologies.</p>
<p>Please also check out my previous articles on Spring AI and Text-to-SQL!</p>
<p><a href="https://blog.kinto-technologies.com/posts/2025-06-11-springAI/">https://blog.kinto-technologies.com/posts/2025-06-11-springAI/</a></p>
<p><a href="https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/">https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/</a></p>
<p>In this article, I&#39;d like to share how I built an AI Agent that automatically analyzes and gathers AWS resource dependencies for products built on AWS, using GitHub Copilot.</p>
<h2>Background and Issues</h2>
<p>The Platform Engineering Team develops and operates two internal tools: Configuration Management Database (CMDB) and an incident management tool (Incident Manager).</p>
<p>CMDB is a configuration management database system that centrally manages configuration information for internal products. It has various features including managing product owners and teams, vulnerability information management. One of these particular features is managing ARN information for AWS resources (ECS, RDS, ALB, CloudFront, etc.) associated with products.</p>
<p>For Incident Manager, there was a requirement to <strong>visualize the topology information (system architecture diagram) of the product where an incident occurred and highlight the root cause</strong> to help us promptly identify it and recover the system after the incident.</p>
<p>However, simply having AWS resource ARN information was insufficient to visualize topology information—we needed to understand <strong>the dependencies between resources (e.g., CloudFront -&gt; ALB -&gt; ECS -&gt; RDS)</strong>.</p>
<p>Previously, we needed to manually configure this dependency information, which led to the following issues:</p>
<ul>
<li>Manual updates to dependencies were required every time when new resources were added</li>
<li>Complex system architectures causes the difficulty in understanding dependencies</li>
<li>Human errors led to missing or incorrect dependency configurations</li>
</ul>
<h2>Solution Approach</h2>
<p>To solve these issues, I decided to <strong>build an AI Agent that automatically analyzes and gathers AWS resource dependencies using the ARN information managed by CMDB</strong>.</p>
<p>After interactions with GitHub Copilot to proceed with the implementation on a trial-and-error basis, I completed an AI Agent with the following capabilities:</p>
<ul>
<li>Automatically analyzes dependencies between AWS resources, based on ARN information retrieved from CMDB</li>
<li>Calls multiple AWS APIs to infer connection relationships from security groups and network configurations</li>
<li>Saves gathered node (AWS resource) and edge (dependency) information to the database</li>
<li>Used for showing a topology diagram when an incident occurs in Incident Manager</li>
</ul>
<h2>Technology Stack</h2>
<ul>
<li>Development support tool: GitHub Copilot (Agent mode - Claude Sonnet 4.5)</li>
<li>AI Agent Framework: LangGraph</li>
<li>LLM: Amazon Bedrock (Claude Sonnet 4.5)</li>
<li>Language: Python 3.12</li>
<li>Key Libraries:<ul>
<li>LangChain, LangChain-AWS</li>
<li>boto3 (AWS SDK for Python)</li>
</ul>
</li>
</ul>
<h2>AI Agent Development Process</h2>
<h3>1. Initial Prompt</h3>
<p>First, I asked GitHub Copilot to design the AI Agent with the following prompt, which is partially abbreviated and edited.</p>
<pre><code>Using the AWS resource ARN information retrieved from CMDB&#39;s ARN management table,
implement an AI Agent that automatically analyzes and gathers dependencies between resources.

Technology Stack:
- LangGraph, LangChain
- Amazon Bedrock (Claude Sonnet 4.5)
- AWS SDK (boto3)
- Python 3.12

Functional Requirements:
- API Endpoint: POST /service_configurations
  - Parameters: sid, environment (both required)
- Search for ARNs in the ARN management table using sid and environment as conditions
- Using the retrieved CloudFront, S3, WAF, ALB, TargetGroup, ECS, RDS, ElastiCache ARN information, shape Nodes and Edges information with LLM and Agent (LangGraph)
- Save gathered information to DB

How to Retrieve Dependencies (Few-shot Examples):

### CloudFront -&gt; S3/ALB Edge Determination Method
1. Get domain name using CloudFront API
   aws cloudfront get-distribution --id {distribution-id}
2. Get Origin from behavior information to determine Edge (relationship)
   - If DomainName contains s3: CloudFront -&gt; S3
   - If DomainName contains ALB: CloudFront -&gt; ALB

### TargetGroup -&gt; ECS Edge Determination Method
1. Get target IP address using elbv2 API
   aws elbv2 describe-target-health --target-group-arn {arn}
2. Match with ECS task IP addresses to create Edge

### ECS -&gt; RDS Edge Determination Method (determined by security permissions, not actual access)
1. Get ENI (Elastic Network Interface) ID from ECS task
   aws ecs describe-tasks --cluster {cluster} --tasks {task-arn}
2. Get ECS task security group ID from ENI
   aws ec2 describe-network-interfaces --network-interface-ids {eni-id}
3. Get RDS security group
   aws rds describe-db-instances --db-instance-identifier {instance-id}
4. Get inbound rules for RDS security group
   aws ec2 describe-security-groups --group-ids {sg-id}
5. Create Edge if ECS security group is included in RDS inbound rules

AWS APIs to be used:
- cloudfront:get_distribution
- cloudfront:list_distributions
- wafv2:list_web_acls
- wafv2:get_web_acl
- wafv2:get_web_acl_for_resource
- elbv2:describe_target_groups
- elbv2:describe_target_health
- elbv2:describe_load_balancers
- elbv2:describe_load_balancer_attributes
- ecs:list_clusters
- ecs:describe_tasks
- ecs:describe_task_definition
- ecs:list_tasks
- ecs:describe_services
- ec2:describe_network_interfaces
- ec2:describe_security_groups
- rds:describe_db_clusters
- rds:describe_db_instances
- elasticache:describe_replication_groups
- elasticache:describe_cache_clusters

Related DDL:
{ARN management table DDL}
{Node information table DDL}
{Edge information table DDL}

Important:
- First understand the current repository structure
- Organize and confirm the plan for the implementation before its start
- Consider extensibility as AWS APIs and Nodes to retrieve may increase
- Keep code simple and straightforward
</code></pre>
<p>Following this prompt, GitHub Copilot first analyzed the entire repository, understood the existing code, and then proposed the following implementation plan, which is partially abbreviated and edited.</p>
<pre><code>(Read files...)

I have understood the repository, so I will summarize the design approach.

Understanding the Repository Structure
The existing project has the following structure:

・FastAPI-based API server
・LLM functionality using **Bedrock (Claude)**
・MySQL database connection functionality (implemented in text_to_sql.py)
・Functionality to call AWS APIs with boto3 (implemented in aws_diagram.py)
・LangChain dependencies (listed in pyproject.toml)

Implementation Approach
1. Architecture Design
Retrieve ARN information from ARN management table (DB)
    ↓
Gather Edges information by calling AWS APIs with LangGraph Agent
    ↓
Save Nodes/Edges information to tables (DB)
    ↓
Return response

2. Module Structure (Newly created files)
・`service_config_router.py`: FastAPI endpoint
・`service_config_agent.py`: Agent implementation using LangGraph
・`aws_tools.py`: AWS API call tool collection
・`service_config_db.py`: DB operations (ARN retrieval, Node/Edge saving)

3. Implementation Phases
・Phase 1 (DB layer): Implement ARN retrieval and Node/Edge saving functions
・Phase 2 (AWS tools layer): Implement CloudFront, ELB, ECS, RDS, etc. API call functions in LangChain Tool format
・Phase 3 (Agent layer): Build LangGraph StateGraph, pass tools to LLM to infer Edge information
・Phase 4 (Router layer): FastAPI endpoint, request validation, and response formatting

4. Key Design Points
・Extensibility: Whitelist approach makes it easy to add new AWS APIs
・Simplicity: Clearer state management with StateGraph and calling each tool using one function per API
・Data flow: Clear flow from API, Agent, AWS APIs, to data save to DB

5. Implementation Code Structure (Pseudocode)
(Code samples)

Is it okay to proceed with implementation based on the above design approach?
Once confirmed, I will start implementation in sequence.
</code></pre>
<p>Since the design approach was not significantly misaligned with what was in my head at this point, I had GitHub Copilot proceed with the implementation.</p>
<h3>2. Iterative Correction Requests</h3>
<p>Due to various factors including basic design I forgot to include in the prompt, detailed design I couldn&#39;t fully communicate, and prompt issues, the generated code didn&#39;t work perfectly on my first implementation attempt. I had to go back and forth with GitHub Copilot multiple times to repeatedly correct the code.</p>
<p>Since I forgot to include the architecture layer separation in the prompt, a large amount of business logic was implemented in the Controller layer.</p>
<p>There were calls to non-existent functions in the Bedrock model invocation process (it was using functions from a different version).</p>
<p>Error handling was insufficient.</p>
<p>Just while I thought it was working, part of Node and Edge information wasn&#39;t being retrieved.</p>
<p>I had to improve the Agent&#39;s prompts...</p>
<p>There were so many issues, but within a few hours, it started working as expected.</p>
<h3>3. Final Completed Code</h3>
<p>GitHub Copilot handled over 90% of the coding. Let me share some key excerpts of what the final code looked like.</p>
<h4>Agent Implementation with LangGraph</h4>
<p>This is the core part of the AI Agent. It defines the processing flow using LangGraph. The definition is partially abbreviated and edited.</p>
<pre><code class="language-python">def create_service_config_agent() -&gt; StateGraph:
    &quot;&quot;&quot;Create system configuration collection Agent&quot;&quot;&quot;
    workflow = StateGraph(AgentState)
    
    # Add nodes
    workflow.add_node(&quot;initialize_nodes&quot;, initialize_nodes)
    workflow.add_node(&quot;collect_edges&quot;, collect_edges_with_llm)
    
    # Set entry point
    workflow.set_entry_point(&quot;initialize_nodes&quot;)
    
    # Conditional branching: collect edges if nodes exist, otherwise end
    workflow.add_conditional_edges(
        &quot;initialize_nodes&quot;,
        should_collect_edges,
        {
            &quot;collect_edges&quot;: &quot;collect_edges&quot;,
            &quot;end&quot;: END
        }
    )
    
    workflow.add_edge(&quot;collect_edges&quot;, END)
    
    return workflow.compile()

def collect_edges_with_llm(state: AgentState) -&gt; AgentState:
    &quot;&quot;&quot;Collect edge information using LLM and tools&quot;&quot;&quot;
    llm = get_llm_for_agent()
    llm_with_tools = llm.bind_tools(AWS_TOOLS)
    
    prompt = f&quot;&quot;&quot;
    You are an expert in analyzing AWS resource dependencies.

    # Task
    From ARN information of the following AWS resources (Nodes), identify and infer the connection relationships (Edges) between resources.
    Understand the characteristics of each AWS service and common architecture patterns, and call appropriate AWS APIs to verify connections.
    
    # Available Nodes
    {nodes_summary}

    # Available Tools
    1. **call_aws_api**: A tool that can call permitted AWS APIs
      - Can retrieve resource details, configurations, and related resources
    
    2. **extract_resource_id_from_arn**: Extract resource ID and other information from ARN
      - Can retrieve parameters (ID, name, etc.) needed for API calls
    
    # Available AWS APIs (no others can be used)
    - cloudfront:get_distribution
    - wafv2:get_web_acl_for_resource
    - elbv2:describe_target_groups
    ...

    # Edge Detection Methods
    Infer and investigate connections between resources from the following perspectives:
    
    ## Common Connection Patterns
    1. **Frontend layer**: CloudFront -&gt; S3/ALB, WAF -&gt; CloudFront/ALB, Route53 domain -&gt; CloudFront
    2. **Load balancer layer**: ALB -&gt; Target Group -&gt; ECS/EC2
    3. **API layer**: API Gateway -&gt; Lambda
    4. **Application layer**: ECS -&gt; RDS/ElastiCache (via security groups), Lambda -&gt; RDS (via security groups)
    5. **Data layer**: RDS, ElastiCache

    ## Connection Detection Approach
    - **Configuration-based**: When resource configuration contains ARNs or IDs of other resources (e.g., CloudFront Origins settings)
    - **Network-based**: When permitted by security group inbound rules (e.g., ECS -&gt; RDS)
    - **Service characteristics**: Connections that can be logically inferred from each AWS service&#39;s role (e.g., TargetGroup -&gt; ECS)

    ## Important Investigation Points
    - **ARN analysis**: First analyze ARN with extract_resource_id_from_arn to identify resource type and required parameters
    - **Incremental investigation**: Don&#39;t call all APIs at once; determine the next needed API based on results
    - **Security groups**: Verify ECS/RDS/ElastiCache connections through security group inbound rules
    - Check if ECS ENI -&gt; Security Group ID -&gt; is included in RDS/ElastiCache SG inbound rules
    - **Error handling**: Continue investigation even if unauthorized APIs or errors are returned
    
    # Few-shot Examples (must be referenced)
    ## Example 1: ECS -&gt; RDS Investigation
    1. Get ECS task ENI: call_aws_api(&quot;ecs&quot;, &quot;describe_tasks&quot;, ...)
    2. Get SG from ENI: call_aws_api(&quot;ec2&quot;, &quot;describe_network_interfaces&quot;, ...)
    3. Get RDS SG: call_aws_api(&quot;rds&quot;, &quot;describe_db_instances&quot;, ...)
    4. Verify SG match -&gt; Create Edge
    ...
    
    # Important Notes
    - Check all resource combinations
    - Continue investigation even if API errors occur
    - Ignore RDS snapshot, parameter group, subnet group, etc.
    - Determine connectivity by security groups

    # Output Format
    When investigation is complete, return results in the following JSON format:
    ```json
    {{
    &quot;nodes&quot;: [
        {{
        &quot;service_name&quot;: &quot;cloudfront&quot;,
        &quot;arn&quot;: &quot;xxx&quot;,
        &quot;resource&quot;: &quot;&quot;
        }}
    ],
    &quot;edges&quot;: [
        {{
        &quot;from_arn&quot;: &quot;xxx&quot;,
        &quot;to_arn&quot;: &quot;xxx&quot;,
        &quot;details&quot;: &quot;xxx&quot;
        }}
    ]
    }}
    ```
    &quot;&quot;&quot;
    
    messages = [HumanMessage(content=prompt)]
    
    try:
        max_iterations = 30  # Maximum iteration count
        edges = []
        
        for iteration in range(max_iterations):
            response = llm_with_tools.invoke(messages)
            messages.append(response)
            
            # Check if there are tool calls
            if hasattr(response, &#39;tool_calls&#39;) and response.tool_calls:
                # Execute tools
                tool_node = ToolNode(AWS_TOOLS)
                tool_results = tool_node.invoke({&quot;messages&quot;: messages})
                
                # Add tool results to messages
                messages.extend(tool_results[&quot;messages&quot;])
                
            else:
                # If no tool calls, process as final response
                try:
                    # Create LLM for structured response
                    structured_llm = llm.with_structured_output(GraphResult)
                    
                    # Add final result summary prompt
                    final_prompt = &quot;&quot;&quot;
                        Investigation complete. Return all discovered edges and new nodes (such as domain names)
                        in JSON format.
                    &quot;&quot;&quot;
                    messages.append(HumanMessage(content=final_prompt))
                    
                    # Get structured response
                    result = structured_llm.invoke(messages)
                    
                    edges = [edge.model_dump() for edge in result.edges]
                    new_nodes = [node.model_dump() for node in result.nodes]
                    
                    # Add new nodes returned by LLM (such as domain names) to existing node list
                    if new_nodes:
                        state[&quot;nodes&quot;].extend(new_nodes)
                    
                except Exception as e:
                    # Fallback processing on error
                    ...

                break
        
        state[&quot;edges&quot;] = edges
        state[&quot;current_step&quot;] = &quot;edges_collected&quot;
        state[&quot;messages&quot;] = messages
        
    except Exception as e:
        ...
        state[&quot;edges&quot;] = []
    
    return state
</code></pre>
<h4>AWS API Call Tools</h4>
<p>These are the tools used by the Agent. A whitelist approach ensures the safety of executable AWS APIs. The tools are partially abbreviated and edited.</p>
<pre><code class="language-python"># Whitelist of allowed AWS APIs
ALLOWED_AWS_APIS: Set[str] = {
    # CloudFront
    &quot;cloudfront:get_distribution&quot;,
    &quot;cloudfront:list_distributions&quot;,
    
    # WAF
    &quot;wafv2:list_web_acls&quot;,
    &quot;wafv2:get_web_acl&quot;,
    &quot;wafv2:get_web_acl_for_resource&quot;,
    ...
}


@tool
def call_aws_api(
    service_name: str,
    method_name: str,
    parameters: Dict[str, Any],
    region: str = &quot;ap-northeast-1&quot;
) -&gt; Dict[str, Any]:
    &quot;&quot;&quot;General AWS API call tool
    
    This tool can only call permitted AWS APIs.
    Call the AWS APIs needed to retrieve Edge information.
    
    Args:
        service_name: AWS service name (lowercase)
            Permitted: &#39;cloudfront&#39;, &#39;wafv2&#39;, &#39;elbv2&#39;, &#39;ecs&#39;, &#39;ec2&#39;, &#39;rds&#39;, &#39;elasticache&#39;, &#39;apigateway&#39;, &#39;lambda&#39;
        method_name: Method name to call (boto3 method name, snake_case)
            Examples: &#39;get_distribution&#39;, &#39;describe_target_health&#39;, &#39;describe_security_groups&#39;
        parameters: Dictionary of parameters to pass to the method
            Examples: {&quot;Id&quot;: &quot;ABC123&quot;} or {&quot;GroupIds&quot;: [&quot;sg-12345&quot;]}
        region: AWS region (default: ap-northeast-1)
    
    Returns:
        Dictionary of API call results
        Returns {&quot;error&quot;: &quot;error message&quot;} in case of error
    
    List of permitted APIs:
        - cloudfront:get_distribution
        - cloudfront:list_distributions
        - wafv2:list_web_acls
        - wafv2:get_web_acl
        - wafv2:get_web_acl_for_resource
        - ...
    
    Examples:
        # Get CloudFront Distribution information
        call_aws_api(
            service_name=&quot;cloudfront&quot;,
            method_name=&quot;get_distribution&quot;,
            parameters={&quot;Id&quot;: &quot;ABC123&quot;}
        )
        
        # Get target group health information
        call_aws_api(
            service_name=&quot;elbv2&quot;,
            method_name=&quot;describe_target_health&quot;,
            parameters={&quot;TargetGroupArn&quot;: &quot;arn:aws:elasticloadbalancing:...&quot;}
        )
        
        # Get security group information
        call_aws_api(
            service_name=&quot;ec2&quot;,
            method_name=&quot;describe_security_groups&quot;,
            parameters={&quot;GroupIds&quot;: [&quot;sg-12345&quot;]}
        )
        
        # Get ECS task information
        call_aws_api(
            service_name=&quot;ecs&quot;,
            method_name=&quot;describe_tasks&quot;,
            parameters={&quot;cluster&quot;: &quot;my-cluster&quot;, &quot;tasks&quot;: [&quot;arn:aws:ecs:...&quot;]}
        )
    &quot;&quot;&quot;
    try:
        # Step 1: Validate if API is in whitelist
        is_valid, error_message = validate_aws_api(service_name, method_name)
        
        if not is_valid:
            return {
                &quot;error&quot;: error_message,
                &quot;error_type&quot;: &quot;unauthorized_api&quot;,
                &quot;allowed_apis&quot;: get_allowed_apis_list()
            }
        
        # Step 2: Get client
        client = get_aws_client(service_name, region)
        
        # Step 3: Check if method exists
        if not hasattr(client, method_name):
            return {&quot;error&quot;: error_msg, &quot;available_methods&quot;: dir(client)}
        
        # Step 4: Get method and execute
        method = getattr(client, method_name)
        response = method(**parameters)
        
        return response
        
    except Exception as e:
        ...
</code></pre>
<p>Here are the key points I focused on in this implementation:</p>
<ul>
<li>Whitelist approach: Prevents LLM from calling arbitrary AWS APIs, ensuring safety</li>
<li>Dynamic API calls: Dynamically executes boto3 methods using Python&#39;s <code>getattr()</code></li>
<li>Detailed docstrings: Includes argument descriptions, usage examples, and permitted API list to allow LLM to understand how to use the tools</li>
<li>Extensibility: Adding new APIs only requires adding them to <code>ALLOWED_AWS_APIS</code></li>
</ul>
<h2>Topology Rendering in Incident Manager</h2>
<p>I created a system topology diagram in Incident Manager using the Node and Edge information gathered by the AI Agent. Here is how it finally looked like:
<img src="/assets/blog/authors/masaki_yamada/20251209/topology.png" alt="topology"></p>
<p>When a system failure occurs, the affected area turns red, helping us to intuitively understand the system architecture and failure location at a glance!</p>
<h2>Thoughts on Implementing with GitHub Copilot</h2>
<h3>Positive Aspects</h3>
<ul>
<li><p>Significant reduction in development time</p>
<ul>
<li>This implementation was completed in about a day. Without GitHub Copilot, I would have needed to start by learning LangGraph, which would have taken at least several weeks.</li>
</ul>
</li>
<li><p>High-quality implementation plan proposals</p>
<ul>
<li>While there&#39;s still room for improvement, following detailed information about what I wanted to achieve and the design in the initial prompt, GitHub Copilot generated the following high-quality implementation plan:<ul>
<li>Proposed a consistent design based on the understanding of the existing repository structure</li>
<li>Proposed a design with both extensibility and simplicity</li>
</ul>
</li>
</ul>
</li>
<li><p>Interactive quality improvement</p>
<ul>
<li>When I pointed out concerns during implementation, corrections were made immediately:<ul>
<li>Architecture improvements</li>
<li>Library version issues</li>
<li>Adding error handling</li>
<li>And more</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3>Challenging Aspects</h3>
<h4>Validation of generated code is essential</h4>
<p>Code generated by generative AI, not just by GitHub Copilot, <strong>requires the person who gave the instructions to take responsibility for reviewing it</strong>. In AI-assisted coding, review takes the most time.</p>
<p>I reviewed the code from the following perspectives:</p>
<ul>
<li>Compliance with existing code conventions: Does it follow the repository&#39;s naming conventions and coding style?</li>
<li>Requirements coverage: Are all specified functional requirements implemented without omissions?</li>
<li>Architecture patterns: Is there appropriate layer separation? Are responsibilities clear?</li>
<li>Implementation appropriateness: Are there more common or simpler implementation methods? Is it unnecessarily complex?</li>
<li>Error handling: Is exception handling properly implemented? Are error messages appropriate?</li>
<li>Operational verification: Does it work as intended when the app is actually started?</li>
</ul>
<h3>Future Plans</h3>
<h4>Adding more Nodes and Edges to gather</h4>
<p>Currently, as an initial trial, I limited the Nodes and Edges retrieval to specific AWS services.</p>
<p>The prompt allows for easy extension as retrieved AWS resources were added, and then the whitelist used for the AWS API call tools includes permitted APIs to retrieve Edge information. Therefore, I plan to gradually increase the number of gathered resources going forward.</p>
<h4>Creating prompt templates</h4>
<p>Since there were oversights in the initial instructions this time, I created a prompt template, which is reusable and helpful to implement new features. By including the following content, I aim to generate higher-quality code from the initial instructions:</p>
<ul>
<li>Feature overview</li>
<li>Technology stack (framework, libraries, language version)</li>
<li>Functional requirements</li>
<li>Architecture requirements (layer structure, error handling, etc.)</li>
<li>Non-functional requirements (extensibility, performance, security)</li>
<li>Confirmation items before implementation (understanding repository structure, checking consistency with existing code, etc.)</li>
<li>Important notes (placed at the end of the prompt with clearly specific rules that must be followed)</li>
</ul>
<h2>Summary</h2>
<p>This time, I implemented an AI Agent that gathers system topology information for about one day using GitHub Copilot.</p>
<p>With the implemented AI Agent, AWS resource dependencies that previously required manual configuration are now automatically gathered, enabling topology visualization when an incident arises in Incident Manager.</p>
<p>I plan to continue actively utilizing generative AI, including GitHub Copilot, to increase development productivity.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[GitHub Copilot を使った AI Agent の構築]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-04-Building_AI_Agent_with_GitHub_Copilot/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-04-Building_AI_Agent_with_GitHub_Copilot/</guid>
            <pubDate>Thu, 04 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[GitHub Copilot を使った AI Agent の構築]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは。KINTOテクノロジーズ プラットフォームグループ Platform Engineeringチームで内製ツールの開発・運用をおこなっている山田です。</p>
<p>過去に書いたSpring AIとText-to-SQLの記事もぜひご覧ください！</p>
<p><a href="https://blog.kinto-technologies.com/posts/2025-06-11-springAI/">https://blog.kinto-technologies.com/posts/2025-06-11-springAI/</a></p>
<p><a href="https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/">https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/</a></p>
<p>今回はGitHub Copilotを活用して、AWS上で構築しているプロダクトの、AWSリソースの依存関係を自動で分析・収集するAI Agentを構築したお話をしたいと思います。</p>
<h2>背景と課題</h2>
<p>Platform Engineeringチームでは、CMDB (Configuration Management Database) とIncident Manager (インシデント管理ツール) という2つの内製ツールを開発・運用しています。</p>
<p>CMDBは構成管理データベースというシステムで、社内プロダクトの構成情報を一元管理しています。CMDBにはプロダクトの担当者や担当チーム、脆弱性情報の管理などさまざまな機能があり、その一つにプロダクトに関連するAWSリソース (ECS、RDS、ALB、CloudFrontなど) のARN情報を管理する機能があります。</p>
<p>Incident Managerでは、インシデント発生時に迅速な原因特定と復旧をサポートするため、<strong>インシデントが発生したプロダクトのトポロジー情報 (システム構成図) と原因箇所を可視化する機能</strong>が求められていました。</p>
<p>しかし、トポロジー情報を可視化するためには、単にAWSリソースのARN情報を持っているだけでは不十分で、<strong>リソース間の依存関係 (例: CloudFront → ALB → ECS → RDS)</strong> を把握する必要がありました。</p>
<p>従来、この依存関係情報は手動で設定する必要があり、以下のような課題がありました。</p>
<ul>
<li>新しいリソースが追加されるたびに手動で依存関係を更新する必要がある</li>
<li>複雑なシステム構成では依存関係の把握が困難</li>
<li>人的ミスによる依存関係の設定漏れや誤り</li>
</ul>
<h2>解決アプローチ</h2>
<p>これらの課題を解決するために、<strong>CMDBが管理しているARN情報を活用して、AWSリソースの依存関係を自動で分析・収集するAI Agentを構築する</strong>ことにしました。</p>
<p>GitHub Copilotと対話しながら実装を進めた結果、以下のような機能を持つAI Agentが完成しました。</p>
<ul>
<li>CMDBから取得したARN情報を起点に、AWSリソース間の依存関係を自動で分析</li>
<li>複数のAWS APIを呼び出して、セキュリティグループやネットワーク設定から接続関係を推論</li>
<li>収集したノード (AWSリソース) とエッジ (依存関係) の情報をデータベースに保存</li>
<li>Incident Managerでインシデント発生時のトポロジー図表示に活用</li>
</ul>
<h2>技術スタック</h2>
<ul>
<li>開発支援ツール: GitHub Copilot (Agentモード - Claude Sonnet 4.5)</li>
<li>AI Agent Framework: LangGraph</li>
<li>LLM: Amazon Bedrock (Claude Sonnet 4.5)</li>
<li>言語: Python 3.12</li>
<li>主要ライブラリ:<ul>
<li>LangChain, LangChain-AWS</li>
<li>boto3 (AWS SDK for Python)</li>
</ul>
</li>
</ul>
<h2>AI Agent構築の流れ</h2>
<h3>1. 最初のプロンプト</h3>
<p>まず初めに、以下のプロンプトでGitHub CopilotにAI Agentを構築するための設計をお願いしました。(一部、省略・編集しています)</p>
<pre><code>CMDBのARN管理テーブルから取得したAWSリソースのARN情報を使って、
リソース間の依存関係を自動で分析・収集するAI Agentを実装してください。

技術スタック:
- LangGraph、LangChain
- Amazon Bedrock (Claude Sonnet 4.5)
- AWS SDK (boto3)
- Python 3.12

機能要件:
- API Endpoint: POST /service_configurations
  - パラメータ: sid, environment (どちらも必須)
- ARN管理テーブルからsid、environmentを条件にARNを検索
- 取得したCloudFront、S3、WAF、ALB、TargetGroup、ECS、RDS、ElastiCacheのARN情報を使って、Nodes、Edges情報をLLMとAgent (LangGraph) で成形
- 収集した情報をDBに保存

依存関係の取得方法 (Few-shot Examples):

### CloudFront → S3/ALB のEdge判定方法
1. CloudFront APIでドメイン名を取得
   aws cloudfront get-distribution --id {distribution-id}
2. ビヘイビア情報からOriginを取得してEdge(紐づき)を判定
   - DomainNameにs3がある場合: CloudFront → S3
   - DomainNameにALBがある場合: CloudFront → ALB

### TargetGroup → ECS のEdge判定方法
1. elbv2のAPIでターゲットのIPアドレスを取得
   aws elbv2 describe-target-health --target-group-arn {arn}
2. ECS APIでタスクのIPアドレスと照合してEdgeを作成

### ECS → RDS のEdge判定方法 (実際のアクセスではなく、セキュリティの許可で判定)
1. ECSタスクからENI (Elastic Network Interface) のIDを取得
   aws ecs describe-tasks --cluster {cluster} --tasks {task-arn}
2. ENIからECSタスクのセキュリティグループIDを取得
   aws ec2 describe-network-interfaces --network-interface-ids {eni-id}
3. RDSのセキュリティグループを取得
   aws rds describe-db-instances --db-instance-identifier {instance-id}
4. RDSセキュリティグループのインバウンドルールを取得
   aws ec2 describe-security-groups --group-ids {sg-id}
5. ECSのセキュリティグループがRDSのインバウンドルールに含まれていればEdge作成

使用予定のAWS API:
- cloudfront:get_distribution
- cloudfront:list_distributions
- wafv2:list_web_acls
- wafv2:get_web_acl
- wafv2:get_web_acl_for_resource
- elbv2:describe_target_groups
- elbv2:describe_target_health
- elbv2:describe_load_balancers
- elbv2:describe_load_balancer_attributes
- ecs:list_clusters
- ecs:describe_tasks
- ecs:describe_task_definition
- ecs:list_tasks
- ecs:describe_services
- ec2:describe_network_interfaces
- ec2:describe_security_groups
- rds:describe_db_clusters
- rds:describe_db_instances
- elasticache:describe_replication_groups
- elasticache:describe_cache_clusters

関連するDDL:
{ARN管理テーブルのDDL}
{Node情報テーブルのDDL}
{Edge情報テーブルのDDL}

重要:
- まずは現状のリポジトリ構成を理解する
- 実装を始める前に整理して、実装方針を確認してから実装を行う
- 呼び出すAWS APIや取得するNodeは増える可能性があるため、拡張性を考慮する
- コードは簡単でシンプルな内容にする
</code></pre>
<p>このプロンプトに対して、GitHub Copilotはまずリポジトリ全体を分析し、既存のコードを理解した上で以下の実装方針を提案してくれました。(一部、省略・編集しています)</p>
<pre><code>(Read files...)

リポジトリの理解ができましたので、設計方針をまとめます。

リポジトリ構造の理解
既存のプロジェクトは以下の構成になっています：

・FastAPIベースのAPIサーバー
・**Bedrock (Claude)**を使用したLLM機能
・MySQLデータベース接続機能（text_to_sql.pyで実装例あり）
・boto3でAWS APIを呼び出す機能（aws_diagram.pyで実装例あり）
・LangChainの依存関係あり（pyproject.tomlに記載）

実装方針
1. アーキテクチャ設計
ARN管理テーブルからARN情報取得 (DB)
    ↓
LangGraph Agent でAWS APIを呼び出し、Edges情報を収集
    ↓
Nodes/Edges情報をテーブルに保存 (DB)
    ↓
レスポンス返却

2. モジュール構成 (新規作成ファイル)
・`service_config_router.py`: FastAPIエンドポイント
・`service_config_agent.py`: LangGraphによるAgent実装
・`aws_tools.py`: AWS API呼び出しツール群
・`service_config_db.py`: DB操作 (ARN取得、Node/Edge保存)

3. 実装フェーズ
・Phase 1 (DB層): ARN取得とNode/Edge保存関数の実装
・Phase 2 (AWSツール層): CloudFront、ELB、ECS、RDS等のAPI呼び出し関数をLangChain Tool形式で実装
・Phase 3 (Agent層): LangGraphのStateGraphを構築し、LLMにツールを渡してEdge情報を推論
・Phase 4 (Router層): FastAPIエンドポイントとリクエスト検証・レスポンス整形

4. 設計の重要ポイント
・拡張性: ホワイトリスト方式で新しいAWS APIを追加しやすく
・シンプルさ: StateGraphで状態管理を明確に、各ツールは1関数=1API呼び出し
・データフロー: API → Agent → AWS APIs → DB保存という明確な流れ

5. 実装コード構造（擬似コード）
(コードのサンプル)

この設計方針で実装を進めてよろしいでしょうか？
確認いただけましたら、順次実装を開始します。
</code></pre>
<p>この設計時点で大きく方針がずれていなかったため、このままGitHub Copilotに実装してもらいました。</p>
<h3>2. 繰り返し修正依頼</h3>
<p>プロンプトに書き忘れた基本設計、すべて伝えきれていない詳細設計、プロンプトの問題など様々な要因はありますが、生成されたコードは一発で完璧に動くことはなく、GitHub Copilotと何度もやりとりをしてコードの修正を繰り返しました。</p>
<p>アーキテクチャのレイヤー分けをプロンプトに書き忘れたため、Controller層に大量にビジネスロジックを実装されてしまったり、</p>
<p>Bedrockのモデル呼び出し処理で存在しない関数呼び出しをしていたり (別バージョンの関数を利用していた)、</p>
<p>エラーハンドリングが足りなかったり、</p>
<p>動いたと思ったら取得できていないNodeとEdgeの情報があったり、</p>
<p>Agentのプロンプトの改善をしたり...</p>
<p>たくさんの問題がありましたが、数時間で想定通りの動作をするようになりました。</p>
<h3>3. 最終的に完成したコード</h3>
<p>9割以上をGitHub Copilotにコーディングをしてもらって最終的にどんなコードになったのか、一部重要な部分を抜粋してご紹介しようと思います。</p>
<h4>LangGraphによるAgent実装</h4>
<p>AI Agentの核心部分です。LangGraphを使って処理フローを定義しています。(一部、省略・編集しています)</p>
<pre><code class="language-python">def create_service_config_agent() -&gt; StateGraph:
    &quot;&quot;&quot;システム構成収集Agentを作成&quot;&quot;&quot;
    workflow = StateGraph(AgentState)
    
    # ノードを追加
    workflow.add_node(&quot;initialize_nodes&quot;, initialize_nodes)
    workflow.add_node(&quot;collect_edges&quot;, collect_edges_with_llm)
    
    # エントリーポイントを設定
    workflow.set_entry_point(&quot;initialize_nodes&quot;)
    
    # 条件分岐: ノードが存在すればEdge収集、なければ終了
    workflow.add_conditional_edges(
        &quot;initialize_nodes&quot;,
        should_collect_edges,
        {
            &quot;collect_edges&quot;: &quot;collect_edges&quot;,
            &quot;end&quot;: END
        }
    )
    
    workflow.add_edge(&quot;collect_edges&quot;, END)
    
    return workflow.compile()

def collect_edges_with_llm(state: AgentState) -&gt; AgentState:
    &quot;&quot;&quot;LLMとツールを使用してエッジ情報を収集&quot;&quot;&quot;
    llm = get_llm_for_agent()
    llm_with_tools = llm.bind_tools(AWS_TOOLS)
    
    prompt = f&quot;&quot;&quot;
    あなたはAWSリソースの依存関係を分析するエキスパートです。

    # タスク
    以下のAWSリソース (Nodes) のARNから、リソース間の接続関係 (Edges) を推測・特定してください。
    各AWSサービスの特性と一般的なアーキテクチャパターンを理解し、適切なAWS APIを呼び出して接続を確認してください。
    
    # 利用可能なNodes
    {nodes_summary}

    # 利用可能なツール
    1. **call_aws_api**: 許可されたAWS APIを呼び出せるツール
      - リソースの詳細情報、設定、関連リソースを取得できます
    
    2. **extract_resource_id_from_arn**: ARNからリソースIDやその他の情報を抽出
      - API呼び出しに必要なパラメータ (ID、名前など) を取得できます
    
    # 使用可能なAWS API (これ以外は使用できません)
    - cloudfront:get_distribution
    - wafv2:get_web_acl_for_resource
    - elbv2:describe_target_groups
    ...

    # Edge検出の方法
    以下の観点から、リソース間の接続を推測・調査してください：
    
    ## 一般的な接続パターン
    1. **フロントエンド層**: CloudFront → S3/ALB、WAF → CloudFront/ALB、Route53ドメイン → CloudFront
    2. **ロードバランサー層**: ALB → ターゲットグループ → ECS/EC2
    3. **API層**: API Gateway → Lambda
    4. **アプリケーション層**: ECS → RDS/ElastiCache (セキュリティグループ経由) 、Lambda → RDS (セキュリティグループ経由) 
    5. **データ層**: RDS、ElastiCache

    ## 接続検出の考え方
    - **設定ベース**: リソースの設定に他のリソースのARNやIDが含まれている場合 (例: CloudFrontのOrigins設定) 
    - **ネットワークベース**: セキュリティグループのインバウンドルールで許可されている場合 (例: ECS → RDS) 
    - **サービス特性**: 各AWSサービスの役割から論理的に推測できる接続 (例: TargetGroup → ECS) 

    ## 重要な調査ポイント
    - **ARNの分析**: まずextract_resource_id_from_arnでARNを解析し、リソースタイプと必要なパラメータを特定
    - **段階的調査**: 一度に全APIを呼ばず、結果を見ながら次に必要なAPIを判断
    - **セキュリティグループ**: ECS/RDS/ElastiCacheの接続はセキュリティグループのインバウンドルールで確認
    - ECSのENI → セキュリティグループID → RDS/ElastiCacheのSGインバウンドルールに含まれるかチェック
    - **エラー対応**: 許可されていないAPIやエラーが返っても、次の調査を継続
    
    # Few-shot Examples (必ず参考にすること)
    ## Example 1: ECS → RDS の調査
    1. ECSタスクのENIを取得: call_aws_api(&quot;ecs&quot;, &quot;describe_tasks&quot;, ...)
    2. ENIからSGを取得: call_aws_api(&quot;ec2&quot;, &quot;describe_network_interfaces&quot;, ...)
    3. RDSのSGを取得: call_aws_api(&quot;rds&quot;, &quot;describe_db_instances&quot;, ...)
    4. SG一致確認 → Edge作成
    ...
    
    # 重要な注意事項
    - すべてのリソースの組み合わせをチェック
    - APIエラーが出ても次の調査を継続
    - RDSの snapshot, parameter group, subnet group 等は無視
    - セキュリティグループで接続可能性を判断

    # 出力形式
    調査が完了したら、以下のJSON形式で結果を返してください：
    ```json
    {{
    &quot;nodes&quot;: [
        {{
        &quot;service_name&quot;: &quot;cloudfront&quot;,
        &quot;arn&quot;: &quot;xxx&quot;,
        &quot;resource&quot;: &quot;&quot;
        }}
    ],
    &quot;edges&quot;: [
        {{
        &quot;from_arn&quot;: &quot;xxx&quot;,
        &quot;to_arn&quot;: &quot;xxx&quot;,
        &quot;details&quot;: &quot;xxx&quot;
        }}
    ]
    }}
    ```
    &quot;&quot;&quot;
    
    messages = [HumanMessage(content=prompt)]
    
    try:
        max_iterations = 30  # 最大反復回数
        edges = []
        
        for iteration in range(max_iterations):
            response = llm_with_tools.invoke(messages)
            messages.append(response)
            
            # ツール呼び出しがあるか確認
            if hasattr(response, &#39;tool_calls&#39;) and response.tool_calls:
                # ツールを実行
                tool_node = ToolNode(AWS_TOOLS)
                tool_results = tool_node.invoke({&quot;messages&quot;: messages})
                
                # ツール結果をメッセージに追加
                messages.extend(tool_results[&quot;messages&quot;])
                
            else:
                # ツール呼び出しがない場合、最終レスポンスとして処理
                try:
                    # 構造化レスポンス用のLLMを作成
                    structured_llm = llm.with_structured_output(GraphResult)
                    
                    # 最終結果の要約プロンプトを追加
                    final_prompt = &quot;&quot;&quot;
                        調査が完了しました。発見したすべてのエッジと新しいノード（ドメイン名など）を
                        JSON形式で返してください。
                    &quot;&quot;&quot;
                    messages.append(HumanMessage(content=final_prompt))
                    
                    # 構造化レスポンスを取得
                    result = structured_llm.invoke(messages)
                    
                    edges = [edge.model_dump() for edge in result.edges]
                    new_nodes = [node.model_dump() for node in result.nodes]
                    
                    # LLMが返した新しいノード（ドメイン名など）を既存のノードリストに追加
                    if new_nodes:
                        state[&quot;nodes&quot;].extend(new_nodes)
                    
                except Exception as e:
                    # エラー時のフォールバック処理
                    ...

                break
        
        state[&quot;edges&quot;] = edges
        state[&quot;current_step&quot;] = &quot;edges_collected&quot;
        state[&quot;messages&quot;] = messages
        
    except Exception as e:
        ...
        state[&quot;edges&quot;] = []
    
    return state
</code></pre>
<h4>AWS API呼び出しツール</h4>
<p>Agentが使用するツール群です。ホワイトリスト方式で実行可能なAWS APIの安全性を確保しています。(一部、省略・編集しています)</p>
<pre><code class="language-python"># 許可するAWS APIのホワイトリスト
ALLOWED_AWS_APIS: Set[str] = {
    # CloudFront
    &quot;cloudfront:get_distribution&quot;,
    &quot;cloudfront:list_distributions&quot;,
    
    # WAF
    &quot;wafv2:list_web_acls&quot;,
    &quot;wafv2:get_web_acl&quot;,
    &quot;wafv2:get_web_acl_for_resource&quot;,
    ...
}


@tool
def call_aws_api(
    service_name: str,
    method_name: str,
    parameters: Dict[str, Any],
    region: str = &quot;ap-northeast-1&quot;
) -&gt; Dict[str, Any]:
    &quot;&quot;&quot;汎用的なAWS API呼び出しツール
    
    このツールは許可されたAWS APIのみを呼び出すことができます。
    Edge情報を取得するために必要なAWS APIを呼び出してください。
    
    Args:
        service_name: AWSサービス名 (小文字) 
            許可: &#39;cloudfront&#39;, &#39;wafv2&#39;, &#39;elbv2&#39;, &#39;ecs&#39;, &#39;ec2&#39;, &#39;rds&#39;, &#39;elasticache&#39;, &#39;apigateway&#39;, &#39;lambda&#39;
        method_name: 呼び出すメソッド名 (boto3のメソッド名、snake_case)
            例: &#39;get_distribution&#39;, &#39;describe_target_health&#39;, &#39;describe_security_groups&#39;
        parameters: メソッドに渡すパラメータの辞書
            例: {&quot;Id&quot;: &quot;ABC123&quot;} や {&quot;GroupIds&quot;: [&quot;sg-12345&quot;]}
        region: AWSリージョン (デフォルト: ap-northeast-1)
    
    Returns:
        API呼び出し結果の辞書
        エラーの場合は {&quot;error&quot;: &quot;エラーメッセージ&quot;} を返す
    
    許可されているAPI一覧:
        - cloudfront:get_distribution
        - cloudfront:list_distributions
        - wafv2:list_web_acls
        - wafv2:get_web_acl
        - wafv2:get_web_acl_for_resource
        - ...
    
    Examples:
        # CloudFront Distribution情報を取得
        call_aws_api(
            service_name=&quot;cloudfront&quot;,
            method_name=&quot;get_distribution&quot;,
            parameters={&quot;Id&quot;: &quot;ABC123&quot;}
        )
        
        # ターゲットグループのヘルス情報を取得
        call_aws_api(
            service_name=&quot;elbv2&quot;,
            method_name=&quot;describe_target_health&quot;,
            parameters={&quot;TargetGroupArn&quot;: &quot;arn:aws:elasticloadbalancing:...&quot;}
        )
        
        # セキュリティグループ情報を取得
        call_aws_api(
            service_name=&quot;ec2&quot;,
            method_name=&quot;describe_security_groups&quot;,
            parameters={&quot;GroupIds&quot;: [&quot;sg-12345&quot;]}
        )
        
        # ECSタスク情報を取得
        call_aws_api(
            service_name=&quot;ecs&quot;,
            method_name=&quot;describe_tasks&quot;,
            parameters={&quot;cluster&quot;: &quot;my-cluster&quot;, &quot;tasks&quot;: [&quot;arn:aws:ecs:...&quot;]}
        )
    &quot;&quot;&quot;
    try:
        # ステップ1: APIホワイトリストに含まれているか検証
        is_valid, error_message = validate_aws_api(service_name, method_name)
        
        if not is_valid:
            return {
                &quot;error&quot;: error_message,
                &quot;error_type&quot;: &quot;unauthorized_api&quot;,
                &quot;allowed_apis&quot;: get_allowed_apis_list()
            }
        
        # ステップ2: クライアントを取得
        client = get_aws_client(service_name, region)
        
        # ステップ3: メソッドが存在するか確認
        if not hasattr(client, method_name):
            return {&quot;error&quot;: error_msg, &quot;available_methods&quot;: dir(client)}
        
        # ステップ4: メソッドを取得して実行
        method = getattr(client, method_name)
        response = method(**parameters)
        
        return response
        
    except Exception as e:
        ...
</code></pre>
<p>以下が今回の実装で意識したポイントです。</p>
<ul>
<li>ホワイトリスト方式: LLMが任意のAWS APIを呼ぶことを防ぎ、安全性を確保</li>
<li>動的API呼び出し: Pythonの<code>getattr()</code>でboto3のメソッドを動的に実行</li>
<li>詳細なdocstring: LLMがツールの使い方を理解するため、引数の説明、使用例、許可API一覧を記載</li>
<li>拡張性: 新しいAPIを追加する場合は、<code>ALLOWED_AWS_APIS</code>に追加するだけ</li>
</ul>
<h2>Incident Managerでのトポロジー描画</h2>
<p>最終的にAI Agentを使って収集したNodeとEdgeの情報で、IncidentManager上でのシステムトポロジー表示はこのようになりました。
<img src="/assets/blog/authors/masaki_yamada/20251209/topology.png" alt="topology"></p>
<p>障害発生時は原因箇所が赤くなるため、ぱっと見で直感的にシステム構成と障害箇所が理解しやすいような図になったかと思います！</p>
<h2>GitHub Copilotを使って実装してみた感想</h2>
<h3>良かった点</h3>
<ul>
<li><p>開発時間の大幅な短縮</p>
<ul>
<li>今回の実装は約1日で完成しました。もしGitHub Copilotなしで実装していたら、LangGraphの学習から始める必要があり、少なくとも数週間はかかっていたと思います。</li>
</ul>
</li>
<li><p>高品質な実装計画の提案</p>
<ul>
<li>まだ改善の余地はありますが最初のプロンプトで実現したいことと設計を詳細に伝えたことで、以下のような質の高い実装計画が生成されました。<ul>
<li>既存リポジトリの構造を理解した上で、一貫性のある設計を提案</li>
<li>拡張性とシンプルさを両立した設計の提案</li>
</ul>
</li>
</ul>
</li>
<li><p>対話的な品質改善</p>
<ul>
<li>実装途中で気になった点を指摘すると、すぐに修正してくれました。<ul>
<li>アーキテクチャの改善</li>
<li>ライブラリバージョンの問題</li>
<li>エラーハンドリングの追加</li>
<li>など</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3>大変だった点</h3>
<h4>生成されたコードの検証が必須</h4>
<p>GitHub Copilotに限らず生成AIが生成したコードは、<strong>指示をした人が責任を持ってレビューをする</strong>必要があります。生成AIを活用したコーディングでは、レビューに一番時間がかかります。</p>
<p>以下のような観点でコードレビューをおこないました。</p>
<ul>
<li>既存コードの規約準拠: リポジトリの命名規則やコーディングスタイルに従っているか</li>
<li>要件の網羅性: 指定した機能要件がすべて実装されているか、漏れがないか</li>
<li>アーキテクチャパターン: 適切なレイヤー分けがされているか、責務が明確か</li>
<li>実装の適切性: より一般的な方法や簡単な実装方法がないか、無駄に複雑になっていないか</li>
<li>エラーハンドリング: 例外処理が適切に実装されているか、エラーメッセージは適切か</li>
<li>動作検証: 実際にアプリを起動させて、意図通りの動作をするか</li>
</ul>
<h3>今後やりたいこと</h3>
<h4>収集するNodeとEdgeの追加</h4>
<p>現状は最初のお試しということで、一部のAWSサービスに絞ってNodeとEdgeを取得するようにしました。</p>
<p>プロンプトで取得するAWSリソースを追加して、AWS API呼び出しツールの利用するホワイトリストに、Edge情報を取得するために許可するAPIを追加すれば簡単に拡張できる実装になっているため、今後少しずつ収集リソースを増やしていきたいと思います。</p>
<h4>プロンプトテンプレートの作成</h4>
<p>今回は最初の指示で考慮不足があったため、今後使いまわせるような新機能実装時のプロンプトテンプレートを作成して、以下の内容を含めることで最初の指示からより高品質なコード生成ができるようにしたいと思います。</p>
<ul>
<li>機能の概要</li>
<li>技術スタック（フレームワーク、ライブラリ、言語バージョン）</li>
<li>機能要件</li>
<li>アーキテクチャ要件（レイヤー構成、エラーハンドリングなど）</li>
<li>非機能要件（拡張性、パフォーマンス、セキュリティ）</li>
<li>実装前の確認事項（リポジトリ構造の理解、既存コードとの整合性確認など）</li>
<li>重要な注意事項（プロンプトの最後に配置、絶対に守るべきルールを明記）</li>
</ul>
<h2>まとめ</h2>
<p>今回はGitHub Copilotを活用して、システムトポロジー情報を収集するAI Agentを約1日で実装しました。</p>
<p>実装したAI Agentにより、従来は手動で設定していたAWSリソースの依存関係が自動収集されるようになり、Incident Manager上でインシデント発生時のトポロジー可視化が実現できました。</p>
<p>今後もGitHub Copilotをはじめとした生成AIを積極的に活用して、開発生産性の向上を目指していきたいと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Four Tips for Running Monthly Company-Wide Study Sessions for Over a Year]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-04-godo-benkyokai-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-04-godo-benkyokai-en/</guid>
            <pubDate>Thu, 04 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Reflecting on how we kept our company-wide study sessions going for more than a year]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>I&#39;m tetsu from the Platform Group.
In this article, I&#39;ll summarize the operations behind our company-wide joint study sessions at KTC.</p>
<p>The study sessions are held monthly, with over 50 participants attending each time.
I&#39;ve compiled the tips and tricks for keeping them going, so this is a must-read for those who want to hold study sessions at their company or are thinking about starting ones!</p>
<h2>Overview of the Study Sessions</h2>
<ul>
<li>Frequency: Once a month</li>
<li>Format: Hybrid (in-person and Zoom)</li>
<li>Scale: Approx. 50–100 people</li>
<li>What the session is like:<ul>
<li>Speakers come from various divisions across the company<ul>
<li>Engineers, designers, directors, HR members, and more</li>
</ul>
</li>
<li>Each person presents for 10–15 minutes; for example, with 3 speakers, it takes an hour in total</li>
<li>A meetup session is held after the study session where attendees can talk with the speakers</li>
<li>Anyone who wants to present can do so, regardless of the presentation topic<ul>
<li>Examples of past presentation topics<ul>
<li>Sharing details about recently released projects</li>
<li>Promoting tools developed internally</li>
<li>Sessions focused on specific topics (on figma study sessions, creative generative AI, security, QA, etc.)</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>Timetable</li>
</ul>
<pre><code>17:05–17:10 Opening
17:10–17:25 Presentation 1
17:25–17:40 Presentation 2
17:40–17:55 Presentation 3
17:55–18:00 Closing
18:00–19:00 Meetup session (optional)
</code></pre>
<p><img src="/assets/blog/authors/t.sugiyama/benkyokai.JPEG" alt="開催雰囲気"></p>
<h2>Background of the Study Sessions</h2>
<p>Before these study sessions began, each group within KTC was already holding their own study sessions and orientations. However, there was a situation where know-how was not easily shared between teams in different divisions or those with little work-related interaction. That was a concern, which some people felt.</p>
<p>To this end, the joint study sessions were launched with an aim of creating a place where teams and divisions with little interaction can share their knowledge with each other and strengthen collaboration between them.</p>
<h2>Continuing Study Sessions Is Surprisingly Difficult</h2>
<p>While many companies share backgrounds similar to the above-mentioned one, many internal study sessions start up but eventually fizzle out.
We&#39;ve managed to continue for over 15 sessions so far, but it certainly hasn&#39;t been smooth sailing.</p>
<p>In general, the barriers to continuity, which we&#39;ve actually faced, include:</p>
<ul>
<li><strong>Operations becoming dependent on specific individuals</strong>: When a heavy workload is shouldered on specific individuals, and if they transfer or leave their organizations, business operation cannot be run smoothly, which may result in the increase in operational costs.</li>
<li><strong>Difficulty in holding hybrid events</strong>: Meeting the demands for both online and on-site participants is harder than you expected</li>
<li><strong>Consistency of participants</strong>: At the launch of study sessions, things are lively, but gradually, only specific participants came to regularly engage in the sessions</li>
</ul>
<p>To overcome these barriers, we&#39;ve put the following four tips into action.</p>
<h2>Tips and Tricks for Continuity</h2>
<h3>Tip 1: Standardizing Operations and Implementing a Rotation System</h3>
<p>To prevent specific individuals attending study sessions from bearing an excessive burden of the operation for the sessions, we&#39;ve implemented the following:</p>
<ul>
<li>Identify all necessary tasks for the operation and manage them as Jira tickets</li>
<li>Assign the tickets to the operation team members on a monthly rotation</li>
</ul>
<p>The first point is to identify all necessary tasks and managing them as Jira tickets. We use Jira&#39;s feature to automatically create its tickets for running the joint study sessions. This allows all members to understand the tasks required for operations.</p>
<p>The second point is to assign the ticket to the operation team members on a monthly rotation. We divide the study session tasks in the following three categories to rotate them: facilitation, venue preparation, and coordination. The task rotation helps all operations team members understand every task, which prevents dependence on specific individuals.</p>
<p>When running study sessions, it&#39;s not uncommon for the operators to become exhausted, which causes the sessions to fall through. For joint study sessions in KTC, we&#39;ve standardized the operation to reduce the workload.</p>
<p>*JIRA tickets are issued and organized as shown below.
<img src="/assets/blog/authors/t.sugiyama/JIRAChicket.png" alt="JIRAのチケット一覧"></p>
<h3>Tip 2: Creating an Environment Where All Employees Can Easily Attend</h3>
<p>Continuing to hold study sessions, we may face issues of consistent participants or lower engagement of online attendees. We need to address these issues because study session can be more valuable when it offers opportunities not only to listen but also to discuss and share opinions among attendees about what they&#39;ve learned. To prevent the issues, we&#39;ve implemented the following measures:</p>
<ul>
<li>Schedule the study session on all employees&#39; Outlook calendars</li>
<li>Hold the session in a communication space shared on a company-wide basis</li>
<li>Prepare a Slack channel for casual chat and opinion exchange that allows online participants to easily join in and the operations team to actively participate in the chat</li>
<li>Avoid holding sessions during busy times like the beginning of the month</li>
</ul>
<p>The goal of the study session isn&#39;t just to attend but to increase business knowledge and technical skills through participation. That said, you can&#39;t get started without attending the session, so it is important to make a framework for casual participation.</p>
<p>Slack for casual chat (See below)
<img src="/assets/blog/authors/t.sugiyama/Slack.png" alt="ワイガヤ"></p>
<h3>Tip 3: Responding to Survey Requests</h3>
<p>This may seem obvious, but we make sure to respond to requests from surveys regarding the study sessions.
For example, we received the following feedback:</p>
<ul>
<li>&quot;I&#39;d like to know about case studies on how business specifications are decided&quot;</li>
<li>&quot;The venue is really quiet when presentation is not provided, so I thought playing some background music might be nice&quot;</li>
<li>&quot;I&#39;d like some salty snacks at the meetup session&quot;</li>
</ul>
<p>We receive various request. Some related to study session topics, some about improving the venue atmosphere, and so on. We read through each one, consider the background, and try to respond to it appropriately.</p>
<ul>
<li><p>&quot; I&#39;d like to know about case studies on how business specifications are determined &quot;</p>
<p>-&gt; Project managers, product managers, and producers handle these business requirements, so it might be good to hear the case studies from these people with multiple perspectives! Additionally, this request probably came from an engineer, so it would be great to create a good point of connection between engineers and the people making decisions on business specifications.</p>
</li>
<li><p>&quot;The venue is really quiet when presentation is not provided, so I thought playing some background music might be nice&quot;</p>
<p>-&gt; Indeed, we understand silence is uncomfortable moment... We could play the background music and have the facilitator fill the time between presentations so there&#39;s no awkward silence!</p>
</li>
<li><p>&quot;I&#39;d like some salty snacks at the networking session&quot;</p>
<p>-&gt; We may have a narrow preference of the snack flavors. Let&#39;s add some salty options!</p>
</li>
</ul>
<h3>Tip 4: Continuous Improvement Cycle</h3>
<p>The tips above were established through retrospectives held among the operations team members after each study session.
At KTC&#39;s joint study sessions, we use the KPT method for retrospectives as shown in the table below, continuing to keep what&#39;s good and improve problems to prevent us from the recurrence. (Improvements from this process get reflected in the operations JIRA tickets.)</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Keep (Good/Worked well/Want to continue)</th>
<th>Problem (Issue/Challenge/Trouble)</th>
<th>Try (Intention for improvements next time)</th>
<th>Discussion on the day</th>
</tr>
</thead>
<tbody><tr>
<td>Overall</td>
<td>This was the first time we held a session on a specific topic. We could do sessions in such style a few more times in the future</td>
<td>Attendance might be low on Tuesdays at 5 P.M.</td>
<td>Change the event time</td>
<td>...</td>
</tr>
<tr>
<td>Operations before the event</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>Preparation / clean-up on the event day</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>Presentations</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>Meetup session</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>Survey</td>
<td>...</td>
<td>...</td>
<td>...</td>
<td>...</td>
</tr>
</tbody></table>
<h2>Positive Effects of Continuing the Study Group for Over a Year</h2>
<p>Here are some positive effects we&#39;ve noticed from continuing the study sessions.</p>
<ul>
<li>It has become a place for exchanging opinions and collaborating with people from different departments<ul>
<li>Particularly from people who work across the company, such as those in divisions engaging in shared internal tools or security-related projects, we especially receive comments, like &quot;I&#39;m happy to be able to exchange opinions&quot;</li>
</ul>
</li>
<li>Requests for presentations from employees and collaborative study sessions with other departments have increased, so we no longer run out of topics<ul>
<li>We receive various requests, such as &quot;I want to practice for an external presentation&quot; or &quot;I introduced a new development method to drive a project forward and want to talk about it&quot;</li>
<li>We, operation members, don&#39;t give a presentation, seeking speakers as volunteers to run the sessions in a manner of that we can respond to various requests, so we haven&#39;t run out of presentation topics</li>
</ul>
</li>
</ul>
<h2>Conclusion</h2>
<p>Finally, our study session participants are taking time out of their busy schedules to attend the sessions even though they could spend time in doing other things. To meet their expectations, we strive to provide high-quality study sessions.</p>
<p>Of course, you can&#39;t run things perfectly from the start. The bottom line is to take that first step, and then, listen to participants&#39; demands and continuously make improvements through retrospectives—that&#39;s what we believe matters.</p>
<p>Thank you for reading to the end!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[月1回、全社横断の合同勉強会を1年以上続けるために実践した4つのこと]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-04-godo-benkyokai/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-04-godo-benkyokai/</guid>
            <pubDate>Thu, 04 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[全社横断の合同勉強会を1年以上継続できた理由を振り返ります]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の4日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>Platformグループのtetsuです。
本記事では、KTCで実施している全社横断の合同勉強会の運営に関する内容をまとめます。</p>
<p>この合同勉強会は毎月開催され、毎回50人以上の方が参加しています。
継続するための工夫やコツをまとめましたので、「社内で勉強会を開催したい方」「これから始めたい方」必見です！</p>
<h2>勉強会の概要</h2>
<ul>
<li>開催頻度：月1回</li>
<li>形式：ハイブリッド（オフライン・Zoom）</li>
<li>規模：約50〜100名</li>
<li>特徴：<ul>
<li>全社の様々な部署から登壇者が集まる<ul>
<li>エンジニア、デザイナー、ディレクター、人事メンバーなど</li>
</ul>
</li>
<li>1人あたり10〜15分の発表 × 3人で計1時間</li>
<li>勉強会後に登壇者と話せる交流会を実施</li>
<li>テーマを問わず、登壇したい人が登壇できるようにする<ul>
<li>過去の登壇テーマ例<ul>
<li>直近でリリースのあったプロジェクトの内容共有</li>
<li>社内向けに作成したツールの宣伝</li>
<li>特定テーマ会（Figma勉強会、クリエイティブ関連の生成AI、セキュリティ、QA など）</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
<li>タイムテーブル</li>
</ul>
<pre><code>17:05～17:10 オープニング
17:10～17:25 発表1
17:25～17:40 発表2
17:40～17:55 発表3
17:55～18:00 クロージング
18:00～19:00 交流会（任意参加）
</code></pre>
<p><img src="/assets/blog/authors/t.sugiyama/benkyokai.JPEG" alt="開催雰囲気"></p>
<h2>勉強会の実施背景</h2>
<p>勉強会が開催される以前は元々はKTC内の各グループで勉強会やオリエンテーションが開催されていました。ただ、他部署や業務で関わりが少ないチーム間ではお互いのノウハウが共有されにくい状況であり、そこに課題を感じている人たちがいました。</p>
<p>そこで、「関わりが少ないチームや他部署間で良いノウハウを共有しあえる場所を作りたい」「部署やチーム間の連携を高められる場所を作りたい」という目的意識から、合同勉強会の発足に至りました。</p>
<h2>勉強会を継続するのって意外と大変</h2>
<p>ただ、上記のような実施背景は多くの企業で持ちながらも、社内勉強会が立ち上がっては消えていくことが多いと思います。
私たちも15回以上継続できていますが、決して順風満帆ではありませんでした。</p>
<p>一般的に、そして私たちも実際に直面した「継続を阻む壁」は以下のようなものです：</p>
<ul>
<li><strong>運営の属人化</strong>: 特定の人に負担が集中し、その人が異動・退職すると立ち行かなくなり、運営工数が上がってしまう</li>
<li><strong>ハイブリッド開催の難しさ</strong>: オンライン・オフライン両方を満足させるのは想像以上に難しい</li>
<li><strong>参加者の固定化</strong>: 最初は盛り上がるが、徐々に参加者が固定化されていく</li>
</ul>
<p>これらの課題に対して、私たちは以下の4つの工夫を実践してきました。</p>
<h2>継続するための工夫・コツ</h2>
<h3>工夫1: 運営タスクの標準化とローテーション制</h3>
<p>運営の属人化を防ぐために、工夫している内容は以下の通りです。</p>
<ul>
<li>やるべきタスクを洗い出し、チケットにして管理</li>
<li>チケットの担当者を毎月運営メンバー内でローテーションでアサイン</li>
</ul>
<p>1点目の「やるべきタスクを洗い出し、チケットにして管理」については、JIRAの「自動化」機能を使い、合同勉強会を実施するためのチケットを自動で作成するようにしています。これにより誰でも運営に必要なタスクを把握することが可能です。</p>
<p>2点目の「チケットの担当者を毎月運営メンバー内でローテーションでアサイン」については、「司会」「会場準備」「調整」の3分類のタスクをローテーションするようにしています。ローテーションすることで運営メンバーが全部のタスクを経験することになるため、属人化を防ぐことができています。</p>
<p>勉強会の運営をするにあたり、運営が疲弊して頓挫してしまうケースも少なくないと思います。KTCの合同勉強会では、運営工数を減らせるように標準化しています。</p>
<p>※JIRAのチケットは以下のように切っています。
<img src="/assets/blog/authors/t.sugiyama/JIRAChicket.png" alt="JIRAのチケット一覧"></p>
<h3>工夫2: 全社員が参加しやすい環境づくり</h3>
<p>参加者が固定化したり、オンラインで参加する人のエンゲージメントが低くなってしまうことがあると思います。勉強会は聞くだけでなく、聞いた内容をもとに意見を言い合えるとより効果があると考えているため、これらは課題になります。これらを防げるように以下のように工夫しています。</p>
<ul>
<li>全社員のOutlook カレンダーに勉強会を登録</li>
<li>会社にある全社員が利用できる交流スペースで実施</li>
<li>オンラインの人でも参加しやすいようにワイガヤ（雑談・意見交換）用のSlackチャンネルを用意し、運営も積極的にワイガヤする</li>
<li>月初など忙しいタイミングの開催は避ける</li>
</ul>
<p>勉強会に参加することがゴールではなく、勉強会への参加を通じて業務知識や技術力の向上に繋がることが大事ではあると思いますが、勉強会に参加しないと始まらないので、参加しやすくすることは大切です。</p>
<p>⇓ワイガヤ用のSlack
<img src="/assets/blog/authors/t.sugiyama/Slack.png" alt="ワイガヤ"></p>
<h3>工夫3: アンケート要望に応える</h3>
<p>当たり前かもしれませんが、アンケートでいただいた要望には応えるようにします。
例えば、アンケートには次のようなものが届きました。</p>
<ul>
<li>「業務仕様をどうやって決めているのかが知れるような事例を聞きたい」</li>
<li>「発表の無いときの会場がすごく静かなので、BGMとかあってもよいかなーと思いました」</li>
<li>「交流会で提供されるお菓子に塩辛いものが欲しい」</li>
</ul>
<p>勉強会のテーマに関わる要望、会場の雰囲気をいい感じにしてほしい要望、など色々と要望をいただきますが、それぞれ目を通して、その背景を考えながら要望に応えるようにします。</p>
<ul>
<li><p>「業務仕様をどうやって決めているのかが知れるような事例を聞きたい」</p>
<p>⇒ プロジェクトマネージャやプロダクトマネージャ、プロデューサーがこういった業務要件を検討するので、これらの人から多角的に聞けるといいかもしれない！あとエンジニアの人から来てそうな要望だから、エンジニア &lt;-&gt; 業務仕様を決める人たちのいい接点が生まれる場にできるといいなあ。 </p>
</li>
<li><p>「発表の無いときの会場がすごく静かなので、BGMとかあってもよいかなーと思いました」</p>
<p>⇒ 確かに、無言の時間って気まずい・・。BGMも改善したほうがいいし、司会の人が間を繋いで気まずい時間が流れないようにしよう！</p>
</li>
<li><p>「交流会で提供されるお菓子に塩辛いものが欲しい」</p>
<p>⇒ 味が偏っているかもしれない。塩辛いの追加してみよう！</p>
</li>
</ul>
<h3>工夫4: 継続的な改善サイクル</h3>
<p>上記の工夫たちは勉強会を開催後に運営メンバー内で振り返りをした結果として生まれたものです。
KTCの合同勉強会の運営では以下の表のようにKPT 法を利用して振り返りを行い、「良いことを継続」「問題は再発しないように改善」を続けています（この改善を通じて、運営のJIRAチケットに反映されるものが増えています）。</p>
<table>
<thead>
<tr>
<th>カテゴリ</th>
<th>Keep（良い/上手い/続けたい）</th>
<th>Problem（問題だ/課題だ/困った）</th>
<th>Try（次回こうしたい）</th>
<th>当日議論</th>
</tr>
</thead>
<tbody><tr>
<td>全体的に</td>
<td>特定のテーマに沿って実施したのが初。今後も何回か実施しても良いと思う</td>
<td>火曜の17時だと人の集まり悪いかも</td>
<td>開催時間を変更する</td>
<td>・・・</td>
</tr>
<tr>
<td>前日までの運営</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
</tr>
<tr>
<td>当日準備/片付け</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
</tr>
<tr>
<td>発表の部</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
</tr>
<tr>
<td>交流会の部</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
</tr>
<tr>
<td>アンケート</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
<td>・・・</td>
</tr>
</tbody></table>
<h2>勉強会を1年以上続けてきた嬉しい効果</h2>
<p>勉強会を継続してきたことで感じた嬉しい効果を紹介します。</p>
<ul>
<li>他部署の人と意見交換ができたり、プロデュースできる場となってる<ul>
<li>社内共通ツールやセキュリティ関連部署など、社内横断で仕事をする人たちから特に「意見交換ができて嬉しい」というコメントをもらえます</li>
</ul>
</li>
<li>社員からの登壇や部署ごとのコラボ勉強会の要望が増えて勉強会ネタに困らなくなってきた<ul>
<li>「外部登壇の練習をしたい」「プロジェクトを推進するのに新しい開発手法を入れてみたから話してみたい」など、いろいろな要望をいただきます</li>
<li>運営自身で登壇することはせず、登壇者を募って実施する形式にしており、いろいろな要望に応えられるようにしているので、ネタには困っていない状態です</li>
</ul>
</li>
</ul>
<h2>最後に</h2>
<p>最後に、勉強会に参加される方々は、他にできることがある中で貴重な時間を割いて参加してくれています。その期待に応えられるよう、私たちは質の高い勉強会の提供を心がけています。</p>
<p>もちろん、最初から完璧な運営ができるわけではありません。大切なのは、まず一歩を踏み出すこと。そして参加者の声に耳を傾け、振り返りを重ねながら、継続的に改善していくことだと考えています。</p>
<p>最後まで読んでいただき、ありがとうございました！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: September 2025 Update]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-03-newcomer-202509-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-03-newcomer-202509-en/</guid>
            <pubDate>Wed, 03 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We interviewed our new members who joined in September 2025 about their impressions after joining and compiled their responses.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I&#39;m watanabe, and I joined in September 2025!</p>
<p>In this article, I interviewed everyone who joined in September 2025 about their impressions right after joining.
I hope this will be useful content for those interested in KINTO Technologies (hereafter KTC) and serve as a reflection for the members who participated!</p>
<h1>10Ryu</h1>
<p><img src="/assets/blog/authors/watanabe/2025-12-03-newcomer-202509/10Ryu.png" alt="Rikumaさんのプロフィール画像"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I work on the lease fee and gross profit calculation system for KINTO used vehicles in the Business Systems Development Division.</li>
<li>My previous job was in automotive sales finance.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>Our team has 5 members.</li>
</ul>
</li>
<li><strong>What is the workplace atmosphere like?</strong><ul>
<li>There are no barriers between members, making communication easy.</li>
<li>It&#39;s easy for the engineering team to step in and make suggestions to the business team.</li>
</ul>
</li>
<li><strong>What was your reason for joining KTC, and were there any surprises after joining?</strong><ul>
<li>Reason for joining: I wanted to work in a job related to automobiles, and I was interested in KINTO&#39;s business even before joining.</li>
<li>Surprises: I was given more autonomy than I expected, and I&#39;ve been able to leverage my past experience. To be honest, I had no coding experience, so I was quite anxious about whether I could make it at a tech company.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>The atmosphere of Muromachi</li>
<li>There are many coffee shops nearby</li>
<li>Being able to buy treats for my family on the way home from work</li>
</ul>
</li>
<li><strong>Question from watanabe to 10Ryu</strong><blockquote>
<p>Please tell us an episode where you felt you could leverage your experience in the automotive industry since joining KTC.</p>
</blockquote>
<ul>
<li>Since I can recognize most vehicles just by hearing the car name, and thanks to my experience in automotive sales finance, I was able to smoothly catch up on business knowledge such as KINTO&#39;s approach to residual value and payment methods.</li>
</ul>
</li>
</ul>
<h1>Tomiyoshi</h1>
<p><img src="/assets/blog/authors/watanabe/2025-12-03-newcomer-202509/tomiyoshi.jpg" alt="とみよしさんのプロフィール画像"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I belong to the QA Group. I&#39;m involved in testing activities of course, but also in automation related to those activities.</li>
<li>In my previous job, I was doing QA at a third-party verification company.</li>
<li>My hobby is tennis, and recently I&#39;m getting into pickleball.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The entire QA Group has 12 members.</li>
<li>Within the group, we&#39;re divided into mobile apps and web apps, and I work on mobile apps.</li>
</ul>
</li>
<li><strong>What is the workplace atmosphere like?</strong><ul>
<li>Communication is easy not only within the team but also with the development side, making it a very good environment.</li>
</ul>
</li>
<li><strong>What was your reason for joining KTC, and were there any gaps between your expectations and reality?</strong><ul>
<li>When I exhibited a booth at JaSST&#39;25 Tokyo, I talked with people from KTC&#39;s QA team. </li>
<li>That sparked my interest and led me to decide to join.</li>
<li>Before I joined, I heard the company was using AI a lot and automating testing. That was exactly how it turned out, so there wasn’t really any surprise.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>I like the break space. It&#39;s a nice, relaxing spot.</li>
</ul>
</li>
<li><strong>Question from 10Ryu to Tomiyoshi</strong><blockquote>
<p>Please tell us what you like about KTC and your favorite part of your cat&#39;s body.</p>
</blockquote>
<ul>
<li>What I like about KTC<ul>
<li>Everyone is eager to take on new challenges.</li>
</ul>
</li>
<li>My favorite part of my cat<ul>
<li>Everything, but burying my face in a slightly chubby side belly is the best.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Rikuma</h1>
<p><img src="/assets/blog/authors/watanabe/2025-12-03-newcomer-202509/lu.jpg" alt="Rikumaさんのプロフィール画像"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>Currently, I work on generative AI-related projects, and recently I&#39;ve been focusing on PoC development for MCP and chatbots.</li>
<li>My hobbies include watching anime, board games, traveling, and skiing.</li>
<li>I try to maintain a work-life balance while finding fulfillment in working with the latest technologies every day.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>We work on a wide range of themes related to generative AI.</li>
<li>In addition to technical verification and PoC development, we promote the use of generative AI internally and externally, and run workshops.</li>
<li>It&#39;s an agile team where we can actively exchange opinions and quickly turn new ideas into reality.</li>
</ul>
</li>
<li><strong>What is the workplace atmosphere like?</strong><ul>
<li>It&#39;s very flat and open, with an atmosphere where anyone can freely share their opinions.</li>
<li>There&#39;s an atmosphere that supports people who take on new challenges, and &quot;Let&#39;s do it!&quot; is something you hear a lot.</li>
<li>I feel the whole team has a strong attitude toward proactively tackling new things.</li>
</ul>
</li>
<li><strong>What was your reason for joining KTC, and were there any surprises after joining?</strong><ul>
<li>I wanted to explore the possibilities of AI more deeply and was drawn to KTC&#39;s environment where I could practice the latest generative AI technologies, which led me to decide to join.</li>
<li>What I noticed after joining KTC is how quickly we can access the latest technologies. It&#39;s fun to be able to quickly turn my ideas into PoCs. I feel it&#39;s an environment with a good balance of speed and stability.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>My previous work place had hot-desking, so now I enjoy being able to place my favorite items at my own desk and create my own personal space.</li>
<li>Also, being able to smoothly consult and share information with members on the same floor is a good point.</li>
</ul>
</li>
<li><strong>Question from Tomiyoshi to Rikuma</strong><blockquote>
<p>How do you feel about KTC&#39;s corporate culture? What do you do on your days off?</p>
</blockquote>
<ul>
<li>How do you feel about KTC&#39;s corporate culture?<ul>
<li>The culture here is open and really encourages you to take on new challenges.</li>
<li>A characteristic is that there are many opportunities to try out your own ideas.</li>
</ul>
</li>
<li>What do you do on your days off?<ul>
<li>On my days off, I spend time relaxing watching anime or playing board games.</li>
<li>I also love traveling, so I go on day trips on weekends or travel abroad during long holidays.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>watanabe</h1>
<p><img src="/assets/blog/authors/watanabe/watanabe.png" alt="![watanabeさんのプロフィール画像](/assets/blog/authors/watanabe/watanabe.png)
"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>The Cloud Security Group I belong to is responsible for security across multiple cloud environments including AWS, Azure, and Google Cloud.</li>
<li>In my previous job, I was broadly responsible for AWS infrastructure construction (IaC), Lambda development, CI/CD considerations, and monitoring tool configuration.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The entire Cloud Security Group has 4 members, with 2 in Tokyo and 2 in Osaka.</li>
</ul>
</li>
<li><strong>What is the workplace atmosphere like?</strong><ul>
<li>The team is very open, and we can freely share concerns and issues.</li>
<li>Although members are split between Tokyo and Osaka, we communicate frequently both online and in person, so the distance doesn&#39;t feel like an issue.</li>
</ul>
</li>
<li><strong>What was your reason for joining KTC, and were there any surprises after joining?</strong><ul>
<li>I had opportunities to be involved in cloud security at my previous job and wanted to deepen my expertise in this area.</li>
<li>I also felt a strong connection with the people I met during the interviews, and that was a key factor in my decision to join.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>It has a modern tech company atmosphere and is a very comfortable working environment.</li>
<li>The dress code is more casual than I expected, which was a pleasant surprise.</li>
</ul>
</li>
<li><strong>Question from Rikuma to watanabe</strong><blockquote>
<p>Is there any work that has left an impression on you since joining?</p>
</blockquote>
<ul>
<li>Currently, I work on cloud security for AWS-related projects. In my previous job, I focused on security from an infrastructure perspective, but in my current role I have also been involved in the area of governance, which has broadened my perspective. That change has left a particularly strong impression on me.</li>
</ul>
</li>
</ul>
<h1>Conclusion</h1>
<p>Thank you all for sharing your impressions after joining the company!</p>
<p>KINTO Technologies is constantly welcoming new members!
We hope you look forward to more articles introducing newcomers from various divisions.</p>
<p>And KINTO Technologies is still recruiting people to work with us in various divisions and positions!
Please check <a href="https://www.kinto-technologies.com/recruit/">here</a> for details!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年9月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-03-newcomer-202509/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-03-newcomer-202509/</guid>
            <pubDate>Wed, 03 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年9月に入社した皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、2025年9月入社のwatanabeです！</p>
<p>本記事では、2025年9月入社のみなさまに入社直後の感想をお伺いし、まとめてみました。
KINTOテクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>10Ryu</h1>
<p><img src="/assets/blog/authors/watanabe/2025-12-03-newcomer-202509/10Ryu.png" alt="10Ryuさんのプロフィール画像"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>業務システム開発部でKINTO中古車のリース料や粗利計算システムを担当しています。</li>
<li>前職は自動車販売金融でした。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>5人体制です。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>メンバー間の壁はなくコミュニケーションが取りやすい環境です。</li>
<li>システム側からビジネス側へ踏み込んだ提案がしやすい環境だと感じています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>動機：自動車に関連する仕事をしたいという思いがあり、入社以前からKINTOのビジネスに興味があった為。</li>
<li>ギャップ：想像していたよりも与えられている裁量が大きいことや、これまでの経験を活かすことができていること。（私自身、コーディングの経験がなかったので、テック企業でやっていけるのか小さくない不安はありました）</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>室町の雰囲気</li>
<li>コーヒー屋さんが周りに多いこと</li>
<li>仕事帰りに家族へお土産を買えること</li>
</ul>
</li>
<li><strong>watanabeさん ⇒　10Ryuさんへの質問</strong><blockquote>
<p>KTC に入ってから自動車業界での経験を活かせたと感じたシーンについて教えてください。</p>
</blockquote>
<ul>
<li>車名を聞けば大抵の車両は分かることや、自動車販売金融の経験があったお陰で、KINTOの残価の考え方や支払い方法といった、業務知識をスムーズにキャッチアップする事が出来ました。</li>
</ul>
</li>
</ul>
<h1>とみよし</h1>
<p><img src="/assets/blog/authors/watanabe/2025-12-03-newcomer-202509/tomiyoshi.jpg" alt="とみよしさんのプロフィール画像"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>QAグループに所属しています。テスト活動に関わるところはもちろんですが、そこに関わる自動化だったりを行っています。</li>
<li>前職は第三者検証にてQAをやっていました。</li>
<li>趣味はテニスで、最近はピックルボールにハマりそうです。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>QAグループ全体で12名です。</li>
<li>その中でモバイルアプリとウェブアプリで分かれており、モバイルアプリ担当です。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>チーム内はもちろんですが、開発サイドともコミュニケーションを取りやすくとても良い環境です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>入社のきっかけはJaSST’25 Tokyoでブースの出店をしてQAの方々とお話ししたことです。</li>
<li>そこから興味を持って入社を決めました。</li>
<li>入社前にはAIを積極的に活用、テスト活動に関しての自動化を進めていくことを聞いており、実際にその通りだったので、ギャップのようなものはほとんどありませんでした。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>休憩スペースが気に入っています。落ち着けるのがちょうどいい感じです。</li>
</ul>
</li>
<li><strong>10Ryuさん ⇒　とみよしさんへの質問</strong><blockquote>
<p>KTCの良いと感じるところと、猫ちゃんの好きな部位を教えてください。</p>
</blockquote>
<ul>
<li>KTCのいいと感じているところ<ul>
<li>何事にも挑戦的なので新しいことにどんどんチャレンジしていくところ</li>
</ul>
</li>
<li>猫ちゃんの好きな部位<ul>
<li>全てですが、ちょっと太った横っ腹に猫吸いするのが最高です</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Rikuma</h1>
<p><img src="/assets/blog/authors/watanabe/2025-12-03-newcomer-202509/lu.jpg" alt="Rikumaさんのプロフィール画像"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>現在、生成AI関連のプロジェクトを担当しており、最近ではMCPやチャットボットのPoC開発に力を入れています。</li>
<li>趣味はアニメ鑑賞、ボードゲーム、旅行、スキーなどです。</li>
<li>メリハリのある働き方を心がけながら、日々最新技術に触れることにやりがいを感じています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>生成AIに関わるテーマであれば幅広く取り組んでいます。</li>
<li>技術検証やPoC開発はもちろん、社内外への生成AI活用の推進活動、ワークショップの企画・運営なども手がけています。</li>
<li>活発な意見交換ができ、スピード感を持って新しいアイデアを形にできる、機動力のあるチームです。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>とてもフラットで風通しが良く、誰でも気軽に意見を出せるオープンな雰囲気です。</li>
<li>新しいことにチャレンジする人を応援してくれる雰囲気があって、「やってみよう！」が自然と口に出る現場です。</li>
<li>チーム全体として、新しいことに前向きに取り組む姿勢が強いと感じています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>AIの可能性をより深く追求したいと思い、最新の生成AI技術を実践できるKTCの環境に惹かれたため、KTCへの入社を決めました。</li>
<li>KTCに入って感じたのは、最新技術へのアクセスが早いことです。自分のアイデアをすぐPoCに落とし込めるのが楽しいです。スピードと安定のバランスがとれている環境だと感じています。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>前職ではフリーアドレスだったため、今は自分の席にお気に入りのアイテムを置いて、自分らしい空間を作れるのが楽しいです。</li>
<li>また、同じフロアのメンバーと相談や情報共有がスムーズにできるのも良い点です。</li>
</ul>
</li>
<li><strong>とみよしさん ⇒　Rikumaさんへの質問</strong><blockquote>
<p>KTCの社風はどう感じていますか？休日は何されてますか？</p>
</blockquote>
<ul>
<li>KTCの社風はどう感じていますか？<ul>
<li>オープンで、チャレンジを歓迎する文化が根づいていると感じます。</li>
<li>自分のアイデアを実際に試す機会が多いのが特徴です。</li>
</ul>
</li>
<li>休日は何されてますか？<ul>
<li>休日はゆっくりアニメを見たり、ボードゲームをしたりして過ごしています。</li>
<li>あとは旅行が好きで、週末の日帰り旅行や長期休暇を利用した海外旅行をしています。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>watanabe</h1>
<p><img src="/assets/blog/authors/watanabe/watanabe.png" alt="watanabeさんのプロフィール画像"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>所属するクラウドセキュリティGは、AWS や Azure、 Google Cloud など複数クラウド環境のセキュリティを担当しています。</li>
<li>前職ではAWSインフラの構築（IaC）やLambda開発、CI/CDの検討、監視ツールの設定など幅広く担当していました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>クラウドセキュリティG全体は4名で、東京に2名、大阪に2名が在籍しています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>チームは非常にオープンで、不安や懸念点も気軽に共有できます。</li>
<li>東京・大阪にメンバーが分かれていますが、オンライン/オフラインで頻繁にやり取りがありますので、それほど距離は感じていません。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>前職でクラウドセキュリティに関わる機会があり、この領域で専門性を高めたいと考えました。</li>
<li>面接を通じて感じた社員の方々の人柄に共感したことも入社の決め手となりました。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>近代的なテック企業という雰囲気でとても働きやすい環境です。</li>
<li>服装も想像以上にラフで、良い意味で驚きました。</li>
</ul>
</li>
<li><strong>Rikumaさん ⇒　watanabeさんへの質問</strong><blockquote>
<p>入社してから、印象に残っている業務はありますか？</p>
</blockquote>
<ul>
<li>現在、AWS関連プロジェクトでクラウドセキュリティを担当しています。前職ではインフラ視点でセキュリティを意識していましたが、現職ではガバナンス領域まで関わることで視野が広がり、その変化が特に印象に残っています。</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTOテクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[The Secret to Achieving 99% Accuracy in Generative AI: Hands-On with AWS Automated Reasoning]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-03-secret-to-increasing-accuracy-of-generative-ai-to-99-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-03-secret-to-increasing-accuracy-of-generative-ai-to-99-en/</guid>
            <pubDate>Wed, 03 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A hands-on exploration and verification of Amazon Bedrock Automated Reasoning]]></description>
            <content:encoded><![CDATA[<p>Reference: <a href="https://www.linkedin.com/pulse/aws-introduces-automated-reasoning-checks-mathematical-certainty-lqvmc">AWS Introduces Automated Reasoning Checks</a><a href="https://www.linkedin.com/pulse/aws-introduces-automated-reasoning-checks-mathematical-certainty-lqvmc">^1</a></p>
<!-- ↓マークダウン チートシート↓ -->

<p>This article is Day 3 entry of the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> 🎅🎄</p>
<h2>0. Introduction</h2>
<p>I&#39;m YOU, an Infrastructure Architect in the Cloud Infrastructure Group (CIG) at KINTO Technologies.</p>
<p>In December 2024, AWS <a href="https://aws.amazon.com/jp/blogs/news/prevent-factual-errors-from-llm-hallucinations-with-mathematically-sound-automated-reasoning-checks-preview/">announced Automated Reasoning at re:Invent</a>, enabling mathematical proofs and logical reasoning for generative AI. At that time, it was introduced as Automated Reasoning Checks, a feature of AWS Bedrock Guardrails. It became generally available in some US and EU regions in August of this year.</p>
<p>This approach fundamentally differs from probabilistic reasoning methods that address uncertainty by assigning probabilities to outcomes. In fact, Automated Reasoning Checks[^2] provide up to 99% verification accuracy, offering provable guarantees for detecting AI hallucinations while also helping detect ambiguity when model outputs are open to multiple interpretations.</p>
<p>To briefly explain AWS Automated Reasoning:</p>
<blockquote>
<p>Automated Reasoning Checks help verify the accuracy of content generated by foundation models (FMs) against domain knowledge. This helps prevent factual errors caused by AI hallucinations. This policy uses mathematical logic and formal verification techniques to verify accuracy, providing deterministic rules and parameters for checking the correctness of AI responses.</p>
</blockquote>
<p>As described above, Automated Reasoning is designed to enable quantitative judgment, continuous tracking, response improvement, and further reduction of generative AI hallucinations. Through this approach, it fundamentally differs from probabilistic reasoning methods that rely on uncertainty, assigning probabilities to outcomes. This is why, as stated in the title, it <a href="https://aws.amazon.com/jp/blogs/aws/minimize-ai-hallucinations-and-deliver-up-to-99-verification-accuracy-with-automated-reasoning-checks-now-available/">provides up to 99% verification accuracy</a>, offering provable guarantees for detecting AI hallucinations while also helping detect ambiguity when model outputs are open to multiple interpretations.</p>
<p>Automated reasoning itself was not originated by AWS—it is a field rooted in mathematical logic. The original automated reasoning began when formal verification techniques from software development started being applied, and AWS now offers this methodology as a service for generative AI.</p>
<p>For those who want to learn more about the concept of automated reasoning, please refer to these documents:</p>
<ul>
<li><a href="https://ja.wikipedia.org/wiki/%E8%87%AA%E5%8B%95%E6%8E%A8%E8%AB%96">Automated Reasoning (Wikipedia)</a> </li>
<li><a href="https://aws.amazon.com/what-is/automated-reasoning/">What is Automated Reasoning?</a></li>
</ul>
<p>Since Automated Reasoning integrates with Bedrock Guardrails, understanding Bedrock Guardrails will make some aspects easier to grasp. This article explains the overall picture of Automated Reasoning functionality, so please note that explanations of Bedrock Guardrails are omitted.
For those interested in Bedrock Guardrails or wanting more details, I&#39;ve written separate articles:</p>
<ul>
<li><a href="https://blog.kinto-technologies.com/posts/2025-06-20-aws-bedrock-guardrails-deployment-inclusion-first-part/">AWS Bedrock Guardrails Deployment: Part 1 - The Need for Generative AI Security</a>: The necessity of guardrails for generative AI</li>
<li><a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6?productVariantID=6A68KiYtH7WX6K7LA9XHNw">TECH BOOK By KINTO Technologies Vol.01</a>: How to implement Bedrock Guardrails on AWS</li>
</ul>
<h2>1. Target Readers, Considerations, and Objectives</h2>
<p>Initially, I thought Automated Reasoning, based on its name alone, would be an automated service that easily prevents generative AI hallucinations. However, it&#39;s quite far from being easily accessible automation—it felt quite advanced. So I&#39;ll first explain specific situations where Automated Reasoning is needed and important considerations.</p>
<h3>Target Readers</h3>
<ul>
<li>Those who want to rigorously detect, track and mitigate LLM response hallucinations</li>
<li>Those implementing generative AI with high governance or compliance requirements where errors are unacceptable</li>
<li>Those developing generative AI applications with complex rules and requirements</li>
<li>Those aiming to achieve AWS-centric Responsible AI</li>
</ul>
<h3>Considerations</h3>
<ul>
<li>This article explains findings based on testing in an English environment. When Japanese support is added in the future, behavior and specifications may change.</li>
<li>Automated Reasoning analyzes and detects only content related to text and documents provided by users. Therefore, it is a service that verifies hallucinations and accuracy—it does not automatically restrict or process content. The <a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/guardrails-automated-reasoning-checks.html">official documentation</a> also recommends using it together with existing Bedrock Guardrails filters.</li>
</ul>
<blockquote>
<p>Amazon Bedrock Guardrails Automated Reasoning Checks do not protect against prompt injection attacks. These checks verify exactly what you submit. If malicious or manipulated content is provided as input, verification will be performed on that content as-is (inappropriate input/output). To detect and block prompt injection attacks, use content filters in combination with Automated Reasoning Checks.
Automated Reasoning only analyzes and detects text related to the Automated Reasoning policy. The remaining content is ignored, and it cannot tell developers whether responses are off-topic. If you need to detect off-topic responses, use other guardrail components such as topic policies.</p>
</blockquote>
<p>As discussed later, Automated Reasoning automatically generates basic policies based on text and documents. However, you need to review and test these auto-generated policies. Understanding both the input context requirements and the Automated Reasoning structure is necessary, so please refer to the limitations and best practices in the links below:</p>
<ul>
<li><a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/guardrails-automated-reasoning-checks.html#automated-reasoning-limitations">Constraints and Considerations</a></li>
<li><a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/guardrails-automated-reasoning-checks.html#automated-reasoning-best-practices">Best Practices</a></li>
</ul>
<h3>Objectives</h3>
<p>I want to focus mainly on understanding the Automated Reasoning structure mentioned in the Considerations section above. For this article, I hope you&#39;ll learn these three things to understand the big picture:</p>
<ul>
<li>Know what components Automated Reasoning has and how it works</li>
<li>Know the flow for testing Automated Reasoning</li>
<li><del>Know when to use Automated Reasoning</del> (This requires knowledge of Guardrails, so I&#39;ll discuss it in a separate article)</li>
</ul>
<h2>2. Overview</h2>
<p>Roughly speaking, the overall picture of Automated Reasoning looks like the diagram below.</p>
<p><img src="/assets/blog/authors/you/03/arc.drawio2-20251125-094007.png" alt="draw.ioを使った自動推論の処理の全体像"></p>
<p>The procedure is:</p>
<ol>
<li>When you input text/documents into Automated Reasoning, a Policy is automatically created</li>
<li>The policy auto-generates Definitions of types, variables, and rules based on the input information
  a. Additional text or documents can be incorporated to expand policy definitions
  b. Generated types, variables, and rules can be edited to match requirements and ensure consistency</li>
<li>Create Tests to verify the policy
  a. Manual creation: Enter hypothetical interactions in QnA pair format
  b. Automatic creation: Scenarios that can verify existing rules are auto-generated</li>
<li>Check verification results to confirm expected outcomes
  a. Verify that expected results match actual results in test execution
  b. If they don&#39;t match, return to step 5</li>
<li>Identify the cause of mismatch and add Annotations to incorrect definitions
  a. Information suggesting the cause is presented in test execution results
  b. Annotations can be added to types, variables, and rules—mainly by modifying natural language descriptions, which triggers corresponding updates</li>
<li>After applying annotations, suggested modifications appear; accept changes if correct</li>
<li>Repeat steps 3-6 to complete a policy that protects the text/document content</li>
<li>Attach the completed policy to a Guardrail</li>
<li>Either use an existing Guardrail or create a new one</li>
<li>Pass LLM application input/responses to the Guardrail and utilize Automated Reasoning Check results
  a. If successful, return results to the user as-is
  b. If failed, use Automated Reasoning Check results to request regeneration from the LLM application
  c. (Optional) Save Automated Reasoning Check results as logs and continue policy reviews</li>
</ol>
<p>In summary, Automated Reasoning is created as a Policy, completed through iterations of Definition → Test → Annotation. The completed policy is attached to AWS Bedrock Guardrails, and when the Guardrail is applied to LLM responses, Automated Reasoning Checks are performed. Subsequent processing varies by the developer&#39;s decisions, but you gain the ability to quantitatively judge whether the LLM is producing correct responses.</p>
<p>By introducing Automated Reasoning, you can develop strategies to eliminate hallucinations while running parallel to existing Guardrails that block malicious content. Being able to verify the accuracy of generative AI with RAG and MCP that reference external information, and detect and correct erroneous LLM responses and uncontrollable incorrect inputs, is extremely attractive.</p>
<p>Next, I&#39;ll use <a href="https://github.com/aws-samples/amazon-bedrock-samples/tree/main/responsible_ai/bedrock-automated-reasoning-checks">samples provided by AWS</a> to explain the three main elements—Definition, Test, and Annotation—following the procedure from Policy creation.</p>
<h2>3. Policy</h2>
<p>![ポリシー作成画面](/assets/blog/authors/you/03/image-20251120-094018.png =800x)</p>
<p>Basically, like other AWS resources, Automated Reasoning can be operated via console, CLI, or SDK.</p>
<blockquote>
<p>CloudFormation is not currently supported. CloudFormation support will be available soon.</p>
</blockquote>
<p>Creating an Automated Reasoning policy on the console is straightforward.</p>
<ul>
<li>Name</li>
<li>Description (optional)</li>
<li>Source (document/text)</li>
<li>Source description</li>
</ul>
<p>After entering the above content and clicking the Create Policy button, the policy contents are automatically created. I used a medical PDF file prepared in the AWS sample, and the policy was generated in about 5-10 minutes. The time to create definitions likely varies depending on the length of the input document.
When you check the created policy, the following screen appears:</p>
<p>![Overview](/assets/blog/authors/you/03/image-20251120-134516.png =800x)</p>
<h2>4. Definitions</h2>
<p>The focus here is on the Definitions at the bottom. When you navigate from this overview screen to the definitions screen, you can confirm that rules, variables, and types are defined.</p>
<h3>Types</h3>
<p>![Custom variable types](/assets/blog/authors/you/03/image-20251120-134952.png =800x)</p>
<p>In addition to types pre-defined by AWS, if there are items in user-provided documents that can be classified as types, they are auto-generated. Creating variables with types and correctly defining rules is fundamental to Automated Reasoning. Explaining each item:</p>
<ul>
<li>Name: The name defining the type, a key used in variables</li>
<li>Description: Content written so Automated Reasoning can make judgments</li>
<li>Values: Individual values to be categorized</li>
<li>Issues: Indicates problems occurring with the type</li>
<li>Annotations: Displays modification content before application</li>
<li>Actions: Three operations available—update, delete, revert</li>
</ul>
<p>When a type is not used in any variable, a warning appears as shown in the image indicating an unused type. At this point, users can decide whether to define a variable using this type or delete it. Types that aren&#39;t used don&#39;t affect operation, so they don&#39;t need to be resolved immediately.</p>
<p>Here, I&#39;ll continue the explanation using <code>RiskCategory</code>, which is used somewhere.</p>
<ul>
<li>Name: <code>RiskCategory</code></li>
<li>Description: Risk category assigned to a patient based on total risk score, indicating estimated risk of 30-day readmission</li>
<li>Values: <code>LOW_RISK</code>, <code>INTERMEDIATE_RISK</code>, <code>HIGH_RISK</code></li>
</ul>
<h3>Variables</h3>
<blockquote>
<p>Variables represent concepts in an Automated Reasoning policy that can be assigned values when translating natural language to formal logic. Policy rules define constraints on valid or invalid values for these variables.</p>
</blockquote>
<p>60 variables are defined, and searching for <code>RiskCategory</code> reveals the variables using this type.</p>
<p>![変数](/assets/blog/authors/you/03/image-20251120-142859.png =800x)</p>
<ul>
<li>Name: <code>riskCategory</code></li>
<li>Custom variable type: <code>RiskCategory</code></li>
<li>Description: Risk category assigned to a patient based on total risk score</li>
</ul>
<p>Since Automated Reasoning translates from natural language to formal logic, its accuracy heavily depends on the quality of variable descriptions. Therefore, the best practices include writing comprehensive variable descriptions. Without comprehensive variable descriptions, Automated Reasoning may return <code>NO_DATA</code> because it cannot convert the input natural language to formal logic expressions.</p>
<h3>Rules</h3>
<blockquote>
<p>Rules are the logic that Automated Reasoning extracts from source documents.</p>
</blockquote>
<p>Automated Reasoning logic is created in SMT-LIB. Satisfiability Modulo Theories (SMT) is a field studying methods to check the satisfiability of first-order formulas, and as part of this, <a href="https://smt-lib.org/about.shtml">SMT-LIB</a>—a common input and output language for SMT users—was developed. In Automated Reasoning, you can modify formulas just by changing rule descriptions, but you can also modify them in SMT-LIB format for reference.</p>
<p>There are 21 rules using <code>riskCategory</code>, mainly creating scenarios with <code>riskCategory</code> plus other variable conditions.</p>
<p>![ルール](/assets/blog/authors/you/03/image-20251120-144856.png =800x)</p>
<p>Explaining the rule at the top: since the variable <code>examplePatient</code> has the following definition:</p>
<ul>
<li>Name: <code>examplePatient</code></li>
<li>Custom variable type: Boolean</li>
<li>Description: Whether this is an example of a patient from guidance with specific characteristics</li>
<li>if <code>examplePatient</code> is true, then <code>riskCategory</code> is equal to <code>HIGH_RISK</code>
→ If it matches an example of a patient with specific characteristics from guidance, the risk category is high risk</li>
</ul>
<p>This is the rule that represents this scenario. Now let&#39;s test this rule.</p>
<h2>5. Tests</h2>
<p>There are two ways to run tests.</p>
<p>![テスト](/assets/blog/authors/you/03/image-20251120-153501.png =800x)</p>
<ul>
<li>Manually define question-and-answer (QnA) pairs</li>
<li>Automatically generate test scenarios</li>
</ul>
<p>This article uses console operations with manual definition as an example for intuitive understanding, but for running many tests mechanically, using automatic generation via CLI or SDK is easier. Before verifying rules with manual definition, let&#39;s try testing with automatic generation.</p>
<h3>Automatic Generation Tests</h3>
<p>![自動生成](/assets/blog/authors/you/03/image-20251120-155533.png =800x)</p>
<p>On the console, clicking the generate button creates test scenarios in this format.
Since <code>riskCategory</code> and <code>maxPostDischargeTelephoneContactHours</code> were in the conditions, let&#39;s search for <code>LOW_RISK</code> from the definitions screen.</p>
<p>![LOW_RISKの検索結果](/assets/blog/authors/you/03/image-20251120-155653.png =800x)</p>
<p>However, there&#39;s no rule with the <code>maxPostDischargeTelephoneContactHours</code> variable. Since this variable&#39;s description is &quot;maximum hours after discharge when post-discharge telephone contact occurs,&quot; users who know this scenario&#39;s requirements can judge its validity. If you determine the scenario is incorrect, you can have the test scenario modified by writing a description, but for now, let&#39;s create a test scenario as suggested.</p>
<p>![テスト詳細画面](/assets/blog/authors/you/03/image-20251120-160258.png =800x)</p>
<p>Then, entering the created test case shows this screen, where you can run the test. When executed:</p>
<p>![自動生成テスト実行結果](/assets/blog/authors/you/03/image-20251120-160500.png =800x)</p>
<p>It succeeded even though there&#39;s no matching rule for the conditions. Why is that?</p>
<p>Because the expected result anticipated by the auto-generated test scenario matched the actual result. There are <a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/validate-automated-reasoning-policy-results.html#automated-reasoning-test-validation-results">7 criteria for judging test results</a> in Automated Reasoning.</p>
<ul>
<li><code>VALID</code>: Claim logically matches rules</li>
<li><code>INVALID</code>: Claim logically contradicts or violates rules</li>
<li><code>SATISFIABLE</code>: Claim is consistent with at least one rule condition, but doesn&#39;t match all relevant rules</li>
<li><code>IMPOSSIBLE</code>: There may be conflicts within the Automated Reasoning policy</li>
<li><code>TRANSLATION_AMBIGUOUS</code>: Ambiguity detected in translation from natural language to logic</li>
<li><code>TOO_COMPLEX</code>: Input contains too much information</li>
<li><code>NO_TRANSLATIONS</code>: Part or all of the input prompt was not converted to logic</li>
</ul>
<p>![maxPostDischargeTelephoneContactHoursの検索結果](/assets/blog/authors/you/03/image-20251120-162758.png =800x)</p>
<ul>
<li><code>riskCategory</code> is equal to <code>LOW_RISK</code></li>
<li><code>maxPostDischargeTelephoneContactHours</code> is equal to 72</li>
</ul>
<p>Since each claim existed as one of the conditions in some rule, it produced a <code>SATISFIABLE</code> actual result, and the test scenario set this as the expected result accordingly.</p>
<h3>Annotations</h3>
<p>Now, let&#39;s assume the requirement is &quot;if the maximum hours for post-discharge telephone contact is 120 hours, the risk category is low risk.&quot; In that case, we need to add a new rule.</p>
<p>![ルール追加](/assets/blog/authors/you/03/image-20251120-163917.png =800x)</p>
<p>At this point, you can add annotations for additions/modifications and apply them.</p>
<p>![注釈適用](/assets/blog/authors/you/03/image-20251120-164020.png =800x)</p>
<p>Proceeding here:</p>
<p>![ポリシー更新レビュー](/assets/blog/authors/you/03/image-20251120-164329.png =800x)</p>
<p>You can review changes and discard, approve, or return to the policy for reconsideration. The rule was generated as expected, so it is approved.</p>
<p>![ルールの変更後画面](/assets/blog/authors/you/03/image-20251120-164652.png =800x)</p>
<p>Once the rule is reflected, the actual result changes to <code>INVALID</code> and shows as failed. Since the new rule was applied, it now clearly judges as <code>INVALID</code>, so we need to change the test scenario expectation.</p>
<p>![テスト修正](/assets/blog/authors/you/03/image-20251120-164916.png =800x)</p>
<p>Changing the expected result to <code>INVALID</code> makes it succeed.</p>
<p>![修正後のテスト実行結果](/assets/blog/authors/you/03/image-20251120-165158.png =800x)</p>
<h3>Manual Definition Tests</h3>
<p>The flow of reviewing results and making corrections with annotations isn&#39;t much different when done manually. Let&#39;s add a test with the following content. For manual input, enter content mimicking actual LLM application responses.</p>
<p>![手動定義](/assets/blog/authors/you/03/image-20251120-165940.png =800x)</p>
<ul>
<li>Input: Mr. Foo is a patient who requires caution with medication use. What is this person&#39;s risk category?</li>
<li>Output: Since this is a patient with specific characteristics from guidance, the risk category is high risk</li>
<li>Expected result: <code>VALID</code></li>
</ul>
<p>![手動定義テスト実行結果](/assets/blog/authors/you/03/image-20251120-170523.png =800x)</p>
<p>The result is, of course, successful.</p>
<p>You can see an added type here—it becomes a Premise that appears in QnA tests. Just as this question assumed the patient has characteristics, it&#39;s a type that provides context, preconditions, or conditions that affect how claims are evaluated.</p>
<p>Additionally, you can check whether Automated Reasoning is translating accurately by anticipating the confidence threshold for translation from natural language to formal logic.</p>
<p>![提案](/assets/blog/authors/you/03/image-20251120-171402.png =300x)</p>
<p>You can also view variable Assignments that prove whether findings are valid, allowing you to quickly check examples of correct and incorrect scenarios.</p>
<p>Tests repeat this process to provide correct policies to LLM applications. To apply this policy to an LLM application, you must attach it to AWS Bedrock Guardrails and use the Guardrail.</p>
<h2>6. Conclusion</h2>
<p>From here, the discussion moves to practical operations of how to perform Automated Reasoning by attaching the created policy to a Guardrail. However, since Automated Reasoning Checks applied to actual applications exist as one feature of Bedrock Guardrails, I think it&#39;s better to explain together with Guardrails. I&#39;ll introduce this along with new Bedrock Guardrails features in the future.</p>
<p><img src="/assets/blog/authors/you/03/image-20251120-174237.png" alt="今回、解説した内容の図"></p>
<p>This article explained the concepts and mechanisms provided by AWS within the overall picture of Automated Reasoning, as well as the flow for designing and completing policies.</p>
<p>Automated Reasoning Checks are a generative AI feature provided by AWS—a tool for verifying the accuracy of AI-generated content. This prevents factual errors caused by AI hallucinations and uses mathematical logic and formal verification techniques to confirm accuracy. Specifically, through the process of policy creation, definition, testing, and annotation, it evaluates generative AI output and can prevent hallucinations. In other words, policies are auto-generated based on user-provided documents, and their accuracy is confirmed through testing. Policies built this way can achieve high accuracy in generative AI implementations through integration with AWS Bedrock Guardrails.</p>
<p>I hope this article has, in some way, deepened your understanding of Automated Reasoning.</p>
<hr>
<p>[^2]: When released in 2024, it was introduced as Automated Reasoning Checks since integration with Guardrails was fundamental. However, in the August GA this year, Automated Reasoning emerged as an independent Bedrock feature. Therefore, this article uses Automated Reasoning Checks when functioning from Guardrails and Automated Reasoning when functioning independently.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/you/03/1754496354180.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[生成AIの正確性を99%まで上げられる秘密：AWSの自動推論を実際に触ってみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-03-secret-to-increasing-accuracy-of-generative-ai-to-99/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-03-secret-to-increasing-accuracy-of-generative-ai-to-99/</guid>
            <pubDate>Wed, 03 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Amazon Bedrock 自動推論を実際に触って検証してみた]]></description>
            <content:encoded><![CDATA[<p>参照：「AWS Introduces Automated Reasoning Checks」<a href="https://www.linkedin.com/pulse/aws-introduces-automated-reasoning-checks-mathematical-certainty-lqvmc">^1</a></p>
<!-- ↓マークダウン チートシート↓ -->

<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の3日目の記事です🎅🎄</p>
<h2>0. はじめに</h2>
<p>KINTOテクノロジーズのCloud Infrastructure G(CIG)でInfrastructure Architectを担当している劉(YOU)です。</p>
<p>2024年12月、AWSは生成AIの数学的証明と論理的推論を実現することができる自動推論を<a href="https://aws.amazon.com/jp/blogs/news/prevent-factual-errors-from-llm-hallucinations-with-mathematically-sound-automated-reasoning-checks-preview/">re:inventで発表</a>しました。当時はAWS Bedrock Guardrailsの機能として自動推論チェックという名称で紹介されており、今年の8月にUS・EUの一部地域で一般公開されています。</p>
<p>このアプローチは、結果に確率を割り当てることで不確実性に対処する確率的推論方法とは根本的に異なります。実際、自動推論チェック<a href="2024%E5%B9%B4%E3%81%AB%E5%85%AC%E9%96%8B%E3%81%95%E3%82%8C%E3%81%9F%E6%99%82%E3%81%AB%E3%81%AF%E3%80%81%E3%82%AC%E3%83%BC%E3%83%89%E3%83%AC%E3%83%BC%E3%83%AB%E3%81%A8%E9%80%A3%E6%90%BA%E3%81%95%E3%82%8C%E3%82%8B%E3%81%93%E3%81%A8%E3%81%8C%E5%9F%BA%E6%9C%AC%E3%81%A7%E3%81%97%E3%81%9F%E3%81%AE%E3%81%A7%E3%80%81%E8%87%AA%E5%8B%95%E6%8E%A8%E8%AB%96%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%81%A8%E3%81%84%E3%81%86%E5%90%8D%E5%89%8D%E3%81%A7%E7%B4%B9%E4%BB%8B%E3%81%95%E3%82%8C%E3%81%BE%E3%81%97%E3%81%9F%E3%80%82%E3%81%97%E3%81%8B%E3%81%97%E3%80%81%E4%BB%8A%E5%B9%B48%E6%9C%88%E3%81%AEGA%E3%81%A7%E3%81%AFBedrock%E3%81%AE%E7%8B%AC%E7%AB%8B%E3%81%95%E3%82%8C%E3%81%9F%E6%A9%9F%E8%83%BD%E3%81%A8%E3%81%97%E3%81%A6%E8%87%AA%E5%8B%95%E6%8E%A8%E8%AB%96%E3%81%8C%E5%87%BA%E3%81%BE%E3%81%97%E3%81%9F%E3%80%82%E3%81%9D%E3%82%8C%E6%95%85%E3%80%81%E6%9C%AC%E8%A8%98%E4%BA%8B%E3%81%A7%E3%81%AF%E3%82%AC%E3%83%BC%E3%83%89%E3%83%AC%E3%83%BC%E3%83%AB%E3%81%8B%E3%82%89%E6%A9%9F%E8%83%BD%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E6%99%82%E3%81%AB%E3%81%AF%E8%87%AA%E5%8B%95%E6%8E%A8%E8%AB%96%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E3%80%81%E5%8D%98%E7%8B%AC%E3%81%A7%E6%A9%9F%E8%83%BD%E3%81%97%E3%81%A6%E3%81%84%E3%82%8B%E6%99%82%E3%81%AB%E3%81%AF%E8%87%AA%E5%8B%95%E6%8E%A8%E8%AB%96%E3%81%A8%E8%A8%98%E8%BF%B0%E3%81%97%E3%81%BE%E3%81%99%E3%80%82">^2</a>は最大99%の検証精度を提供し、AIのハルシネーションを検出する上で証明可能な保証を提供すると同時に、モデルの出力が複数の解釈に開放されているときに曖昧さの検出を支援します。</p>
<p>AWSが提供している自動推論を簡単にお伝えしますと、</p>
<blockquote>
<p>自動推論チェックは、基礎モデル（FM）によって生成されたコンテンツのドメイン知識に対する正確性を検証するのに役立ちます。これは、AIのハルシネーションによる事実の誤りを防ぐのに役立ちます。このポリシーは、数学的論理と正式な検証技術を使用して精度を検証し、AI応答が正確性をチェックするための決定的なルールとパラメータを提供します。</p>
</blockquote>
<p>上記の通り生成AIのハルシネーションの定量的な判断・持続的な追跡・応答の向上・更なる改善を果たすために
構成されています。このようなアプローチによって、不確実性に頼る確率的推論方法とは根本的に異なり、結果に確率を割り当てします。それがタイトルにも記載した通り<a href="https://aws.amazon.com/jp/blogs/aws/minimize-ai-hallucinations-and-deliver-up-to-99-verification-accuracy-with-automated-reasoning-checks-now-available/">最大99%の検証精度を提供</a>し、AIのハルシネーションを検出する上で証明可能な保証を提供すると同時に、モデルの出力が複数の解釈に開放されている時に曖昧さの検出を支援します。</p>
<p>自動推論自体はAWSが起案したことではなく、数理論理学を起源とする一分野です。それを利用し、ソフトウェア開発の検証技術を使い始めたことが元々の自動推論であって、その方法論を生成AI向けのサービスとして提供しています。</p>
<p>自動推論という概念についてもっと知りたい方はこちらの文書を参考にして下さい。</p>
<ul>
<li><a href="https://ja.wikipedia.org/wiki/%E8%87%AA%E5%8B%95%E6%8E%A8%E8%AB%96">自動推論</a> </li>
<li><a href="https://aws.amazon.com/what-is/automated-reasoning/">What is Automated Reasoning?</a></li>
</ul>
<p>自動推論はBedrock Guardrailsと連携しているサービスなので、Bedrock Guardrailsを理解していると分かりやすい部分があります。本記事では自動推論の機能の全体像について解説するため、Bedrock Guardrailsについての説明は省略しますのでご了承ください。
Bedrock Guardrailsにご興味のある方やもっと詳細を知りたい方は別記事を作成しているのでぜひご覧下さい。</p>
<ul>
<li><a href="https://blog.kinto-technologies.com/posts/2025-06-20-aws-bedrock-guardrails-deployment-inclusion-first-part/">AWS Bedrock Guardrailsの導入取り組み:前編-生成AIセキュリティの必要性</a> ：生成AIのガードレールの必要性</li>
<li><a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6?productVariantID=6A68KiYtH7WX6K7LA9XHNw">TECH BOOK By KINTO Technologies Vol.01：KINTOテクノロジーズ 執筆部</a> ：AWSでBedrock Guardrailsの実装方法</li>
</ul>
<h2>1. 本記事の対象読者・注意点・目的</h2>
<p>筆者は当初、自動推論は名前だけ見て生成AIのハルシネーションを簡単に防げる自動化サービスかなと考えました。しかし、手軽に触れる自動化とは距離が遠く、かなりレベルが高く感じたので、自動推論が必要な具体的な状況や注意点を先に説明します。</p>
<h3>対象読者</h3>
<ul>
<li>LLM レスポンスのハルシネーションを厳密に検出・追跡・応答の向上・改善したい方</li>
<li>高いガバナンスや誤りが許されないコンプライアンス要件がある生成AIの実装をしている方</li>
<li>複雑なルールや要件のある生成AIアプリケーションを開発している方</li>
<li>AWS中心の「責任ある生成AI」の実現を目指している方</li>
</ul>
<h3>注意点</h3>
<ul>
<li>本記事では英語環境での検証結果をもとに解説しています。今後、日本語対応が行われた際には挙動や仕様に変更が生じる可能性があります。</li>
<li>自動推論はユーザー側に提供されたテキスト・ドキュメントと関連する内容のみを分析し、検出する仕組みになっています。それ故、あくまでもハルシネーション・正確性を検証するサービスであって、自動的に制限・処理を行ったりしません。<a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/guardrails-automated-reasoning-checks.html">公式文書</a>でも、既存のBedrock Guardrailsフィルタと一緒に使うことをお勧めしています。</li>
</ul>
<blockquote>
<p>Amazon Bedrock Guardrailsの自動推論チェックは、プロンプトインジェクション攻撃から保護しません。これらのチェックは、あなたが送信した内容を正確に検証します。悪意のあるコンテンツや操作されたコンテンツが入力として提供された場合、検証はそのコンテンツに対してそのまま実行されます（不適切な入力・出力）。プロンプトインジェクション攻撃を検出してブロックするには、コンテンツフィルタと自動推論チェックを組み合わせて使用します。
自動推論は、自動推論ポリシーに関連するテキストのみを分析し、検出します。残りのコンテンツは無視され、回答がトピックから外れているかどうかを開発者に伝えることはできません。トピックから外れた応答を検出する必要がある場合は、トピックポリシーなどの他のガードレールコンポーネントを使用します。</p>
</blockquote>
<p>後述する内容ですが、自動推論はテキスト・ドキュメントを元に基本的なポリシーを自動生成してくれます。しかし、この自動生成されたポリシーを検討してテストする必要があります。入力したコンテキストの要件と自動推論の構造に対する理解の両方が必要ですので下記リンクの制限事項とベストプラクティスをご参照ください。</p>
<ul>
<li><a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/guardrails-automated-reasoning-checks.html#automated-reasoning-limitations">制約事項と考慮事項</a></li>
<li><a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/guardrails-automated-reasoning-checks.html#automated-reasoning-best-practices">ベストプラクティス</a></li>
</ul>
<h3>目的</h3>
<p>前述の「注意点」で取り上げた「自動推論の構造に対する理解」をメインに話したいと思います。それで、本記事としては大きな枠を理解することから下記の三つを知って頂ければと思います。</p>
<ul>
<li>自動推論がどんなパーツを持っていて、どうやって機能するかを知る</li>
<li>自動推論をどんなフローで検証するかを知る</li>
<li><del>自動推論をどういう時に活用できるかを知る</del>（こちらはガードレールの知識が要りますので、別記事で話します）</li>
</ul>
<h2>2. 全体像</h2>
<p>自動推論の全体像をざっくりと表しますと、下の図のようになっています。</p>
<p><img src="/assets/blog/authors/you/03/arc.drawio2-20251125-094007.png" alt="draw.ioを使った自動推論の処理の全体像"></p>
<p>手順としては、</p>
<ol>
<li>自動推論でテキスト・ドキュメントを入れると、自動で「ポリシー」が作成される</li>
<li>ポリシーは入力した情報を基にタイプ・変数・ルールの「定義」が自動生成される
  a. 追加のテキストやドキュメントを取り込み、ポリシーの定義を拡張できる
  b. 生成されたタイプ・変数・ルールを要件に合わせて編集し、整合性を確保する</li>
<li>ポリシーを検証するために「テスト」を作成する
  a. 手動で作成：QnA ペアを形式で仮定のインタラクションを入力
  b. 自動で作成：既存のルールを確認できるシナリオが自動で生成される</li>
<li>検証結果を確認して意図している結果が出るかを確認
  a. テスト実行結果で期待される結果と実際の結果が一致するかを確認
  b. 一致していない場合、5に戻る</li>
<li>一致していない原因を把握して定義の中で間違っている場所に「注釈」を付ける
  a. テスト実行結果の中で、原因を推測することができる情報が提示される
  b. 注釈を付けられる場所はタイプ、変数, ルールになっていて、主に自然言語になっている説明を修正したら、そこに合わせて修正されるようになっている</li>
<li>注釈適用をしたらそこに合わせた内容で修正案が出て、正しければ変更を受け入れる</li>
<li>3から6を繰り返してテキスト・ドキュメントの内容を守るためのポリシーを完成する</li>
<li>完成されたポリシーをガードレールに紐付ける</li>
<li>既存のガードレールがあるか、新しく作成する必要がある</li>
<li>LLMアプリケーションの入力・応答をガードレールに渡し、自動推論チェックの結果を活用する
  a. 成功した場合、ユーザー側に結果をそのまま返す
  b. 失敗した場合、自動推論チェックの結果を利用してLLMアプリケーションから再作成を要求する
  c. （オプション）自動推論チェックの結果をログとして保存し、ポリシーの見直しを続ける</li>
</ol>
<p>全体をまとめると、自動推論は「ポリシー」として作成されて「定義」→「テスト」→「注釈」を重ねながら完成することになります。完成されたポリシーをAWS Bedrock Guardrailに紐付けて、LLMの応答にガードレールを適用したら自動推論チェックを遂行します。その後の処理は開発者の意思によって異なりますが、LLMが正しい応答を出しているのかを定量的に判断ができるようになります。</p>
<p>自動推論を導入することで、悪性のものを遮断する既存のガードレールと並行しながら、ハルシネーションを無くす戦略を立てることが可能になります。そして、外部情報を参照するRAGとMCPを付けた生成AIの正確性を検証することもできるし、間違ったLLMの応答・開発者が制御できない誤りの入力を検知して修正できるのは非常に魅力的です。</p>
<p>次は、<a href="https://github.com/aws-samples/amazon-bedrock-samples/tree/main/responsible_ai/bedrock-automated-reasoning-checks">AWSから提供しているサンプル</a>を利用して上記の手順を沿い実際に「ポリシー」の作成から「定義」「テスト」「注釈」の三つを中心に解説します。</p>
<h2>3. ポリシー</h2>
<p>![ポリシー作成画面](/assets/blog/authors/you/03/image-20251120-094018.png =800x)</p>
<p>基本的に自動推論も他のAWSリソースと同じく、コンソール・CLI・SDKで操作することができます。</p>
<blockquote>
<p>CloudFormation は現在サポートされていません。CloudFormation のサポートは間もなく開始されます。</p>
</blockquote>
<p>コンソール上で自動推論のポリシーの作成は簡単に実施できます。</p>
<ul>
<li>名前</li>
<li>説明（オプション）</li>
<li>ソース（ドキュメント・テキスト）</li>
<li>ソースの説明</li>
</ul>
<p>上記の内容を記入して「ポリシー作成」ボタンを押すと、自動でポリシーの中身を作成してくれます。筆者はAWSのサンプルで準備された医療に関するPDFのファイルを使いましたが、5〜10分くらいでポリシーが生成されました。入れた文書の長さによって定義が作られる時間は変わると思います。
作られたポリシーを確認すると、次のような画面が出ます</p>
<p>![Overview](/assets/blog/authors/you/03/image-20251120-134516.png =800x)</p>
<h2>4. 定義</h2>
<p>ここで注目する所は、下にある定義(Definitions)です。このオーバービューの画面から定義の画面に遷移しますと、ルールと変数およびタイプが定義されていることが確認できます。</p>
<h3>タイプ</h3>
<p>![Custom variable types](/assets/blog/authors/you/03/image-20251120-134952.png =800x)</p>
<p>AWS側から事前に定義されているタイプ以外にも、ユーザーから提供された文書の中にタイプとして分類する項目があれば自動生成されます。タイプを持って変数を作り、ルールを正しく定義することが自動推論の基本になっています。各項目を説明すると、</p>
<ul>
<li>名前：タイプを定義する名称、変数で使われるキー</li>
<li>説明：自動推論が判断できるようにする内容を記述</li>
<li>値(Values)：区分される個別の値を記入</li>
<li>問題(Issues)：タイプで起こっている問題を表す</li>
<li>注釈：適用前の修正内容を表示する</li>
<li>アクション：更新、削除、リバート、三つの動作ができる</li>
</ul>
<p>タイプは変数で使われていない場合、画像のように使用されてないタイプ(Unused type)として警告が出ます。この時にはこのタイプを利用して変数を定義するか、削除するかをユーザー側で判断して扱うことができます。タイプは使われていなくても動作することに影響はないのですぐに解決しなくても大丈夫です。</p>
<p>ここでは、どこかで使用されている<code>RiskCategory</code>を基準にこの後の説明を続けます。</p>
<ul>
<li>名前：<code>RiskCategory</code>（リスクカテゴリ）</li>
<li>説明：30日間の再入院の推定リスクを示す、総リスクスコアに基づいて患者に割り当てられたリスクカテゴリ</li>
<li>値：<code>LOW_RISK</code>, <code>INTERMEDIATE_RISK</code>, <code>HIGH_RISK</code> (低リスク、中リスク、高リスク)</li>
</ul>
<h3>変数</h3>
<blockquote>
<p>変数は、自然言語を正式なロジックに変換するときに値を割り当てることができる自動推論ポリシーの概念を表します。ポリシールールは、これらの変数の有効または無効な値に対する制約を定義します。</p>
</blockquote>
<p>変数は60個が定義されてますが、<code>RiskCategory</code>を検索したらこのタイプが使われていた変数が確認できます。</p>
<p>![変数](/assets/blog/authors/you/03/image-20251120-142859.png =800x)</p>
<ul>
<li>名前：<code>riskCategory</code></li>
<li>カスタム変数タイプ：<code>RiskCategory</code></li>
<li>説明：総リスクスコアに基づいて患者に割り当てられたリスクカテゴリ</li>
</ul>
<p>自動推論は自然言語から形式ロジックへ翻訳するので、その精度は変数の記述品質に大きく依存します。そのため、ベストプラクティスにも「包括的な変数の説明を記述する」が記載されています。包括的な変数の説明がなければ、自動推論は、入力された自然言語を正式な論理表現に変換できないため、<code>NO_DATA</code>を返す可能性がありますのでご注意ください。</p>
<h3>ルール</h3>
<blockquote>
<p>ルールは、Automated Reasoning がソースドキュメントから抽出するロジックです。</p>
</blockquote>
<p>自動推論のロジックはSMT-LIBで作成されました。充足可能性モジュロ理論（SMT）は一次式の充足可能性をチェックする方法を研究する領域で、その一環として<a href="https://smt-lib.org/about.shtml">SMT-LIB</a>というSMT利用者の共通入力言語と出力言語が開発されています。自動推論ではルールの説明を変えるだけで式を修正してくれますが、SMT-LIB様式で修正することもできるので参考にしてください。</p>
<p> <code>riskCategory</code>が使われているルールは21個で、主に<code>riskCategory</code>＋他の変数条件でシナリオが作られています。</p>
<p>![ルール](/assets/blog/authors/you/03/image-20251120-144856.png =800x)</p>
<p>一番上にあるルールを解説しますと、<code>examplePatient</code>という変数が下記の定義を持っているので</p>
<ul>
<li><p>名前：<code>examplePatient</code></p>
</li>
<li><p>カスタム変数タイプ：Boolean</p>
</li>
<li><p>説明：これが特定の特徴を持つガイダンスからの患者の例であるかどうか</p>
</li>
<li><p>if <code>examplePatient</code> is true, then <code>riskCategory</code> is equal to <code>HIGH_RISK</code>
→ 特定の特徴を持つガイダンスがある患者の例で当てはまったらリスクカテゴリが高リスクである</p>
</li>
</ul>
<p>というシナリオを表現しているルールになります。では、このルールをテストしてみましょう。</p>
<h2>5. テスト</h2>
<p>テストを実施する方法は2つあります。</p>
<p>![テスト](/assets/blog/authors/you/03/image-20251120-153501.png =800x)</p>
<ul>
<li>question-and-answer (QnA) ペアを手動で定義</li>
<li>テストシナリオを自動的に生成</li>
</ul>
<p>本記事では直観的に確認できるようにするため、コンソール操作で手動定義を例に挙げますが、多数のテストを機械的に実行するにはCLIやSDKで自動生成を活用した方が楽です。手動定義でルールを検証する前に自動生成を利用してテストしてみます。</p>
<h3>自動生成テスト</h3>
<p>![自動生成](/assets/blog/authors/you/03/image-20251120-155533.png =800x)</p>
<p>コンソールでは生成のボタンを押すと、こういう形でテストシナリオを作成してくれます。
<code>riskCategory</code>と<code>maxPostDischargeTelephoneContactHours</code>が条件にありましたので、定義の画面から<code>LOW_RISK</code>で検索してみます。</p>
<p>![LOW_RISKの検索結果](/assets/blog/authors/you/03/image-20251120-155653.png =800x)</p>
<p>しかし、<code>maxPostDischargeTelephoneContactHours</code>の変数があるルールがないです。この変数の説明は「退院後、退院後の電話連絡が発生する最大時間」ですので、このシナリオの要件を知っているユーザーが妥当性を判断することができます。シナリオが違っていたと判断したら、説明を書くことでテストシナリオを修正してもらえることもできますが、一旦、提案してくれたままテストシナリオを作ってみます。</p>
<p>![テスト詳細画面](/assets/blog/authors/you/03/image-20251120-160258.png =800x)</p>
<p>そうしたら、作成されたテストケースに入ったらこのような画面が出て、テストを実行することができます。実行しますと、</p>
<p>![自動生成テスト実行結果](/assets/blog/authors/you/03/image-20251120-160500.png =800x)</p>
<p>条件と合っているルールがないのに成功しました。その理由はなんでしょうか？</p>
<p>自動で生成されたテストシナリオで想定した期待される結果（Expected result）が実際結果（Actual result）と一致していたからです。自動推論でテストの結果を<a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/validate-automated-reasoning-policy-results.html#automated-reasoning-test-validation-results">判断する基準は7つ</a>あります。</p>
<ul>
<li><code>VALID</code>：クレーム(Claim)がルールと論理的に一致</li>
<li><code>INVALID</code>：クレームがルールと論理的に矛盾・違反</li>
<li><code>SATISFIABLE</code>：クレームがルールの条件と少なくとも 1 つ一貫していますが、関連するすべてのルールに一致していない</li>
<li><code>IMPOSSIBLE</code>：自動推論ポリシー内に競合がある可能性</li>
<li><code>TRANSLATION_AMBIGUOUS</code>：自然言語からロジックへの翻訳で曖昧さが検出</li>
<li><code>TOO_COMPLEX</code>：入力に含まれる情報が多すぎる</li>
<li><code>NO_TRANSLATIONS</code>：入力プロンプトの一部またはすべてがロジックに変換されなかった</li>
</ul>
<p>![maxPostDischargeTelephoneContactHoursの検索結果](/assets/blog/authors/you/03/image-20251120-162758.png =800x)</p>
<ul>
<li><code>riskCategory</code> is equal to <code>LOW_RISK</code></li>
<li><code>maxPostDischargeTelephoneContactHours</code> is equal to 72</li>
</ul>
<p>それぞれのクレームがどこかのルールの条件の一つとして存在していたので<code>SATISFIABLE</code>の実際結果を出していて、テストシナリオではそれを踏まえて期待される結果にしたことになります。</p>
<h3>注釈</h3>
<p>ここで、要件が「退院後、退院後の電話連絡が発生する最大時間が120時間だったら、リスクカテゴリが低リスクである」だと仮定してみましょう。その場合、新しいルールを追加する必要があります。</p>
<p>![ルール追加](/assets/blog/authors/you/03/image-20251120-163917.png =800x)</p>
<p>この時に、追加・修正するために注釈を付けることができて、注釈を適用することが可能になります。</p>
<p>![注釈適用](/assets/blog/authors/you/03/image-20251120-164020.png =800x)</p>
<p>ここで進めると、</p>
<p>![ポリシー更新レビュー](/assets/blog/authors/you/03/image-20251120-164329.png =800x)</p>
<p>変更事項を確認して廃棄したり、承認したり、それともポリシーに戻って見直しすることができます。想定通りにルールが生成されましたので承認します。</p>
<p>![ルールの変更後画面](/assets/blog/authors/you/03/image-20251120-164652.png =800x)</p>
<p>ルールが反映されたら、実際結果が<code>INVALID</code>になって失敗に変わりました。ルールが新しく適用されたので明確に<code>INVALID</code>だと判断するように変わったので、テストシナリオの想定を変更する必要があります。</p>
<p>![テスト修正](/assets/blog/authors/you/03/image-20251120-164916.png =800x)</p>
<p>期待される結果を<code>INVALID</code>に変更したら、成功するように変わります。</p>
<p>![修正後のテスト実行結果](/assets/blog/authors/you/03/image-20251120-165158.png =800x)</p>
<h3>手動定義テスト</h3>
<p>結果をみて注釈を付けながら修正する流れは、手動で行うことも大きく変わることはないです。下記のような内容でテストを追加してみます。手動で入れる内容は実際のLLMアプリケーションの応答を真似して入れます。</p>
<p>![手動定義](/assets/blog/authors/you/03/image-20251120-165940.png =800x)</p>
<ul>
<li>インプット：ホゲホゲさんは医薬品の使用に注意が必要な患者です。この人のリスクカテゴリは？</li>
<li>アウトプット：特定の特徴を持つガイダンスがある患者なので、リスクカテゴリは高リスクである</li>
<li>期待される結果：<code>VALID</code></li>
</ul>
<p>![手動定義テスト実行結果](/assets/blog/authors/you/03/image-20251120-170523.png =800x)</p>
<p>結果は当然ですが、成功になっています。</p>
<p>ここで追加されたタイプが見えますが、質疑応答のテストで登場する前提（Premise）になります。今回は質問に特徴がある患者であることを前提としたように、クレームの評価方法に影響するコンテキスト、前提条件、または条件を提供するタイプです。</p>
<p>それ以外に、自動推論が自然言語から形式ロジックへの翻訳で持つ信頼スコア(Confidence threshold)を想定して、正確に翻訳しているのかを確認することもできて、</p>
<p>![提案](/assets/blog/authors/you/03/image-20251120-171402.png =300x)</p>
<p>調査結果が有効かどうかを証明する変数の割り当て(Assignments)を見て、正しいシナリオや正しくないシナリオの例をすぐに確認できます。</p>
<p>テストは今までの過程を重ねて、LLMアプリケーションに正しいポリシーを提供するようにします。このポリシーをLLMアプリケーションに適用するためにはAWS Bedrock Guardrailsに紐付けて、ガードレールを使用しなければなりません。</p>
<h2>6. まとめ</h2>
<p>この後は、作成したポリシーをガードレールに紐付けて、自動推論をどのように行うかの実運用の話になりますが、実際にアプリケーションに適用する自動推論チェックはBedrock Guardrailsの機能の一つとしてあるため、ガードレールの説明と一緒にした方がいいと思います。今後、Bedrock Guardrailsの新機能と一緒に紹介させてください。</p>
<p><img src="/assets/blog/authors/you/03/image-20251120-174237.png" alt="今回、解説した内容の図"></p>
<p>本記事では自動推論の全体像の中で、AWSが提供する概念と仕組み、そしてポリシーを設計・完成させるフローについて説明しました。</p>
<p>自動推論チェックは、AWSが提供する生成AIの機能であり、AIが生成したコンテンツの正確性を検証するためのツールです。これにより、AIのハルシネーションによる事実の誤りを防ぎ、数学的論理と形式的検証技術を使用して精度を確認します。具体的には、ポリシーの作成、定義、テスト、注釈のプロセスを経て、生成AIの出力を評価し、ハルシネーションを抑止することができます。言い換えますと、ユーザーが提供する文書を基にポリシーが自動生成され、テストを通じてその正確性を確認する仕組みです。これにより作り上げたポリシーはAWSのBedrock Guardrailsとの連携により、生成AIの実装において高い正確性を実現できます。</p>
<p>本記事を通じて、自動推論の理解が少しでも深めていただけたら嬉しいです。</p>
<hr>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/you/03/1754496354180.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Still Using Claude Code Out of the Box? An Android Developer Tries SubAgents & Skills]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-11-android-with-claude-skills-and-agents-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-11-android-with-claude-skills-and-agents-en/</guid>
            <pubDate>Wed, 03 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I wanted to use Claude Code more efficiently, so I tried out SubAgents and Skills. Here are their characteristics and examples of how to use them in Android development.]]></description>
            <content:encoded><![CDATA[<p>This article is the Day 3 entry for <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> 🎅🎄</p>
<h2>Introduction</h2>
<p>Hello, I&#39;m JongSeok, an Android app developer at KINTO Technologies.</p>
<p>While developing Android apps with Claude Code, I had been using the SubAgents feature a bit.
As I continued using it, I started thinking there might be ways to use it more efficiently, so I did some research.
That&#39;s when I discovered a recently announced feature called Skills.</p>
<p>I took this opportunity to try both SubAgents and Skills, and here&#39;s a summary of what I learned.</p>
<h2>1. SubAgents?</h2>
<p>In short, they are <strong>specialists with their own workspace</strong>.</p>
<p>When you normally chat with Claude Code, everything goes into a single context (conversation flow).
But SubAgents work in a separate context and only report back the results.
Think of it like a team leader delegating work to a specialist and receiving a report.</p>
<h3>1.1 Key Features</h3>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Independent Context</strong></td>
<td>Doesn&#39;t clutter the main conversation</td>
</tr>
<tr>
<td><strong>Specialized Prompts</strong></td>
<td>Can set role-specific instructions</td>
</tr>
<tr>
<td><strong>Tool Permission Restrictions</strong></td>
<td>Can allow only necessary features</td>
</tr>
</tbody></table>
<h3>1.2 How to Create</h3>
<p>There are two ways to create SubAgents.</p>
<p><strong>1. Create via Command (Recommended)</strong></p>
<p>In Claude Code, you can easily create them with the <code>/agents</code> command.</p>
<table>
<thead>
<tr>
<th>List</th>
<th>Make</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/jongseok/claudecode/agent-1.png" alt="List"></td>
<td><img src="/assets/blog/authors/jongseok/claudecode/agent-2.png" alt="make"></td>
</tr>
</tbody></table>
<p>You can check Agents defined in the Project or create new ones.</p>
<p><strong>2. Create Files Directly</strong>
You can also add <code>.md</code> files to the <code>.claude/agents/</code> folder.
<img src="/assets/blog/authors/jongseok/claudecode/agent-3.png" alt="tree"></p>
<h3>1.3 Use Case: kotlin-method-namer</h3>
<p>Coming up with method names in Kotlin can be surprisingly tricky.
So I created a SubAgent that suggests method names following Android/Kotlin style.</p>
<p><strong>When to Use?</strong></p>
<ul>
<li>When you&#39;re unsure how to express a method&#39;s functionality in English</li>
<li>When you want to verify if it follows Kotlin naming conventions</li>
<li>When you want better method name candidates</li>
</ul>
<pre><code class="language-md">---
name: kotlin-method-namer
description: Expert for suggesting Android/Kotlin method names
tools: Read, Glob, Grep
model: sonnet
color: cyan
---

You are an expert Android Kotlin developer specializing in 
creating clear, idiomatic method names.
...
</code></pre>
<p>(<a href="/assets/blog/authors/jongseok/claudecode/kotlin-method-namer.md">Full version</a>)</p>
<p><strong>DEMO</strong>
SubAgents need to be called directly by name.
Since I set <code>color: cyan</code>, you can see when the Agent is running.
<img src="/assets/blog/authors/jongseok/claudecode/agent-demo.gif" alt="AgentDemo"></p>
<table>
<thead>
<tr>
<th>DEMO Result</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/jongseok/claudecode/agent-4.png" alt="result"></td>
</tr>
<tr>
<td>It suggested the name <code>initializeVariable()</code>.</td>
</tr>
</tbody></table>
<p>As you can see, SubAgents let you create Agents specialized for specific tasks.
If you have recurring specialized work, turning them into SubAgents might improve your efficiency.</p>
<h2>2. Skills?</h2>
<p>In short, they are <strong>your personal work guidebook</strong>.</p>
<p>If SubAgents are specialists with their own workspace,
Skills are like adding specialized knowledge to the main agent.</p>
<p>If you prepare a guidebook in advance for Claude to reference during work,
it will automatically recognize and use it when you request related tasks.</p>
<h3>2.1 Key Features</h3>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Integrated into Main Context</strong></td>
<td>Unlike SubAgents, operates in the main conversation, not a separate space</td>
</tr>
<tr>
<td><strong>Auto-invoked</strong></td>
<td>Claude automatically uses it when you request related work</td>
</tr>
<tr>
<td><strong>Progressive Disclosure</strong></td>
<td>Loads only necessary information in stages</td>
</tr>
<tr>
<td><strong>Deterministic</strong></td>
<td>Script execution guarantees consistent results</td>
</tr>
</tbody></table>
<h3>2.2 What is Progressive Disclosure?</h3>
<p>This is the core design principle of Skills.
Instead of loading everything at once, it retrieves only the necessary information in stages.</p>
<table>
<thead>
<tr>
<th>Stage</th>
<th>Content Loaded</th>
<th>Timing</th>
</tr>
</thead>
<tbody><tr>
<td>Stage 1</td>
<td><code>name</code>, <code>description</code></td>
<td>Loaded into system prompt at startup</td>
</tr>
<tr>
<td>Stage 2</td>
<td>SKILL.md body</td>
<td>When related work is requested</td>
</tr>
<tr>
<td>Stage 3</td>
<td>Additional files (scripts, references, etc.)</td>
<td>Only when needed</td>
</tr>
</tbody></table>
<p>Like a well-organized guidebook, it reads only what&#39;s needed: table of contents -&gt; relevant chapter -&gt; appendix.
This means <strong>you can install multiple Skills without wasting context.</strong></p>
<h3>2.3 How to Create</h3>
<p>Create a skill folder inside the <code>.claude/skills/</code> folder and add a <code>SKILL.md</code> file.</p>
<pre><code>project/
└── .claude/
    └── skills/
        └── your-skill-name/
            └── SKILL.md
</code></pre>
<p>SKILL.md</p>
<pre><code class="language-md">---
name: wildcard-import-fixer
description: Converts wildcard imports (e.g., import java.util.*) to specific imports in Kotlin/Java files.
allowed-tools: Bash, Read, Glob, Grep
---
</code></pre>
<p><strong>YAML Frontmatter</strong></p>
<table>
<thead>
<tr>
<th>Item</th>
<th>Rule</th>
<th align="center">Required</th>
</tr>
</thead>
<tbody><tr>
<td><code>name</code></td>
<td>Lowercase + hyphens, 64 characters or less</td>
<td align="center">✅</td>
</tr>
<tr>
<td><code>description</code></td>
<td>200 characters or less, critical for Claude to determine when to use it</td>
<td align="center">✅</td>
</tr>
<tr>
<td><code>version</code></td>
<td>For version management (e.g., 1.0.0)</td>
<td align="center">-</td>
</tr>
</tbody></table>
<p><strong>SKILL.md Body</strong></p>
<ul>
<li>Recommended to be under 500 lines</li>
<li>Split into separate files if it gets long (e.g., <code>reference.md</code>, <code>scripts/</code>)</li>
</ul>
<p><strong>Security</strong></p>
<ul>
<li>Never hardcode sensitive information like API keys or passwords</li>
<li>Only install skills from trusted sources</li>
</ul>
<blockquote>
<p>💡 <strong>Difference from SubAgents</strong></p>
<ul>
<li>SubAgents: <code>.claude/agents/filename.md</code> (file-based)</li>
<li>Skills: <code>.claude/skills/skill-name/SKILL.md</code> (folder-based)</li>
</ul>
</blockquote>
<h3>2.4 Use Case: wildcard-import-fixer</h3>
<blockquote>
<p>💡 Skills can execute scripts.
Since this is an Android project, I used Kotlin, but Python and JavaScript (npm) are also supported.</p>
</blockquote>
<p>I created a Skill that converts common Kotlin wildcard imports (<code>import java.util.*</code>) to individual imports.</p>
<p><strong>When to Use?</strong></p>
<ul>
<li>When you want to clean up wildcard imports</li>
<li>When you want to improve code quality</li>
<li>When you want to make import statements explicit</li>
</ul>
<p><strong>Folder Structure</strong></p>
<pre><code>.claude/skills/wildcard-import-fixer/
├── SKILL.md                  ← Required
├── scripts/
│   └── fix-wildcards.kts     ← Optional (add yourself)
├── templates/
│   └── report_template.html  ← Optional (add yourself)
├── backups/                  ← Auto-generated (when script runs)
└── reports/                  ← Auto-generated (when script runs)
</code></pre>
<blockquote>
<p>💡 Only <code>SKILL.md</code> is required.
Other files and folders can be freely structured according to your needs.</p>
</blockquote>
<p><strong>Why Use Scripts?</strong></p>
<p>If you have the LLM generate code each time, results can vary slightly.
But if you prepare scripts in advance, <strong>the same results are guaranteed every time</strong>.</p>
<p>This is the <strong>Deterministic</strong> characteristic of Skills.</p>
<p><strong>SKILL.md</strong></p>
<pre><code class="language-md">---
name: wildcard-import-fixer
description: Converts wildcard imports (e.g., import java.util.*) to specific imports in Kotlin/Java files.
allowed-tools: Bash, Read, Glob, Grep
---

# Wildcard Import Fixer

Automatically converts wildcard imports to specific imports 
in Kotlin and Java files by analyzing actual class usage in the code.

## Instructions

### Step 1: Analyze the Request
When a user asks to fix wildcard imports:
1. Determine the scope (entire project, specific directory, or single file)
2. Decide if a dry-run preview is appropriate first
3. Check if backups should be created

### Step 2: Run the Fixer Script
Execute the appropriate command based on the scope...

### Step 3: Review Results
After running, check the console output and HTML report...

### Step 4: Handle Edge Cases
If no usage is detected, suggest removing the unused import...
</code></pre>
<p>(<a href="/assets/blog/authors/jongseok/claudecode/SKILL.md">Full version</a>)</p>
<blockquote>
<p>💡 Writing instructions in this Step format helps Claude follow them in order.</p>
</blockquote>
<p><strong>DEMO</strong></p>
<p><img src="/assets/blog/authors/jongseok/claudecode/skills-demo.gif" alt="SkillsDemo">
In this demo, after running once, I reset the conversation with <code>/clear</code> and requested the same task again.</p>
<ul>
<li>Even after <code>/clear</code>, the Skills&#39; <code>name</code> and <code>description</code> are reloaded (Progressive Disclosure)</li>
<li>Since it&#39;s script execution, <strong>the same results are returned every time</strong> (Deterministic)</li>
</ul>
<p>This is the strength of Skills.</p>
<table>
<thead>
<tr>
<th>DEMO Result</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/jongseok/claudecode/skills-result.png" alt="result"></td>
</tr>
<tr>
<td>I also made it generate HTML for easy result viewing.</td>
</tr>
</tbody></table>
<h2>3. SubAgents vs Skills</h2>
<ul>
<li><strong>SubAgents</strong>: Specialists working in a separate room. They only report back results.</li>
<li><strong>Skills</strong>: Manuals you keep on hand. Referenced when needed for work.</li>
</ul>
<h3>3.1 Comparison Table</h3>
<table>
<thead>
<tr>
<th>Item</th>
<th>SubAgents</th>
<th>Skills</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Context</strong></td>
<td>Independent (separate workspace)</td>
<td>Integrated into main</td>
</tr>
<tr>
<td><strong>Invocation</strong></td>
<td>Called directly by name</td>
<td>Auto-invoked</td>
</tr>
<tr>
<td><strong>File Structure</strong></td>
<td><code>.claude/agents/filename.md</code></td>
<td><code>.claude/skills/skill-name/SKILL.md</code></td>
</tr>
<tr>
<td><strong>Unit</strong></td>
<td>File-based</td>
<td>Folder-based</td>
</tr>
<tr>
<td><strong>Script Execution</strong></td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td><strong>Best For</strong></td>
<td>Complex workflows, parallel tasks</td>
<td>Repetitive routine tasks</td>
</tr>
</tbody></table>
<h3>3.2 When to Use Each</h3>
<p><strong>When to Use SubAgents</strong></p>
<ul>
<li>When you need a specialized perspective, like code reviews</li>
<li>When you don&#39;t want to clutter the main conversation</li>
<li>For complex tasks with multiple steps</li>
</ul>
<p><strong>When to Use Skills</strong></p>
<ul>
<li>When you repeat the same task</li>
<li>When you want consistent results (Deterministic)</li>
<li>When you want to reuse across multiple projects</li>
</ul>
<h3>3.3 Using Them Together</h3>
<p>SubAgents and Skills <strong>complement each other</strong> rather than compete.</p>
<p><strong>Example: Automating PR Reviews</strong></p>
<ol>
<li><code>compose-reviewer</code> (SubAgent) reviews Compose code</li>
<li><code>kotlin-style-checker</code> (Skill) performs style checks</li>
<li><code>test-generator</code> (SubAgent) generates test code</li>
</ol>
<p>SubAgents handle specialized judgment while Skills handle consistent rule checking.</p>
<h2>4. Summary</h2>
<p>After trying SubAgents and Skills, I&#39;ve started to see when to use each.</p>
<table>
<thead>
<tr>
<th>When You Need</th>
<th>Use</th>
</tr>
</thead>
<tbody><tr>
<td>A specialized perspective for reviews</td>
<td>SubAgents</td>
</tr>
<tr>
<td>To keep the main conversation clean</td>
<td>SubAgents</td>
</tr>
<tr>
<td>To run the same task with identical results every time</td>
<td>Skills</td>
</tr>
<tr>
<td>To automate with scripts</td>
<td>Skills</td>
</tr>
<tr>
<td>Complex workflows requiring both</td>
<td>Combination</td>
</tr>
</tbody></table>
<p>I&#39;ve just started using them, but it seems like delegating repetitive tasks to Skills and tasks requiring specialized judgment to SubAgents improves efficiency.</p>
<p>If you&#39;re interested, give them a try!</p>
<h2>References</h2>
<p><strong>SubAgents</strong></p>
<ul>
<li><a href="https://docs.claude.com/en/docs/claude-code/sub-agents">SubAgents - Claude Docs</a></li>
</ul>
<p><strong>Skills</strong></p>
<ul>
<li><a href="https://docs.claude.com/en/docs/claude-code/skills">Agent Skills - Claude Docs</a></li>
<li><a href="https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills">Equipping agents for the real world with Agent Skills</a></li>
<li><a href="https://github.com/anthropics/skills">anthropics/skills - GitHub</a></li>
</ul>
<p><strong>Comparison</strong></p>
<ul>
<li><a href="https://www.claude.com/blog/skills-explained">Skills explained - Claude Blog</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/jongseok/claudecode/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[まだClaude Codeを素のまま使ってますか？― Android開発者がSubAgents & Skillsを試してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-11-android-with-claude-skills-and-agents/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-11-android-with-claude-skills-and-agents/</guid>
            <pubDate>Wed, 03 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Claude Codeをもっと効率的に使いたくて、SubAgentsとSkillsを試してみました。それぞれの特徴と、Androidでの活用例を紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2025</a> の3日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>はじめまして。KINTOテクノロジーズでAndroidアプリ開発を担当しているJongSeokです。</p>
<p>Claude Codeを使ってAndroid開発をしながら、SubAgents機能は少し使っていました。
使っているうちに、もっと効率よく使えるんじゃないかと思い始めて、いろいろ調べてみました。
そしたら最近発表された Skills という機能も見つけました。</p>
<p>この機会に SubAgents と Skills、両方試してみたので、その内容をまとめてみました。</p>
<h2>1. SubAgents?</h2>
<p>一言で言うと <strong>「別の作業スペースを持つ専門家」</strong> です。</p>
<p>普通にClaude Codeと会話すると、すべてが一つのコンテキスト（会話の流れ）に入ります。
でもSubAgentsは別のコンテキストで作業して、結果だけ報告してくれます。
チームで例えると、リーダーが専門家に仕事を任せて結果報告を受けるイメージですね。</p>
<h3>1.1 主な特徴</h3>
<table>
<thead>
<tr>
<th>特徴</th>
<th>説明</th>
</tr>
</thead>
<tbody><tr>
<td><strong>独立したコンテキスト</strong></td>
<td>メインの会話を汚さない</td>
</tr>
<tr>
<td><strong>専門化されたプロンプト</strong></td>
<td>役割に特化した指示を設定できる</td>
</tr>
<tr>
<td><strong>ツール権限の制限</strong></td>
<td>必要な機能だけ許可できる</td>
</tr>
</tbody></table>
<h3>1.2 作り方</h3>
<p>SubAgentsを作る方法は2つあります。</p>
<p><strong>1. コマンドで作成（推奨）</strong></p>
<p>Claude Codeでは <code>/agents</code> コマンドで簡単に作成できます。</p>
<table>
<thead>
<tr>
<th>List</th>
<th>Make</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/jongseok/claudecode/agent-1.png" alt="List"></td>
<td><img src="/assets/blog/authors/jongseok/claudecode/agent-2.png" alt="make"></td>
</tr>
</tbody></table>
<p>Projectに定義されているAgentsの確認や、新規作成ができます。</p>
<p><strong>2. 直接ファイルを作成</strong>
<code>.claude/agents/</code> フォルダに <code>.md</code> ファイルを追加して作ることもできます。
<img src="/assets/blog/authors/jongseok/claudecode/agent-3.png" alt="tree"></p>
<h3>1.3 活用例：kotlin-method-namer</h3>
<p>Kotlinでメソッド名を考えるのは意外と悩むポイントです。<br>そこで、Android/Kotlinのスタイルに合ったメソッド名を提案してくれるSubAgentを作ってみました。</p>
<p><strong>どんな時に使う？</strong></p>
<ul>
<li>メソッドの機能を英語でどう表現するか迷う時</li>
<li>Kotlinの命名規則に合っているか確認したい時</li>
<li>より良いメソッド名の候補が欲しい時</li>
</ul>
<pre><code class="language-md">---
name: kotlin-method-namer
description: Expert for suggesting Android/Kotlin method names
tools: Read, Glob, Grep
model: sonnet
color: cyan
---

You are an expert Android Kotlin developer specializing in 
creating clear, idiomatic method names.
...
</code></pre>
<p>(<a href="/assets/blog/authors/jongseok/claudecode/kotlin-method-namer.md">Full version</a>)</p>
<p><strong>DEMO</strong>
SubAgentsは直接指定して呼ぶ必要があります。
<code>color: cyan</code>を設定したので、Agentが実行されているのが分かります。
<img src="/assets/blog/authors/jongseok/claudecode/agent-demo.gif" alt="AgentDemo"></p>
<table>
<thead>
<tr>
<th>DEMOの結果</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/jongseok/claudecode/agent-4.png" alt="result"></td>
</tr>
<tr>
<td><code>initializeVariable()</code>という名前を提案してくれました。</td>
</tr>
</tbody></table>
<p>このように、SubAgentsは特定の作業に特化したAgentsを作れます。<br>繰り返し発生する専門的な作業があれば、SubAgentsにしてみると効率が上がるかもしれません。</p>
<h2>2. Skills?</h2>
<p>一言で言うと <strong>「自分だけの業務ガイドブック」</strong> です。</p>
<p>SubAgentsが別の作業スペースを持つ専門家だとすれば、<br>Skillsはメインエージェントに専門知識を追加するイメージです。</p>
<p>Claudeが作業する時に参考にするガイドブックを事前に作っておくと、<br>関連する作業を依頼した時に自動で認識して使ってくれます。</p>
<h3>2.1 主な特徴</h3>
<table>
<thead>
<tr>
<th>特徴</th>
<th>説明</th>
</tr>
</thead>
<tbody><tr>
<td><strong>メインコンテキストに統合</strong></td>
<td>SubAgentsと違い、別空間ではなくメインの会話で動作</td>
</tr>
<tr>
<td><strong>自動認識 (Auto-invoked)</strong></td>
<td>関連する作業を依頼すると、Claudeが自動で使用</td>
</tr>
<tr>
<td><strong>Progressive Disclosure</strong></td>
<td>必要な情報だけを段階的にロード</td>
</tr>
<tr>
<td><strong>Deterministic</strong></td>
<td>スクリプト実行で一貫性のある結果を保証</td>
</tr>
</tbody></table>
<h3>2.2 Progressive Disclosureとは？</h3>
<p>Skillsの核心となる設計原則です。<br>一度にすべての内容をロードせず、段階的に必要な情報だけを取得します。</p>
<table>
<thead>
<tr>
<th>段階</th>
<th>ロードされる内容</th>
<th>タイミング</th>
</tr>
</thead>
<tbody><tr>
<td>1段階</td>
<td><code>name</code>, <code>description</code></td>
<td>起動時にシステムプロンプトへロード</td>
</tr>
<tr>
<td>2段階</td>
<td>SKILL.md 本文</td>
<td>関連する作業を依頼した時</td>
</tr>
<tr>
<td>3段階</td>
<td>追加ファイル (scripts, references など)</td>
<td>必要な時だけ</td>
</tr>
</tbody></table>
<p>整理されたガイドブックのように、目次 → 該当チャプター → 付録の順で必要なものだけ読む方式です。<br>これにより、<strong>複数のSkillsをインストールしてもコンテキストを無駄にせず使えます。</strong></p>
<h3>2.3 作り方</h3>
<p><code>.claude/skills/</code> フォルダ内にスキルフォルダを作成し、<code>SKILL.md</code> ファイルを追加します。</p>
<pre><code>project/
└── .claude/
    └── skills/
        └── your-skill-name/
            └── SKILL.md
</code></pre>
<p>SKILL.md</p>
<pre><code class="language-md">---
name: wildcard-import-fixer
description: Converts wildcard imports (e.g., import java.util.*) to specific imports in Kotlin/Java files.
allowed-tools: Bash, Read, Glob, Grep
---
</code></pre>
<p><strong>YAML Frontmatter</strong></p>
<table>
<thead>
<tr>
<th>項目</th>
<th>ルール</th>
<th align="center">必須</th>
</tr>
</thead>
<tbody><tr>
<td><code>name</code></td>
<td>小文字 + ハイフン使用、64文字以下</td>
<td align="center">✅</td>
</tr>
<tr>
<td><code>description</code></td>
<td>200文字以下、Claudeがいつ使うか判断する重要な部分</td>
<td align="center">✅</td>
</tr>
<tr>
<td><code>version</code></td>
<td>バージョン管理用 (例: 1.0.0)</td>
<td align="center">-</td>
</tr>
</tbody></table>
<p><strong>SKILL.md 本文</strong></p>
<ul>
<li>500行以下推奨</li>
<li>長くなったら別ファイルに分離 (例: <code>reference.md</code>, <code>scripts/</code>)</li>
</ul>
<p><strong>セキュリティ</strong></p>
<ul>
<li>APIキー、パスワードなどの機密情報をハードコーディング禁止</li>
<li>信頼できるソースのスキルのみインストール</li>
</ul>
<blockquote>
<p>💡 <strong>SubAgentsとの違い</strong>  </p>
<ul>
<li>SubAgents: <code>.claude/agents/ファイル名.md</code> (ファイル単位)</li>
<li>Skills: <code>.claude/skills/スキル名/SKILL.md</code> (フォルダ単位)</li>
</ul>
</blockquote>
<h3>2.4 活用例: wildcard-import-fixer</h3>
<blockquote>
<p>💡 Skillsではスクリプトを実行できます。<br>今回はAndroidプロジェクトなのでKotlinを使用しましたが、Python、JavaScript（npm）なども対応しています。</p>
</blockquote>
<p>Kotlinでよくある wildcard import (<code>import java.util.*</code>) を個別のimportに変換するSkillを作ってみました。</p>
<p><strong>どんな時に使う？</strong></p>
<ul>
<li>wildcard importを整理したい時</li>
<li>コード品質を改善したい時</li>
<li>import文を明確にしたい時</li>
</ul>
<p><strong>フォルダ構成</strong></p>
<pre><code>.claude/skills/wildcard-import-fixer/
├── SKILL.md                  ← 必須
├── scripts/
│   └── fix-wildcards.kts     ← 任意（自分で追加）
├── templates/
│   └── report_template.html  ← 任意（自分で追加）
├── backups/                  ← 自動生成（スクリプト実行時）
└── reports/                  ← 自動生成（スクリプト実行時）
</code></pre>
<blockquote>
<p>💡 必須なのは <code>SKILL.md</code> だけです。<br>その他のファイルやフォルダは用途に合わせて自由に構成できます。</p>
</blockquote>
<p><strong>なぜスクリプトを使う？</strong></p>
<p>LLMにコードを毎回生成させると、結果が微妙に変わることがあります。<br>でもスクリプトを事前に用意しておけば、<strong>毎回同じ結果</strong>が保証されます。</p>
<p>これがSkillsの <strong>Deterministic</strong> な特徴です。</p>
<p><strong>SKILL.md</strong></p>
<pre><code class="language-md">---
name: wildcard-import-fixer
description: Converts wildcard imports (e.g., import java.util.*) to specific imports in Kotlin/Java files.
allowed-tools: Bash, Read, Glob, Grep
---

# Wildcard Import Fixer

Automatically converts wildcard imports to specific imports 
in Kotlin and Java files by analyzing actual class usage in the code.

## Instructions

### Step 1: Analyze the Request
When a user asks to fix wildcard imports:
1. Determine the scope (entire project, specific directory, or single file)
2. Decide if a dry-run preview is appropriate first
3. Check if backups should be created

### Step 2: Run the Fixer Script
Execute the appropriate command based on the scope...

### Step 3: Review Results
After running, check the console output and HTML report...

### Step 4: Handle Edge Cases
If no usage is detected, suggest removing the unused import...
</code></pre>
<p>(<a href="/assets/blog/authors/jongseok/claudecode/SKILL.md">Full version</a>)</p>
<blockquote>
<p>💡 このようにStep形式で指示を書くと、Claudeが順番通りに作業を進めてくれます。</p>
</blockquote>
<p><strong>DEMO</strong></p>
<p><img src="/assets/blog/authors/jongseok/claudecode/skills-demo.gif" alt="SkillsDemo">
このデモでは、一度実行した後に <code>/clear</code> で会話をリセットして、もう一度同じ作業を依頼しています。</p>
<ul>
<li><code>/clear</code> してもSkillsの <code>name</code> と <code>description</code> は再ロードされる（Progressive Disclosure）</li>
<li>スクリプト実行なので、<strong>毎回同じ結果</strong>が返ってくる（Deterministic）</li>
</ul>
<p>これがSkillsの強みです。</p>
<table>
<thead>
<tr>
<th>DEMOの結果</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/jongseok/claudecode/skills-result.png" alt="result"></td>
</tr>
<tr>
<td>結果を一目で確認しやすくhtmlにもするようにしました。</td>
</tr>
</tbody></table>
<h2>3. SubAgents vs Skills</h2>
<ul>
<li><strong>SubAgents</strong>: 別室で作業する専門家。結果だけ報告してくれる。</li>
<li><strong>Skills</strong>: 手元に置いておくマニュアル。必要な時に参照して作業する。</li>
</ul>
<h3>3.1 比較表</h3>
<table>
<thead>
<tr>
<th>項目</th>
<th>SubAgents</th>
<th>Skills</th>
</tr>
</thead>
<tbody><tr>
<td><strong>コンテキスト</strong></td>
<td>独立（別の作業スペース）</td>
<td>メインに統合</td>
</tr>
<tr>
<td><strong>呼び出し方</strong></td>
<td>直接指定して呼ぶ</td>
<td>自動認識（Auto-invoked）</td>
</tr>
<tr>
<td><strong>ファイル構成</strong></td>
<td><code>.claude/agents/ファイル名.md</code></td>
<td><code>.claude/skills/スキル名/SKILL.md</code></td>
</tr>
<tr>
<td><strong>単位</strong></td>
<td>ファイル単位</td>
<td>フォルダ単位</td>
</tr>
<tr>
<td><strong>スクリプト実行</strong></td>
<td>❌</td>
<td>✅</td>
</tr>
<tr>
<td><strong>向いている作業</strong></td>
<td>複雑なワークフロー、並列作業</td>
<td>繰り返しのルーティン作業</td>
</tr>
</tbody></table>
<h3>3.2 使い分け</h3>
<p><strong>SubAgentsを使う場面</strong></p>
<ul>
<li>コードレビューなど、専門的な視点が必要な時</li>
<li>メインの会話を汚したくない時</li>
<li>複数のステップがある複雑な作業</li>
</ul>
<p><strong>Skillsを使う場面</strong></p>
<ul>
<li>同じ作業を繰り返す時</li>
<li>一貫した結果が欲しい時（Deterministic）</li>
<li>複数のプロジェクトで再利用したい時</li>
</ul>
<h3>3.3 組み合わせて使う</h3>
<p>SubAgentsとSkillsは競合するものではなく、<strong>補完関係</strong>にあります。</p>
<p><strong>例: PRレビューの自動化</strong></p>
<ol>
<li><code>compose-reviewer</code> (SubAgent) がComposeコードをレビュー</li>
<li><code>kotlin-style-checker</code> (Skill) でスタイルチェック</li>
<li><code>test-generator</code> (SubAgent) がテストコードを生成</li>
</ol>
<p>SubAgentが専門的な判断を、Skillが一貫したルールチェックを担当します。</p>
<h2>4. まとめ</h2>
<p>今回SubAgentsとSkillsを試してみて、使い分けが少し見えてきました。</p>
<table>
<thead>
<tr>
<th>こんな時は</th>
<th>使うもの</th>
</tr>
</thead>
<tbody><tr>
<td>専門的な視点でレビューしてほしい</td>
<td>SubAgents</td>
</tr>
<tr>
<td>メインの会話を汚したくない</td>
<td>SubAgents</td>
</tr>
<tr>
<td>同じ作業を毎回同じ結果で実行したい</td>
<td>Skills</td>
</tr>
<tr>
<td>スクリプトで自動化したい</td>
<td>Skills</td>
</tr>
<tr>
<td>両方必要な複雑なワークフロー</td>
<td>組み合わせ</td>
</tr>
</tbody></table>
<p>まだ使い始めたばかりですが、繰り返しの作業はSkillsに、専門的な判断が必要な作業はSubAgentsに任せると効率が上がりそうです。</p>
<p>興味がある方はぜひ試してみてください！</p>
<h2>References</h2>
<p><strong>SubAgents</strong></p>
<ul>
<li><a href="https://docs.claude.com/en/docs/claude-code/sub-agents">SubAgents - Claude Docs</a></li>
</ul>
<p><strong>Skills</strong></p>
<ul>
<li><a href="https://docs.claude.com/en/docs/claude-code/skills">Agent Skills - Claude Docs</a></li>
<li><a href="https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills">Equipping agents for the real world with Agent Skills</a></li>
<li><a href="https://github.com/anthropics/skills">anthropics/skills - GitHub</a></li>
</ul>
<p><strong>比較</strong></p>
<ul>
<li><a href="https://www.claude.com/blog/skills-explained">Skills explained - Claude Blog</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/jongseok/claudecode/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Accessibility-Friendly Social Media Posts Anyone Can Create]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-02-SNSA11y-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-02-SNSA11y-en/</guid>
            <pubDate>Tue, 02 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Alt text was properly added to our posts on X (formerly Twitter). We spoke with the person in charge about their accessibility mindset—something they were able to focus on precisely because they were an accessibility beginner.]]></description>
            <content:encoded><![CDATA[<p>Hello, I&#39;m Moriya (emim) from the Engineering Office.</p>
<p>In this article, I&#39;ll be reporting on the topic mentioned in the title as a cross-post for December 2nd of both the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a> and the <a href="https://adventar.org/calendars/11364">Accessibility Advent Calendar 2025</a>.</p>
<p>I work in the Engineering Office, a division dedicated to strengthening our organization’s development capabilities, where I focus on design. Alongside that, as a personal ongoing activity, I engage in advocacy to raise awareness about digital accessibility.</p>
<p>I was amazed by a staff member who spontaneously did some wonderful accessibility work, and I asked him about what he did and what motivated him.</p>
<h2>Who Added Alt Text to the X (formerly Twitter) Post!?</h2>
<p>First, please take a look at the following post.</p>
<p><a href="https://x.com/KintoTech_Dev/status/1976163362016043509?s=20">https://x.com/KintoTech_Dev/status/1976163362016043509?s=20</a></p>
<p>Does it just look like an introduction to our Fukuoka office?</p>
<p>Take a close look: the attached photo has been given wonderfully thoughtful alt text.</p>
<p>![KINTOテクノロジーズの10月9日のX投稿に4枚の写真が添付されていて、すべてに「ALT」ボタンが表示されている様子のキャプチャー](/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_post1.png =600x)</p>
<p>![ビルの写真に「KINTOテクノロジーズの福岡オフィスが入居する福岡大名ガーデンシティのビル外観」と代替テキストが付いているのをポップアップしたところ](/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_post2.png =500x)</p>
<p>We have a chat channel called team-accessibility within the company where we regularly share information. However, the person who made this post wasn&#39;t someone who actively participated in those discussions, so I was absolutely astonished.</p>
<p>Someone unexpectedly added proper alt text!!!</p>
<p>I spoke with the following two people for this article:</p>
<ul>
<li>Takenaka<ul>
<li>Background: After 12 years of development and PM experience at a system integrator, worked as a Scrum Master for in-house development at a bank. Currently serves as manager of the Developer Relations Group.</li>
<li>Is the one who made the X post</li>
</ul>
</li>
<li><a href="/authors/acf3af63-9aa2-58be-b785-971d0a0d2a75/">Yukachi</a><ul>
<li>Background: First job was in the travel industry; moved to IT after being hit by the COVID-19 pandemic. At KINTO Technologies, originally handled accounting work, then over time transitioned to a dedicated role in the Developer Relations Group, handling event management and social media outreach.</li>
<li>Is very interested in accessibility recently</li>
</ul>
</li>
</ul>
<h2>About the Alt Text in This Post</h2>
<p>The alt text added to these four images in the post. One screen reader user commented that it was high quality and wonderful, very readable (or rather, easy to listen to), and described it with an abstract expression as alt text that feels like a gentle breeze.</p>
<p>So I asked Takenaka why he added alt text this time, and received the following response.</p>
<p>—— (Takenaka) I had heard of digital accessibility, but didn&#39;t really understand what it meant. This time, the ALT button happened to stand out when I was posting on X, and I&#39;d been exposed to accessibility information daily through the team-accessibility chat. I&#39;d also heard it was gaining attention, so I gave it a go.</p>
<p>I’d been putting things out there without knowing who’d see them, and it’s wild that they had a subliminal impact here!</p>
<p>I also asked if they had any difficulty writing it.</p>
<p>—— (Takenaka) I understood that I needed to write it, but I didn’t know how. So right before, I did a quick internet search and tried to keep it concise, making sure not to include anything outside the main text. I only found out how it would actually appear after checking the published post.</p>
<p>I understood that this is a feature often overlooked unless you pay attention. For people familiar with accessibility, the button labeled ALT may be visible at a glance, but it’s important to keep in mind that some people won’t notice it at all.</p>
<h2>Insights Gained Through Alt Text</h2>
<p>Isn’t it wonderful to imagine a world where even those who aren’t very aware of accessibility add alternative text? I was both surprised and touched, so I posted about how I tracked down who had added it internally, and that post received quite a lot of likes.</p>
<p><a href="https://x.com/emim/status/1977921547869561149?s=20">https://x.com/emim/status/1977921547869561149?s=20</a></p>
<p>When I shared the feedback again, Yukachi was really moved. She’s not an engineer, so she usually doesn’t show up in the accessibility chat.</p>
<p>I asked both of them again about these responses.</p>
<p>—— (Takenaka) I was surprised it became a topic, and I realized that even alt text alone could spark reactions.</p>
<p>—— (Yukachi) I simply wasn&#39;t aware that you could add alt text, and I learned for the first time that adding it helps some people. Accessibility was something I’d never really thought about, but now I’m curious if there’s something I can do too.</p>
<p>Hearing these words, I felt I&#39;d received answers that clearly showed how even small things, when communicated as feedback, can influence people&#39;s behavior. She also added the following opinion.</p>
<p>—— (Yukachi) I think it&#39;s important, but since I don&#39;t know what to do, it would be nice to have more learning opportunities that even beginners can understand.</p>
<p>In recent years, accessibility-related study groups have certainly increased compared to the past. However, one of the industry’s challenges is that they tend to skip the basics and cater more to advanced participants.</p>
<p>On the other hand, the Digital Agency released the <a href="https://www.digital.go.jp/resources/standard_guidelines">Digital Society Promotion Standard Guidelines</a> in October, which includes DS-672.1 Web Accessibility Guidebook for Public Relations, summarized for PR personnel (accessibility beginners).</p>
<p><a href="https://www.digital.go.jp/resources/standard_guidelines">https://www.digital.go.jp/resources/standard_guidelines</a></p>
<p>We’re planning to use these publicly available materials to organize study sessions for the Developer Relations Group. After checking with the Digital Agency, we received confirmation that we’re free to use them as much as we like. If we have the capacity, we’d also like to invite external participants to join these sessions. So please look forward to what we’ll be doing next year!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_title.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[専門家でなくてもできたアクセシビリティを意識したSNS投稿]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-02-SNSA11y/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-02-SNSA11y/</guid>
            <pubDate>Tue, 02 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[X（旧Twitter）にポストの際に適切に代替テキストが付与されていました。アクセシビリティ初心者だからこそ意識できた、アクセシビリティについての心がけを担当者に聞きました。]]></description>
            <content:encoded><![CDATA[<p>こんにちは、Engineering Officeの守谷（emim）です。</p>
<p>この記事では、<a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a>と<a href="https://adventar.org/calendars/11364">アクセシビリティ Advent Calendar 2025</a>の12月2日のクロスポストとして表題の件をレポートしていきます。</p>
<p>普段わたしは社内で、組織の開発力を上げることをミッションにした部署（Engineering Office）でデザイン周りのことを考える傍ら、個人活動でライフワークとしているアクセシビリティの啓発活動を行っています。</p>
<p>そこで、想定外（？）に自発的に素敵なアクセシビリティ活動を行ってくれたスタッフがいたので、その内容と心意気について伺いました。</p>
<h2>X（旧Twitter）ポストに誰が代替テキストを！？</h2>
<p>まずは、以下のポストをぜひご覧ください。</p>
<p><a href="https://x.com/KintoTech_Dev/status/1976163362016043509?s=20">https://x.com/KintoTech_Dev/status/1976163362016043509?s=20</a></p>
<p>ただの弊社福岡オフィスのご紹介に見えますか？</p>
<p>よくご覧ください、添付されている写真に、なんとも素敵な代替テキスト（alt）が付与されています。</p>
<p>![KINTOテクノロジーズの10月9日のX投稿に4枚の写真が添付されていて、すべてに「ALT」ボタンが表示されている様子のキャプチャー](/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_post1.png =600x)</p>
<p>![ビルの写真に「KINTOテクノロジーズの福岡オフィスが入居する福岡大名ガーデンシティのビル外観」と代替テキストが付いているのをポップアップしたところ](/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_post2.png =500x)</p>
<p>社内のチャットに「team-accessibility」というものがあり、普段から情報共有などを行っています。しかしこのポストを行ってくれたのは、そこで前のめりで発言している方でもなかったので、わたしは仰天しました。</p>
<p>そんな方でも適切な代替テキストを付けてくれるとは！！！</p>
<p>今回お話しを伺ったのは、以下の2人です。</p>
<ul>
<li>竹中さん<ul>
<li>経歴：SIer企業で開発やPMを12年経験した後、銀行の内製開発でスクラムマスターを担当<br>  現在は技術広報のマネージャーを務める</li>
<li>ポストをされた張本人</li>
</ul>
</li>
<li><a href="/authors/acf3af63-9aa2-58be-b785-971d0a0d2a75/">ゆかちさん</a><ul>
<li>経歴：1社目は旅行業界、コロナ禍の打撃を受けIT業界へ<br>  KINTOテクノロジーズではもともと経理事務を担当〜時を経て技術広報専任となり、イベント運営やSNSの発信などを担当している</li>
<li>アクセシビリティについて今とても関心を高めている人</li>
</ul>
</li>
</ul>
<p>（身内ですが敬称付きで統一して掲載しています。）</p>
<h2>このポストの代替テキストについて</h2>
<p>この4枚の投稿に付けられた代替テキスト。とあるスクリーンリーダーユーザーは「クオリティが高くて素敵ですね！とても読みやすい（聞きやすい）です」「抽象的な表現なのですが、そよ風が吹くような代替テキスト」と評してくれました。</p>
<p>そこで、どうして今回代替テキストを付けようと思ったか、竹中さんに聞いてみると、以下のような意見をいただきました。</p>
<p>―― （竹中さん）アクセシビリティは聞いたことがあったけれど、内容についてはあまり理解していませんでした。今回は、X投稿時にたまたま「ALT」と書かれたボタンが目立っていたことと、「team-accessibility」のチャットで日々アクセシビリティの情報に触れており、世間でも注目が高まっていると聞いていたので、設定しようと思いました。</p>
<p>普段から誰に届くともわからず伝えていたことが、こんな所でサブリミナル効果を発揮するとは！</p>
<p>記述方法など困らなかったか？も聞いてみました。</p>
<p>―― （竹中さん）記載の必要があることはわかったけれど具体がわからなかったため、直前にネット検索を行い「簡潔に、本文に書いていない内容を入れない」ことを意識しました。投稿されるまでどんな感じで入るのかわからなかった為、投稿されたものを確認して「こうなるのか」と初めてわかりました。</p>
<p>改めて、普段何気なく利用していると気付かない機能だということがわかりました。アクセシビリティに慣れている人だと、「ALT」と書かれたボタンを目視できたりもしますが、そこも気付かない人も居るということを意識する必要がありそうです。</p>
<h2>「代替テキスト」を通しての気付き</h2>
<p>「アクセシビリティを普段意識してない」人が「代替テキストをつける」世界線、とても素敵ではないですか？個人的にびっくり（ほっこり）したので「誰が付けたか社内で探し出したよ」という旨をポストしたら、結構な「いいね」をいただきました。</p>
<p><a href="https://x.com/emim/status/1977921547869561149?s=20">https://x.com/emim/status/1977921547869561149?s=20</a></p>
<p>これらの反響があったことを、更に改めて共有をしたら感動してくれたのが、ゆかちさんです。ゆかちさんも普段は（エンジニアではないこともあり）アクセシビリティのチャットには出てこない方です。</p>
<p>2人にあらためて、この反響について尋ねてみました。</p>
<p>―― （竹中さん）話題になるんだ？とびっくりしました。代替テキストだけで反応があることに、なるほどと思いました。</p>
<p>―― （ゆかちさん）シンプルに「代替テキストを付けられる」ことを把握していなかったし、なおかつこれをつけることで助かる人がいるんだな、ということを初めて知りました。アクセシビリティという概念全体が今まで気にしていなかったジャンルだし、自分でもできることがあるのかなと気になりだしました。</p>
<p>この言葉を聞いて、ちょっとしたことでもフィードバックとして伝えると「人の行動に影響を与える」ということが明らかになったように思う回答を得られました。また、さらにこのような意見も加えてくれました。</p>
<p>―― （ゆかちさん）大事なことだとは思うけれど、何をしていいのかがわからないため、初心者でもわかる学習機会がもっとあるといいですね。</p>
<p>昨今、アクセシビリティ界隈では過去に比べ、確かに勉強会は増えてきています。それでも前提を飛ばした上級者向けになってきているのも業界課題です。</p>
<p>一方で、デジタル庁が10月にまとめて公開してくれた「<a href="https://www.digital.go.jp/resources/standard_guidelines">デジタル社会推進標準ガイドライン</a>」に、広報担当者（アクセシビリティ初心者）向けにまとめた「DS-672.1 ウェブアクセシビリティ広報向けガイドブック」などがあります。</p>
<p><a href="https://www.digital.go.jp/resources/standard_guidelines">https://www.digital.go.jp/resources/standard_guidelines</a></p>
<p>こういった公開資料を利用して、技術広報メンバー向けの勉強会などを企画しようと考えています。きちんとデジタル庁の担当の方にも確認をしたら「いくらでも使ってください」との回答をいただきました。余裕があったら、外部の方も招待する形での勉強会などもやってみたいと考えていますので、来年の我々に乞うご期待を！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/emim/2025-12-02-SNSA11y/2025-12-02-SNSA11y_title.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Rule-Based Schema-Driven Development with Orval × Feature-Sliced Design]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-02-schema-driven-development-with-orval-and-fsd-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-02-schema-driven-development-with-orval-and-fsd-en/</guid>
            <pubDate>Tue, 02 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article introduces a schema-driven development approach that combines Orval—which auto-generates type definitions and TanStack Query hooks from OpenAPI—with Feature-Sliced Design and its layer-based directory rules.]]></description>
            <content:encoded><![CDATA[<p>This article is the Day 2 entry of the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a>.</p>
<h2>Introduction</h2>
<p>Hello!
I&#39;m high-g (<a href="https://x.com/high_g_engineer">@high_g_engineer</a>) from the Master Maintenance Tool Development Team in the KINTO Backend Development Group, KINTO Development Division at Osaka Tech Lab.</p>
<p>In modern frontend development with heavy API integration, have you ever experienced challenges like these?</p>
<ul>
<li>Manually writing API type definitions often leads to missed updates when the spec changes</li>
<li>Auto-generated files scattered everywhere often make it unclear where to import from</li>
<li>Team members interpreting directory structures differently often lead to debates during code reviews</li>
</ul>
<p>The keywords to solve these challenges are <strong>type safety</strong>, <strong>schema-driven</strong>, <strong>auto-generation</strong>, and <strong>directory design</strong>.</p>
<p>This article introduces an approach where OpenAPI serves as the single source of truth for auto-generating type-safe code, managed according to Feature-Sliced Design rules.</p>
<p>Specifically, we&#39;ll walk through what code Orval generates and explain effective design patterns aligned with Feature-Sliced Design&#39;s directory structure.</p>
<h3>What This Article Covers</h3>
<ul>
<li>The flow of outputting types and custom hooks from OpenAPI using Orval</li>
<li>Detailed examples of the code Orval generates</li>
<li>Feature-Sliced Design&#39;s layer structure and import rules</li>
<li>Design patterns for managing Orval-generated code within Feature-Sliced Design&#39;s directory structure</li>
</ul>
<h3>Target Audience</h3>
<ul>
<li>Frontend developers tired of manually managing REST APIs and type definitions</li>
<li>Developers using TypeScript + React</li>
<li>Those interested in designs resilient to API changes</li>
<li>Those interested in establishing directory structure rules</li>
</ul>
<h2>Foundational Knowledge</h2>
<h3>OpenAPI</h3>
<p>OpenAPI is a standard for defining HTTP APIs in a machine-readable format. By describing API specifications in YAML or JSON, you gain benefits like:</p>
<ul>
<li>Clearly defined API inputs and outputs</li>
<li>Automated documentation generation</li>
<li>Prevention of discrepancies between client and server</li>
</ul>
<h4>Example: Partial OpenAPI Definition (Simplified)</h4>
<pre><code class="language-yaml">openapi: 3.1.0
paths:
  /posts:
    get:
      summary: Get list of posts
      parameters:
        - name: page
          in: query
          schema:
            type: integer
      responses:
        &quot;200&quot;:
          description: Success
          content:
            application/json:
              schema:
                $ref: &quot;#/components/schemas/GetPostsResponse&quot;
    post:
      summary: Create a post
      requestBody:
        content:
          application/json:
            schema:
              $ref: &quot;#/components/schemas/CreatePostRequest&quot;
      responses:
        &quot;201&quot;:
          description: Created
          content:
            application/json:
              schema:
                $ref: &quot;#/components/schemas/CreatePostResponse&quot;
  /posts/{postId}:
    put:
      summary: Update a post
      parameters:
        - name: postId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: &quot;#/components/schemas/UpdatePostRequest&quot;
      responses:
        &quot;200&quot;:
          description: Success
          content:
            application/json:
              schema:
                $ref: &quot;#/components/schemas/Post&quot;
    delete:
      summary: Delete a post
      parameters:
        - name: postId
          in: path
          required: true
          schema:
            type: string
      responses:
        &quot;204&quot;:
          description: No Content
components:
  schemas:
    Post:
      type: object
      required: [id, title, createdAt, updatedAt, status]
      properties:
        id:
          type: string
        title:
          type: string
        body:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        status:
          type: string
          enum: [draft, published, archived]
    GetPostsResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: &quot;#/components/schemas/Post&quot;
        total:
          type: integer
        page:
          type: integer
    CreatePostRequest:
      type: object
      required: [title]
      properties:
        title:
          type: string
        body:
          type: string
    CreatePostResponse:
      allOf:
        - $ref: &quot;#/components/schemas/Post&quot;
        - type: object
          properties:
            createdBy:
              type: string
    UpdatePostRequest:
      type: object
      properties:
        title:
          type: string
        body:
          type: string
        status:
          type: string
          enum: [draft, published, archived]
</code></pre>
<p>This YAML defines the following:</p>
<ul>
<li><code>/posts</code> endpoint: list retrieval (GET) and creation (POST)</li>
<li><code>/posts/{postId}</code> endpoint: update (PUT) and deletion (DELETE)</li>
</ul>
<h3>About Schema-Driven Development</h3>
<h4>Problems with Traditional Manual Management</h4>
<p>Previously, frontend developers performed tasks like these manually:</p>
<pre><code class="language-typescript">// Manually writing type definitions
type Post = {
  id: string;
  title: string;
  body?: string;
  createdAt: string;
  updatedAt: string;
  status: &quot;draft&quot; | &quot;published&quot; | &quot;archived&quot;;
};

// Manually writing API calls
const getPost = async (id: string): Promise&lt;Post&gt; =&gt; {
  const response = await fetch(`/api/posts/${id}`);
  return response.json();
};
</code></pre>
<p>This approach has the following problems:</p>
<ol>
<li><p><strong>Cost of manual updates</strong></p>
<ul>
<li>Check OpenAPI → manually update type definitions → update all usage sites</li>
</ul>
</li>
<li><p><strong>Risk of missed updates</strong></p>
<ul>
<li>Type definitions and actual API specs get out of sync</li>
<li>Easy to miss updates when the same type is used in multiple places</li>
</ul>
</li>
<li><p><strong>Documentation and code desynchronization</strong></p>
<ul>
<li>OpenAPI ≠ implementation code can happen</li>
</ul>
</li>
</ol>
<h4>The Schema-Driven Development Approach</h4>
<p>To address these manual management problems, a development methodology emerged: define the schema (API specification) first, then proceed with implementation.</p>
<pre><code class="language-shell">Traditional: Implementation → Documentation (afterthought) → Discrepancies with spec

Schema-driven: Schema definition → Auto-generation → Implementation → Done
              (Implementation = Documentation, always in sync)
</code></pre>
<h4>Characteristics of Schema-Driven Development</h4>
<ul>
<li><strong>Implementation = Documentation</strong>: API specs and code are always synchronized</li>
<li><strong>Type safety</strong>: API inconsistencies detected at compile time</li>
<li><strong>Development efficiency</strong>: No manual type definition work</li>
<li><strong>Team collaboration</strong>: Both frontend and backend reference the same OpenAPI</li>
</ul>
<h3>Orval</h3>
<p>Orval is a tool that <strong>auto-generates</strong> TypeScript type definitions and custom hooks <strong>with a single command</strong> from OpenAPI specifications.</p>
<h4>Main Features of Orval</h4>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Auto-generated type definitions</strong></td>
<td>Automatically creates types for API requests and responses</td>
</tr>
<tr>
<td><strong>Auto-generated custom hooks</strong></td>
<td>Also auto-generates hooks for TanStack Query and others</td>
</tr>
<tr>
<td><strong>Multiple library support</strong></td>
<td>Supports not just TanStack Query but also Axios and other libraries</td>
</tr>
<tr>
<td><strong>Mock generation</strong></td>
<td>Can also generate mock data for testing</td>
</tr>
</tbody></table>
<h4>Benefits of Using Orval</h4>
<ul>
<li><strong>Time savings</strong>: Zero time spent hand-writing type definitions or API call code</li>
<li><strong>Error prevention</strong>: Eliminates typos and spec misreadings from manual writing</li>
<li><strong>Always current</strong>: Just regenerate when OpenAPI is updated to stay current</li>
</ul>
<h4>Orval&#39;s Role in Schema-Driven Development</h4>
<p>Summarizing the content so far, Orval&#39;s role in schema-driven development is as follows:</p>
<pre><code class="language-shell">OpenAPI (single source of truth)
    ↓
Auto-generation by Orval keeps type definitions + TanStack Query hooks always in sync
    ↓
Low-cost, type-safe development is possible
</code></pre>
<h3>Feature-Sliced Design</h3>
<p>As mentioned at the beginning, the ongoing project adopts <strong>Feature-Sliced Design</strong> as an architectural pattern for frontend code organization.</p>
<p>Feature-Sliced Design is an architecture that organizes the codebase using three concepts: <strong>Layers</strong>, <strong>Slices</strong>, and <strong>Segments</strong>.</p>
<table>
<thead>
<tr>
<th>Concept</th>
<th>Description</th>
<th>Examples</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Layer</strong></td>
<td>Division by application responsibility. From top: <code>app</code> → <code>pages</code> → <code>features</code> → <code>entities</code> → <code>shared</code> (5 layers). <code>app</code> handles routing and layouts for the entire app, <code>pages</code> handles screens corresponding to URLs</td>
<td><code>app/</code>, <code>pages/</code>, <code>features/</code></td>
</tr>
<tr>
<td><strong>Slice</strong></td>
<td>Division unit by business domain or feature within each layer</td>
<td><code>features/auth/</code>, <code>entities/user/</code></td>
</tr>
<tr>
<td><strong>Segment</strong></td>
<td>Division by technical role within a slice</td>
<td><code>ui/</code>, <code>model/</code>, <code>api/</code></td>
</tr>
</tbody></table>
<pre><code class="language-shell">src/
├── features/          ← Layer
│   ├── auth/          ← Slice
│   │   ├── ui/        ← Segment
│   │   ├── model/     ← Segment
│   │   └── index.ts
</code></pre>
<p>This structure clarifies where to put what, enabling the team to unify code placement rules.</p>
<h4>Feature-Sliced Design Directory Structure</h4>
<p>Our team operates with the following directory structure. The segment divisions (<code>api/</code>, <code>model/</code>, <code>ui/</code>, etc.) are customized to fit the project.</p>
<pre><code class="language-shell">workspaces/typescript/src/
├── app/               ← ① Application layer: routing, global settings
│   ├── layouts/         Layouts used across all pages
│   ├── routes/          Routing definitions
│   └── App.tsx          Root tsx file
│
├── pages/             ← ② Pages layer: each page component (corresponds to URL)
│   ├── users/
│   └── login/
│
├── features/          ← ③ Features layer: reusable business logic
│   ├── {slice}/           Divided by domain into units called slices (e.g., user, auth)
│   │   ├── {component}/   Components belonging to the domain
│   │   │   ├── model/       Logic portion
│   │   │   ├── ui/          UI portion
│   │   │   └── index.ts     Public API (barrel file)
│   │   ...
│   ...
│
├── entities/          ← ④ Entities layer: business domain definitions
│   ├── user/            Various domains
│   │   ├── @x             Cross-import notation *described later
│   │   ├── api/           Imports and uses auto-generated files from shared/ (facade)
│   │   │   ├── hooks.ts     API hooks
│   │   │   └── index.ts     Public API (barrel file)
│   │   ├── model/       Domain logic
│   │   ├── ui/          Minimal UI staying within the domain
│   │   └── index.ts     Public API (barrel file)
│   ...
│
└── shared/            ← ⑤ Shared layer: project-independent utilities
    ├── api/
    │   └── generated/   Auto-generated files by Orval (modification prohibited)
    │       ├── types.ts
    │       ├── hooks.ts
    │       └── client.ts
    ├── config/          Configuration constants
    ├── errors/          Commonly used error functions
    ├── lib/             Utility functions
    └── ui/              Generic UI components
</code></pre>
<h4>Feature-Sliced Design Layer Import Restriction Rules</h4>
<p>The most important rule of Feature-Sliced Design: <strong>A layer can only import from layers below itself.</strong>
Additionally, mutual imports between the same layer are also prohibited in principle (exception described later with <code>@x</code> notation).</p>
<pre><code class="language-shell">app ← Top level (highest abstraction)
  ↓ import allowed
pages
  ↓
features
  ↓
entities

* shared can be imported from any layer
</code></pre>
<p>This means the following rules are established within the project:</p>
<ul>
<li>✅ pages/ can import from features/, entities/, shared/</li>
<li>✅ features/ can import from entities/, shared/</li>
<li>✅ entities/ can import from shared/</li>
<li>❌ entities/ must not import from features/ or pages/</li>
<li>❌ shared/ must not import from any other layer</li>
</ul>
<h4>Special Role of the Entities Layer: entities/@x (Cross-Import Notation)</h4>
<p>However, in the entities layer, business domains often relate to each other. For example, cases like &quot;Post references User&quot; occur.</p>
<p>To solve this, a special import method allowed only within the entities layer is the <code>@x</code> notation.</p>
<h5>Directory Structure Example</h5>
<pre><code class="language-shell">entities/
├── user/
│   ├── @x/
│   │   └── post.ts       # Types/functions exposed for external slices
│   ├── model/
│   │   └── types.ts      # Type definitions used internally
│   ├── ui/
│   └── index.ts          # Normal public API
│
└── post/
    ├── model/
    │   └── usePost.ts    # Wants to reference user&#39;s types from here
    └── index.ts
</code></pre>
<h5>Usage Example</h5>
<pre><code class="language-typescript">// When using entities/user from entities/post/model/usePost.ts

// ❌ Normal import (Feature-Sliced Design violation: import between same layer)
import type { User } from &quot;@/entities/user&quot;;

// ✅ Cross-import using @x (allowed)
import type { User } from &quot;@/entities/user/@x/post&quot;;

// The @x directory represents &quot;cross-import-specific API that this slice exposes externally.&quot;
</code></pre>
<p>By using <code>@x</code>, it becomes explicit that something is intentionally exposed externally, making dependency tracking easier.</p>
<p>Now that we&#39;ve covered the foundational knowledge, let&#39;s get into the main topic.</p>
<h2>How to Use Orval and Output Code</h2>
<h3>Setup</h3>
<p>The ongoing project uses <strong>pnpm</strong> as the package manager.
Also, we&#39;ll proceed assuming OpenAPI is already defined.</p>
<pre><code class="language-bash"># Install Orval
pnpm add -D orval
</code></pre>
<p>Next, create the Orval configuration file.
Note: hooks are defined to format auto-generated code with Biome.</p>
<pre><code class="language-typescript">// orval.config.ts
import { defineConfig } from &quot;orval&quot;;

const API_DIR = &quot;./src/shared/api&quot;;
const INPUT_DIR = &quot;../../docs/api&quot;;
const GENERATED_DIR = `${API_DIR}/generated`;

export default defineConfig({
  postApi: {
    hooks: {
      afterAllFilesWrite: &quot;pnpm format:write:generate&quot;,
    },
    input: {
      target: `${INPUT_DIR}/openapi.yaml`,
    },
    output: {
      clean: true,
      biome: true,
      client: &quot;react-query&quot;,
      override: {
        mutator: {
          path: `${API_DIR}/customInstance.ts`,
          name: &quot;useCustomInstance&quot;,
        },
        query: {
          useSuspenseQuery: true,
          version: 5,
        },
      },
      schemas: `${GENERATED_DIR}/model`,
      target: `${GENERATED_DIR}/hooks/index.ts`,
    },
  },
});
</code></pre>
<p>Next, create a custom instance that executes API requests. This is used as the <code>mutator</code> specified in the Orval configuration.</p>
<pre><code class="language-typescript">// src/shared/api/customInstance.ts

import { ApiHttpError, type ErrorDetail } from &quot;../errors&quot;;
import { getAccessToken } from &quot;../lib&quot;;

const BASE_URL = import.meta.env.VITE_API_BASE_URL || &quot;&quot;;

// Type definition for request configuration
export type RequestConfig = {
  url: string;
  method: &quot;GET&quot; | &quot;POST&quot; | &quot;PUT&quot; | &quot;DELETE&quot; | &quot;PATCH&quot;;
  headers?: Record&lt;string, string&gt;;
  params?: Record&lt;string, unknown&gt;;
  data?: unknown;
  signal?: AbortSignal;
};

// Request function using Fetch API
const fetchApi = async &lt;T&gt;(config: RequestConfig): Promise&lt;T&gt; =&gt; {
  const { url, method, headers = {}, params, data, signal } = config;

  // Get authentication token
  const token = getAccessToken();

  // Build query parameters
  const queryString = params
    ? `?${new URLSearchParams(params as Record&lt;string, string&gt;).toString()}`
    : &quot;&quot;;
  const fullUrl = `${BASE_URL}${url}${queryString}`;

  // Build headers
  const requestHeaders: Record&lt;string, string&gt; = {
    &quot;Content-Type&quot;: &quot;application/json&quot;,
    ...headers,
  };

  if (token) {
    requestHeaders.Authorization = `Bearer ${token}`;
  }

  // Build request options
  const options: RequestInit = {
    method,
    headers: requestHeaders,
    signal,
  };

  if (data &amp;&amp; [&quot;POST&quot;, &quot;PUT&quot;, &quot;PATCH&quot;].includes(method)) {
    options.body = JSON.stringify(data);
  }

  const response = await fetch(fullUrl, options);

  // Error handling
  if (!response.ok) {
    let errorMessage = `API error: ${response.status}`;
    let errorDetails: ErrorDetail[] = [];

    try {
      const errorData = await response.json();
      errorDetails = errorData?.errors?.details ?? [];
      if (typeof errorData.message === &quot;string&quot;) {
        errorMessage = errorData.message;
      }
    } catch {
      // Use default message if JSON parsing fails
    }

    throw new ApiHttpError({
      status: response.status,
      message: errorMessage,
      details: errorDetails,
    });
  }

  // For 204 No Content
  if (response.status === 204) {
    return null as T;
  }

  return response.json();
};

// Custom instance function used by Orval
export const useCustomInstance = &lt;T&gt;(config: RequestConfig): Promise&lt;T&gt; =&gt; {
  const controller = new AbortController();

  const promise = fetchApi&lt;T&gt;({
    ...config,
    signal: controller.signal,
  });

  // For TanStack Query&#39;s cancel functionality
  // @ts-expect-error dynamically adding cancel property
  promise.cancel = () =&gt; controller.abort();

  return promise;
};

export default useCustomInstance;
</code></pre>
<p>This <code>useCustomInstance</code> is used when executing HTTP requests within the hooks that Orval generates. You can centralize project-specific settings here, such as attaching authentication tokens and error handling.</p>
<p>In actual projects, token refresh processing and retry logic are often added. For details, see the <a href="https://orval.dev/guides/custom-client">Orval Official Documentation - Custom Client</a>.</p>
<p>All that’s left is to run the code generation.</p>
<pre><code class="language-bash"># Run code generation
pnpm orval
</code></pre>
<p>In the ongoing project, we periodically run <code>pnpm orval</code> to batch-apply API spec changes.</p>
<h3>Actual Examples of Orval-Generated Code</h3>
<p>Now let&#39;s look at specific examples of what Orval actually generates.</p>
<h4>Generated Output 1: Type Definitions</h4>
<p>From OpenAPI&#39;s <code>Post</code> schema, TypeScript types like the following are auto-generated.</p>
<pre><code class="language-typescript">// src/shared/api/generated/types.ts
// ↓ Auto-generated from OpenAPI

export type Post = {
  id: string;
  title: string;
  body?: string;
  createdAt: string; // ISO 8601 format
  updatedAt: string;
  status: &quot;draft&quot; | &quot;published&quot; | &quot;archived&quot;;
};

export type GetPostsResponse = {
  items: Post[];
  total: number;
  page: number;
};

export type CreatePostRequest = {
  title: string;
  body?: string;
};

export type CreatePostResponse = Post &amp; {
  createdBy: string;
};

export type UpdatePostRequest = {
  title?: string;
  body?: string;
  status?: &quot;draft&quot; | &quot;published&quot; | &quot;archived&quot;;
};
</code></pre>
<h5>Important Points</h5>
<ul>
<li>OpenAPI schemas become types directly</li>
<li><code>enum</code> is converted to TypeScript Union Types</li>
<li>Required/optional (<code>?</code>) distinction is automatically determined</li>
<li>Since it&#39;s a generated file, do not modify it (will be overwritten on next run)</li>
</ul>
<h4>Generated Output 2: TanStack Query Custom Hooks</h4>
<p>Orval also auto-generates TanStack Query hooks. The following is a simplified example for easier understanding (actual generated code includes custom instances and detailed type definitions).</p>
<pre><code class="language-typescript">// src/shared/api/generated/hooks.ts
// ↓ Orval generates TanStack Query hooks (simplified example)

import { useSuspenseQuery, useMutation } from &quot;@tanstack/react-query&quot;;
import type {
  UseSuspenseQueryOptions,
  UseMutationOptions,
} from &quot;@tanstack/react-query&quot;;
import type {
  Post,
  GetPostsResponse,
  CreatePostRequest,
  CreatePostResponse,
  UpdatePostRequest,
} from &quot;./model&quot;;
import { useCustomInstance } from &quot;../customInstance&quot;;

type SecondParameter&lt;T extends (...args: never) =&gt; unknown&gt; = Parameters&lt;T&gt;[1];

// GET request → useSuspenseQuery hook
export const useGetPosts = &lt;
  TData = Awaited&lt;ReturnType&lt;ReturnType&lt;typeof useCustomInstance&lt;GetPostsResponse&gt;&gt;&gt;&gt;,
  TError = Error,
&gt;(
  options?: {
    query?: Partial&lt;UseSuspenseQueryOptions&lt;GetPostsResponse, TError, TData&gt;&gt;;
    request?: SecondParameter&lt;ReturnType&lt;typeof useCustomInstance&gt;&gt;;
  }
) =&gt; {
  const customInstance = useCustomInstance&lt;GetPostsResponse&gt;();

  return useSuspenseQuery({
    queryKey: [&quot;posts&quot;],
    queryFn: () =&gt; customInstance({ url: `/api/posts`, method: &quot;GET&quot; }),
    ...options?.query,
  });
};

// POST request → useMutation hook
export const useCreatePost = &lt;TError = Error, TContext = unknown&gt;(
  options?: {
    mutation?: UseMutationOptions&lt;CreatePostResponse, TError, CreatePostRequest, TContext&gt;;
    request?: SecondParameter&lt;ReturnType&lt;typeof useCustomInstance&gt;&gt;;
  }
) =&gt; {
  const customInstance = useCustomInstance&lt;CreatePostResponse&gt;();

  return useMutation({
    mutationFn: (data: CreatePostRequest) =&gt;
      customInstance({
        url: `/api/posts`,
        method: &quot;POST&quot;,
        data,
      }),
    ...options?.mutation,
  });
};

// PUT request
export const useUpdatePost = &lt;TError = Error, TContext = unknown&gt;(
  postId: string,
  options?: {
    mutation?: UseMutationOptions&lt;Post, TError, UpdatePostRequest, TContext&gt;;
    request?: SecondParameter&lt;ReturnType&lt;typeof useCustomInstance&gt;&gt;;
  }
) =&gt; {
  const customInstance = useCustomInstance&lt;Post&gt;();

  return useMutation({
    mutationFn: (data: UpdatePostRequest) =&gt;
      customInstance({
        url: `/api/posts/${postId}`,
        method: &quot;PUT&quot;,
        data,
      }),
    ...options?.mutation,
  });
};

// DELETE request
export const useDeletePost = &lt;TError = Error, TContext = unknown&gt;(
  postId: string,
  options?: {
    mutation?: UseMutationOptions&lt;void, TError, void, TContext&gt;;
    request?: SecondParameter&lt;ReturnType&lt;typeof useCustomInstance&gt;&gt;;
  }
) =&gt; {
  const customInstance = useCustomInstance&lt;void&gt;();

  return useMutation({
    mutationFn: () =&gt;
      customInstance({
        url: `/api/posts/${postId}`,
        method: &quot;DELETE&quot;,
      }),
    ...options?.mutation,
  });
};
</code></pre>
<h5>Convenience of These Hooks</h5>
<ul>
<li>TypeScript type inference automatically infers <code>data</code> type as <code>GetPostsResponse</code></li>
<li>Error handling is also type-safe (Error type is determined)</li>
<li>TanStack Query features like caching and refetching work as-is</li>
<li>No manual API URL entry needed (prevents URL typos)</li>
</ul>
<h3>Key Points for Using Orval-Generated Code</h3>
<table>
<thead>
<tr>
<th>Feature</th>
<th>Benefit</th>
</tr>
</thead>
<tbody><tr>
<td>Automatic OpenAPI tracking</td>
<td>API spec change → re-run → fully synchronized</td>
</tr>
<tr>
<td>Types and hooks are linked</td>
<td>Return type of <code>useGetPosts</code> is also auto-inferred</td>
</tr>
<tr>
<td>Utilizes TypeScript generics</td>
<td>Error handling is also type-safe</td>
</tr>
<tr>
<td>Plugin extensible</td>
<td>Can add custom generation logic</td>
</tr>
<tr>
<td>Strong for API versioning</td>
<td>Supports generation from older API spec versions</td>
</tr>
</tbody></table>
<h4>Generated Code Must Not Be Modified</h4>
<p>Running <code>pnpm orval</code> overwrites type definitions and custom hooks, so files under <code>src/shared/api/generated/</code> are <strong>modification prohibited</strong>.</p>
<pre><code class="language-typescript">// ❌ Do not modify directly like this
// src/shared/api/generated/hooks.ts

export const useGetPosts = () =&gt; {
  // ↓ This code will be overwritten on Orval re-run
  return useSuspenseQuery({
    // ...
  });
};
</code></pre>
<h4>Customization Is Done in the Entities Layer</h4>
<p>When customization is needed, wrap in the entities layer to provide your own interface. This centralizes dependencies on generated code in one place.</p>
<pre><code class="language-typescript">// src/entities/post/api/hooks.ts

import { useGetPosts as useGetPostsGenerated } from &quot;@/shared/api/generated&quot;;

/**
 * Provides a user-friendly interface
 * - Hides details of Orval-generated code
 * - Returns organized return values
 */
export const usePosts = () =&gt; {
  const { data, isLoading, error } = useGetPostsGenerated();

  return {
    posts: data?.items ?? [],
    isLoading,
    hasError: !!error,
  };
};
</code></pre>
<p>Detailed implementation patterns are explained in the next chapter.</p>
<h2>Implementation Patterns and Structural Design</h2>
<p>From here, we&#39;ll introduce <strong>3 design patterns</strong> for effectively using Orval-generated code.</p>
<h3>Pattern A: Simple Wrapping</h3>
<p><strong>Scenario</strong>: API to get a list of posts</p>
<h4>Step 1: Check Orval-Generated Code</h4>
<p>The <code>useGetPosts</code> shown in &quot;Generated Output 2: TanStack Query Custom Hooks&quot; above is used as-is.</p>
<h4>Step 2: Wrap in Entities Layer</h4>
<pre><code class="language-typescript">// src/entities/post/api/hooks.ts

import { useGetPosts as useGetPostsGenerated } from &quot;@/shared/api/generated&quot;;

/**
 * Custom hook to get list of posts
 * Isolates dependency on shared/api/generated to entities layer
 */
export const usePosts = () =&gt; {
  const { data, isLoading, error } = useGetPostsGenerated();

  return {
    posts: data?.items ?? [],
    isLoading,
    hasError: !!error,
  };
};
</code></pre>
<h4>Step 3: Public API</h4>
<pre><code class="language-typescript">// src/entities/post/api/index.ts

export { usePosts } from &quot;./hooks&quot;;
</code></pre>
<h4>Step 4: Use in Features Layer</h4>
<pre><code class="language-typescript">// src/features/PostManagement/ui/PostList.tsx

import { usePosts } from &quot;@/entities/post/api&quot;;

function PostList() {
  const { posts, isLoading } = usePosts();

  if (isLoading) return &lt;div&gt;Loading...&lt;/div&gt;;

  return (
    &lt;ul&gt;
      {posts.map((post) =&gt; (
        &lt;li key={post.id}&gt;{post.title}&lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}
</code></pre>
<h4>Benefits of This Pattern</h4>
<ul>
<li>Orval-generated code changes are limited to <code>entities/post/api</code></li>
<li><code>features/PostManagement</code> only needs to know the simple interface</li>
<li>Testing also works with mocking just <code>entities/post/api</code></li>
</ul>
<h4>From a Feature-Sliced Design Perspective</h4>
<ul>
<li><code>entities/post/api</code> <strong>creates a boundary</strong> between Orval (external) and features (internal)</li>
<li>Features don&#39;t know the details of generated code</li>
<li>Modification scope can be limited to entities</li>
</ul>
<h3>Pattern B: Combining Multiple APIs</h3>
<p><strong>Scenario</strong>: When both &quot;list of posts + post details&quot; are needed</p>
<p>Multiple API calls need to be combined. This is also handled in the entities layer.</p>
<p>Note: The following example assumes a <code>useGetPostDetails</code> hook is separately generated by Orval.</p>
<h4>Step 1: Combine Multiple APIs in Entities Layer</h4>
<pre><code class="language-typescript">// src/entities/post/api/hooks.ts

import {
  useGetPosts as useGetPostsGenerated,
  useGetPostDetails as useGetPostDetailsGenerated,
} from &quot;@/shared/api/generated&quot;;

/**
 * Combines multiple API calls
 * Callers don&#39;t need to be aware of this complexity
 */
export const usePostWithDetails = (postId: string) =&gt; {
  const { data: posts, isLoading: postsLoading } =
    useGetPostsGenerated();

  const { data: details, isLoading: detailsLoading } =
    useGetPostDetailsGenerated(postId);

  return {
    posts: posts?.items ?? [],
    details: details ?? null,
    isLoading: postsLoading || detailsLoading,
    // Also provide convenient derived data
    hasDetails: !!details,
  };
};
</code></pre>
<h4>Step 2: Use from Features Layer</h4>
<p>Callers don&#39;t need to know the complexity.</p>
<pre><code class="language-typescript">// src/features/PostManagement/ui/PostDetail.tsx

import { usePostWithDetails } from &quot;@/entities/post/api&quot;;

function PostDetail({ postId }: Props) {
  const { posts, details, isLoading, hasDetails } = usePostWithDetails(postId);

  // Hide Complexity in entities layer!
  return &lt;div&gt;{hasDetails &amp;&amp; &lt;PostInfo details={details} /&gt;}&lt;/div&gt;;
}
</code></pre>
<h3>Pattern C: Unified Error Handling</h3>
<p><strong>Scenario</strong>: When you want to handle errors in a common format</p>
<p>Convert Orval-generated error types to custom error types.</p>
<h4>Step 1: Define and Convert Error Types in Entities Layer</h4>
<pre><code class="language-typescript">// src/entities/post/api/hooks.ts

export type ApiError = {
  message: string;
  code: &quot;NETWORK_ERROR&quot; | &quot;NOT_FOUND&quot; | &quot;UNAUTHORIZED&quot; | &quot;SERVER_ERROR&quot;;
  details?: unknown;
};

export type UsePostsResult = {
  posts: Post[];
  isLoading: boolean;
  error: ApiError | null;
  retry: () =&gt; void;
};

export const usePosts = (): UsePostsResult =&gt; {
  const { data, isLoading, error, refetch } = useGetPostsGenerated();

  // Convert Orval-generated error type to custom error type
  const mappedError: ApiError | null = error
    ? {
        message: error.message || &quot;An error occurred&quot;,
        code: mapErrorCode(error),
        details: error,
      }
    : null;

  return {
    posts: data?.items ?? [],
    isLoading,
    error: mappedError,
    retry: () =&gt; refetch(),
  };
};

// Helper function
// TanStack Query&#39;s error is treated as Error type
// Assumes custom instance throws Error with status code
type ApiErrorWithStatus = Error &amp; { status?: number };

function mapErrorCode(error: unknown): ApiError[&quot;code&quot;] {
  if (!navigator.onLine) return &quot;NETWORK_ERROR&quot;;

  const apiError = error as ApiErrorWithStatus;
  if (apiError.status === 404) return &quot;NOT_FOUND&quot;;
  if (apiError.status === 401) return &quot;UNAUTHORIZED&quot;;

  return &quot;SERVER_ERROR&quot;;
}
</code></pre>
<h4>Step 2: Unified Error Processing in Features Layer</h4>
<p>Error handling becomes unified on the caller side.</p>
<pre><code class="language-typescript">// src/features/PostManagement/ui/PostList.tsx

import { usePosts } from &quot;@/entities/post/api&quot;;

function PostList() {
  const { posts, isLoading, error, retry } = usePosts();

  if (error) {
    return (
      &lt;div&gt;
        &lt;p&gt;Error: {error.message}&lt;/p&gt;
        &lt;button onClick={retry}&gt;Retry&lt;/button&gt;
      &lt;/div&gt;
    );
  }

  // ... normal processing below
}
</code></pre>
<h3>Architecture Diagram: Orval + Feature-Sliced Design</h3>
<p>Here&#39;s a diagram summarizing the patterns so far. Since dependency directions are unified, the scope of change impact becomes clear.</p>
<pre><code class="language-shell">shared/api/generated/  ← Orval output (modification prohibited)
  ├─ useGetPosts
  ├─ useCreatePost
  ├─ useGetPostDetails
  └─ types.ts
       ↓
    [Boundary]
       ↓
entities/post/api/     ← Layer wrapping Orval output (modifiable)
  ├─ usePosts (customized version)
  ├─ usePostWithDetails (multiple API combination)
  ├─ ApiError type
  └─ index.ts (public API)
       ↓
features/              ← Features layer
  ├─ PostManagement/
  │   ├─ ui/PostList.tsx
  │   ├─ ui/PostDetail.tsx
  │   ├─ lib/...
  │   └─ index.ts
  ...
       ↓
pages/                 ← Pages layer
  └─ PostPage/
       ↓
app/                   ← Application layer
  ├─ routes/
  └─ ...
</code></pre>
<h2>Impressions After Adoption</h2>
<h3>✅ Benefits</h3>
<ul>
<li><strong>Dramatically improved type safety</strong>: Cannot go back to development with manually typed definitions.</li>
<li><strong>High resilience to API changes</strong>: Modifications complete in one place (entities layer).</li>
<li><strong>Documentation = Code</strong>: OpenAPI and code can always stay synchronized.</li>
<li><strong>Improved team-wide efficiency</strong>: Smooth flow from API design → implementation → testing.</li>
<li><strong>Fewer bugs</strong>: Bugs from type mismatches have nearly disappeared.</li>
</ul>
<h3>⚠️ Important Notes</h3>
<ul>
<li><strong>Learning cost for the entire team</strong>: Feature-Sliced Design is an architecture that takes time to master, requiring understanding from all team members.</li>
<li><strong>Wait time until OpenAPI is finalized</strong>: For UI implementation involving API spec changes, you need to wait for OpenAPI updates to complete. As a countermeasure, using mock APIs like MSW allows frontend development to proceed in parallel.</li>
<li><strong>Need for compatibility checks during Orval version upgrades</strong>: During Orval major version upgrades, generated code format may change, so checking release notes before upgrading is necessary.</li>
</ul>
<h2>Summary</h2>
<p>Schema-driven development using Orval significantly improves <strong>resilience to API changes</strong> and <strong>type safety</strong> in frontend development.</p>
<p>In the ongoing project, Orval was introduced from the start, reducing communication costs between backend and frontend engineers and nearly eliminating wasteful implementation costs.</p>
<p>Additionally, while adopting Feature-Sliced Design took time for the entire team to understand and implement in code, the clear rules improved code readability and maintainability.</p>
<p>If you&#39;re experiencing challenges like the following, please try the Orval × Feature-Sliced Design combination:</p>
<ul>
<li>Manually writing API types and custom hooks, incurring costs</li>
<li>Schema-driven development is already adopted, but there are no directory structure rules</li>
<li>Auto-generated files are imported from various places</li>
</ul>
<p>Thank you for reading to the end.</p>
<h2>References</h2>
<ul>
<li><a href="https://orval.dev/">Orval Official Documentation</a></li>
<li><a href="https://spec.openapis.org/oas/v3.1.0">OpenAPI Specification v3.1.0</a></li>
<li><a href="https://tanstack.com/query/latest">TanStack Query</a></li>
<li><a href="https://feature-sliced.design/ja">Feature-Sliced Design Official Documentation</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/high-g/20251202/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Orval × Feature-Sliced Design で実現するルールベースなスキーマ駆動開発]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-02-schema-driven-development-with-orval-and-fsd/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-02-schema-driven-development-with-orval-and-fsd/</guid>
            <pubDate>Tue, 02 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[OpenAPIから型定義とTanStack Queryフックを自動生成するOrvalと、層構造に基づくディレクトリルールを持つFeature-Sliced Designを組み合わせ、スキーマ駆動開発を実践する手法を紹介します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の2日目の記事です🎅🎄</p>
<h2><strong>はじめに</strong></h2>
<p>こんにちは！
KINTO開発部 KINTOバックエンド開発G マスターメンテナンスツール開発チーム、Osaka Tech Lab 所属の high-g（<a href="https://x.com/high_g_engineer">@high_g_engineer</a>）です。</p>
<p>API 連携が多い現代のフロントエンド開発において、こんな課題を感じたことはないでしょうか？</p>
<ul>
<li>API の型定義を手動で書いていて、仕様変更のたびに修正漏れが発生する</li>
<li>自動生成ファイルの置き場所がバラバラで、どこから何を import すればいいか分からない</li>
<li>チームメンバーごとにディレクトリ構造の解釈が異なり、コードレビューで議論になる</li>
</ul>
<p>これらの課題を解決するキーワードが、<strong>「型安全」「スキーマ駆動」「自動生成」「ディレクトリ設計」</strong> です。</p>
<p>この記事では、OpenAPI を唯一の情報源として型安全なコードを自動生成し、それを Feature-Sliced Design のルールに則って管理するアプローチを紹介します。</p>
<p>具体的には、Orval で「どんなコードが生成されるのか」を見ながら、Feature-Sliced Design のディレクトリ構造に沿った効果的な設計パターンを解説します。</p>
<h3><strong>この記事で紹介すること</strong></h3>
<ul>
<li>OpenAPI を元に Orval から型とカスタムフックを出力する流れ</li>
<li>Orval が実際に出力するコード例の詳細</li>
<li>Feature-Sliced Design の層構造とインポートルール</li>
<li>Orval 生成コードを Feature-Sliced Design のディレクトリ構造で管理する設計パターン</li>
</ul>
<h3><strong>想定読者</strong></h3>
<ul>
<li>REST API と型定義の手動管理に疲れているフロントエンド開発者</li>
<li>TypeScript + React を使っている方</li>
<li>API 変更に強い設計に興味がある方</li>
<li>ディレクトリ構造のルール化に関心がある方</li>
</ul>
<h2><strong>基礎となる知識</strong></h2>
<h3><strong>OpenAPI について</strong></h3>
<p>OpenAPI は、HTTP API をプログラムで解釈可能な形式で定義するための標準です。API の仕様を YAML または JSON で記述することで、以下のようなメリットがあります。</p>
<ul>
<li>API の入出力が明確に定義される</li>
<li>ドキュメント生成が自動化される</li>
<li>クライアント・サーバー間での齟齬を防ぐ</li>
</ul>
<h4><strong>例：OpenAPI の記述の一部（簡略版）</strong></h4>
<pre><code class="language-yaml">openapi: 3.1.0
paths:
  /posts:
    get:
      summary: 投稿一覧を取得
      parameters:
        - name: page
          in: query
          schema:
            type: integer
      responses:
        &quot;200&quot;:
          description: Success
          content:
            application/json:
              schema:
                $ref: &quot;#/components/schemas/GetPostsResponse&quot;
    post:
      summary: 投稿を作成
      requestBody:
        content:
          application/json:
            schema:
              $ref: &quot;#/components/schemas/CreatePostRequest&quot;
      responses:
        &quot;201&quot;:
          description: Created
          content:
            application/json:
              schema:
                $ref: &quot;#/components/schemas/CreatePostResponse&quot;
  /posts/{postId}:
    put:
      summary: 投稿を更新
      parameters:
        - name: postId
          in: path
          required: true
          schema:
            type: string
      requestBody:
        content:
          application/json:
            schema:
              $ref: &quot;#/components/schemas/UpdatePostRequest&quot;
      responses:
        &quot;200&quot;:
          description: Success
          content:
            application/json:
              schema:
                $ref: &quot;#/components/schemas/Post&quot;
    delete:
      summary: 投稿を削除
      parameters:
        - name: postId
          in: path
          required: true
          schema:
            type: string
      responses:
        &quot;204&quot;:
          description: No Content
components:
  schemas:
    Post:
      type: object
      required: [id, title, createdAt, updatedAt, status]
      properties:
        id:
          type: string
        title:
          type: string
        body:
          type: string
        createdAt:
          type: string
          format: date-time
        updatedAt:
          type: string
          format: date-time
        status:
          type: string
          enum: [draft, published, archived]
    GetPostsResponse:
      type: object
      properties:
        items:
          type: array
          items:
            $ref: &quot;#/components/schemas/Post&quot;
        total:
          type: integer
        page:
          type: integer
    CreatePostRequest:
      type: object
      required: [title]
      properties:
        title:
          type: string
        body:
          type: string
    CreatePostResponse:
      allOf:
        - $ref: &quot;#/components/schemas/Post&quot;
        - type: object
          properties:
            createdBy:
              type: string
    UpdatePostRequest:
      type: object
      properties:
        title:
          type: string
        body:
          type: string
        status:
          type: string
          enum: [draft, published, archived]
</code></pre>
<p>この YAML は以下を定義しています。</p>
<ul>
<li><code>/posts</code> エンドポイント：一覧取得（GET）と作成（POST）</li>
<li><code>/posts/{postId}</code> エンドポイント：更新（PUT）と削除（DELETE）</li>
</ul>
<h3><strong>スキーマ駆動開発について</strong></h3>
<h4><strong>従来の手動管理の問題</strong></h4>
<p>これまで、私たちフロントエンド開発者は以下のような作業を手動で行っていました。</p>
<pre><code class="language-typescript">// 手動で型定義を書く
type Post = {
  id: string;
  title: string;
  body?: string;
  createdAt: string;
  updatedAt: string;
  status: &quot;draft&quot; | &quot;published&quot; | &quot;archived&quot;;
};

// API 呼び出しも手で書く
const getPost = async (id: string): Promise&lt;Post&gt; =&gt; {
  const response = await fetch(`/api/posts/${id}`);
  return response.json();
};
</code></pre>
<p>この方法には以下の問題があります。</p>
<ol>
<li><p><strong>手動修正のコスト</strong></p>
<ul>
<li>OpenAPI を確認 → 型定義を手で修正 → 利用箇所をすべて修正</li>
</ul>
</li>
<li><p><strong>修正漏れのリスク</strong></p>
<ul>
<li>型定義と実際の API 仕様がズレる</li>
<li>複数の箇所で同じ型を使っていると漏れが生じやすい</li>
</ul>
</li>
<li><p><strong>ドキュメントとコードの非同期</strong></p>
<ul>
<li>OpenAPI ≠ 実装コード になることもある</li>
</ul>
</li>
</ol>
<h4><strong>スキーマ駆動開発のアプローチ</strong></h4>
<p>上記で挙げた手動対応の問題を解消するために考えられたのが、「スキーマ（API 仕様）を最初に定義し、そこから実装を進める」開発手法です。</p>
<pre><code class="language-shell">従来：実装 → ドキュメント（後付け） → 仕様との齟齬

スキーマ駆動：スキーマ定義 → 自動生成 → 実装 → 完了
              （実装 = ドキュメント、常に同期）
</code></pre>
<h4><strong>スキーマ駆動開発の特徴</strong></h4>
<ul>
<li><strong>実装 = ドキュメント</strong>：常に API 仕様とコードが同期</li>
<li><strong>型安全性</strong>：コンパイル時に API 不整合を検出</li>
<li><strong>開発効率</strong>：型定義の手作業が不要</li>
<li><strong>チーム連携</strong>：フロントエンドとバックエンド両方が同じ OpenAPI を参照</li>
</ul>
<h3><strong>Orval について</strong></h3>
<p>Orval は、OpenAPI の仕様書から TypeScript の型定義やカスタムフックなどのコードを<strong>コマンドひとつで自動生成</strong>してくれるツールです。</p>
<h4><strong>Orval の主な特徴</strong></h4>
<table>
<thead>
<tr>
<th>特徴</th>
<th>説明</th>
</tr>
</thead>
<tbody><tr>
<td><strong>型定義の自動生成</strong></td>
<td>API のリクエスト・レスポンスの型を自動で作成</td>
</tr>
<tr>
<td><strong>カスタムフックの自動生成</strong></td>
<td>TanStack Query などのフックも自動生成</td>
</tr>
<tr>
<td><strong>複数ライブラリ対応</strong></td>
<td>TanStack Query だけでなく、Axios などのライブラリにも対応</td>
</tr>
<tr>
<td><strong>モック生成</strong></td>
<td>テスト用のモックデータも生成可能</td>
</tr>
</tbody></table>
<h4><strong>Orval を使うメリット</strong></h4>
<ul>
<li><strong>時間の節約</strong>：型定義や API 呼び出しコードを手書きする時間がゼロに</li>
<li><strong>ミスの防止</strong>：手書きによるタイプミスや仕様の読み間違いがなくなる</li>
<li><strong>常に最新</strong>：OpenAPI が更新されたら、再生成するだけで最新の状態に</li>
</ul>
<h4><strong>スキーマ駆動開発における Orval の役割</strong></h4>
<p>ここまでの内容をまとめると、スキーマ駆動開発における Orval の役割は以下のようになります。</p>
<pre><code class="language-shell">OpenAPI（信頼できる唯一の情報源）
    ↓
Orval によるコードの自動生成で、 型定義 + TanStack Query フックを常に同期
    ↓
低コストで型安全な開発が可能
</code></pre>
<h3><strong>Feature-Sliced Design について</strong></h3>
<p>冒頭でも触れた通り、現在開発中のプロジェクトでは、フロントエンドのコード構成に <strong>Feature-Sliced Design</strong> というアーキテクチャパターンを採用しています。</p>
<p>Feature-Sliced Design は、コードベースを <strong>レイヤー</strong>、<strong>スライス</strong>、<strong>セグメント</strong> という3つの概念で整理するアーキテクチャです。</p>
<table>
<thead>
<tr>
<th>概念</th>
<th>説明</th>
<th>例</th>
</tr>
</thead>
<tbody><tr>
<td><strong>レイヤー (Layer)</strong></td>
<td>アプリケーションの責務による分割。上位から <code>app</code> → <code>pages</code> → <code>features</code> → <code>entities</code> → <code>shared</code> の5層。<code>app</code> はルーティングやレイアウトなどアプリ全体の設定、<code>pages</code> は URL に対応する画面を担当</td>
<td><code>app/</code>, <code>pages/</code>, <code>features/</code></td>
</tr>
<tr>
<td><strong>スライス (Slice)</strong></td>
<td>各レイヤー内でのビジネスドメインや機能ごとの分割単位</td>
<td><code>features/auth/</code>, <code>entities/user/</code></td>
</tr>
<tr>
<td><strong>セグメント (Segment)</strong></td>
<td>スライス内での技術的な役割による分割</td>
<td><code>ui/</code>, <code>model/</code>, <code>api/</code></td>
</tr>
</tbody></table>
<pre><code class="language-shell">src/
├── features/          ← レイヤー
│   ├── auth/          ← スライス
│   │   ├── ui/        ← セグメント
│   │   ├── model/     ← セグメント
│   │   └── index.ts
</code></pre>
<p>この構造により、「どこに何を置くか」が明確になり、チーム全体でコードの配置ルールを統一できます。</p>
<h4><strong>Feature-Sliced Design のディレクトリ構造</strong></h4>
<p>私たちのチームでは、以下のようなディレクトリ構造で運用しています。セグメント（<code>api/</code>、<code>model/</code>、<code>ui/</code> など）の分け方はプロジェクトに合わせてカスタマイズしています。</p>
<pre><code class="language-shell">workspaces/typescript/src/
├── app/               ← ① アプリケーション層：ルーティング、グローバル設定
│   ├── layouts/         全体的なページで利用するレイアウト
│   ├── routes/          ルーティング定義
│   └── App.tsx          ルートとなるtsxファイル
│
├── pages/             ← ② ページ層：各ページコンポーネント（URLに対応）
│   ├── users/
│   └── login/
│
├── features/          ← ③ 機能層：再利用可能なビジネスロジック
│   ├── {slice}/           ドメインごとにスライスという単位で分割 (例:user, auth)
│   │   ├── {component}/   ドメインに属するコンポーネント
│   │   │   ├── model/       ロジック部分
│   │   │   ├── ui/          UI部分
│   │   │   └── index.ts     公開API（バレルファイル）
│   │   ...
│   ...
│
├── entities/          ← ④ エンティティ層：ビジネスドメインの定義
│   ├── user/            各種ドメイン
│   │   ├── @x             クロスインポート記法 ※後述
│   │   ├── api/           shared/ の自動生成ファイルを import して利用（ファサード）
│   │   │   ├── hooks.ts     apiフック
│   │   │   └── index.ts     公開API（バレルファイル）
│   │   ├── model/       ドメインロジック
│   │   ├── ui/          ドメイン内にとどまる最小レベルのUI
│   │   └── index.ts     公開API（バレルファイル）
│   ...
│
└── shared/            ← ⑤ 共有層：プロジェクト非依存のユーティリティ
    ├── api/
    │   └── generated/   Orval による自動生成ファイル（修正禁止）
    │       ├── types.ts
    │       ├── hooks.ts
    │       └── client.ts
    ├── config/          設定定数
    ├── errors/          共通利用エラー関数
    ├── lib/             ユーティリティ関数
    └── ui/              汎用UIコンポーネント
</code></pre>
<h4><strong>Feature-Sliced Design の層間インポート制限ルール</strong></h4>
<p>Feature-Sliced Design の最も重要なルール：<strong>レイヤーは自身より下位のレイヤーのみをインポート可能</strong> です。<br>また、同一レイヤー間の相互インポートも原則不可です（例外は後述の <code>@x</code> 記法）。</p>
<pre><code class="language-shell">app ← 最上位（抽象度が高い）
  ↓ import可能
pages
  ↓
features
  ↓
entities

※ shared はどのレイヤーからもインポート可能
</code></pre>
<p>つまり、以下のようなルールがプロジェクト内で設けられています。</p>
<ul>
<li>✅ pages/ は features/、entities/、shared/ を import 可能</li>
<li>✅ features/ は entities/、shared/ を import 可能</li>
<li>✅ entities/ は shared/ を import 可能</li>
<li>❌ entities/ は features/ や pages/ を import してはいけない</li>
<li>❌ shared/ は他のどのレイヤーも import してはいけない</li>
</ul>
<h4><strong>entities 層の特別な役割：entities/@x（クロスインポート記法）</strong></h4>
<p>しかし、entities 層ではビジネスドメイン同士が関連することが多く、例えば「Post が User を参照する」といったケースが発生します。</p>
<p>これを解決するために、entities 層内でのみ許可される特別なインポート方法が <code>@x</code> 記法です。</p>
<h5><strong>ディレクトリ構造の例</strong></h5>
<pre><code class="language-shell">entities/
├── user/
│   ├── @x/
│   │   └── post.ts       # 外部スライス向けに公開する型・関数
│   ├── model/
│   │   └── types.ts      # 内部で使用する型定義
│   ├── ui/
│   └── index.ts          # 通常の公開API
│
└── post/
    ├── model/
    │   └── usePost.ts    # ここから user の型を参照したい
    └── index.ts
</code></pre>
<h5><strong>使用例</strong></h5>
<pre><code class="language-typescript">// entities/post/model/usePost.ts から entities/user を使用する場合

// ❌ 通常のインポート（Feature-Sliced Design違反：同一レイヤー間のインポート）
import type { User } from &quot;@/entities/user&quot;;

// ✅ @x を使ったクロスインポート（許可）
import type { User } from &quot;@/entities/user/@x/post&quot;;

// @x ディレクトリは「このスライスが外部に公開する、クロスインポート専用のAPI」を表します。
</code></pre>
<p><code>@x</code> を使うことで、「意図的に外部公開している」ことが明示され、依存関係が追跡しやすくなります。</p>
<p>では、基礎となる知識が押さえられたところで、ここから本題に入っていきます。</p>
<h2><strong>Orval の使い方と出力コード</strong></h2>
<h3><strong>セットアップ</strong></h3>
<p>現在開発中のプロジェクトでは <strong>pnpm</strong> をパッケージマネージャーとして使用しています。
また、OpenAPI は予め定義された前提で話を進めます。</p>
<pre><code class="language-bash"># Orval のインストール
pnpm add -D orval
</code></pre>
<p>次に、Orval の設定ファイルを作成します。<br>※ hooks は、自動生成されたコードを Biome で format するために定義しています。</p>
<pre><code class="language-typescript">// orval.config.ts
import { defineConfig } from &quot;orval&quot;;

const API_DIR = &quot;./src/shared/api&quot;;
const INPUT_DIR = &quot;../../docs/api&quot;;
const GENERATED_DIR = `${API_DIR}/generated`;

export default defineConfig({
  postApi: {
    hooks: {
      afterAllFilesWrite: &quot;pnpm format:write:generate&quot;,
    },
    input: {
      target: `${INPUT_DIR}/openapi.yaml`,
    },
    output: {
      clean: true,
      biome: true,
      client: &quot;react-query&quot;,
      override: {
        mutator: {
          path: `${API_DIR}/customInstance.ts`,
          name: &quot;useCustomInstance&quot;,
        },
        query: {
          useSuspenseQuery: true,
          version: 5,
        },
      },
      schemas: `${GENERATED_DIR}/model`,
      target: `${GENERATED_DIR}/hooks/index.ts`,
    },
  },
});
</code></pre>
<p>次に、API リクエストを実行するカスタムインスタンスを作成します。これは Orval の設定で指定した <code>mutator</code> として使用されます。</p>
<pre><code class="language-typescript">// src/shared/api/customInstance.ts

import { ApiHttpError, type ErrorDetail } from &quot;../errors&quot;;
import { getAccessToken } from &quot;../lib&quot;;

const BASE_URL = import.meta.env.VITE_API_BASE_URL || &quot;&quot;;

// リクエスト設定の型定義
export type RequestConfig = {
  url: string;
  method: &quot;GET&quot; | &quot;POST&quot; | &quot;PUT&quot; | &quot;DELETE&quot; | &quot;PATCH&quot;;
  headers?: Record&lt;string, string&gt;;
  params?: Record&lt;string, unknown&gt;;
  data?: unknown;
  signal?: AbortSignal;
};

// Fetch API を使用したリクエスト関数
const fetchApi = async &lt;T&gt;(config: RequestConfig): Promise&lt;T&gt; =&gt; {
  const { url, method, headers = {}, params, data, signal } = config;

  // 認証トークンを取得
  const token = getAccessToken();

  // クエリパラメータの構築
  const queryString = params
    ? `?${new URLSearchParams(params as Record&lt;string, string&gt;).toString()}`
    : &quot;&quot;;
  const fullUrl = `${BASE_URL}${url}${queryString}`;

  // ヘッダーの構築
  const requestHeaders: Record&lt;string, string&gt; = {
    &quot;Content-Type&quot;: &quot;application/json&quot;,
    ...headers,
  };

  if (token) {
    requestHeaders.Authorization = `Bearer ${token}`;
  }

  // リクエストオプションの構築
  const options: RequestInit = {
    method,
    headers: requestHeaders,
    signal,
  };

  if (data &amp;&amp; [&quot;POST&quot;, &quot;PUT&quot;, &quot;PATCH&quot;].includes(method)) {
    options.body = JSON.stringify(data);
  }

  const response = await fetch(fullUrl, options);

  // エラーハンドリング
  if (!response.ok) {
    let errorMessage = `API error: ${response.status}`;
    let errorDetails: ErrorDetail[] = [];

    try {
      const errorData = await response.json();
      errorDetails = errorData?.errors?.details ?? [];
      if (typeof errorData.message === &quot;string&quot;) {
        errorMessage = errorData.message;
      }
    } catch {
      // JSONパースに失敗した場合はデフォルトメッセージを使用
    }

    throw new ApiHttpError({
      status: response.status,
      message: errorMessage,
      details: errorDetails,
    });
  }

  // 204 No Content の場合
  if (response.status === 204) {
    return null as T;
  }

  return response.json();
};

// Orval で使用するカスタムインスタンス関数
export const useCustomInstance = &lt;T&gt;(config: RequestConfig): Promise&lt;T&gt; =&gt; {
  const controller = new AbortController();

  const promise = fetchApi&lt;T&gt;({
    ...config,
    signal: controller.signal,
  });

  // TanStack Query のキャンセル機能用
  // @ts-expect-error cancel プロパティを動的に追加
  promise.cancel = () =&gt; controller.abort();

  return promise;
};

export default useCustomInstance;
</code></pre>
<p>この <code>useCustomInstance</code> は Orval が生成するフック内で HTTP リクエストを実行する際に使用されます。認証トークンの付与やエラーハンドリングなど、プロジェクト固有の設定をここに集約できます。</p>
<p>実際のプロジェクトでは、トークンリフレッシュ処理やリトライロジックなどを追加することが多いです。詳細は <a href="https://orval.dev/guides/custom-client">Orval 公式ドキュメント - Custom Client</a> を参照してください。</p>
<p>あとは、コード生成を実行するだけです。</p>
<pre><code class="language-bash"># コード生成実行
pnpm orval
</code></pre>
<p>現在開発中のプロジェクトでは、定期的に <code>pnpm orval</code> を実行し、API 仕様の変更をまとめて反映する運用をしています。</p>
<h3><strong>Orval が生成するコード実例</strong></h3>
<p>それでは、Orval が実際に何を生成するか、具体例で見ていきます。</p>
<h4><strong>生成物 1：型定義</strong></h4>
<p>OpenAPIの <code>Post</code> スキーマから、以下のような TypeScript 型が自動生成されます。</p>
<pre><code class="language-typescript">// src/shared/api/generated/types.ts
// ↓ OpenAPIから自動生成される

export type Post = {
  id: string;
  title: string;
  body?: string;
  createdAt: string; // ISO 8601形式
  updatedAt: string;
  status: &quot;draft&quot; | &quot;published&quot; | &quot;archived&quot;;
};

export type GetPostsResponse = {
  items: Post[];
  total: number;
  page: number;
};

export type CreatePostRequest = {
  title: string;
  body?: string;
};

export type CreatePostResponse = Post &amp; {
  createdBy: string;
};

export type UpdatePostRequest = {
  title?: string;
  body?: string;
  status?: &quot;draft&quot; | &quot;published&quot; | &quot;archived&quot;;
};
</code></pre>
<h5><strong>重要なポイント</strong></h5>
<ul>
<li>OpenAPIのスキーマがそのまま型になる</li>
<li><code>enum</code> は TypeScript の Union Type に変換される</li>
<li>必須・オプション（<code>?</code>）の区別も自動判定される</li>
<li>生成ファイルなので修正してはいけない（次の再実行で上書きされる）</li>
</ul>
<h4><strong>生成物 2：TanStack Query カスタムフック</strong></h4>
<p>Orval は TanStack Query のフックも自動生成します。以下は理解しやすいよう簡略化した例です（実際の生成コードはカスタムインスタンスや詳細な型定義を含みます）。</p>
<pre><code class="language-typescript">// src/shared/api/generated/hooks.ts
// ↓ Orval が TanStack Query のフックを生成（簡略化した例）

import { useSuspenseQuery, useMutation } from &quot;@tanstack/react-query&quot;;
import type {
  UseSuspenseQueryOptions,
  UseMutationOptions,
} from &quot;@tanstack/react-query&quot;;
import type {
  Post,
  GetPostsResponse,
  CreatePostRequest,
  CreatePostResponse,
  UpdatePostRequest,
} from &quot;./model&quot;;
import { useCustomInstance } from &quot;../customInstance&quot;;

type SecondParameter&lt;T extends (...args: never) =&gt; unknown&gt; = Parameters&lt;T&gt;[1];

// GET リクエスト → useSuspenseQuery フック
export const useGetPosts = &lt;
  TData = Awaited&lt;ReturnType&lt;ReturnType&lt;typeof useCustomInstance&lt;GetPostsResponse&gt;&gt;&gt;&gt;,
  TError = Error,
&gt;(
  options?: {
    query?: Partial&lt;UseSuspenseQueryOptions&lt;GetPostsResponse, TError, TData&gt;&gt;;
    request?: SecondParameter&lt;ReturnType&lt;typeof useCustomInstance&gt;&gt;;
  }
) =&gt; {
  const customInstance = useCustomInstance&lt;GetPostsResponse&gt;();

  return useSuspenseQuery({
    queryKey: [&quot;posts&quot;],
    queryFn: () =&gt; customInstance({ url: `/api/posts`, method: &quot;GET&quot; }),
    ...options?.query,
  });
};

// POST リクエスト → useMutation フック
export const useCreatePost = &lt;TError = Error, TContext = unknown&gt;(
  options?: {
    mutation?: UseMutationOptions&lt;CreatePostResponse, TError, CreatePostRequest, TContext&gt;;
    request?: SecondParameter&lt;ReturnType&lt;typeof useCustomInstance&gt;&gt;;
  }
) =&gt; {
  const customInstance = useCustomInstance&lt;CreatePostResponse&gt;();

  return useMutation({
    mutationFn: (data: CreatePostRequest) =&gt;
      customInstance({
        url: `/api/posts`,
        method: &quot;POST&quot;,
        data,
      }),
    ...options?.mutation,
  });
};

// PUT リクエスト
export const useUpdatePost = &lt;TError = Error, TContext = unknown&gt;(
  postId: string,
  options?: {
    mutation?: UseMutationOptions&lt;Post, TError, UpdatePostRequest, TContext&gt;;
    request?: SecondParameter&lt;ReturnType&lt;typeof useCustomInstance&gt;&gt;;
  }
) =&gt; {
  const customInstance = useCustomInstance&lt;Post&gt;();

  return useMutation({
    mutationFn: (data: UpdatePostRequest) =&gt;
      customInstance({
        url: `/api/posts/${postId}`,
        method: &quot;PUT&quot;,
        data,
      }),
    ...options?.mutation,
  });
};

// DELETE リクエスト
export const useDeletePost = &lt;TError = Error, TContext = unknown&gt;(
  postId: string,
  options?: {
    mutation?: UseMutationOptions&lt;void, TError, void, TContext&gt;;
    request?: SecondParameter&lt;ReturnType&lt;typeof useCustomInstance&gt;&gt;;
  }
) =&gt; {
  const customInstance = useCustomInstance&lt;void&gt;();

  return useMutation({
    mutationFn: () =&gt;
      customInstance({
        url: `/api/posts/${postId}`,
        method: &quot;DELETE&quot;,
      }),
    ...options?.mutation,
  });
};
</code></pre>
<h5><strong>このフックの便利さ</strong></h5>
<ul>
<li>TypeScript の型推論により、<code>data</code> の型が自動的に <code>GetPostsResponse</code> に推論される</li>
<li>エラーハンドリングも型安全（Error の型が決まっている）</li>
<li>TanStack Query のキャッシング、再フェッチなどの機能もそのまま使える</li>
<li>API URL の手入力が不要（URL の記述ミスを防げる）</li>
</ul>
<h3><strong>Orval 生成コードの活用ポイント</strong></h3>
<table>
<thead>
<tr>
<th>特徴</th>
<th>メリット</th>
</tr>
</thead>
<tbody><tr>
<td>OpenAPI の自動追跡</td>
<td>API 仕様変更 → 再実行 → 完全に同期</td>
</tr>
<tr>
<td>型とフックが連動</td>
<td><code>useGetPosts</code> の戻り値の型も自動推論</td>
</tr>
<tr>
<td>TypeScript ジェネリクスを活用</td>
<td>エラーハンドリングも型安全</td>
</tr>
<tr>
<td>プラグイン拡張可能</td>
<td>カスタム生成ロジックを追加できる</td>
</tr>
<tr>
<td>API のバージョン管理に強い</td>
<td>古いバージョンの API 仕様からの生成もサポート</td>
</tr>
</tbody></table>
<h4><strong>生成コードは修正禁止</strong></h4>
<p><code>pnpm orval</code> を実行すると型定義とカスタムフックが上書きされるため、 <code>src/shared/api/generated/</code> 配下のファイルは <strong>修正禁止</strong> です。</p>
<pre><code class="language-typescript">// ❌ こうやって直接修正してはいけない
// src/shared/api/generated/hooks.ts

export const useGetPosts = () =&gt; {
  // ↓ このコードは Orval の再実行で上書きされる
  return useSuspenseQuery({
    // ...
  });
};
</code></pre>
<h4><strong>カスタマイズは entities 層で行う</strong></h4>
<p>カスタマイズが必要な場合は、entities 層でラップして独自のインターフェースを提供します。これにより、生成コードへの依存を一箇所に集約できます。</p>
<pre><code class="language-typescript">// src/entities/post/api/hooks.ts

import { useGetPosts as useGetPostsGenerated } from &quot;@/shared/api/generated&quot;;

/**
 * 利用側に使いやすいインターフェースを提供
 * - Orval 生成コードの詳細を隠蔽
 * - 戻り値を整理して返す
 */
export const usePosts = () =&gt; {
  const { data, isLoading, error } = useGetPostsGenerated();

  return {
    posts: data?.items ?? [],
    isLoading,
    hasError: !!error,
  };
};
</code></pre>
<p>詳細な実装パターンは次章で解説します。</p>
<h2><strong>実装パターンと構造設計</strong></h2>
<p>ここから、Orval が生成したコードを効果的に使うための<strong>設計パターン</strong>を 3 つ紹介します。</p>
<h3><strong>パターン A：単純なラッピング</strong></h3>
<p><strong>シナリオ</strong>：投稿一覧を取得する API</p>
<h4><strong>ステップ 1：Orval による生成コードを確認</strong></h4>
<p>前述の「生成物 2：TanStack Query カスタムフック」で示した <code>useGetPosts</code> がそのまま使用されます。</p>
<h4><strong>ステップ 2：entities 層でラッピング</strong></h4>
<pre><code class="language-typescript">// src/entities/post/api/hooks.ts

import { useGetPosts as useGetPostsGenerated } from &quot;@/shared/api/generated&quot;;

/**
 * 投稿一覧を取得するカスタムフック
 * shared/api/generated への依存を entities層に隔離
 */
export const usePosts = () =&gt; {
  const { data, isLoading, error } = useGetPostsGenerated();

  return {
    posts: data?.items ?? [],
    isLoading,
    hasError: !!error,
  };
};
</code></pre>
<h4><strong>ステップ 3：公開 API</strong></h4>
<pre><code class="language-typescript">// src/entities/post/api/index.ts

export { usePosts } from &quot;./hooks&quot;;
</code></pre>
<h4><strong>ステップ 4：features 層で使用</strong></h4>
<pre><code class="language-typescript">// src/features/PostManagement/ui/PostList.tsx

import { usePosts } from &quot;@/entities/post/api&quot;;

function PostList() {
  const { posts, isLoading } = usePosts();

  if (isLoading) return &lt;div&gt;読み込み中...&lt;/div&gt;;

  return (
    &lt;ul&gt;
      {posts.map((post) =&gt; (
        &lt;li key={post.id}&gt;{post.title}&lt;/li&gt;
      ))}
    &lt;/ul&gt;
  );
}
</code></pre>
<h4><strong>このパターンのメリット</strong></h4>
<ul>
<li>Orval の生成コード変更が <code>entities/post/api</code> に限定される</li>
<li><code>features/PostManagement</code> はシンプルなインターフェースだけを知ればいい</li>
<li>テストも <code>entities/post/api</code> をモック一箇所で OK</li>
</ul>
<h4><strong>Feature-Sliced Design の視点</strong></h4>
<ul>
<li><code>entities/post/api</code> が Orval（外部）と features（内部）の<strong>境界を作る</strong></li>
<li>features は generated コードの詳細を知らない</li>
<li>修正範囲を entities に限定できる</li>
</ul>
<h3><strong>パターン B：複数 API の組み合わせ</strong></h3>
<p><strong>シナリオ</strong>：「投稿一覧 + 投稿の詳細」が必要な場合</p>
<p>複数の API 呼び出しを組み合わせる必要があります。これも entities 層で対応します。</p>
<p>※ 以下の例では、投稿詳細を取得する <code>useGetPostDetails</code> フックが別途 Orval で生成されている想定です。</p>
<h4><strong>ステップ 1：entities 層で複数 API を組み合わせ</strong></h4>
<pre><code class="language-typescript">// src/entities/post/api/hooks.ts

import {
  useGetPosts as useGetPostsGenerated,
  useGetPostDetails as useGetPostDetailsGenerated,
} from &quot;@/shared/api/generated&quot;;

/**
 * 複数のAPI呼び出しを組み合わせる
 * 呼び出し側はこの複雑性を意識しない
 */
export const usePostWithDetails = (postId: string) =&gt; {
  const { data: posts, isLoading: postsLoading } =
    useGetPostsGenerated();

  const { data: details, isLoading: detailsLoading } =
    useGetPostDetailsGenerated(postId);

  return {
    posts: posts?.items ?? [],
    details: details ?? null,
    isLoading: postsLoading || detailsLoading,
    // 便利な導出データも提供
    hasDetails: !!details,
  };
};
</code></pre>
<h4><strong>ステップ 2：features 層から利用</strong></h4>
<p>利用側は複雑さを知らなくて OK です。</p>
<pre><code class="language-typescript">// src/features/PostManagement/ui/PostDetail.tsx

import { usePostWithDetails } from &quot;@/entities/post/api&quot;;

function PostDetail({ postId }: Props) {
  const { posts, details, isLoading, hasDetails } = usePostWithDetails(postId);

  // 複雑さは entities層に隠蔽！
  return &lt;div&gt;{hasDetails &amp;&amp; &lt;PostInfo details={details} /&gt;}&lt;/div&gt;;
}
</code></pre>
<h3><strong>パターン C：エラーハンドリングの統一</strong></h3>
<p><strong>シナリオ</strong>：エラーを共通のフォーマットで扱いたい場合</p>
<p>Orval 生成のエラー型を、独自のエラー型に変換します。</p>
<h4><strong>ステップ 1：entities 層でエラー型を定義・変換</strong></h4>
<pre><code class="language-typescript">// src/entities/post/api/hooks.ts

export type ApiError = {
  message: string;
  code: &quot;NETWORK_ERROR&quot; | &quot;NOT_FOUND&quot; | &quot;UNAUTHORIZED&quot; | &quot;SERVER_ERROR&quot;;
  details?: unknown;
};

export type UsePostsResult = {
  posts: Post[];
  isLoading: boolean;
  error: ApiError | null;
  retry: () =&gt; void;
};

export const usePosts = (): UsePostsResult =&gt; {
  const { data, isLoading, error, refetch } = useGetPostsGenerated();

  // Orval生成のエラー型を独自のエラー型に変換
  const mappedError: ApiError | null = error
    ? {
        message: error.message || &quot;エラーが発生しました&quot;,
        code: mapErrorCode(error),
        details: error,
      }
    : null;

  return {
    posts: data?.items ?? [],
    isLoading,
    error: mappedError,
    retry: () =&gt; refetch(),
  };
};

// ヘルパー関数
// TanStack Query の error は Error 型として扱われる
// カスタムインスタンス側でステータスコードを含めた Error を throw する想定
type ApiErrorWithStatus = Error &amp; { status?: number };

function mapErrorCode(error: unknown): ApiError[&quot;code&quot;] {
  if (!navigator.onLine) return &quot;NETWORK_ERROR&quot;;

  const apiError = error as ApiErrorWithStatus;
  if (apiError.status === 404) return &quot;NOT_FOUND&quot;;
  if (apiError.status === 401) return &quot;UNAUTHORIZED&quot;;

  return &quot;SERVER_ERROR&quot;;
}
</code></pre>
<h4><strong>ステップ 2：features 層で統一的にエラー処理</strong></h4>
<p>利用側ではエラーハンドリングが統一されます。</p>
<pre><code class="language-typescript">// src/features/PostManagement/ui/PostList.tsx

import { usePosts } from &quot;@/entities/post/api&quot;;

function PostList() {
  const { posts, isLoading, error, retry } = usePosts();

  if (error) {
    return (
      &lt;div&gt;
        &lt;p&gt;エラー: {error.message}&lt;/p&gt;
        &lt;button onClick={retry}&gt;再試行&lt;/button&gt;
      &lt;/div&gt;
    );
  }

  // ... 以下、通常の処理
}
</code></pre>
<h3><strong>Orval + Feature-Sliced Design のアーキテクチャ図</strong></h3>
<p>ここまでのパターンを図にまとめました。依存の方向が統一されているため、変更の影響範囲が明確になります。</p>
<pre><code class="language-shell">shared/api/generated/  ← Orvalの生成物（修正禁止）
  ├─ useGetPosts
  ├─ useCreatePost
  ├─ useGetPostDetails
  └─ types.ts
       ↓
    [境界線]
       ↓
entities/post/api/     ← Orvalの生成物をラッピングする層（修正可能）
  ├─ usePosts（カスタマイズ版）
  ├─ usePostWithDetails（複数API組み合わせ）
  ├─ ApiError型
  └─ index.ts（公開API）
       ↓
features/              ← 機能層
  ├─ PostManagement/
  │   ├─ ui/PostList.tsx
  │   ├─ ui/PostDetail.tsx
  │   ├─ lib/...
  │   └─ index.ts
  ...
       ↓
pages/                 ← ページ層
  └─ PostPage/
       ↓
app/                   ← アプリケーション層
  ├─ routes/
  └─ ...
</code></pre>
<h2><strong>導入してみた感想</strong></h2>
<h3><strong>✅ メリット</strong></h3>
<ul>
<li><strong>型安全性が圧倒的に向上</strong>：手入力で型を定義する開発には戻れません。</li>
<li><strong>API 変更への耐性が高い</strong>：一箇所修正（entities 層）で全てが完了します。</li>
<li><strong>ドキュメント = コード</strong>：OpenAPI とコードを常に同期できます。</li>
<li><strong>チーム全体の効率が向上</strong>：API 設計 → 実装 → テストの流れがスムーズです。</li>
<li><strong>バグが減る</strong>：型不整合によるバグがほぼなくなります。</li>
</ul>
<h3><strong>⚠️ 注意点</strong></h3>
<ul>
<li><strong>チーム全体での学習コストがかかる</strong>：Feature-Sliced Design は習得に時間がかかるアーキテクチャであり、チーム全員の理解が必要です。</li>
<li><strong>OpenAPI 確定までの待ち時間が発生</strong>：API 仕様変更を伴う UI 実装では、OpenAPI の更新完了を待つ必要があります。対策として MSW などのモック API を活用することで、フロントエンド開発を並行して進められます。</li>
<li><strong>Orval のバージョンアップ時の互換性確認の必要性</strong>：Orval のメジャーバージョンアップ時には生成コードの形式が変わる可能性があるため、アップグレード前にリリースノートの確認が必要です。</li>
</ul>
<h2><strong>まとめ</strong></h2>
<p>Orval を利用したスキーマ駆動開発は、フロントエンド開発における <strong>「API 変更への耐性」</strong> と <strong>「型安全性」</strong> を大幅に向上させます。</p>
<p>現在開発中のプロジェクトでは、立ち上げ当初から Orval を導入しており、バックエンドとフロントエンドのエンジニア間のコミュニケーションコストが軽減され、無駄な実装コストもほとんどなくなりました。</p>
<p>また、Feature-Sliced Design の導入により、チーム全体で理解しコードに落とし込むまでに時間がかかったものの、明確なルールのおかげでコードの可読性と保守性が向上しました。</p>
<p>以下のような課題を感じている方は、ぜひ Orval × Feature-Sliced Design の組み合わせを試してみてください。</p>
<ul>
<li>API の型やカスタムフックを手書きしていて、コストがかかっている</li>
<li>スキーマ駆動開発は導入済みだが、ディレクトリ構造にルールがない</li>
<li>自動生成ファイルを様々な場所から import している状態</li>
</ul>
<p>最後までお読みいただきありがとうございました。</p>
<h2><strong>参考文献</strong></h2>
<ul>
<li><a href="https://orval.dev/">Orval 公式ドキュメント</a></li>
<li><a href="https://spec.openapis.org/oas/v3.1.0">OpenAPI Specification v3.1.0</a></li>
<li><a href="https://tanstack.com/query/latest">TanStack Query</a></li>
<li><a href="https://feature-sliced.design/ja">Feature-Sliced Design 公式ドキュメント</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/high-g/20251202/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How to Auto-Generate Tech Blog Cover Images with an Image‑Generation AI]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-01-ai-agent-blog-cover-image-generation-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-01-ai-agent-blog-cover-image-generation-en/</guid>
            <pubDate>Mon, 01 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Automatically create tech blog cover images in a single generation using image generation AI. Learn practical implementation methods that free you from design headaches.]]></description>
            <content:encoded><![CDATA[<p>This article is the Day 1 entry of the KINTO Technologies Advent Calendar 2025:santa::christmas_tree:</p>
<p>I&#39;m okapi from the Mobile team in the QA Group.</p>
<p>When creating images with AI, have you ever experienced telling the AI exactly what you want through prompts, only to have your intent misunderstood, forcing you to redo it multiple times?</p>
<p>This time, I&#39;ve created a prompt that can generate tech blog cover images in a single generation!</p>
<p>This approach can be applied to other image creation tasks as well. Please give it a try!</p>
<h2>How to Use</h2>
<h3>AI Image‑generation to Use</h3>
<p>Microsoft Copilot
*Basically, any generative AI tool that supports image generation will work (e.g., ChatGPT, DALL-E, Midjourney, etc.)</p>
<h3>Prompt Modification Points</h3>
<p>There are only 4 points to modify in the prompt!</p>
<table>
<thead>
<tr>
<th>Modification Points</th>
<th>Content</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1. Display Title Setting</strong></td>
<td>Paste your tech blog title into Display Title</td>
</tr>
<tr>
<td><strong>2. Style Selection</strong></td>
<td>Choose 1 from ■Style and delete the other 2</td>
</tr>
<tr>
<td><strong>3. Overview Setting</strong></td>
<td>Paste your tech blog content into ■Tech Blog Content</td>
</tr>
<tr>
<td><strong>4. Previous Cover Image Attachment</strong></td>
<td>Attach your previously used cover image to the prompt</td>
</tr>
</tbody></table>
<h3>Actual Prompt Used</h3>
<pre><code>Please create a cover image for a tech blog.
Please use the same format as the image used in the previous tech blog I&#39;m attaching.

■Purpose
I want to create a cover image for the following blog article.

■Display Title
(Paste article title here)

■Notes
・Please ensure there are no unnatural Japanese expressions.
・Please keep the image style and layout the same as before.
・The title should be placed in the center or a prominent position

■Style
(Choose one from the following 3)
・Tech feel (technical atmosphere)
・AI and machine learning style (collaboration with generative AI)
・Motion graphics style (even as a still image)

■Tech Blog Content
(Paste article overview here)
</code></pre>
<h3>Previous Cover Image Attached</h3>
<p><img src="/assets/blog/authors/okapi/native-app-qa/thumbnail.png" alt="以前のカバー画像"></p>
<h2>Images Actually Created for This Article</h2>
<p>For this article, I created images in the 3 patterns mentioned above.</p>
<table>
<thead>
<tr>
<th>Style</th>
<th>Generated Image</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Tech feel (technical atmosphere)</strong></td>
<td><img src="/assets/blog/authors/okapi/cover_image/teckan.png" alt="テック感"></td>
</tr>
<tr>
<td><strong>AI and machine learning style (collaboration with generative AI)</strong></td>
<td><img src="/assets/blog/authors/okapi/cover_image/ai_and_machine.png" alt="AI系"></td>
</tr>
<tr>
<td><strong>Motion graphics style (even as a still image)</strong></td>
<td><img src="/assets/blog/authors/okapi/cover_image/mosyongurahhic.png" alt="モーション風"></td>
</tr>
</tbody></table>
<h2>Key Points of Prompt Design Explained</h2>
<table>
<thead>
<tr>
<th>Point</th>
<th>Explanation</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1. Minimize modification points</strong></td>
<td>By narrowing down the parts that need modification in the prompt to 4 points, I created a template that anyone can easily use with copy-paste.</td>
</tr>
<tr>
<td><strong>2. Attach reference images</strong></td>
<td>Simply saying &quot;with the same atmosphere as before&quot; in words doesn&#39;t accurately convey to AI. By attaching actual images, you can share ambiguous parts that are difficult to express such as layout, color scheme, and font feel.</td>
</tr>
<tr>
<td><strong>3. Structured instructions</strong></td>
<td>By clearly categorizing information into purpose, title, notes, style, and content, it becomes easier for AI to understand the priority of each element.</td>
</tr>
<tr>
<td><strong>4. Specify elements to avoid</strong></td>
<td>By including negative instructions like &quot;avoid unnatural Japanese expressions,&quot; it becomes easier to select natural Japanese.</td>
</tr>
<tr>
<td><strong>5. Present options (style)</strong></td>
<td>Instead of leaving everything to AI, by making it a format where you choose from 3 styles, simply adding this option makes the prompt customizable.</td>
</tr>
</tbody></table>
<h2>Not Complete (Still Not Perfect)</h2>
<p>This template can mass-produce images while maintaining reproducibility just by replacing 4 points: title, style, overview, and reference image.</p>
<h3>Actually, There Are 2 Issues</h3>
<p>Although I could generate images in one shot, upon closer inspection, the following 2 problems were found.</p>
<h4>Issue 1: Japanese Typos</h4>
<p>Despite stating &quot;avoid unnatural Japanese expressions&quot; in the prompt, typos occurred.</p>
<p><strong>Tech feel (technical atmosphere):</strong></p>
<ul>
<li>It&#39;s written as &quot;自動化化&quot;, where &quot;化&quot; is repeated</li>
<li>The display of &quot;開発&quot; is distorted</li>
</ul>
<p><img src="/assets/blog/authors/okapi/cover_image/teckan_syuusei.png" alt="テック感の誤字"></p>
<p><strong>AI and machine learning style (collaboration with generative AI):</strong></p>
<ul>
<li>It&#39;s written as &quot;機械学&quot;, where &quot;習&quot; is missing</li>
</ul>
<p><img src="/assets/blog/authors/okapi/cover_image/ai_and_machine_syuusei.png" alt="Iと機械学習系の誤字"></p>
<h4>Issue 2: Logo Modification</h4>
<p>I wanted the reference image&#39;s logo to be reproduced, but the generative AI subtly modified the logo.</p>
<p>At first glance they look the same, but upon closer inspection, you can see that <strong>the design has subtly changed</strong>.</p>
<p><img src="/assets/blog/authors/okapi/cover_image/rogohikaku.png" alt="ロゴの差分"></p>
<p>From a copyright and brand guidelines perspective, <strong>we want to avoid logo modifications</strong>.</p>
<h3>Tried to Improve</h3>
<h4>Response to Issue 1: Japanese Typos</h4>
<p>I adjusted the prompt notes so the Japanese wouldn’t sound awkward, and tried out a few versions.</p>
<table>
<thead>
<tr>
<th>Trial</th>
<th>Changes</th>
<th>Result</th>
</tr>
</thead>
<tbody><tr>
<td><strong>①</strong></td>
<td>・Please use correct kanji and phrases for Japanese text.<br>・Ensure no typos or meaningless Japanese is included.<br>・Make Japanese text in the image natural and readable.</td>
<td><strong>No change</strong> - Same typos occurred</td>
</tr>
<tr>
<td><strong>②</strong></td>
<td>・Avoid unnatural Japanese expressions.<br>・Please review that kanji are correct before creating.</td>
<td><strong>All kanji disappeared</strong> - Text stopped displaying</td>
</tr>
<tr>
<td><strong>③</strong></td>
<td>Attached image with &quot;Please correct 自動化化 in the upper left to 自動化&quot;</td>
<td><strong>Not corrected</strong> - Original typo remained</td>
</tr>
</tbody></table>
<h4>Response to Issue 2: Logo Modification</h4>
<p>To accurately reproduce the logo, I tried the following prompt adjustments.
Additional prompt: I want the logo below to be exactly the same, so please attach the logo and use the attached file(rogo.png).
As a result, a logo close to the original was created, but the angle and thickness were slightly different.</p>
<h2>Conclusion</h2>
<p>While image creation itself can be done in one shot, there were the following 2 issues:</p>
<ol>
<li><strong>Japanese kanji expressions</strong> - Difficult to control perfectly with prompts alone</li>
<li><strong>Accurate logo reproduction</strong> - AI automatically modifies it</li>
</ol>
<p>Through this trial and error process, I think we can understand the characteristics and limitations of generative AI and find more effective ways to use it.</p>
<p><strong>Practical operational method:</strong></p>
<ul>
<li>Leave layout, atmosphere, and background generation to the image‑generation AI (AI&#39;s strong point)</li>
<li>Add logos later with image editing tools (humans control accurately)</li>
</ul>
<p>With this combination, I found that we can <strong>leverage the strengths of image-generation AI while ensuring a certain level of quality</strong>.</p>
<p>This tech blog cover image was created using the above operation with the logo manually pasted.</p>
<p>If anyone has a prompt that can accurately reproduce kanji and logos in AI-generated images, please let me know!</p>
<p>I’ll write more articles when I find new and interesting AI applications, along with challenges and solutions.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/okapi/cover_image/ai_and_machine_syuusei3.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[画像生成AIでテックブログのカバー画像を自動生成する方法]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-01-ai-agent-blog-cover-image-generation/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-01-ai-agent-blog-cover-image-generation/</guid>
            <pubDate>Mon, 01 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[画像生成AIを活用して、テックブログのカバー画像を一度の生成で自動作成。デザインの悩みから解放される実践的な実装方法を解説します。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の1日目の記事です🎅🎄</p>
<h2>はじめに</h2>
<p>QAグループのMobileチームのokapiです。</p>
<p>AIで画像を作成するとき、「こういう画像が欲しい！」とプロンプトで伝えたはずなのに、なぜか意図が伝わらず、何度もやり直す羽目に…。そんな経験、ありませんか?</p>
<p>そこで今回は、テックブログのカバー画像を「一度の生成」で作れるプロンプトを作成しました!</p>
<p>この考え方は、他の画像作成にも応用可能です。ぜひ活用してみてください。</p>
<h2>使い方</h2>
<h3>使用する画像生成AI</h3>
<p>Microsoft Copilot
※画像生成に対応した生成AIツールであれば、基本的にどれでもOKです（例：ChatGPT、DALL-E、Midjourneyなど）</p>
<h3>プロンプトの修正箇所</h3>
<p>プロンプトの修正箇所は、たったの4点だけ!</p>
<table>
<thead>
<tr>
<th>修正箇所</th>
<th>内容</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1. 表示タイトルの設定</strong></td>
<td>執筆したテックブログのタイトルを「表示タイトル」に貼り付ける</td>
</tr>
<tr>
<td><strong>2. スタイルの選択</strong></td>
<td>「■スタイル」から1つ選び、残り2つを削除</td>
</tr>
<tr>
<td><strong>3. 概要の設定</strong></td>
<td>執筆したテックブログの内容を「■テックブログの記載内容」に貼り付ける</td>
</tr>
<tr>
<td><strong>4. 以前のカバー画像添付</strong></td>
<td>以前使っていたカバー画像を「プロンプト」に添付</td>
</tr>
</tbody></table>
<h3>実際に使ったプロンプト</h3>
<pre><code>テックブログのカバー画像を作成してください。
添付している前回のテックブログで使用した画像と同じ形式でお願いします。

■目的 
以下のブログ記事のカバー画像を作成したいです。  

■表示タイトル  
（ここに記事のタイトルを貼り付け）

■注意点  
・日本語表現に不自然な点が入らないようにしてください。  
・画像のテイストやレイアウトは前回と同様でお願いします。  
・タイトルが中央または目立つ位置に配置されていること

■スタイル  
（下記3つからお好きなのを選ぶ）
・テック感（技術的な雰囲気）  
・AIと機械学習系（生成AIとの協働）  
・モーショングラフィック風（静止画でも）

■テックブログの記載内容
（ここに記事の概要を貼り付け）
</code></pre>
<h3>添付した以前のカバー画像</h3>
<p><img src="/assets/blog/authors/okapi/native-app-qa/thumbnail.png" alt="以前のカバー画像"></p>
<h2>本記事で実際に作った画像</h2>
<p>本記事では、上記3パターンのスタイルで作りました。</p>
<table>
<thead>
<tr>
<th>スタイル</th>
<th>生成された画像</th>
</tr>
</thead>
<tbody><tr>
<td><strong>テック感（技術的な雰囲気）</strong></td>
<td><img src="/assets/blog/authors/okapi/cover_image/teckan.png" alt="テック感"></td>
</tr>
<tr>
<td><strong>AIと機械学習系（生成AIとの協働）</strong></td>
<td><img src="/assets/blog/authors/okapi/cover_image/ai_and_machine.png" alt="AI系"></td>
</tr>
<tr>
<td><strong>モーショングラフィック風（静止画でも）</strong></td>
<td><img src="/assets/blog/authors/okapi/cover_image/mosyongurahhic.png" alt="モーション風"></td>
</tr>
</tbody></table>
<h2>プロンプト設計のポイント解説</h2>
<table>
<thead>
<tr>
<th>ポイント</th>
<th>解説</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1. 修正箇所を最小限に設定</strong></td>
<td>プロンプト内で修正が必要な箇所を「4点に絞り込む」ことで、誰でも簡単にコピペで使いやすいテンプレートとしました。</td>
</tr>
<tr>
<td><strong>2. 参考画像の添付</strong></td>
<td>言葉だけで「前回と同じ雰囲気で」と伝えても、AIには正確に伝わりません。実際の画像を添付することで、レイアウト・配色・フォント感などの「伝えるのが難しい曖昧な部分」を共有できます。</td>
</tr>
<tr>
<td><strong>3. 構造化された指示</strong></td>
<td>目的・タイトル・注意点・スタイル・内容と、情報を明確に分類することで、AIが各要素の優先度を理解しやすくなります。</td>
</tr>
<tr>
<td><strong>4. 避けたい要素の明記</strong></td>
<td>「日本語表現に不自然な点が入らないように」という否定形の指示を入れることで、自然な日本語を選定しやすくなります。</td>
</tr>
<tr>
<td><strong>5. 選択肢を提示(スタイル)</strong></td>
<td>AIに完全に任せるのではなく、3つのスタイルから選ぶ形式にすることで、ここを増やすだけで、カスタマイズが可能なプロンプトとなります。</td>
</tr>
</tbody></table>
<h2>おわらない（まだ完璧ではない）</h2>
<p>今回のテンプレートは「タイトル・スタイル・概要・参考画像」の4点を差し替えるだけで、再現性を保ったまま量産できます。</p>
<h3>実は課題が2つあります</h3>
<p>一発で画像生成できたものの、よく見ると以下の2つの問題が発生していました。</p>
<h4>課題1: 日本語の誤字</h4>
<p>「日本語表現に不自然な点が入らないように」とプロンプトに記載しているにも関わらず、誤字が発生していました。</p>
<p><strong>テック感(技術的な雰囲気):</strong></p>
<ul>
<li>「自動化<strong>化</strong>」となっている(「化」が重複)</li>
<li>「開発」が表示崩れ</li>
</ul>
<p><img src="/assets/blog/authors/okapi/cover_image/teckan_syuusei.png" alt="テック感の誤字"></p>
<p><strong>AIと機械学習系(生成AIとの協働):</strong></p>
<ul>
<li>「機械学」となっている(「習」が抜けている)</li>
</ul>
<p><img src="/assets/blog/authors/okapi/cover_image/ai_and_machine_syuusei.png" alt="AIと機械学習系の誤字"></p>
<h4>課題2: ロゴの改変</h4>
<p>参考画像のロゴを再現してほしかったのですが、生成AIによってロゴが微妙に改変されてしまいました。</p>
<p>一見すると同じように見えますが、よく見ると<strong>細部のデザインが変わっている</strong>ことがわかります。</p>
<p><img src="/assets/blog/authors/okapi/cover_image/rogohikaku.png" alt="ロゴの差分"></p>
<p>著作権やブランドガイドラインの観点から、<strong>ロゴの改変は避けたい</strong>ところです。</p>
<h3>改善を試みました</h3>
<h4>課題1への対応: 日本語の誤字</h4>
<p>プロンプトの注意点(日本語表現に不自然な点が入らないように)をカスタマイズして複数パターンを試してみました。</p>
<table>
<thead>
<tr>
<th>試行</th>
<th>変更内容</th>
<th>結果</th>
</tr>
</thead>
<tbody><tr>
<td><strong>①</strong></td>
<td>・日本語の文字は、正しい漢字・語句を使用してください。<br>・誤字脱字や意味不明な日本語が含まれないようにしてください。<br>・画像内の日本語テキストは、自然で読みやすい表現にしてください。</td>
<td><strong>変わらず</strong> - 同様の誤字が発生</td>
</tr>
<tr>
<td><strong>②</strong></td>
<td>・日本語表現に不自然な点が入らないようにしてください。<br>・作成する前に漢字は正しい漢字となっているかレビューしてから作成してください。</td>
<td><strong>漢字自体が全部消される</strong> - テキストが表示されなくなる</td>
</tr>
<tr>
<td><strong>③</strong></td>
<td>画像を添付の上、「左上の『自動化化』→『自動化』に修正してください」</td>
<td><strong>修正されず</strong> - 元の誤字がそのまま残る</td>
</tr>
</tbody></table>
<h4>課題2への対応: ロゴの改変</h4>
<p>ロゴを正確に再現するため、以下のプロンプト調整を試しました。
追加プロンプト：ロゴを添付の上、下のロゴは全く同じとしたいので、添付している（rogo.png）を貼り付けて使ってください。
を追加した結果、本物に近いロゴができますが、微妙に角度や太さが異なりました。</p>
<h2>おわりに</h2>
<p>画像作成自体は一発でできるものの、以下の2つの課題がありました:</p>
<ol>
<li><strong>日本語の漢字表現</strong> - プロンプトだけでは完璧な制御が難しい</li>
<li><strong>ロゴの正確な再現</strong> - AIが自動的に改変してしまう</li>
</ol>
<p>このようなトライ&amp;エラーを繰り返すことで、生成AIの特性や限界を理解でき、より効果的な活用方法を見つけられると感じています。</p>
<p><strong>現実的な運用方法:</strong></p>
<ul>
<li>画像生成AIに「レイアウト・雰囲気・背景」の生成を任せる(画像生成AIの得意分野)</li>
<li>「ロゴ」は画像編集ツールで後から追加(人間が正確に制御)</li>
</ul>
<p>この組み合わせで、<strong>画像生成AIの強みを活かしつつ、一定の品質も担保できる</strong>と分かりました。</p>
<p>本テックブログカバー画像は、上記運用で手動でロゴを貼り付けて作りました。</p>
<p>もし「AIで生成した画像内の漢字やロゴを正確に再現できるプロンプト」をお持ちの方がいらっしゃいましたら、ぜひ教えてください!</p>
<p>今後も「おもしろいAIの活用方法」や「つまずいた課題と解決策」を見つけたら、記事を執筆していきます。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/okapi/cover_image/ai_and_machine_syuusei3.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Launching the KINTO Technologies Advent Calendar 2025]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-01-start-kinto-advent-calendar-2025-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-01-start-kinto-advent-calendar-2025-en/</guid>
            <pubDate>Mon, 01 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTO Technologies is hosting an Advent Calendar again this year. This is the fifth time, with two series on free topics and a total of 50 articles planned.]]></description>
            <content:encoded><![CDATA[<p>This article is the Day 1 entry for the <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTO Technologies Advent Calendar 2025</a>🎅🎄</p>
<p>Hello, I&#39;m <a href="https://x.com/ukcpo">Yukachi (@ukcpo)</a> from the Developer Relations Group!
KINTO Technologies is doing an Advent Calendar again this year☆*</p>
<p>This is the fifth year for our Advent Calendar!
This year, we have two series on free topics with a total of 50 articles planned!</p>
<p>@<a href="https://qiita.com/advent-calendar/2025/kinto-technologies">card</a></p>
<p>In the past, the Developer Relations Group would individually reach out to each division to gather contributions for the Advent Calendar, but this time, most of the participants were recruited through a Slack channel call for submissions＼(^^)／
By the way, half of the 50 total articles will be written by first-time contributors!
It&#39;s wonderful to see so many members taking on the challenge, thinking &quot;why not give it a try&quot;!</p>
<p>As a side note, since last year&#39;s Advent Calendar, the Developer Relations Group has been hosting a casual 30-minute consultation session every day via Slack huddle during the writing period.
Being able to pop into the huddle and quickly ask questions when you&#39;re a bit stuck lowers the barrier to seeking help, so from our perspective as the hosts, it feels like a great initiative. What do you think!?
We&#39;re happy to receive questions like &quot;This is my first time writing, so is it okay to ask a basic question...?&quot;</p>
<p>![スクリーンショット](/assets/blog/authors/uka/advent/advent1.png =400x)<br><em>Our manager who&#39;s great at creating an approachable atmosphere</em></p>
<p>With all that said, we hope you will enjoy our company&#39;s &quot;real&quot; through the Advent Calendar!
Updates will be posted daily on our <a href="https://x.com/KINTOTech_Dev">official X account (@KINTOTech_Dev)</a>.
If any articles catch your interest, please give them a read♩</p>
<p>One more month left this year—let&#39;s do our best＼(^^)／</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uka/advent.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[KINTO Technologies Advent Calendar 2025 をはじめます]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-12-01-start-kinto-advent-calendar-2025/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-12-01-start-kinto-advent-calendar-2025/</guid>
            <pubDate>Mon, 01 Dec 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズは今年もアドベントカレンダーを実施します。今年で5回目、フリーテーマで2シリーズ、計50記事公開予定です。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2025/kinto-technologies">KINTOテクノロジーズ Advent Calendar 2025</a> の 1 日目の記事です🎅🎄</p>
<p>こんにちは、技術広報の <a href="https://x.com/ukcpo">ゆかち(@ukcpo)</a> です！
KINTOテクノロジーズは今年もアドベントカレンダーを実施します☆*</p>
<p>アドベントカレンダー、今年で 5 回目！
今年はフリーテーマで 2 シリーズ、計 50 記事公開予定です！</p>
<p>@<a href="https://qiita.com/advent-calendar/2025/kinto-technologies">card</a></p>
<p>過去アドベントカレンダーは技術広報から各部署へ個別で声がけをして集めておりましたが、
今回はSlackチャンネルでの公募でほとんどの数が集まりました＼(^^)／
ちなみに合計 50 記事中半分のメンバーが執筆デビューとなります！
せっかくなので・・とチャレンジしてくれるメンバーが多くて素敵！！！</p>
<p>余談ですが、去年のアドベントカレンダーからアドベントの執筆時期は技術広報メンバーが毎日 30 分、
Slackのハドルにてゆるっと相談会を開いております。
ちょっと困った時にハドルに入ればサクッと聞けちゃうの、
相談のハードルが下がると思うので開催している側として良い取り組みな気がしていますがいかがでしょう！？
&quot; 初の執筆なので初歩的な質問なのですがいいですか・・？ &quot;
などちょこちょこ相談いただけて嬉しいです！</p>
<p>![スクリーンショット](/assets/blog/authors/uka/advent/advent1.png =400x)<br><em>相談しやすい雰囲気をつくるのが得意なマネ</em></p>
<p>そんなこんなで弊社の &quot;リアル&quot; をアドベントカレンダーを通して楽しんでいただけたら嬉しいです！
更新内容は <a href="https://x.com/KINTOTech_Dev">公式X(@KINTOTech_Dev)</a> にて毎日更新予定です。
気になる記事があればぜひ目を通してみてください♩</p>
<p>今年もあと1ヶ月、頑張りましょう〜＼(^^)／</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uka/advent.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AI画像編集ツール徹底比較！どれが一番自然？ Google・OpenAI・ByteDanceの最新モデルを検証【2025秋】]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-11-07-image-edit-evaluation/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-11-07-image-edit-evaluation/</guid>
            <pubDate>Fri, 07 Nov 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[AI画像編集ツールを徹底比較！]]></description>
            <content:encoded><![CDATA[<h1>AI画像編集ツール徹底比較！どれが一番自然？ Google・OpenAI・ByteDanceの最新モデルを検証【2025秋】</h1>
<h2>はじめに</h2>
<p>2025年10月24日（金）、弊社の大阪テックラボにてCO-LAB Tech Night vol.4に発表者として参加してきました。「gpt-image-1 Gemini 2.5 Flash Image Seedream4.0の一貫性を検証」と題しまして発表させていただきました。最近GoogleからNano Banana proが発表されましたので、内容を一部アップデートしましてこちらでも共有させていただきたいと思います。</p>
<p>内容としてはOpenAI、Google、ByteDanceが提供する画像生成・編集機能を使って同じお題で画像編集を行い、どの様な出力結果になるのかを検証するといったものになります。
どのツールがいいのかなと悩んでいる方はぜひ参考にしていただければと思います。</p>
<p><strong>改めて、gpt-image-1とは？</strong>
こちらはOpenAIが2025年3月に発表し、GPT-4oに標準搭載された画像生成・編集が可能な機能です。APIなどでは「gpt-image-1」というモデル名で参照されることもあります。
<strong>input_fidelity</strong>というパラメータが提供されており、「high」に設定することで編集時の被写体やキャラクターの一貫性を格段に上がると話題になりました。参照機能なし、インペイント機能あり。</p>
<p><strong>Gemini 3 Pro Imageとは？</strong>
通称Nano Bananaと呼ばれ被写体やキャラクターの一貫性を維持する能力に特化してチューニングされていると言われています。発表当初はその性能の高さにSNSで非常に話題になりました。3 Proになって、「文字生成」機能が強化された様です。Nano Bananaの提供開始は2025年8月。参照機能あり、インペイント機能なし。</p>
<p><strong>Seedream4.0とは？</strong>
特徴としては4K出力という巨大な画像が出力可能で、スピードも速いことからプロの商用ワークフローにも耐えうると言われています。こちらも被写体やキャラクターの一貫性の高さがSNSなどで話題に上りました。2025年9月に提供開始されました。参照機能あり、インペイント機能あり。</p>
<p><strong>AIの弱点：被写体やキャラクターの一貫性の維持</strong>
画像の一部分だけを編集したい場合においても、画像全体が生成し直されてしまうことから一貫性を保つことが難しく、生成を繰り返しているうちに元の画像から徐々に見た目が変化してしまうことが起こりますが、そうした「弱点」は現在克服されつつあります。</p>
<p>というわけで、これら3つのツールの出力画像を比較検証してみたいと思います。
ちなみに、Gemini 3 Pro Image、Seedream4.0に関しては参照機能（別の画像を参考に元の画像を変更する機能）を使用しておりますが、gpt-image-1は参照機能がないので、元の画像に被写体を重ねるといった方法で編集を行なっております。ですので、プロンプトはなるべく同じ内容にしつつもgpt-image-1は若干違う内容で行いました。
また、平均10枚前後を出力してその中で最も良好な結果を示した出力を掲載しています。</p>
<h2>お題01:空白のラベルに文字を記載する</h2>
<p>空白のラベルに商品名を記載するというお題です。
![ボトル](/assets/blog/authors/aoshima/image-edit-evaluation/01/sample01.jpg =50%x)</p>
<h3>出力結果の比較</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/01/gpt.png =50%x)
やや文字が歪（いびつ）になっていますが、大きな破綻は見られませんでした。全体的に一貫性が保たれています。</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/01/gemini.jpg =50%x)
文字の太さ、大きさも問題なく、特に破綻は見られませんでした。全体的に一貫性が保たれています。</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/01/seedream.jpg =50%x)
文字の太さ、大きさも問題なく、特に破綻は見られませんでした。全体的に一貫性が保たれています。</p>
<h2>お題02:真上からのアングルに変更</h2>
<p>横から撮影された花瓶に入ったひまわりを真上からのアングルに変更するというお題です。
![ひまわり](/assets/blog/authors/aoshima/image-edit-evaluation/02/sample01.jpg =50%x) </p>
<h3>出力結果の比較</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/02/gpt.png =50%x)
アングルは真上からになっています。なんとなく花びらが人工的な感じがします。</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/02/gemini.jpg =50%x)
アングルは真上からになっていますが、花がやや小さいかもしれません。花びらの質感などは自然です。</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/02/seedream.jpg =50%x)
アングルはやや斜め上からになってしまっていますが、花の質感や右側のひまわりの花びらがひとつだけ向きが変わってしまっている部分を再現できています。</p>
<h2>お題03:家族を車の後部座席に座らせる</h2>
<p>家族写真に映っている人々を車の後部座席に座っている様に編集するというお題です。</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/03/sample01.jpg" alt="家族写真"></th>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/03/sample02.jpg" alt="シート画像"></th>
</tr>
</thead>
</table>
<h3>出力結果の比較</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/03/gpt.png =50%x)
顔の一貫性は維持されていないようです。そして全体的にやや黄色味がかってしまっています。ただ人物とシートの大きさのバランスはいいと思います。</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/03/gemini.png =50%x)
バランス、色味などかなり完成度高く出力されていると思われます。</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/03/seedream.jpg =50%x)
色合いが暗くでてしまっていますが、人物の一貫性やバランスはかなり完成度が高いと感じました。</p>
<h2>お題04:男女をソファに座らせる</h2>
<p>別々の画像の男女を仲良くソファに座っている様に編集するというお題です。</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/04/sample01.jpg" alt="女性"></th>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/04/sample02.jpg" alt="ソファ"></th>
</tr>
</thead>
</table>
<h3>出力結果の比較</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/04/gpt.png =50%x)
構図が被写体により過ぎていますし、人物もちょっとソファに対して大きく感じます。顔の一貫性は保たれていないようです。</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/04/gemini.jpg =50%x)
一見よさそうなのですが、ソファに対して女性がやや小さいようです。</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/04/seedream.jpg =50%x)
こちらは構図、ソファと人物の大きさのバランス、顔の一貫性、いずれも問題ないようです。</p>
<h2>お題05:雑誌のタイトルを日本語へ変更する</h2>
<p>雑誌のタイトルを日本語へ変更するというお題です。
![雑誌を持っている女性](/assets/blog/authors/aoshima/image-edit-evaluation/05/sample01.jpg =50%x) </p>
<h3>出力結果の比較</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/05/gpt.png =50%x)
タイトルはかなり極太になっておりますが、破綻はしていない様です。写真もお城の写真になっていて日本ぽさがでています。</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/05/gemini.jpg =50%x)
さすがに文字生成がレベルアップしたということで、かなり良い出力になっています。タイトル下のサブタイトルまで入っております。しかもこちらも日本語として破綻していないです。満開の桜と赤く色づいた紅葉の木々は違和感ありますね。</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/05/seedream.jpg =50%x)
こちらも特にタイトルは破綻していないですが、不要な句読点が入ってしまっています。小さな文字については破綻してしまっています。写真については港町っぽい場所と桜で日本ぽさは出ていると思います。</p>
<h2>お題06:男性の髪型をイラストに合わせて変更</h2>
<p>男性の髪型をイラストの髪型に合わせて変更し、3枚並べて出力するというお題です。</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/06/sample01.jpg" alt="男性"></th>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/06/sample02.jpg" alt="髪型イラスト"></th>
</tr>
</thead>
</table>
<h3>出力結果の比較</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/06/gpt.png =50%x)
被写体が大きく出力される傾向がありそうです。顔の一貫性は保たれていない様です。また髪型は長髪パーマは再現できなかった様です。</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/06/gemini.jpg =50%x)
長髪パーマの顔はややイラストに引っ張られていると言った感じですが、全体的にイラストの感じを再現できています。</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/06/seedream.jpg =50%x)
今まで高い一貫性を見せてきたSeedream4.0ですが、髪型についてはなかなか指示通りの結果になってくれませんでした。</p>
<p>この様に比較検証を行なってみると、それぞれの癖などが分かってくるので非常に興味深いと感じました。ただしこの様に各社ごとのバラツキや傾向など今は顕著に見えておりますが、すぐにその差も分からないくらいに精度を上げてくるのではないかと思います。</p>
<h2>最後に安全な画像生成・編集について</h2>
<p>この様に画像編集を簡単に行える様になってきましたが、簡単であるがゆえに意図せずとも間違った使い方をしてしまう可能性があります。</p>
<ul>
<li>著作権を侵害する様なアウトプットをしない</li>
<li>生成の過程で著作権を侵害する恐れのあるものを参照しない</li>
</ul>
<p>こういったことはもちろんのこと、</p>
<ul>
<li>未成年者など社会的立場の弱い人々に配慮する</li>
<li>差別や侮辱といった暴力から他者の尊厳を守る</li>
<li>ディープフェイクなどによる誤情報の拡散をしない</li>
</ul>
<p>画像生成・編集を行う際には、この様な配慮も忘れないことを心がけたいと思っております。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoshima/image-edit-evaluation/title.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[AI Image Editing Tool Comparison! Which is the Most Realistic Image Generator through Testing the Latest Models: Google, OpenAI, and ByteDance? [Fall 2025]]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-11-07-image-edit-evaluation-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-11-07-image-edit-evaluation-en/</guid>
            <pubDate>Sat, 01 Nov 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A thorough comparison of AI image editing tools!]]></description>
            <content:encoded><![CDATA[<h1>AI Image Editing Tool Comparison! Which is the Most Realistic Image Generator through Testing the Latest Models: Google, OpenAI, and ByteDance? [Fall 2025]</h1>
<h2>Introduction</h2>
<p>On Friday, October 24, 2025, I participated as a presenter at CO-LAB Tech Night vol.4 held at our Osaka Tech Lab. At this event, I presented on the topic of testing the consistency of gpt-image-1, Gemini 2.5 Flash Image, and Seedream 4.0. Since Google recently released Nano Banana pro, I have updated some of the content and would like to share it here as well.</p>
<p>The content involves using image generation and editing features provided by OpenAI, Google, and ByteDance to perform image editing on the same tasks and examine what kind of output results we get. If you are wondering which tool is the most suitable for you, I hope this will be helpful.</p>
<p><strong>What is gpt-image-1?</strong>
The model is released in March 2025 and integrated as standard in GPT-4o with a feature capable of image generation and editing. In APIs and other contexts, it is sometimes called by its model name gpt-image-1.
A parameter called <strong>input_fidelity</strong> is enabled. Setting the parameter to high helps significantly improve the consistency of subjects and characters in image editing, which gathered attention. The model has no reference feature but inpainting feature.</p>
<p><strong>What is Gemini 3 Pro Image?</strong>
Commonly known as Nano Banana, it is said to be tuned specifically to maintain consistency of subjects and characters. At its launch, the model with high performance become a hot topic on social media. Upgraded to 3 Pro, the model’s text generation feature is apparently enhanced. Nano Banana was released in August 2025 with reference feature support but without inpainting.</p>
<p><strong>What is Seedream 4.0?</strong>
The model includes a feature to output massive 4K images with fast speed, as well as a capability of handling professional commercial workflows. Particularly, the model’s high subject and character consistency also became a topic on social media. The model was released in September 2025 with both reference and inpainting feature support.</p>
<p><strong>AI&#39;s Weakness: Maintaining the Subject and Character Consistency</strong>
When you want to edit only part of an image, AI models often regenerate the entire image, which makes it difficult to maintain the image consistency. Iterated generation by AI models causes gradual changes in the original image; however, such weakness is currently being overcome.</p>
<p>So, I would like to compare and examine the output images from these three tools.
By the way, for Gemini 3 Pro Image and Seedream 4.0, I used the reference feature (a function that modifies the original image by referencing another image). On the other hand, for gpt-image-1, which does not have a reference feature, I performed editing by overlaying subjects onto the original image. Therefore, while I tried to keep the prompts for all the tools as similar as possible, I applied the ones with slightly different content for gpt-image-1.
In this comparison experiment, I generated an average of around 10 images and am posting the output that showed the best results among them.</p>
<h2>Task 01: Writing Texts on a Blank Label</h2>
<p>The task is to write a product name on a blank label on a bottle in the following image.
![ボトル](/assets/blog/authors/aoshima/image-edit-evaluation/01/sample01.jpg =50%x)</p>
<h3>Comparison of Output Results</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/01/gpt.png =50%x)
The text is somewhat distorted, but no major breakdown was observed. Overall consistency is maintained.</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/01/gemini.jpg =50%x)
The thickness and size of the text are fine, and no particular breakdown was observed. Overall consistency is maintained.</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/01/seedream.jpg =50%x)
The thickness and size of the text are fine, and no particular breakdown was observed. Overall consistency is maintained.</p>
<h2>Task 02: Change to a Top-Down Angle</h2>
<p>The task is to change an image of sunflowers in a vase photographed from the side to a top-down angle.
![ひまわり](/assets/blog/authors/aoshima/image-edit-evaluation/02/sample01.jpg =50%x)</p>
<h3>Comparison of Output Results</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/02/gpt.png =50%x)
The angle is from directly above the sunflowers. Their petals somehow feel artificial.</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/02/gemini.jpg =50%x)
The angle is from directly above the sunflowers, but they may look slightly small. The texture of the petals looks realistic.</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/02/seedream.jpg =50%x)
The image is ended up being slightly from above at an angle, but the texture of the flowers and the detail where just a sunflower on the right side has changed direction have been reproduced.</p>
<h2>Task 03: Seat a Family in the Back Seat of a Vehicle</h2>
<p>The task is to edit the people in a family photo as if they are seated in the back seat of a vehicle.</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/03/sample01.jpg" alt="家族写真"></th>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/03/sample02.jpg" alt="シート画像"></th>
</tr>
</thead>
</table>
<h3>Comparison of Output Results</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/03/gpt.png =50%x)
Facial consistency does not appear to be maintained. And the overall color of the image appeared somewhat yellowish. However, the balance between the people and seat sizes seems good.</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/03/gemini.png =50%x)
The balance, color tone, and other aspects appear to be output with quite high quality.</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/03/seedream.jpg =50%x)
The color tone came out dark, but I felt the consistency of the people and the balance were quite high quality.</p>
<h2>Task 04: Seat a Man and Woman on a Sofa</h2>
<p>The task is to edit a man and woman from separate images to appear sitting together on a sofa.</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/04/sample01.jpg" alt="女性"></th>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/04/sample02.jpg" alt="ソファ"></th>
</tr>
</thead>
</table>
<h3>Comparison of Output Results</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/04/gpt.png =50%x)
The composition is too close to the subjects, and the people feel a bit large in comparison to the sofa. Facial consistency does not appear to be maintained.</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/04/gemini.jpg =50%x)
At first glance it looks good, but the people are small in comparison to the sofa.</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/04/seedream.jpg =50%x)
There is seemingly no problem with all the composition, balance between sofa and people sizes, and facial consistency.</p>
<h2>Task 05: Convert English Title on a Magazine to Japanese</h2>
<p>The task is to convert an English magazine title to Japanese.
![雑誌を持っている女性](/assets/blog/authors/aoshima/image-edit-evaluation/05/sample01.jpg =50%x)</p>
<h3>Comparison of Output Results</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/05/gpt.png =50%x)
The title font has become quite bold, but it does not appear to be broken. The cover page photo has also changed to the one showing a castle, giving it a Japanese feel.</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/05/gemini.jpg =50%x)
Since the model’s text generation feature was enhanced, the output is quite good. The magazine in the image even includes the Japanese subtitle below the title. Moreover, the subtitle clearly makes sense as Japanese. The cover shows cherry blossoms in full bloom alongside red autumn leaves, which is an odd seasonal mismatch.</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/05/seedream.jpg =50%x)
The title font is not particularly broken, but unnecessary punctuation is inserted. Small text in the cover has broken down. The photo shows a harbor town-like location with cherry blossoms, giving it a Japanese feel.</p>
<h2>Task 06: Change a Man&#39;s Hairstyle Based on an Illustration</h2>
<p>The task is to restyle a man&#39;s hair to match three different hairstyles from an illustration, outputting the results side by side.</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/06/sample01.jpg" alt="男性"></th>
<th><img src="/assets/blog/authors/aoshima/image-edit-evaluation/06/sample02.jpg" alt="髪型イラスト"></th>
</tr>
</thead>
</table>
<h3>Comparison of Output Results</h3>
<p><strong>gpt-image-1</strong>
![gpt-image-1](/assets/blog/authors/aoshima/image-edit-evaluation/06/gpt.png =50%x)
There seems that subjects tend to be output large. Facial consistency does not appear to be maintained. Also, the long-permed hairstyle could not be reproduced.</p>
<p><strong>Gemini 3 Pro Image</strong>
![Gemini 3 Pro Image](/assets/blog/authors/aoshima/image-edit-evaluation/06/gemini.jpg =50%x)
The face with long permed hair leans slightly toward an illustrated look, but overall the hairstyles from the illustration have been reproduced well.</p>
<p><strong>Seedream4.0</strong>
![Seedream4.0](/assets/blog/authors/aoshima/image-edit-evaluation/06/seedream.jpg =50%x)
Seedream 4.0, which had shown high consistency so far, did not produce results according to instructions when it came to hairstyles.</p>
<p>Conducting this kind of comparative examination reveals the quirks of each, which I found very interesting. While the differences and tendencies of each model are now clearly visible, I think the models will quickly improve their accuracy to the level where these differences become indistinguishable.</p>
<h2>Finally, about Safe Image Generation and Editing</h2>
<p>As image editing becomes increasingly easy, the risk of unintentional misuse also grows. To this end, we should NOT:</p>
<ul>
<li>Produce output that infringes copyright; or</li>
<li>Reference things that may infringe copyright in the generation process.</li>
</ul>
<p>Of course, as well as these, we should also:</p>
<ul>
<li>Show consideration for minors and others in socially vulnerable positions;</li>
<li>Protect the dignity of others from violence such as discrimination and insult; or</li>
<li>Not spread misinformation through deepfakes.</li>
</ul>
<p>I would like to take the above points in consideration when performing image generation and editing.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoshima/image-edit-evaluation/title.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: August 2025 Update]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-31-newcomer-202508-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-31-newcomer-202508-en/</guid>
            <pubDate>Fri, 31 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We interviewed and compiled impressions from four members who joined in August 2025.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I&#39;m Nomura, who joined in August 2025!</p>
<p>In this article, I interviewed four new members who joined KINTO Technologies Corporation (KTC) in August 2025 about their impressions right after joining to compile their responses.
I hope this provides useful information for those interested in KTC, especially those considering joining us, as well as serving as a reflection for the members who participated.
I would be happy if you could get interested in our company through learning from the article about the corporate culture, atmosphere, and candid voices of its new employees.</p>
<h1>Hikaru Matsukura - MatsuIchi</h1>
<p><img src="/assets/blog/authors/nomura/2025-10-31-newcomer-202508/matsuIchi.png" alt="松倉一光(Hikaru Matsukura)のプロフィール画像"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I&#39;m in the Mobile App Development Group, where I&#39;m responsible for developing the backend that mobile apps access.</li>
<li>I mainly work in the domain of Dealer Digital Transformation (DX) projects.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>We work as one team with members from the Digital Transformation Development Group, Digital Transformation Solution Group, and Digital Transformation Engagements Group to deliver products to accelerate DX.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><ul>
<li>Being able to have discussions with the Cloud Infrastructure Team about infrastructure matters is extremely helpful and encouraging.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>Everyone proactively takes initiative and moves forward—it&#39;s amazing! I’ll do my best, not to falling behind.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I thought like, &quot;So it&#39;s finally my turn!&quot; (lol)</li>
<li>I&#39;d like to continue to share information if I get any chance to post my blogs in the future.</li>
</ul>
</li>
<li><strong>Question from Yusuke Miwa to Hikaru Matsukura</strong><blockquote>
<p>Are you a cat person or a dog person?</p>
</blockquote>
<ul>
<li>I like both! My wife&#39;s family has a cat, so I probably have more opportunities to interact with cats.</li>
</ul>
</li>
</ul>
<h1>Hiroki Nomura</h1>
<p><img src="/assets/blog/authors/nomura/nomura.png" alt="野村 宏樹(Hiroki Nomura)のプロフィール画像"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I belong to the AI First Group, which is a cross-functional organization. I’m based in Nagoya.</li>
<li>In my previous job, I worked as a system integrator (SIer) for an automotive company, particularly focusing on applying generative AI to automotive control areas.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>We do everything AI-related.</li>
<li>As a cross-functional group, we also develop systems and tools related to generative AI.</li>
<li>We&#39;re involved in developing and supporting KINTO products and internal generative AI apps, as well as Toyota Group projects.</li>
<li>Additionally, using the knowledge shared across the whole company, we are providing generative AI training externally.</li>
<li>Last month, we held the Vibe Coding Week event, and we&#39;ve been implementing internal initiatives to master generative AI.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><ul>
<li>Unlike the atmosphere of traditional Japanese companies, here you continuously think for yourself and keeping up a cycle of input and output.</li>
<li>In terms of tools used in KTC, I was surprised by the difference between Teams culture and Slack culture! I really appreciate the culture where everyone actively posts.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site??</strong><ul>
<li>We focus on achieving what we want to accomplish by any means necessary, rather than thinking about the means first!</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>This is a culture that was not at my previous job. I personally write technical blogs, so I feel comfortable with it.</li>
<li>By producing output, I can objectively reflect on my own activities, so it&#39;s very positive!</li>
</ul>
</li>
<li><strong>Question from Hikaru Matsukura to Hiroki Nomura</strong><blockquote>
<p>What do you recommend us as a local specialty food in Nagoya? I used to frequently go to Nan House, a buffet-style Indian restaurant in Higashiura-cho (that is currently closed, though).</p>
</blockquote>
<ul>
<li>I like hitsumabushi. It&#39;s so expensive that I can&#39;t enjoy it often, though...</li>
<li>There&#39;s Sumiyaki Una Fuji Meieki Branch on the 1st floor of the building where our office is located. Please try it when you come to Nagoya!</li>
</ul>
</li>
</ul>
<h1>Yusuke Miwa - Y.M</h1>
<p><img src="/assets/blog/authors/nomura/2025-10-31-newcomer-202508/miwa_yusuke.png" alt="三輪 優介(Yusuke Miwa)のプロフィール画像"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I belong to the New Vehicle Subscriptions Product Management Group, working at the Muromachi office.</li>
<li>In my previous job, I worked as a PM to develop a furusato nozei (hometown tax) website, planning and driving projects.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>Our group has 7 members, and I communicate with developers and people from different divisions.</li>
<li>In practice, I manage QCD for new vehicle subscription development projects and other miscellaneous projects to complete them.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><ul>
<li>Communication among engineers in KTC is more active than at my previous company. Not only do they publish tech blogs, but they also actively communicate with others during project development and requirements confirmation, which is very helpful!</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>Rather than developing products simply by following requirements, we thoroughly understand the product users’ intent behind their requests and continuously try to define the requirements and build the structures to produce maximum effect.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>This is my first experience to share information externally, so I&#39;m nervous (lol).</li>
</ul>
</li>
<li><strong>Question from Hiroki Nomura to Yusuke Miwa</strong><blockquote>
<p>What are you most into these days, whether for work or personal life?</p>
</blockquote>
<ul>
<li>On the personal side, I&#39;m into watching baseball! I used to go to baseball games only a few times a year, but this year I&#39;m going once or twice a month!</li>
</ul>
</li>
</ul>
<h1>Closing</h1>
<p>Thanks to everyone for sharing their impressions after joining KTC!</p>
<p>KINTO Technologies is constantly welcoming new members!
We&#39;ll continue to feature more blog posts to introduce new members from various divisions going forward, so we hope you to look forward to them.</p>
<p>And we are still recruiting members to work with us in various divisions and positions!
Please check <a href="https://www.kinto-technologies.com/recruit/">here</a> for details!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年8月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-31-newcomer-202508/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-31-newcomer-202508/</guid>
            <pubDate>Fri, 31 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年8月に入社した4人の皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、2025年8月入社の野村です！</p>
<p>本記事では、KINTO テクノロジーズ株式会社（以下、KTC）に2025年8月に入社した新メンバー4名の入社直後の感想をお伺いし、まとめました。
KTCに興味のある方、特に入社を検討されている方や、今回参加したメンバーの振り返りとして、有益な情報をお届けします。
KTCの社風や雰囲気、新入社員の生の声を通じて、当社の魅力を感じていただければ幸いです。</p>
<h1>松倉一光 (Hikaru Matsukura) - MatsuIchi</h1>
<p><img src="/assets/blog/authors/nomura/2025-10-31-newcomer-202508/matsuIchi.png" alt="松倉一光(Hikaru Matsukura)のプロフィール画像"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>モバイルアプリ開発Gにてモバイルアプリがアクセスするバックエンドの開発を担当しています。</li>
<li>主に販売店DXの領域で活動しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>DX開発G、DXソリューションG、DXエンゲージメントGの方々とOneTeamでDXプロダクトの提供に取り組んでいます。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>クラウドインフラチームにインフラ周りの相談ができるのはすごく頼りになり有難いです。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>みなさん主体的にどんどん動いていてすごい！置いていかれないように頑張ります。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>ついに来たか～、思いました（笑）</li>
<li>今後も機会があれば情報発信していきたいです。</li>
</ul>
</li>
<li><strong>三輪 優介さん ⇒　松倉一光さんへの質問</strong><blockquote>
<p>猫派ですか？犬派ですか？</p>
</blockquote>
<ul>
<li>どっちも好きです！　妻の実家で猫飼ってるので、猫と触れ合う機会の方が多いかも。</li>
</ul>
</li>
</ul>
<h1>野村 宏樹(Hiroki Nomura)</h1>
<p><img src="/assets/blog/authors/nomura/nomura.png" alt="野村 宏樹(Hiroki Nomura)のプロフィール画像"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>AIファーストGという横串組織に所属しています。私の拠点は名古屋です。</li>
<li>前職は自動車会社のSIerとして、とくに自動車制御領域への生成AI活用に取り組んでいました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>AIに関わることならなんでもやります。</li>
<li>横ぐし組織として、生成AIに関する仕組みやツールの整備もしています。</li>
<li>KINTOのプロダクトや社内生成AIアプリの開発やサポート、トヨタグループの案件にも携わっていますね。</li>
<li>さらに、横串組織として溜まったナレッジをもとに、社外に向けて生成AI研修もしています。</li>
<li>先月はVibeCodingWeekも実施しており、生成AIを使いこなすための社内施策も実施していました。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>JTCてきな会社の雰囲気とは逆で、自ら考えてInputとOutputをし続けていくところです。</li>
<li>ツール観点でいうと、Teams文化とSlack文化はここまで違うのか！と驚きました。皆さんが積極的に投稿する文化がとてもいいなと感じてます。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>手段先行になるのではなく、やりたいことを実現できるための手段は問わない！</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>前職ではなかった文化です。個人的に技術ブログを書いているので抵抗はないですね。</li>
<li>Outputをすることで、客観的に自身の活動の振り返りができるので、とてもPositiveです！</li>
</ul>
</li>
<li><strong>松倉一光さん ⇒　野村 宏樹さんへの質問</strong><blockquote>
<p>おすすめ名古屋めしはなんですか！？　自分は昔（今はもうなくなっちゃいましたが）東浦町にあったナンハウスの食べ放題によく行ってました。</p>
</blockquote>
<ul>
<li>私はひつまぶしが好きですねー。高いので頻度高く食べれるものではないですが...</li>
<li>オフィスの1Fに「炭焼 うな富士 名駅店」がありますね。名古屋にいらしたときにはぜひ！</li>
</ul>
</li>
</ul>
<h1>三輪 優介(Yusuke Miwa) - Y.M</h1>
<p><img src="/assets/blog/authors/nomura/2025-10-31-newcomer-202508/miwa_yusuke.png" alt="三輪 優介(Yusuke Miwa)のプロフィール画像"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>新車サブスク開発プロダクトマネジメントGに所属してます。室町オフィスで勤務です。</li>
<li>前職では、ふるさと納税サイトの開発PMとしてプロジェクト計画・推進を行なっておりました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>グループとしては7名ですが、開発者や様々な部署の方とコミュニケーションを取ってます。</li>
<li>実務としては、新車サブスク開発におけるプロジェクト案件やハギレ案件のQCD管理を行い、プロジェクトを完遂させる動きをしてます。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>エンジニアの皆さんのコミュニケーションが以前所属していた会社よりも活発で、テックブログの発信はもちろん、プロジェクト開発や要件確認の場面でも積極的にコミュニケーションを取ってくださるので、とても助かっています！</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>要求を鵜呑みにして開発するのではなく、要求意図をしっかり理解して最大効果が出るような要件定義と体制構築を日々試行錯誤してます。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>外部発信するという経験は初めてなので、緊張してますw</li>
</ul>
</li>
<li><strong>野村 宏樹さん ⇒　三輪 優介さんへの質問</strong><blockquote>
<p>仕事でもプライベートでも、一番ハマっていることは？</p>
</blockquote>
<ul>
<li>プライベートですが、野球観戦にハマってます！毎年数回程度しか行ってなかったのですが、今年は月に1~2回の頻度で行ってます！</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Exhibiting at Techbook Fest 19!]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement-en/</guid>
            <pubDate>Fri, 31 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTO Technologies makes its debut at Techbook Fest 19! We will distribute a 284-page technical doujinshi packed with practical technical knowledge from 13 engineers, free of charge]]></description>
            <content:encoded><![CDATA[<p>Hello, I&#39;m Uehara, a backend engineer in the FACTORY E-commerce Development Group at KINTO Technologies.
We will be participating in Techbook Fest 19 as the KINTO Technologies Writing Club formed by volunteer engineers from KINTO Technologies.
※This activity is conducted by volunteers for the purpose of sharing technical knowledge and is not an official corporate activity.</p>
<h2>What is Techbook Fest?</h2>
<p>Techbook Fest is a doujinshi convention exclusively for technical books. It will be held for 16 days from November 15 (Sat) to November 30 (Sun), 2025, both in an online marketplace and at a physical venue.</p>
<p>The offline event will be held on November 16 (Sun) at Ikebukuro Sunshine City Exhibition Hall D. It will bring together knowledge from a wide range of technical fields and provide a space where participants with a keen interest in technology can interact directly.
At the physical venue, you can pick up books and talk directly with the authors, while at the online venue, you can access technical books from anywhere in Japan.
The appeal lies in gaining access to valuable knowledge available only here, including specialized technologies and practical know‑how that are seldom addressed in commercial publishing.</p>
<p>For more details, please check the official Techbook Fest website.
<a href="https://techbookfest.org/event/tbf19">https://techbookfest.org/event/tbf19</a></p>
<h2>New Book to be Distributed</h2>
<p>For distribution at Techbook Fest 19, volunteers from within KINTO Technologies formed the KINTO Technologies Writing Club and wrote the technical book &quot;TECH BOOK By KINTO Technologies Vol.01.&quot;
It is an omnibus-format book that compiles chapters written by each author on their respective themes.</p>
<p>![表紙](/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/cover.webp =300x)
<em>Cover of the new book &quot;TECH BOOK By KINTO Technologies Vol.01.&quot;</em></p>
<p>This book covers a wide range of topics, from testing methods in Go language to AWS security, generative AI, infrastructure automation, mobile development, and organizational building.</p>
<p>Chapter 1: Best Practices for Unit Testing with Go
Chapter 2: Why Using IAM Roles Provides Higher Security Than IAM Users
Chapter 3: Let&#39;s Implement Responsible Generative AI Using AWS
Chapter 4: Practical Frontend Error Monitoring Learned from New Relic Implementation
Chapter 5: Past, Present, and Future of Cloud Infrastructure Automation and Integrated Management Using Infrastructure as Code
Chapter 6: Getting Started with iOS App Development
Chapter 7: Toyota Group&#39;s In-House Development Organization Pursuing Collaboration with AI Through Both Culture and Technology
Chapter 8: Creating an Autonomous Organization with the Power of Facilitation
Chapter 9: Let&#39;s Create an MCP Server with Azure Functions
Chapter 10: Balancing AWS Governance and Agility
Chapter 11: Making AWS Operations Easier Using Generative AI
Chapter 12: Recommendations for Home K8s Starting (About) Two Laps Behind
Chapter 13: Let&#39;s Try a Hands-On Session with AWS IoT Greengrass Using a Sample Application</p>
<p>We will distribute this book, packed with content, <strong>for free</strong>!</p>
<h2>Recommended For</h2>
<ul>
<li>Those interested in development using Go language or AWS</li>
<li>Those who want to learn about mobile app development or infrastructure automation</li>
<li>Those who want to know practical applications of generative AI</li>
<li>Those interested in organizational building or team management</li>
<li>Those interested in in-house development within the Toyota Group</li>
</ul>
<p>We hope that the technical challenges and solutions covered in each chapter will serve as a reference for those facing similar problems.</p>
<h2>Venue</h2>
<p>We will exhibit at both physical and online venues.</p>
<h3>Physical Venue</h3>
<ul>
<li>Date and Time: Sunday, November 16, 2025</li>
<li>Location: Ikebukuro Sunshine City Exhibition Hall D (Cultural Center Building 2F)</li>
<li>Booth Number: O15</li>
</ul>
<p><img src="/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/map.webp" alt="会場マップ">
<em>Floor map at Techbook Fest 19. The KINTO Technologies Writing Club booth is &quot;お15&quot;</em></p>
<h3>Online Venue</h3>
<ul>
<li>Saturday, November 15 - Sunday, November 30, 2025</li>
<li>Electronic version PDF will be distributed at the online marketplace</li>
</ul>
<h2>Conclusion</h2>
<p>If you visit the Techbook Fest 19 physical venue, please come see us at booth お15.
For those who live far away and cannot attend in person, we plan to publish the electronic version on the Techbook Fest online marketplace, so please take a look!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/ogp.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[技術書典19に出展します！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-31-techbookfest19-announcement/</guid>
            <pubDate>Fri, 31 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズが技術書典19に初出展！13名のエンジニアによる実践的な技術知見を詰め込んだ284ページの技術同人誌を無料配布します]]></description>
            <content:encoded><![CDATA[<p>こんにちは、KINTOテクノロジーズのFACTORY EC開発グループでバックエンドエンジニアをやっている上原です。
技術書典19に、KINTOテクノロジーズの有志エンジニアによるサークル「KINTOテクノロジーズ執筆部」として参加します。
※本活動は有志による技術知識の共有を目的としており、企業の公式活動ではありません。</p>
<h2>技術書典とは？</h2>
<p>技術書典は、技術書限定の同人誌即売会イベントです。2025年11月15日(土)から11月30日(日)の16日間、オンラインマーケットとオフライン会場で開催されます。</p>
<p>オフライン会場は11月16日(日)に池袋サンシャインシティ 展示ホールDで開催されます。幅広い技術分野の知見が集まり、技術に対してアンテナが高い参加者同士が直接交流できる場となっています。
オフライン会場では実際に著者と対話しながら本を手に取ることができ、オンライン会場では全国どこからでも技術書を入手できます。
商業出版では扱われにくい専門的な技術や実践的なノウハウなど、ここでしか得られない貴重な知見に出会えるのが魅力です。</p>
<p>詳細は技術書典の公式サイトでもご確認いただけます。
<a href="https://techbookfest.org/event/tbf19">https://techbookfest.org/event/tbf19</a></p>
<h2>今回頒布する新書</h2>
<p>技術書典19での頒布に向けKINTOテクノロジーズの社内有志でKINTOテクノロジーズ執筆部を結成し、技術本「TECH BOOK By KINTO Technologies Vol.01」を執筆しました。
それぞれの著者がそれぞれのテーマで書き上げた章たちを一冊にまとめた、オムニバス形式の本となっています。</p>
<p><a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6">https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6</a></p>
<p>![表紙](/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/cover.webp =300x)
<em>新書「TECH BOOK By KINTO Technologies Vol.01」の表紙。</em></p>
<p>本書では、Go言語でのテスト手法から、AWSセキュリティ、生成AI、インフラ自動化、モバイル開発、組織づくりまで、幅広いテーマを扱っています。</p>
<p>第1章 Goで学ぶ単体テストのベストプラクティス
第2章 なぜIAM Roleを使うことが、IAMユーザよりセキュリティが高いのか
第3章 「責任ある生成AI」をAWSを使って実装してみよう
第4章 New Relic導入から学ぶフロントエンドエラー監視の実践
第5章 Infrastructure as Codeを利用したクラウドインフラ自動化・統合管理の過去・現在・未来
第6章 今から始めるiOSアプリ開発
第7章 トヨタグループ内製開発組織が追求する、カルチャー×技術両輪によるAIとの協業
第8章 ファシリテーションの力で自律した組織を作りたい
第9章 AzureFunctionsでMCPサーバーを作ってみよう
第10章 AWSのガバナンスとアジリティの両立
第11章 AWSの運用を生成AIを活用して楽にする
第12章 (多分)２周遅れくらいから始めるおうちK8sのススメ
第13章 AWS IoT Greengrassをサンプルアプリを使ってハンズオンしてみよう</p>
<p>たっぷり内容が詰まった本書を<strong>無料</strong>で配布します！</p>
<h2>こんな方におすすめ</h2>
<ul>
<li>Go言語やAWSを使った開発に興味がある方</li>
<li>モバイルアプリ開発やインフラ自動化について学びたい方</li>
<li>生成AIの実践的な活用方法を知りたい方</li>
<li>組織づくりやチームマネジメントに関心がある方</li>
<li>トヨタグループの内製開発に興味がある方</li>
</ul>
<p>各章で扱う技術的な課題と解決方法が、同じような問題に直面している方々の参考になれば幸いです。</p>
<h2>会場</h2>
<p>オフライン・オンライン会場ともに出展します。</p>
<h3>オフライン会場</h3>
<ul>
<li>開催日時: 2025年11月16日(日)</li>
<li>場所: 池袋サンシャインシティ 展示ホールD（文化会館ビル2F）</li>
<li>ブース番号: お15</li>
</ul>
<p><img src="/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/map.webp" alt="会場マップ">
<em>技術書典19でのフロアマップ。KINTOテクノロジーズ執筆部のブースは「お15」になります</em></p>
<h3>オンライン会場</h3>
<ul>
<li>2025年11月15日(土)～11月30日(日)</li>
<li>オンラインマーケットにて電子版pdfが配布される</li>
</ul>
<p><a href="https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6">https://techbookfest.org/product/qCPrJpWLmKnLt7eWVd9zJ6</a></p>
<h2>おわりに</h2>
<p>技術書典19 オフライン会場へお越しの際は、ぜひブース「お15」でお会いしましょう。
遠方にいて現地に行けない方も、電子版を技術書典オンラインマーケットで公開予定ですので、ぜひご覧ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uehara/2025-10-24-techbookfest19-announcement/ogp.jpeg" length="0" type="image/jpeg"/>
        </item>
        <item>
            <title><![CDATA[Analyzing Amazon GuardDuty Threats with Amazon Q Developer]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-30-amazon-q-developer-log-analysis-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-30-amazon-q-developer-log-analysis-en/</guid>
            <pubDate>Thu, 30 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to efficiently analyze Amazon GuardDuty threat detection logs using Amazon Q Developer, along with our verification results.]]></description>
            <content:encoded><![CDATA[<h1>Exploring Threat Analysis in Amazon GuardDuty with Amazon Q Developer</h1>
<h2>Introduction</h2>
<p>Hello! I&#39;m Otaka from KINTO Technologies&#39; Cloud Security Group.
My daily work involves establishing cloud environment guardrails and monitoring and improvement activities using CSPM and threat detection.</p>
<p>As part of our group&#39;s daily operations, we perform detailed log analysis when Amazon GuardDuty detects threats to verify whether they represent normal operations. To streamline this log analysis process, I conducted a verification using Amazon Q Developer.</p>
<h2>Responding to Threat Detection with Amazon GuardDuty</h2>
<p>At KINTO Technologies, we use Amazon GuardDuty for threat detection in our AWS environment.
When Amazon GuardDuty detects a threat, notifications are sent to a Slack channel. Upon receiving these notifications, the security team performs detailed log analysis. When necessary, we interview the relevant users and record progress in JIRA tickets to manage response status.</p>
<p><img src="/assets/blog/authors/k.otaka/threat-detection-1.png" alt="現在の GuardDuty の脅威検知対応のフロー">
<em>Current Amazon GuardDuty threat detection response flow</em></p>
<p>In this initiative, we aim to improve efficiency in the log analysis task within the threat detection response flow by utilizing Amazon Q Developer.</p>
<p><img src="/assets/blog/authors/k.otaka/threat-detection-with-ai-1.png" alt="AI を活用した GuardDuty の脅威検知対応のフロー">
<em>Amazon GuardDuty threat detection response flow incorporating AI-based log analysis</em></p>
<h2>What is Amazon Q Developer</h2>
<p>Amazon Q Developer is a development assistant AI provided by AWS.
It specializes in cloud development using AWS and supports various use cases such as:</p>
<ul>
<li>Investigating resources, events, and activities in AWS environments</li>
<li>Troubleshooting AWS environments</li>
<li>Development, debugging, and coding assistance</li>
<li>Document creation</li>
</ul>
<p>Amazon Q Developer can analyze related logs for threats detected by Amazon GuardDuty even in its default state. For example, when requesting log investigation along with the Findings ID of a threat detected by Amazon GuardDuty, Amazon Q Developer autonomously conducts investigation and analysis using built-in tools as shown below.
(The followings are excerpts of the log investigation process performed by Amazon Q Developer.)</p>
<p><img src="/assets/blog/authors/k.otaka/log-analysis-with-default-q-44.png" alt="デフォルト状態の Amazon Q Developer のログ調査1">
<em>Amazon Q Developer autonomously performing log analysis</em></p>
<p>The report generated by Amazon Q Developer summarizes the detected threats, including the user who performed the operation and affected resources, and also includes recommended response suggestions.</p>
<p><img src="/assets/blog/authors/k.otaka/log-analysis-with-default-q-qqqq.png" alt="デフォルト状態の Amazon Q Developer のログ分析結果">
<em>Analysis report output by Amazon Q Developer in default state</em></p>
<p>In actual operations, we also check and analyze the authentication status of the user who performed the action and the log of the operation time to determine whether it was a normal activity. In addition to the above, further investigation is required.
Also, while conducting detailed log investigation and analysis through repeated interactions with Amazon Q Developer via chat, various challenges became apparent.</p>
<h2>Challenges in Log Analysis</h2>
<p>While Amazon Q Developer can analyze threats detected by Amazon GuardDuty even in its default state, there are many challenges in performing practical-level detailed log analysis.</p>
<h3>1. AWS API Throttling Limits</h3>
<p>When Amazon Q Developer receives error responses to API requests executed using built-in tools or tools provided by MCP servers, it autonomously makes corrections and resends the API request.
For example, if API request syntax is incorrect, it autonomously corrects the command and resends the request. However, if API request retries become too frequent, the throttling limit may be reached, which can prevent the necessary information from being obtained and ultimately lead to inaccurate or unexpected responses.</p>
<p><img src="/assets/blog/authors/k.otaka/rate_limit_cloudtrail.png" alt="CloudTrail のスロットリング制限">
<em>API requests executed by Amazon Q Developer reach throttling limits, preventing information retrieval.</em></p>
<h3>2. Collected Log Limitations</h3>
<p>Amazon Q Developer can search and analyze logs stored in AWS CloudTrail, but cannot analyze logs that haven&#39;t been collected in the first place.
AWS CloudTrail automatically collects management event logs (such as IAM user creation or EC2 instance launches) by default, but data event logs (such as file uploads to S3 buckets and Lambda function executions) are not collected unless manually enabled. Therefore, even when Amazon GuardDuty detects suspicious access to S3 buckets or Lambda functions, if data event log collection hasn&#39;t been enabled beforehand, searching AWS CloudTrail logs with Amazon Q Developer cannot retrieve information necessary for detailed analysis.
If Amazon Q Developer cannot obtain information necessary for log analysis, it may generate inaccurate responses based on speculation.</p>
<p><img src="/assets/blog/authors/k.otaka/search_dataevent_log.png" alt="CloudTrail データイベントの検索">
<em>Amazon Q Developer attempts to search data events even though data event collection is not enabled in AWS CloudTrail.</em></p>
<p>Since collecting and storing logs in AWS environments incurs costs, organizations may intentionally not collect or store certain logs based on budget constraints. It&#39;s necessary to organize which logs Amazon Q Developer can utilize when analyzing threats detected by Amazon GuardDuty, considering the current log collection status in the AWS environment.</p>
<h3>3. Context Window Limitations</h3>
<p>The length of text that generative AI can process at once is called the context window, representing the AI&#39;s memory.Context windows are counted in units called tokens, and within a certain token count range including past interactions, the AI can generate responses based on previous conversation content.
When extremely long text is input into prompts or long interactions continue with the generative AI, and the total token count exceeds the context window range, Amazon Q Developer forgets previously given instructions, potentially failing to provide expected responses or outputting inaccurate information.</p>
<p><img src="/assets/blog/authors/k.otaka/context_overflow_2.png" alt="コンテキストウィンドウの制限">
<em>When context window is exceeded, Amazon Q Developer automatically summarizes past information and forgets detailed content</em></p>
<p>Amazon Q Developer also has context window limitations, currently set at 200K tokens (as of October 16, 2025).
Token counting includes information obtained through API requests and responses. It&#39;s necessary to not only avoid unnecessary prompt input but also avoid repeating unnecessary API calls that consume unnecessary tokens.</p>
<h3>4. Output Consistency Challenges</h3>
<p>Generative AI produces different output formats and included information each time, even when given identical prompts. When instructing Amazon Q Developer to generate log analysis reports, the information included in reports and report structure may differ each time.
In such cases, it becomes difficult to obtain necessary information and consider responses based on reports.</p>
<h2>Approaches to Overcome Challenges</h2>
<p>As shown above, there are many challenges in performing detailed analysis of threats detected by Amazon GuardDuty using Amazon Q Developer.
To overcome these challenges and make Amazon Q Developer operate as expected, improvements are needed through two approaches:</p>
<ul>
<li>Enable Amazon Q Developer to efficiently collect information necessary for detailed log analysis and generate responses based on accurate information.</li>
<li>Provide Amazon Q Developer with detailed instructions regarding procedures for conducting detailed log analysis and necessary operations, enabling it to execute tasks similar to actual analysis work.</li>
</ul>
<p>Specifically, improvements were made through MCP integration and prompt adjustments for Amazon Q Developer.</p>
<h3>1. MCP Integration</h3>
<p>MCP (Model Context Protocol) is a standard protocol defined for generative AI to integrate with external tools and data sources. Amazon Q Developer supports MCP. By specifying the MCP servers to integrate in the configuration files and launching Amazon Q Developer, you can use external tools and reference data.</p>
<p>For detailed log analysis of threats detected by Amazon GuardDuty, the following tools are integrated with Amazon Q Developer.</p>
<table>
<thead>
<tr>
<th>Tool</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>Built-in</td>
<td>Tools for reading/writing files to local environments and operating AWS resources via API are provided by default.</td>
</tr>
<tr>
<td>AWS Knowledge MCP Server</td>
<td>Tools for accessing AWS knowledge including latest AWS news and blog information are provided.</td>
</tr>
<tr>
<td>AWS Documentation MCP Server</td>
<td>Tools for accessing AWS documentation information are provided.</td>
</tr>
<tr>
<td>CloudTrail MCP Server</td>
<td>Tools for searching and analyzing logs using AWS CloudTrail are provided.</td>
</tr>
<tr>
<td>Splunk MCP Server</td>
<td>Tools for searching and analyzing logs stored in Splunk are provided.</td>
</tr>
</tbody></table>
<p>At our company, we have built a SIEM (Security Information and Event Management) using Splunk Cloud, aggregating various logs such as AWS CloudTrail logs and Entra ID authentication logs. By integrating with the Splunk MCP Server provided by Splunk and providing tools that can execute Splunk Cloud search queries, Amazon Q Developer can perform detailed cross-sectional analysis not limited to logs stored in AWS CloudTrail.</p>
<h3>2. Prompt Engineering</h3>
<p>Prompt engineering is a technique for eliciting intended responses and improving response accuracy by carefully designing the input provided to generative AI. By providing generative AI with instruction background and additional information, or giving detailed instructions regarding task execution, adjustments are made to generate expected responses.</p>
<p>For detailed log analysis of threats detected by Amazon GuardDuty, response accuracy is improved by providing Amazon Q Developer with the following information through prompts.</p>
<table>
<thead>
<tr>
<th>Item</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody><tr>
<td>System configuration information necessary for log investigation</td>
<td>- What logs are stored<br>- Where they are stored, etc.</td>
<td>CloudTrail logs are stored in Splunk Cloud.</td>
</tr>
<tr>
<td>Explicitly instruct log investigation procedures</td>
<td>- Tools to use for log analysis<br>- What to investigate, etc.</td>
<td>- Prioritize using Splunk MCP Server for log analysis.<br> - Consider operations between 09:00 and 18:00 as normal and low risk.</td>
</tr>
<tr>
<td>Specify output format (report content)</td>
<td>- Specify report output format<br>- Specify information to include in report</td>
<td>- Create reports in Markdown format.<br> - Create reports with the following chapter structure: (chapter structure listed below)</td>
</tr>
<tr>
<td>Provide specific command execution samples</td>
<td>Splunk search queries, etc.</td>
<td>Execute the following command to verify authentication status.<br>&#39;index=example userPrincipalName=&quot;<email address>&quot; &#124; stats count by status.errorCode, status.failureReason &#124; sort -count&#39;</td>
</tr>
<tr>
<td>Clearly state prohibitions</td>
<td>- What operations should not be executed<br>- What information should not be included in output</td>
<td>- Do not calculate day of week from dates yourself.<br>- Do not make associations based on speculation or assumptions.</td>
</tr>
</tbody></table>
<p>In prompt engineering, insufficient information provided to generative AI may not effectively adjust output content. Prompts were adjusted to the appropriate level of detail by specifying conditions and instructions using generative AI.</p>
<h2>Improvements and Results from Adjustments</h2>
<p>After implementing the above adjustments, the following report was generated when requesting log analysis from Amazon Q Developer again. The instructions input to the prompt were the same text as when requesting analysis from Amazon Q Developer in its default state.</p>
<pre><code># GuardDuty Finding Detailed Analysis Report

**Finding ID**: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
**Detection Region**: ap-northeast-1  
**AWS Account ID**: 000000000000 
**AWS Account Name**: (Unknown)  
**Analysis Date/Time**: 2025-10-16 17:44:14  

## 1. Executive Summary

### Key Findings
- **Actual User Identification**: User Name (&lt; User Email Address &gt;)
- **Outsourced User**: Identified as outsourced company user by &quot;xxxx-&quot; prefix
- **False Positive Possibility**: High (possibility of normal work by legitimate user)
- **CloudTrail Logs**: Target AWS account logs do not exist in Splunk, detailed authentication chain tracking unavailable

### Emergency Response Necessity Assessment
- **Urgency**: Low
- **Reason**: Activity by legitimate user during normal business hours, Entra ID authentication also normal

### Key Investigation Results Summary
- Finding occurrence time: September 9, 2025 13:20:05 JST (Monday during normal business hours)
- Operation content: GetObject API execution from S3 bucket &quot;&lt; S3-Bucket-Name &gt;&quot;
- User: User Name from outsourced company
- Abnormality: Anomaly detected by GuardDuty machine learning, but legitimate operation during business hours

### Recommended Actions (Prioritized)
1. **Priority: Low** - Confirm work content with relevant user (just in case)
2. **Priority: Low** - Verify S3 access logs in detail (if necessary)
3. **Priority: Medium** - Consider adjusting GuardDuty machine learning baseline

## 2. Threat Overview

**Threat Type**: Exfiltration:S3/AnomalousBehavior  
**Severity**: 8.0 (High)  
**Detection Content**: Anomalous S3 API calls by IAM entity

The &quot;anomalous&quot; S3 GetObject API call detected by GuardDuty indicates deviation from machine learning baseline, but is actually determined to be operation by legitimate user during normal business hours.

## 3. Threat Details

### Detailed Attack Method Analysis
- **API**: S3 GetObject
- **Target Bucket**: &lt; S3-Bucket-Name &gt;
- **Authentication Method**: AuthHeader (legitimate AWS authentication)
- **User Agent**: aws-sdk-rust/1.3.8 (development tool usage)

### Attacker Profiling
Not an actual attacker, but operation by legitimate user &quot;User Name&quot;:
- User from outsourced company (&quot;xxx-&quot; prefix)
- Uses AWS SSO for normal work
- Track record of work in development environment (sandbox)

### Attack Timeline Analysis
- **2025-09-09 13:20:05 JST**: S3 GetObject API execution
- **2025-09-09 13:29:43 JST**: Detection by GuardDuty
- **Same date/time**: Confirmed successful AWS SSO login in Entra ID

### Detailed Impact Scope Assessment
- **Affected Resources**: S3 bucket &quot;&lt; S3-Bucket-Name &gt;&quot;
- **Data Leakage Risk**: Low (sandbox bucket, legitimate user)
- **Lateral Movement Risk**: None
- **Persistent Threat**: None

### Comparison with Similar Attack Patterns
Anomaly detection by GuardDuty machine learning, differs from actual attack patterns:
- Use of legitimate authentication chain
- Operation during normal business hours
- Work in development environment

### Security Control Bypass Techniques
No bypass techniques used, all through legitimate authentication/authorization processes:
- SSO login via Identity Center
- Operation with appropriate role permissions
- Use of legitimate AWS SDK

## 4. Principal Information

**User Name**: &lt; Role Name &gt;  
**Actual User**: User Name (&lt; User Email Address &gt;)  
**User Type**: AssumedRole (via Identity Center)  
**Access Key ID**: &lt; Access Key ID &gt;  
**Principal ID**: &lt; Principle ID&gt;  

### Detailed User Attributes
- **Name**: User Name
- **Affiliation**: Outsourced company (&quot;xxx-&quot; prefix)
- **Email Address**: &lt; User Email address&gt;
- **Role**: &lt; Role Name &gt;

## 5. Abnormality Analysis

### Time-based Abnormality Assessment
- **Finding Occurrence Date**: Monday (normal business day)
- **Finding Occurrence Time**: 13:20:05 JST (normal business hours)
- **Abnormality Level**: **Low** - Activity during normal business hours
- **Risk Assessment**: Low risk

### Geographic Abnormality
- **Access Source**: Japan (&lt; ISP Name &gt;)
- **Expected Business Location**: Within Japan
- **Abnormality**: None - Access from expected business location

### Operation Pattern Abnormality
- **Tool Used**: aws-sdk-rust (development tool)
- **Operation Content**: S3 GetObject (data retrieval)
- **Target**: sandbox bucket (development environment)
- **Abnormality**: Low - Normal operation in development work

### Authentication Chain Abnormality
- **Entra ID Authentication**: Normal (error code 0)
- **Identity Center**: Normal SSO session
- **AWS Authentication**: Legitimate AssumedRole
- **Abnormality**: None - All legitimate processes

### Entra ID Authentication Log Analysis Results

#### Executed Splunk Queries and Results

**1. Basic Information Retrieval**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; | table userDisplayName userPrincipalName

**Result**: Confirmed user name &quot;User Name&quot;

**2. 30-Day Behavior Pattern**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; earliest=-30d | stats count, values(location.city), values(deviceDetail.browser), values(appDisplayName) by userPrincipalName

**Result**: 
- Total login count: 59 times
- Access region: &lt; Access Area &gt;
- Browser: Chrome 138.0.0
- Apps used: AWS SSO, Microsoft 365 Support Service, Microsoft Account Controls V2, Microsoft Teams Web Client

**3. Time Pattern Analysis**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; earliest=-7d | eval weekday=strftime(_time, &quot;%A&quot;), hour=strftime(_time, &quot;%H&quot;) | stats count by weekday, hour | sort weekday, hour

**Result**: 
- Many accesses at 16:00 hour regardless of weekdays/holidays
- Multiple accesses at midnight 0:00 hour confirmed (Friday, Thursday, Tuesday, Wednesday)
- Access patterns outside normal business hours also exist

**4. Authentication Status**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; | stats count by status.errorCode, status.failureReason | sort -count

**Result**: 
- Successful logins: 28 times (error code 0)
- Authentication failures: Not confirmed
- Authentication success rate: 100%

**5. Authentication Logs Around Finding Occurrence Time**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; (Around Finding occurrence time)

**Result**: 
- Successful AWS SSO login from IPv6 address (&lt; IPv6 Address &gt;)
- Access from within Japan
- Authentication successful (error code 0)

### Identity Verification Results
**Identity Confirmation through Behavior Analysis**: 
- **Match with Normal Patterns**: High degree of match
- **Access Region**: Access from normal &lt; Access Area &gt;
- **Applications Used**: Normal use of AWS SSO
- **Authentication Success Rate**: 100% (no abnormal failures)
- **User authenticity**: **High** - Matches normal behavior patterns

## 6. Risk Assessment

### Overall Risk Assessment: **Low**

#### Rationale
1. **Operation by Legitimate User**: Operation by actual outsourced user
2. **Normal Business Hours**: Normal business hours on Monday at 13:20
3. **Legitimate Authentication Process**: Normal authentication chain via Identity Center
4. **Development Environment**: Development work in sandbox bucket
5. **Identity Verification**: Matches legitimate user&#39;s normal patterns in Entra ID logs

#### False Positive Possibility
**High** - GuardDuty&#39;s machine learning baseline may not have sufficiently learned this user&#39;s normal patterns

#### Security Impact
- **Data Leakage Risk**: Extremely low
- **System Compromise Risk**: None
- **Lateral Spread Risk**: None

## 7. Detailed Investigation

### CloudTrail Log Existence Confirmation Results
**Detailed authentication chain tracking is difficult because CloudTrail logs for target AWS account (000000000000) do not exist in Splunk**

Executed query:
index=&lt; example &gt; sourcetype=aws:cloudtrail account=000000000000 | head 1

Result: 0 logs found

### Candidate User Investigation from Source IP Address
**Candidate User Investigation by Geographic Proximity**:
- Direct search with IP address from Finding details (&lt; IP Address &gt;): 0 results
- Actual authentication logs use IPv6 address (&lt; IPv6 Address &gt;)
- Both accesses from within Japan with geographic consistency

### Points Requiring Additional Investigation
1. **GuardDuty Baseline Adjustment**: Learning normal patterns for outsourced users
2. **S3 Access Logs**: Verify detailed logs at bucket level if necessary
3. **User Work Confirmation**: Confirm work content at relevant time (just in case)

### Unclear Points or Questions
1. Specific learning baseline that GuardDuty determined as &quot;abnormal&quot;
2. Access patterns of other users to same bucket
3. Business necessity for using aws-sdk-rust tool

## 8. Recommended Countermeasures

### Immediate Response (Priority: Low)
1. **User Confirmation**: Confirm work content with User Name (just in case)
2. **Continuous Monitoring**: Monitor for recurrence with similar patterns

### Short-term Response (Within 1 week)
1. **GuardDuty Adjustment**: Promote learning of normal patterns for outsourced users
2. **Access Pattern Analysis**: Establish normal access patterns for sandbox bucket

### Medium to Long-term Response (Within 1 month)
1. **Baseline Optimization**: Adjust GuardDuty machine learning baseline
2. **Monitoring Rule Review**: Establish monitoring rules for outsourced users
3. **Documentation**: Document normal development work patterns

### Reasons for Determining No Response Necessary
- Legitimate operation by legitimate user during normal business hours
- All authentication processes normal
- Security risk extremely low
- High possibility of false positive

**Conclusion**: This Finding is determined to be a false positive, requiring no response as a security incident. Recommend adjusting GuardDuty baseline.
</code></pre>
<p>Compared to requesting log analysis of threats detected by Amazon GuardDuty from Amazon Q Developer in its default state, the following improvements were observed.
By adjusting Amazon Q Developer through MCP integration and prompt engineering, the information included in log analysis output and its content could be brought closer to expectations.</p>
<ul>
<li>By specifying report format to generate in prompts, necessary information was stably included in output content across multiple verifications.</li>
<li>Risk assessment of detected threats could be performed using log data on Splunk Cloud, enabling analysis closer to actual practice.</li>
</ul>
<p>However, further improvements are needed in the following aspects:</p>
<ul>
<li>Through multiple verifications, reports containing inaccurate information were sometimes generated.</li>
<li>Despite clear instructions in prompts, outputs ignoring instructions sometimes occurred.</li>
<li>When AWS CloudTrail logs were not imported to Splunk Cloud, risk assessment was sometimes performed using only Entra ID logs without searching AWS CloudTrail logs directly using tools provided by CloudTrail MCP Server.</li>
</ul>
<p>Hallucinations cannot be completely eliminated, and continuous adjustment of Amazon Q Developer is necessary while continuing to verify output content and monitor executed operations.</p>
<h2>Conclusion</h2>
<p>While log analysis using Amazon Q Developer has many challenges in its default state, by performing MCP integration and prompt adjustments, responses that appear usable for actual log analysis could be generated. However, the generated content cannot be completely trusted, and continued adjustment and improvement regarding response accuracy and actually performed operations appear necessary.</p>
<p>Additionally, since this verification focused on methods of utilizing Amazon Q Developer for log analysis, this article does not address other aspects. From the perspective of securely leveraging generative AI, there remain many challenges to be improved for practical operation, such as enforcing minimum privilege management for MCP servers and handling credential management.</p>
<p>I will continue to explore utilization methods for Amazon Q Developer while working on improving and strengthening security operations.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Amazon Q Developer を活用した Amazon GuardDuty の脅威分析の試み]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-30-amazon-q-developer-log-analysis/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-30-amazon-q-developer-log-analysis/</guid>
            <pubDate>Thu, 30 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Amazon Q Developer を活用して、Amazon GuardDuty の脅威検知ログを効率的に分析する方法とその検証結果を紹介します。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは！KINTO テクノロジーズ、クラウドセキュリティ G の大高です。
普段は、クラウド環境のガードレール整備と CSPM や脅威検知を利用した監視やカイゼン活動に取り組んでいます。</p>
<p>私たちのグループでは、日々の業務の一環として、 Amazon GuardDuty が検知した脅威に関して、ログの詳細分析を行い、正常な操作であるかを確認する場面があります。このログ分析の効率化を目指して、Amazon Q Developer を活用した検証をしてみました。</p>
<h1>Amazon GuardDuty の脅威検知の対応</h1>
<p>KINTO テクノロジーズでは、 AWS 環境の脅威検知に Amazon GuardDuty を利用しています。
Amazon GuardDuty が脅威を検知すると、Slack のチャンネルに通知が届くようになっており、この通知を受けて、セキュリティ担当者がログの詳細分析を行います。必要に応じて、対象のユーザーにヒアリングを行いながら、 JIRA チケットに経過を記録し、対応状況を管理しています。</p>
<p><img src="/assets/blog/authors/k.otaka/threat-detection-1.png" alt="現在の GuardDuty の脅威検知対応のフロー">
<em>現在の Amazon GuardDuty の脅威検知対応のフロー</em></p>
<p>今回の取り組みでは、脅威検知後の対応フローのうち、ログ分析のタスクを Amazon Q Developer を活用して、効率化することを目指しています。</p>
<p><img src="/assets/blog/authors/k.otaka/threat-detection-with-ai-1.png" alt=" AI を活用した GuardDuty の脅威検知対応のフロー">
<em>AI によるログ分析を組み込んだ場合の Amazon GuardDuty の脅威検知対応のフロー</em></p>
<h1>Amazon Q Developer とは</h1>
<p>Amazon Q Developer は、 AWS が提供する開発アシスタント AI です。
AWS を活用したクラウド開発に特化しており、以下のような様々なユースケースに対応しています。</p>
<ul>
<li>AWS 環境のリソース、イベント、アクティビティの調査</li>
<li>AWS 環境のトラブルシューティング</li>
<li>開発・デバッグ・コーディング支援</li>
<li>ドキュメントの作成</li>
</ul>
<p>Amazon Q Developer は、デフォルトの状態でも Amazon GuardDuty で検知した脅威について、関連ログを分析することができます。例えば、Amazon GuardDuty で検知された脅威の Findings ID とともにログ調査を依頼すると、以下のように Amazon Q Developer は Built-In のツールを使用して、自律的に調査と分析を行ってくれます。
(以下は、 Amazon Q Developer がログ調査を行う過程を一部抜粋したものです。)</p>
<p><img src="/assets/blog/authors/k.otaka/log-analysis-with-default-q-44.png" alt="デフォルト状態の Amazon Q Developer のログ調査1">
<em>Amazon Q Developer が自律的にログ分析を行う様子</em></p>
<p>Amazon Q Developer が生成したレポートには、操作を行なったユーザーや影響を受けたリソースなど検知した脅威の概要がまとめられており、推奨される対応の提案も含まれています。</p>
<p><img src="/assets/blog/authors/k.otaka/log-analysis-with-default-q-qqqq.png" alt="デフォルト状態の Amazon Q Developer のログ分析結果">
<em>デフォルトの状態で Amazon Q Developer が出力した分析レポート</em></p>
<p>しかし、実際の業務では、操作を行なったユーザーの認証の状態や操作時間のログも含めて、確認・分析を行い正常な動作であるかを判断しており、上記の内容に加え、さらなる追加の調査が必要です。
また、詳細なログ調査・分析を行うために、Amazon Q Developer とチャットによるやり取りを重ねる中で、様々な課題が見えてきました。</p>
<h1>ログ分析を行う上での課題</h1>
<p>Amazon Q Developer は、デフォルトの状態でも Amazon GuardDuty で検知した脅威の分析を行うことができますが、実務レベルのログの詳細分析を行うためには多くの課題があります。</p>
<h3>1. AWS API のスロットリング制限</h3>
<p>Amazon Q Developer は、 Built-In のツールや MCP サーバーにより提供されているツールを使用して実行した API リクエストにエラーレスポンスが返ってきた場合、自律的に修正を行い、再度、 API リクエストを送信します。
例えば、 API リクエストの文法が誤っていた場合は、自律的にコマンドの修正を行い、再度、リクエストを送信します。しかし、 API リクエストの再送が多くなると、スロットリングの上限に達してしまい、最終的に必要な情報を取得できないまま、不正確な回答や期待とは異なる回答を生成してしまうことがあります。</p>
<p><img src="/assets/blog/authors/k.otaka/rate_limit_cloudtrail.png" alt="CloudTrail のスロットリング制限">
<em>Amazon Q Developer で実行した API リクエストがスロットリングの上限に到達してしまい、情報の取得ができない。</em></p>
<h3>2. 収集しているログの制限</h3>
<p>Amazon Q Developer は AWS CloudTrail に保存されているログを検索・分析できますが、そもそもログが取得されていない場合は分析することができません。
AWS CloudTrail では、デフォルトで管理イベント（ IAM ユーザーの作成や EC2 インスタンスの起動など）のログは自動的に収集されますが、データイベント（ S3 バケットへのファイルアップロードや Lambda 関数の実行など）のログは手動で設定を有効化しなければ収集されません。そのため、Amazon GuardDuty が S3 バケットや Lambda 関数への不審なアクセスを検知した場合でも、事前にデータイベントのログ収集を有効化していなければ、Amazon Q Developer で AWS CloudTrail ログを検索しても詳細な分析に必要な情報を取得することができません。
Amazon Q Developer がログ分析に必要な情報を取得できなければ、推測による不正確な回答の生成をしてしまう可能性があります。</p>
<p><img src="/assets/blog/authors/k.otaka/search_dataevent_log.png" alt="CloudTrail データイベントの検索">
<em>AWS CloudTrail でデータイベントの取得を有効化していないが、 Amazon Q Developer はデータイベントの検索を試みる。</em></p>
<p>AWS 環境のログの収集・保存にはコストがかかるため、会社や組織の予算に合わせて、意図的にログの収集・保存を実施していない場合もあります。現在の AWS 環境のログの収集状況を考慮して、 Amazon GuardDuty で検知した脅威の分析を行う際に、Amazon Q Developer がどのログを活用することができるかについて整理する必要があります。</p>
<h3>3. コンテキストウィンドウの制限</h3>
<p>生成 AI が一度に処理することができるテキストの長さのことをコンテキストウィンドウと言い、生成 AI の「記憶力」を指し示します。コンテキストウィンドウは、トークンという単位でカウントされており、過去のやり取りを含めて一定のトークン数の範囲内であれば過去のやりとりの内容を踏まえた受け答えを生成することができます。
非常に長い文章をプロンプトに入力したり、生成 AI と長いやり取りを続けたりして、トークン数の合計がコンテキストウィンドウの範囲を超えると、Amazon Q Developer は、過去に与えた指示を忘れてしまい、期待した回答をしてくれなかったり、不正確な情報を出力してしまったりする可能性が高まります。</p>
<p><img src="/assets/blog/authors/k.otaka/context_overflow_2.png" alt="コンテキストウィンドウの制限">
<em>コンテキストウィンドウを超過すると、 Amazon Q Developer が自動的に過去情報を要約して、詳細な内容は忘れてしまう</em></p>
<p>Amazon Q Developer もコンテキストウィンドウの制限があり、 200K トークンまでとなっています。（2025年10月16日現在）
トークン数のカウントには、 API のリクエストやレスポンスにより得られた情報も含まれます。無駄なプロンプトの入力を避けるだけでなく、不要な API コールを繰り返して、不要なトークンを消費しないように注意する必要があります。</p>
<h3>4. 出力の一貫性の課題</h3>
<p>生成 AI は、全く同じプロンプトを与えたとしても、生成する回答の出力フォーマットや含まれる情報が毎回異なります。Amazon Q Developer にログ分析のレポートを生成するように指示した場合でも、レポートに含まれる情報が毎回異なったり、レポートの構成が毎回異なったりする可能性があります。
このような場合、必要な情報を得ることができず、レポートに基づいて対応を検討したりすることが難しくなります。</p>
<h1>課題を克服するための工夫</h1>
<p>このように Amazon Q Developer で Amazon GuardDuty が検知した脅威を詳細分析するには、多くの課題があります。
これらの課題を乗り越えて、Amazon Q Developer を期待通りに動作をさせるために、以下の2つのアプローチで改善が必要です。</p>
<ul>
<li>Amazon Q Developer がログの詳細分析に必要となる情報を効率的に収集して、正確な情報に基づく回答を生成できるようにする。</li>
<li>Amazon Q Developer にログの詳細分析を実施する手順や必要な操作に関する詳細な指示を与えて、実際の分析業務と同様にタスクを実行させる。</li>
</ul>
<p>具体的には Amazon Q Developer に、MCP 連携とプロンプトの調整を行うことで改善を図りました。</p>
<h3>1. MCP 連携</h3>
<p>MCP（ Model Context Protocol ）とは、生成 AI が外部のツールやデータソースと連携を行うために定められた標準プロトコルです。Amazon Q Developer は、MCP に対応しています。連携したい MCP サーバーを設定ファイルに記入して、 Amazon Q Developer を起動することで、外部のツールの利用や、データ参照を行うことができます。</p>
<p>Amazon GuardDuty で検知した脅威に関するログの詳細分析を行うために、 Amazon Q Developer に以下のツールを連携しています。</p>
<table>
<thead>
<tr>
<th>ツール</th>
<th>内容</th>
</tr>
</thead>
<tbody><tr>
<td>Built-in</td>
<td>ローカル環境へのファイルの読み書きや、API による AWS リソースの操作が実行できるツールがデフォルトで提供されています。</td>
</tr>
<tr>
<td>AWS Knowledge MCP Server</td>
<td>AWS に関する最新のニュースやブログの情報を含む AWS ナレッジにアクセスするためのツールが提供されています。</td>
</tr>
<tr>
<td>AWS Documentation MCP Server</td>
<td>AWS に関するドキュメント情報にアクセスするためのツールが提供されています。</td>
</tr>
<tr>
<td>CloudTrail MCP Server</td>
<td>AWS CloudTrail を使用してログを検索したり、分析を行うためのツールが提供されています。</td>
</tr>
<tr>
<td>Splunk MCP Server</td>
<td>Splunk に保存されているログを検索したり、分析を行うためのツールが提供されています。</td>
</tr>
</tbody></table>
<p>弊社では、Splunk Cloud を利用して SIEM ( Security Information and Event Management ) を構築しており、AWS CloudTrail のログや Entra ID の認証ログといったさまざまなログが集約されています。 Splunk 社が提供する Splunk MCP Server と連携し、 Splunk Cloud の検索クエリを実行することができるツールを提供することで、Amazon Q Developer が AWS CloudTrail に保存されたログに限らず、横断的に詳細分析を行うことができるようにしています。</p>
<h3>2. プロンプトエンジニアリング</h3>
<p>プロンプトエンジニアリングとは、生成 AI に与える入力情報を工夫することで、意図した応答を引き出したり、回答精度の向上を図る手法です。生成 AI に対して、指示の背景や追加の情報を提供したり、タスクの遂行に関する詳細な指示を与えることで、期待した回答を生成するように調整を行っていきます。</p>
<p>Amazon GuardDuty で検知した脅威に関するログの詳細分析では、 Amazon Q Developer に以下のような情報をプロンプトで提供することで回答精度の改善を図っています。</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>記載内容</th>
<th>例</th>
</tr>
</thead>
<tbody><tr>
<td>ログ調査に必要となるシステム構成の情報</td>
<td>・どんなログが保存されているか<br>・どこに保存されているかなど</td>
<td>CloudTrail のログは、Splunk Cloud に保存されています。</td>
</tr>
<tr>
<td>ログの調査手順を明示的に指示</td>
<td>・ログ分析の際に使用するツール<br>・何を調査するかなど</td>
<td>・ログ分析の際は、Splunk MCP Server を優先的に使用してください。<br> ・操作時間帯が 09:00 〜 18:00 の時間帯は正常な動作であり、低リスクと判断してください。</td>
</tr>
<tr>
<td>出力形式（レポートの内容）を具体的に指定</td>
<td>・レポートの出力形式の指定<br>・レポートに含める情報の指定</td>
<td>・レポートは、Markdown 形式で作成してください。<br> ・レポートは、以下の章立てで作成してください。: （以下、章立てを記載）</td>
</tr>
<tr>
<td>実行するコマンドの具体的サンプルを提供</td>
<td>Splunk の検索クエリなど</td>
<td>認証状況の確認では、以下のコマンドを実行してください。<br>&#39;index=example userPrincipalName=&quot;&lt;メールアドレス&gt;&quot; &#124; stats count by status.errorCode, status.failureReason &#124; sort -count&#39;</td>
</tr>
<tr>
<td>禁止事項の明記</td>
<td>・実行をしてはいけない操作は何か<br>・出力に含めてはいけない情報は何か</td>
<td>・自分で日付から曜日を計算しないでください。<br>・推測や仮定に基づく関連付けはしないでください。</td>
</tr>
</tbody></table>
<p>プロンプトエンジニアリングにおいては、生成 AI に与える情報量が少ないと、出力内容の調整に効果が出ないことがあります。プロンプトに含む条件や指示内容を生成 AI を使用しながらできる限り詳細に記載することで、適切な情報量になるようにプロンプトを調整しました。</p>
<h1>工夫による改善と成果</h1>
<p>上記の調整を実施後、改めて Amazon Q Developer にログ分析を依頼した結果、生成されたレポートが以下になります。プロンプトに入力した指示は、デフォルト状態の Amazon Q Developer に分析を依頼したときと同じ文章を入力しています。</p>
<pre><code># GuardDuty Finding 詳細分析レポート

**Finding ID**: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
**検出リージョン**: ap-northeast-1  
**AWSアカウントID**: 000000000000 
**AWSアカウント名**: （不明）  
**分析日時**: 2025-10-16 17:44:14  

## 1. エグゼクティブサマリー

### 重要な発見
- **実際のユーザー特定**: User Name（ &lt; User Email Address &gt; ）
- **外部委託ユーザー**: 「xxxx-」プレフィックスにより外部委託会社のユーザーと判定
- **誤検知の可能性**: 高い（正規ユーザーによる通常業務の可能性）
- **CloudTrailログ**: 対象AWSアカウントのログがSplunkに存在せず、詳細な認証チェーン追跡は不可

### 緊急対応の要否判断
- **緊急度**: 低
- **理由**: 正規ユーザーによる通常業務時間帯での活動、Entra ID認証も正常

### 主要調査結果の要点
- Finding発生時刻: 2025年9月9日 13:20:05 JST（月曜日の通常業務時間）
- 操作内容: S3バケット「&lt; S3-Bucket-Name &gt;」からのGetObject API実行
- ユーザー: 外部委託会社の User Name
- 異常性: GuardDutyの機械学習による異常検知だが、業務時間帯での正規操作

### 推奨アクション（優先度付き）
1. **優先度: 低** - 当該ユーザーへの業務内容確認（念のため）
2. **優先度: 低** - S3アクセスログの詳細確認（必要に応じて）
3. **優先度: 中** - GuardDutyの機械学習ベースライン調整検討

## 2. 脅威の概要

**脅威タイプ**: Exfiltration:S3/AnomalousBehavior  
**重要度**: 8.0（高）  
**検出内容**: IAMエンティティによる異常なS3 API呼び出し

GuardDutyが検出した「異常な」S3 GetObject API呼び出しは、機械学習ベースラインからの逸脱を示しているが、実際には正規ユーザーによる通常業務時間帯での操作と判定される。

## 3. 脅威の詳細

### 攻撃手法の詳細分析
- **API**: S3 GetObject
- **対象バケット**: &lt; S3-Bucket-Name &gt;
- **認証方式**: AuthHeader（正規のAWS認証）
- **User Agent**: aws-sdk-rust/1.3.8（開発ツール使用）

### 攻撃者のプロファイリング
実際の攻撃者ではなく、正規ユーザー「 User Name 」による操作：
- 外部委託会社（「xxx-」プレフィックス）のユーザー
- 通常業務でAWS SSOを使用
- 開発環境（sandbox）での作業実績あり

### 攻撃の時系列分析
- **2025-09-09 13:20:05 JST**: S3 GetObject API実行
- **2025-09-09 13:29:43 JST**: GuardDutyによる検出
- **同日同時刻**: Entra IDでのAWS SSOログイン成功を確認

### 影響範囲の詳細評価
- **影響を受けたリソース**: S3バケット「&lt; S3-Bucket-Name &gt;」
- **データ漏洩リスク**: 低（sandboxバケット、正規ユーザー）
- **横展開リスク**: なし
- **継続的脅威**: なし

### 類似攻撃パターンとの比較
GuardDutyの機械学習による異常検知であり、実際の攻撃パターンとは異なる：
- 正規認証チェーンの使用
- 通常業務時間帯での操作
- 開発環境での作業

### セキュリティ制御の回避手法
回避手法は使用されておらず、全て正規の認証・認可プロセスを経由：
- Identity Center経由のSSOログイン
- 適切なロール権限での操作
- 正規のAWS SDK使用

## 4. プリンシパル情報

**ユーザー名**: &lt; Role Name &gt;  
**実際のユーザー**: User Name（ &lt; User Email Address &gt; ）  
**ユーザータイプ**: AssumedRole（Identity Center経由）  
**アクセスキーID**: &lt; Access Key ID &gt;  
**プリンシパルID**: &lt; Principle ID&gt;  

### ユーザー属性詳細
- **氏名**: User Name
- **所属**: 外部委託会社（「xxx-」プレフィックス）
- **メールアドレス**: &lt; User Email address&gt;
- **ロール**: &lt; Role Name &gt;

## 5. 異常性分析

### 時間帯による異常性評価
- **Finding発生日**: 月曜日（通常業務日）
- **Finding発生時刻**: 13:20:05 JST（通常業務時間）
- **異常性レベル**: **低** - 通常業務時間帯での活動
- **リスク評価**: 低リスク

### 地理的異常性
- **アクセス元**: 日本（&lt; ISP Name &gt;）
- **想定業務拠点**: 日本国内
- **異常性**: なし - 想定される業務拠点からのアクセス

### 操作パターンの異常性
- **使用ツール**: aws-sdk-rust（開発ツール）
- **操作内容**: S3 GetObject（データ取得）
- **対象**: sandboxバケット（開発環境）
- **異常性**: 低 - 開発業務での通常操作

### 認証チェーンの異常性
- **Entra ID認証**: 正常（エラーコード0）
- **Identity Center**: 正常なSSOセッション
- **AWS認証**: 正規のAssumedRole
- **異常性**: なし - 全て正規プロセス

### Entra ID認証ログ分析結果

#### 実行したSplunkクエリと結果

**1. 基本情報取得**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; | table userDisplayName userPrincipalName

**結果**: ユーザー名「 User Name 」を確認

**2. 30日行動パターン**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; earliest=-30d | stats count, values(location.city), values(deviceDetail.browser), values(appDisplayName) by userPrincipalName

**結果**: 
- 総ログイン回数: 59回
- アクセス地域: &lt; Access Area &gt;
- ブラウザ: Chrome 138.0.0
- 利用アプリ: AWS SSO, Microsoft 365 Support Service, Microsoft Account Controls V2, Microsoft Teams Web Client

**3. 時間パターン分析**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; earliest=-7d | eval weekday=strftime(_time, &quot;%A&quot;), hour=strftime(_time, &quot;%H&quot;) | stats count by weekday, hour | sort weekday, hour

**結果**: 
- 平日・休日問わず16時台のアクセスが多い
- 深夜0時台のアクセスも複数回確認（金曜、木曜、火曜、水曜）
- 通常業務時間外のアクセスパターンも存在

**4. 認証状況**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; | stats count by status.errorCode, status.failureReason | sort -count

**結果**: 
- 成功ログイン: 28回（エラーコード0）
- 認証失敗: 確認されず
- 認証成功率: 100%

**5. Finding発生時刻周辺の認証ログ**:

index=&lt; example &gt; userPrincipalName=&quot;&lt; User Email Address &gt;&quot; (Finding発生時刻周辺)

**結果**: 
- IPv6アドレス（&lt; IPv6 Address &gt;）からのAWS SSOログイン成功
- 日本国内からのアクセス
- 認証成功（エラーコード0）

### 本人性確認結果
**振舞い分析による本人確認**: 
- **通常パターンとの一致**: 高い一致度
- **アクセス地域**: 通常の&lt; Access Area &gt;からのアクセス
- **利用アプリケーション**: AWS SSOの通常利用
- **認証成功率**: 100%（異常な失敗なし）
- **本人らしさ**: **高い** - 通常の行動パターンと一致

## 6. リスク評価

### 総合リスク評価: **低**

#### 根拠
1. **正規ユーザーによる操作**: 実在する外部委託ユーザーによる操作
2. **通常業務時間**: 月曜日13:20の通常業務時間帯
3. **正規認証プロセス**: Identity Center経由の正常な認証チェーン
4. **開発環境**: sandboxバケットでの開発作業
5. **本人性確認**: Entra IDログで本人の通常パターンと一致

#### 誤検知の可能性
**高い** - GuardDutyの機械学習ベースラインが、このユーザーの通常パターンを十分に学習していない可能性

#### セキュリティ影響
- **データ漏洩リスク**: 極低
- **システム侵害リスク**: なし
- **横展開リスク**: なし

## 7. 詳細調査

### CloudTrailログ存在確認結果
**対象AWSアカウント（000000000000）のCloudTrailログがSplunkに存在しないため、詳細な認証チェーン追跡は困難**

実行したクエリ:
index=&lt; example &gt; sourcetype=aws:cloudtrail account=000000000000 | head 1

結果: 0件のログが見つかりました

### ソースIPアドレスからの候補ユーザー調査
**地理的近接性による候補ユーザー調査**:
- Finding詳細のIPアドレス（&lt; IP Address &gt;）での直接検索: 0件
- 実際の認証ログではIPv6アドレス（&lt; IPv6 Address &gt;）を使用
- 両方とも日本国内からのアクセスで地理的整合性あり

### 追加調査すべき点
1. **GuardDutyベースライン調整**: 外部委託ユーザーの通常パターン学習
2. **S3アクセスログ**: 必要に応じてバケットレベルでの詳細ログ確認
3. **ユーザー業務確認**: 当該時刻での業務内容確認（念のため）

### 不明な点や疑問点
1. GuardDutyが「異常」と判定した具体的な学習ベースライン
2. 同一バケットでの他ユーザーのアクセスパターン
3. aws-sdk-rustツール使用の業務上の必要性

## 8. 推奨対応策

### 即座の対応（優先度: 低）
1. **ユーザー確認**: User Name 氏への業務内容確認（念のため）
2. **継続監視**: 同様のパターンでの再発監視

### 短期対応（1週間以内）
1. **GuardDuty調整**: 外部委託ユーザーの正常パターン学習促進
2. **アクセスパターン分析**: sandboxバケットでの通常アクセスパターン確立

### 中長期対応（1ヶ月以内）
1. **ベースライン最適化**: GuardDutyの機械学習ベースライン調整
2. **監視ルール見直し**: 外部委託ユーザー向けの監視ルール策定
3. **ドキュメント整備**: 正常な開発作業パターンの文書化

### 対応不要と判断される理由
- 正規ユーザーによる通常業務時間帯での正当な操作
- 全ての認証プロセスが正常
- セキュリティリスクが極めて低い
- 誤検知の可能性が高い

**結論**: このFindingは誤検知と判定され、セキュリティインシデントとしての対応は不要。GuardDutyのベースライン調整を推奨。
</code></pre>
<p>デフォルト状態の Amazon Q Developer に Amazon GuardDuty で検知した脅威のログ分析を依頼した場合と比較して、次のような改善が見られました。
MCP 連携やプロンプトエンジニアリングにより Amazon Q Developer を調整したことで、ログ分析の出力に含まれる情報とその内容を期待するものに近づけることができました。</p>
<ul>
<li>プロンプト内で生成するレポートのフォーマットを指定したことで、複数回の検証でも出力内容に必要な情報が安定して含まれるようになりました。</li>
<li>Splunk Cloud 上のログデータを利用して、検知した脅威のリスク判定を行うこともできており、より実務に近い分析を行うことができました。</li>
</ul>
<p>一方で、以下の観点では、さらなる改善が必要になります。</p>
<ul>
<li>複数回の検証を行う中で、不正確な情報を含むレポートが生成されることもありました。</li>
<li>プロンプト内で明確な指示をしているにもかかわらず、指示を無視した出力がされることがありました。</li>
<li>Splunk Cloud 上に AWS CloudTrail のログが取り込まれていない場合に、CloudTrail MCP Server が提供するツールを使用して、直接 AWS CloudTrail のログを検索せずに、Entra ID のログのみでリスク判断を行なうことがありました。</li>
</ul>
<p>完全にハルシネーションを排除することはできず、引き続き、出力内容の検証と実行した操作をモニタリングしながら、 Amazon Q Developer の調整を継続的に行う必要があります。</p>
<h1>まとめ</h1>
<p>Amazon Q Developer を活用したログ分析は、デフォルトの状態では課題も多いですが、 MCP 連携やプロンプトの調整を行うことにより、実際にログ分析に活用できそうな応答を生成できるようになりました。しかし、その生成内容を完全に信用することはできず、回答の正確さや実際に操作した内容などについては、引き続き、調整と改善を行なっていく必要がありそうです。</p>
<p>また、今回は Amazon Q Developer をログ分析に活用するための方法に焦点を当てて検証を行ったため、本記事では言及していませんが、生成 AI のセキュアな活用という観点で、MCP サーバの最小権限の管理や認証情報の管理など、実際に運用を行なっていくためには、まだまだ改善すべき課題はあります。</p>
<p>引き続き、 Amazon Q Developer の活用方法を模索しながら、セキュリティ運用の改善・強化に取り組んでいきたいと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ITオリエンテーションとその改善について]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-27-ITオリエンテーションとその改善について/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-27-ITオリエンテーションとその改善について/</guid>
            <pubDate>Mon, 27 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[支援先で行っている新規入場者に対するITオリエンテーションと、その改善について]]></description>
            <content:encoded><![CDATA[<h1>ITオリエンテーションとその改善について</h1>
<h2>はじめに</h2>
<p>KINTOテクノロジーズ（KTC）のコーポレートITでは、グループ会社のIT業務支援も行っています。 
私は現在、グループ会社にてヘルプデスク業務の支援を担当しており、その中で新規入場者向けのITオリエンテーション（以下、オリエン）も実施しています。
その中で、PCとスマホの取り扱い方法や業務環境の説明や初回のパスワード変更をサポートしております。
他社のIT部門がどのようにオリエンを行っているかを知る機会は少ないと体感しているので、少しでも参考になればと思い記事にまとめました。</p>
<hr>
<h2>背景と課題</h2>
<p>オリエンは月に1〜2回の頻度で実施されています。 
これまでは、担当者の経験と知識に依存した運用で、資料はなく、簡単なメモ（いわゆる“あんちょこ”）のみが存在していました。
もともと1人でヘルプデスク業務を担っていたため、資料化の必要性がなかったという背景があります。 
しかし、属人化から脱却し、組織として対応できる体制を目指すために私がジョインし、改善に取り組むことになりました。
（正直なところ、オリエンを担当した初日、「これはつらい…」と感じたのが一番のきっかけです（笑））</p>
<hr>
<h2>オリエンのゴール設定</h2>
<p>業務においてPCがない環境というのは考えられないため、よりスムーズに業務に入っていただけるように <strong>「会社PC・スマホを使える状態になっている」「自社のおおまかなPC環境を理解している」</strong> 状態になってもらう。をゴールとして設定しました。
改善についても、参加者がよりゴールに達成しやすくなるように行いました。</p>
<hr>
<h2>実施した改善</h2>
<p>改善は主に以下の2点に取り組みました。</p>
<h3>1. 資料化</h3>
<p>これまでのメモをベースに、説明内容を体系的に整理し、資料として整備しました。 
システムの説明や注意点を網羅し、説明漏れがないように構成しています。
また、略称が多く使われる環境のため、資料内で都度解説を加えるようにしました。
さらに、以下の工夫も取り入れています。</p>
<h4>アイスブレイクの工夫</h4>
<p>入場者は緊張していることが多く、理解力や質問のしやすさに影響します。 
そこで、ヘルプデスクメンバーの自己紹介を資料に盛り込み、趣味や好きなことを「ゆるめ」に紹介することで、親しみやすさとリラックス感を演出しています。
支援先は堅めな会社なので、盛り込むのには勇気が必要でしたが、より効果的に行うにはこれが必要だ。という信念をもとに対応しました。</p>
<h4>想いの表明</h4>
<p>我々が「社内のサポートメンバーとして、ビジネスを止めないために活動している」ということを、熱量高めに伝えるようにしています。
「困ったら遠慮なく頼ってほしい」というメッセージは、問い合わせの増加による稼働超過。過度な依存につながる懸念もありますが、サポート部門としての存在価値は、頼られることにあると考えています。
良好な関係性が築けていないと、我々起因で仕組みを変えたりする際のヒアリングや、課題解決がスムーズに進みません。 
また、親密感を持ってもらうことは、結果的に不要な問い合わせを減らしたり、トラブルの解決時間削減の効果もあると感じています。</p>
<h4>講師もわかりやすい資料化</h4>
<p>属人化脱却のためには、講師をやる難易度も下げる必要があります。
そのため、参加者にとってもわかりやすいだけでなく、講師にとってもわかりやすい資料にする必要があります。
その際に工夫したのは下記の2点です。</p>
<h5>説明の流れを意識</h5>
<p> オリエンの中で、PCのパスワード変更があるのですが、それをスムーズにするため、以下の順序で資料を整理しました。</p>
<ol>
<li>業務スマホのロック解除して使える状態にする</li>
<li>スマホの初期パスコード変更と生体認証設定</li>
<li>PCへのログイン（スマホを利用した多要素認証解除）</li>
<li>PCパスワードの変更</li>
</ol>
<p>この順番にすることで、必要となる操作が一本道となり、参加者も講師も迷いなく進められます。</p>
<h5>1ページ1コンテンツ</h5>
<p>1ページに詰め込みすぎず、「このページで何を説明するか」を明確にしました。
これも参加者は理解しやすく、講師も話す内容を把握しやすくなります。</p>
<hr>
<h3>2. 振り返りの仕組み</h3>
<p>オリエン終了後、なるべく早くチームで振り返り会を実施しています。 
記憶が新しいうちに、KPTA（Keep・Problem・Try・Action）のフレームワークを使って小さなところでも改善点を洗い出し、次回に活かしています。
改善案（Action）はチームのタスク管理に組み込み、漏れがないようにしております。
この振り返りで改善をし続けています。</p>
<p>具体的には、下記のようなActionを実行しました。</p>
<ul>
<li>順番を入れ替えることで重複の説明を排除した</li>
<li>参加者にも簡単な自己紹介をしてもらうことで、同期間のコミュニケーションを促進した</li>
</ul>
<hr>
<h2>オリエンテーションの内容</h2>
<p>改善後のオリエンでは、以下のような内容を説明・実施しています。
時間としては、1時間をとってもらっております。
雑談や質問の時間もとっており、はじめての緊張を和らげつつ、自社の環境について知ってもらう時間としております。</p>
<ol>
<li>メンバーの自己紹介 </li>
<li>業務環境（VDI）の説明 </li>
<li>初回ログインとパスワード変更 </li>
<li>機器取り扱いの注意点 </li>
<li>サポート窓口の案内</li>
</ol>
<hr>
<h2>やってみて感じたこと</h2>
<p>参加者からは「今までこんなに楽しい説明会はなかった」といった感想をいただくことが増えました。 
特に、我々の自己紹介をきっかけに声をかけてもらえることが多くなり、関係性の構築にもつながっています。
また、特定のメンバーに依存せず、誰でもオリエンを担当できる体制が整い、組織としての対応力も向上しました。</p>
<hr>
<h2>さいごに</h2>
<p>オリエンの参加者は、緊張や不安を抱えていることが多いです。 
業務に欠かせないPCやスマホも、会社ごとに環境が異なるため、初めてその会社の機器に触れた際に戸惑うところが多いのは当然だと思います。
KTCのようなITのプロ集団でも戸惑われる方がそれなりにいらっしゃるので、IT専業ではないグループ会社ではなおさらです。
また、活躍を期待されて入場しているので「うまくできない自分を見せるわけにはいかない」という心理はとても強く働き、質問や相談がしづらくなります。 </p>
<p>だからこそ、 <strong>「誰でも最初はうまくできない」「ITのプロである私も失敗した」</strong> ということを積極的に伝え、安心して頼ってもらえる雰囲気づくりを心がけています。</p>
<p>ITオリエンテーションは様々な立場の方に実施するため、我々の存在を認知してもらい、頼ってもらえる関係性を築いていくための第一歩になるものだと思います。
こういうところから、組織でのIT活用の促進、ひいては事業の加速にまでつながっていると信じています。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Vibe Coding：Playwrightで中古車テストデータ生成を自動化！作業時間を大幅短縮した話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-24-QA-VibeCoding-中古車/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-24-QA-VibeCoding-中古車/</guid>
            <pubDate>Fri, 24 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[QAグループがVibe Coding Weekに参加し、Playwrightで中古車データの自動作成を実現。業務の効率化を実現した取り組みについて紹介します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>QAグループのWebチームのroです。
実際のQA案件業務においては、中古車のテストデータを大量に生成する必要がありますが、その手順は複雑で注意点も多く、作業には多くの時間を要していました。
この課題を解決するため、Playwrightを活用し、中古車データ生成の全工程を自動化しようと思っています。
この取り組みを社内の「Vibe Coding」イベントにおいて実施し、Vibe Codingの手法を用いて中古車データ自動生成用のPythonスクリプトを実装しました。</p>
<h2>Vibe Codingとは？</h2>
<p>Vibe Codingとは、生成AIと人間のエンジニアが協働して行う新しい開発スタイルです。
AIが「コード生成」や「調査」といった重い作業を引き受け、人間は「品質の検証・改善」に集中することで、高品質かつ効率的な開発を行えます。
Vibe Codingは、以下３つの特徴があります：
自由度が高い：テーマやルールが緩く、各自好きな技術や課題に取り組める
交流の場：普段関わらないメンバーとも一緒に作業でき、知識やスキル共有のきっかけになる
モチベーション向上：一人でやるよりも集中しやすく、楽しみながら成果を出せる</p>
<h2>中古車データ自動作成のスクリプトの流れ</h2>
<p><img src="/assets/blog/authors/ro/1.png" alt="図"></p>
<h2>中古車データ自動作成のスクリプトのシステム構成図</h2>
<p><img src="/assets/blog/authors/ro/4.png" alt="図"></p>
<h2>スクリプト実装中に頑張ったこと</h2>
<p>実際に中古車データ自動作成用のスクリプトを実装中、いくつかの課題に直面しましたが、試行錯誤と視点の転換を重ねることで、最終的に問題を解決することができました。</p>
<p><strong>1. S3バケットに中古車契約の情報ファイルをアップロードする</strong> 
S3バケットに中古車契約の情報ファイルをアップロードする手順があります。これまで、この手順は中古車チームに依頼して操作してもらっていました。今回は、この手順も自動化するために、事前に中古車チームとクラウドチームに相談し、S3へのアクセス権限を付与していただきました。S3の接続権限を得たことで、Pythonを用いてS3へのファイル自動アップロードを実現できるようになりました。
コードの例：  </p>
<pre><code> # SSOでログイン済みのプロファイルを指定
 session = boto3.Session(profile_name=&quot;your_profile_name&quot;)

 # S3クライアント作成
 s3 = session.client(&quot;s3&quot;)

 # アップロード先情報
 bucket_name = &quot;your-bucket-name&quot;
 local_file_path = r&quot;path/to/your/local/file.txt&quot;
 s3_object_key = &quot;path/to/s3/object.txt&quot;

 try:
     s3.upload_file(local_file_path, bucket_name, s3_object_key)
 print(f&quot;{local_file_path} を S3バケット &#39;{bucket_name}&#39; の &#39;{s3_object_key}&#39; にアップロードしました。&quot;)
 except Exception as e:
     print(f&quot;アップロードに失敗しました: {e}&quot;)
    
</code></pre>
<p> <strong>2. 輸送依頼のExcelファイルの自動生成</strong> 
 本来は、サイトから輸送依頼用のExcelファイルをダウンロードし、その中から不要な内容を削除したうえで、中古車契約の情報を再入力し、再度サイトへアップロードする必要がありました。しかし、この煩雑な手順は時間がかかるうえ、ミスが発生しやすいという課題がありました。今回、Pythonを活用することで、不要な内容を自動削除し、必要な情報を抽出してExcelへ自動記入し、最後にサイトへ自動アップロードすることが可能になりました。これまで手作業で行っていた面倒な作業から解放できました！
 コードの例： </p>
<pre><code># 元のCSVファイルパス
folder = r&quot;C:\Users\×××\Downloads&quot;
pattern = os.path.join(folder, &quot;req_000550_4*.csv&quot;)
files = glob.glob(pattern)

# 正規表現で「req_000550_」＋19桁の英数字に一致するファイルを抽出
regex = re.compile(r&quot;req_000550_[0-9A-Za-z]+\.csv$&quot;)
target_files = [f for f in files if regex.search(os.path.basename(f))]

input_file = target_files[0]
#print(f&quot;対象ファイル: {input_file}&quot;)

# ===== CSV読み込み =====
df = pd.read_csv(input_file, header=None)

# ===== セル更新 =====
df.iloc[1, 1] = display_text1  # B2
df.iloc[1, 2] = display_text3_formatted  # C2
df.iloc[1, 3] = display_text2  # D2

# G2, H2, I2, J2 → 列番号は G=6, H=7, I=8, J=9（0始まり）
today_str = datetime.now().strftime(&quot;%Y%m%d&quot;)
for col in [6, 7, 8, 9]:
    df.iloc[1, col] = today_str

# ===== 保存ファイル名作成 =====
safe_display_text3 = display_text3.replace(&quot;:&quot;, &quot;-&quot;).replace(&quot; &quot;, &quot;_&quot;)
new_filename = f&quot;req_000550_{safe_display_text3}.csv&quot;
output_file = os.path.join(folder, new_filename)

# ===== CSV保存 =====
df.to_csv(output_file, index=False, header=False, encoding=&quot;utf-8-sig&quot;)
print(f&quot;更新完了: {output_file}&quot;)

await page.get_by_role(&quot;button&quot;, name=&quot;輸送状況を登録する&quot;).click()
new_filename = f&quot;req_000550_{safe_display_text3}.csv&quot;
filePath = rf&quot;C:/Users/×××/Downloads/{new_filename}&quot;
route = os.path.abspath(filePath)
async with page.expect_file_chooser() as fc_info:
    await page.get_by_role(&quot;button&quot;, name=&quot;受付状況更新ファイル(CSV形式 UTF-8/CRLF/BOMあり) prepended action&quot;).click()
file_chooser = await fc_info.value
await file_chooser.set_files(route)
await page.get_by_role(&quot;button&quot;, name=&quot;アップロードする&quot;).click()
await page.get_by_role(&quot;button&quot;, name=&quot;OK&quot;).click()
</code></pre>
<p><strong>3. 各システム間におけるデータ反映時間の把握</strong> 
１つの中古車データに対して、あるシステム上での操作が完了した後、一定の時間を待たなければ、他のシステムで次の操作を行うことができません。この待機時間の管理は容易ではありませんが、自動化スクリプトを活用することで、正確に待機時間を把握し、時間になれば自動的に次の操作へ進めることが可能になりました。これにより、業務効率が大幅に向上しました。</p>
<h2>感想</h2>
<p>今回の成果物は、今後の案件業務に活用できそうです。
従来200台の中古車データの作成に約2ヶ月かかっていたが、自動化により工数を削減することを期待できそうです。</p>
<h2>成果</h2>
<p><img src="/assets/blog/authors/ro/5.png" alt="図"></p>
<p>今回は、中古車データ生成の自動化を実現しただけでなく、今後につながる有益な経験と発想を得ることができました。
この経験を活かし、今後のQA業務においても、自動化できる業務をさらに発掘し、段階的に実現していきたいと考えています。</p>
<h2>おわりに</h2>
<p>今回のVibe Coding Weekでは、中古車データの自動作成のチャレンジを実施し、短期間で成果を得ることができました。
今後も自動化、AIに力を入れて、データ自動作成だけではなく、テスト設計、テスト実施も自動化にしていきたいと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ro/Designer.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[InlineContent in Jetpack Compose: The Hidden Gem for Complex Text UI]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025_10_24_inline_content_jetpack_compose_en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025_10_24_inline_content_jetpack_compose_en/</guid>
            <pubDate>Fri, 24 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[The inlineContent of Jetpack Compose Text composable for complex Text UI]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello! I&#39;m <a href="https://www.linkedin.com/in/raseln/">Rasel</a>, and today I want to share something that most Android developers overlook - the <code>inlineContent</code> property of Jetpack Compose&#39;s <code>Text</code> composable.</p>
<p>Recently, while working on the <a href="https://top.myroute.fun/">my route</a> app at KINTO Technologies Corporation, we encountered a UI challenge that seemed simple but turned out to be quite complex: displaying colored benefit labels inline with ticket names in our ticket usage history screen.</p>
<p>![Figma design showing inline label and ticket name](/assets/blog/authors/ahsan_rasel/inline-content-compose/FigmaDesign.png =250x)</p>
<blockquote>
<p><em>The tickets shown are samples. Please check the app for tickets that are actually on sale.</em></p>
</blockquote>
<p>As you can see in the design, we needed a pink labeled text with rounded corners that flows naturally with the ticket name text. Most developers would reach for <code>Row</code> or <code>FlowRow</code> to solve this, but these approaches have significant limitations when dealing with text that needs to wrap and flow naturally.</p>
<h2>The Problem: When Row and FlowRow Fall Short</h2>
<p>Consider this UI pattern from our actual design:</p>
<pre><code>[With coupon] One-day Pass for all Kagoshima City buses, trams and ferry routes.
</code></pre>
<p>Where <code>[With coupon]</code> is a pink label with rounded corners that must appear seamlessly inline with the text.</p>
<h3>Approach 1: Using Row (The Naive Solution)</h3>
<pre><code class="language-kotlin">@Composable
fun TicketNameWithRow(name: String, benefitLabel: String) {
    Row(
        horizontalArrangement = Arrangement.spacedBy(4.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Box(
            modifier = Modifier
                .background(
                    color = MaterialTheme.colors.subPink, // Design system color
                    shape = RoundedCornerShape(2.dp),
                )
                .padding(horizontal = 4.dp),
            contentAlignment = Alignment.Center,
        ) {
            Text(
                text = benefitLabel,
                style = MaterialTheme.typography.body3Bold,
                color = MaterialTheme.colors.onPrimaryHighEmphasis,
            )
        }

        Text(
            text = name,
            style = MaterialTheme.typography.body2Bold,
        )
    }
}
</code></pre>
<p><strong>Result:</strong>
![Result of Row approach: label and text misaligned](/assets/blog/authors/ahsan_rasel/inline-content-compose/WithRow.png =250x)</p>
<p><strong>Problems with Row:</strong></p>
<ul>
<li>Label doesn&#39;t align perfectly with text baseline</li>
<li>Inconsistent spacing when text wraps</li>
<li>Breaks the natural text flow</li>
</ul>
<h3>Approach 2: Using FlowRow (Better, But Still Limited)</h3>
<pre><code class="language-kotlin">@Composable
fun TicketNameWithFlowRow(name: String, benefitLabel: String) {
    FlowRow(
        horizontalArrangement = Arrangement.spacedBy(4.dp),
        verticalAlignment = Alignment.CenterVertically,
    ) {
        Box(
            modifier = Modifier
                .background(
                    color = MaterialTheme.colors.subPink,
                    shape = RoundedCornerShape(2.dp),
                )
                .padding(horizontal = 4.dp),
            contentAlignment = Alignment.Center,
        ) {
            Text(
                text = benefitLabel,
                style = MaterialTheme.typography.body3Bold,
                color = MaterialTheme.colors.onPrimaryHighEmphasis,
            )
        }

        Text(
            text = name,
            style = MaterialTheme.typography.body2Bold,
        )
    }
}
</code></pre>
<p><strong>Result:</strong>
![Result of FlowRow approach: label and text still not aligned](/assets/blog/authors/ahsan_rasel/inline-content-compose/WithFlowRow.png =250x)</p>
<p>As shown above, the UI still doesn&#39;t align perfectly with our design. The label and text do not flow naturally when wrapping.</p>
<h2>Another Approach: Using AnnotatedString (SpanStyle)</h2>
<p>Before Jetpack Compose supported true inline composables, many developers tried to achieve similar effects using only <code>AnnotatedString</code> and <code>SpanStyle</code>. This approach uses background color and rounded corners via <code>SpanStyle</code> to style part of the text as a label.</p>
<h3>Approach 3: AnnotatedString with SpanStyle</h3>
<p>This method is limited compared to <code>inlineContent</code>, but can be useful for simple cases where you only need background color and text styling (not custom composables or padding).</p>
<pre><code class="language-kotlin">@Composable
fun TicketNameWithAnnotatedString(name: String, benefitLabel: String) {
    val cornerRadius = with(LocalDensity.current) { 2.dp.toPx() }
    val drawStyle = Stroke(pathEffect = PathEffect.cornerPathEffect(cornerRadius))
    val nameStyle = MaterialTheme.typography.body2Bold.toSpanStyle()
    val benefitStyle = MaterialTheme.typography.body3Bold.copy(
        color = MaterialTheme.colors.onPrimaryHighEmphasis,
        background = MaterialTheme.colors.subPink,
        drawStyle = drawStyle,
    ).toSpanStyle()

    val annotatedString =
        remember(name, benefitLabel) {
            buildAnnotatedString {
                withStyle(style = benefitStyle) {
                    append(&quot; $benefitLabel &quot;) // Add spaces for padding effect
                }
                append(&quot; &quot;)
                withStyle(style = nameStyle) {
                    append(name)
                }
            }
        }

    Text(text = annotatedString)
}
</code></pre>
<p><strong>Result:</strong>
![Result of AnnotatedString approach: label with background color, no rounded corners](/assets/blog/authors/ahsan_rasel/inline-content-compose/WithAnnotatedString.png =250x)</p>
<p><strong>Limitations:</strong></p>
<ul>
<li>Rounded corners are not achieved properly while using proper value from design (background is mostly a rectangle, and needs to adjust value to make it rounded, still proper rounding is not achieved)</li>
<li>No custom composable or icon support</li>
<li>Padding is simulated with spaces, not true padding</li>
<li>Baseline alignment is good, but label may not look as polished as with <code>inlineContent</code></li>
<li>Text styling also gets changed from design</li>
</ul>
<p><strong>When to use:</strong></p>
<ul>
<li>When you only need a colored background and simple text styling</li>
<li>When you want a dependency-free, simple solution for basic badges</li>
</ul>
<h2>The Solution: InlineContent - The Proper Way</h2>
<p><code>InlineContent</code> allows you to embed custom composable directly within text, treating them as characters in the text flow. Here&#39;s how we implemented it in the my route app:</p>
<h3>Step 1: Understanding the Production Implementation</h3>
<p>Below is a simplified version of our production code, showing how to use <code>inlineContent</code> to embed a custom label inside text:</p>
<pre><code class="language-kotlin">@Composable
private fun TicketNameWithBenefit(
    name: String,
    benefitLabel: String,
) {
    val nameStyle = MaterialTheme.typography.body2Bold.toSpanStyle()

    val annotatedString = remember(name, benefitLabel) {
        buildAnnotatedString {
            appendInlineContent(benefitLabel) // Use label text as unique key
            append(&quot; &quot;)
            withStyle(style = nameStyle) {
                append(name)
            }
        }
    }

    // ... (inline content implementation)

    Text(
        text = annotatedString,
        inlineContent = inlineContent,
    )
}
</code></pre>
<h3>Step 2: Dynamic Width Calculation (The Critical Part)</h3>
<p>Calculating the exact width for the label background is essential for a seamless look. Here is how we do it:</p>
<p>The most challenging aspect is calculating the exact width needed for the label background:</p>
<pre><code class="language-kotlin">val density = LocalDensity.current
val textMeasurer = rememberTextMeasurer()
val horizontalPadding = 4.dp

val benefitLabelBgWidthSp = remember(benefitLabel) {
    val textLayoutMeasure = textMeasurer.measure(
        text = benefitLabel,
        style = benefitStyle,
    )
    with(density) {
        (textLayoutMeasure.size.width.toDp() + (horizontalPadding * 2)).toSp()
    }
}
</code></pre>
<p><strong>Why this is complex:</strong></p>
<ol>
<li><strong>Text Measurement</strong>: We need to measure the text before rendering</li>
<li><strong>Unit Conversion</strong>: Convert between pixels, dp, and sp correctly</li>
<li><strong>Padding Calculation</strong>: Include padding in the total width</li>
<li><strong>Density Awareness</strong>: Handle different screen densities</li>
</ol>
<h3>Step 3: Creating the Inline Content Mapping</h3>
<p>Now, we map the label to a composable using <code>InlineTextContent</code>, ensuring it aligns and sizes perfectly:</p>
<pre><code class="language-kotlin">val inlineContent = remember(benefitLabel, benefitLabelBgWidthSp) {
    mapOf(
        benefitLabel to InlineTextContent(
            placeholder = Placeholder(
                width = benefitLabelBgWidthSp,
                height = benefitStyle.lineHeight,
                placeholderVerticalAlign = PlaceholderVerticalAlign.Center,
            ),
        ) {
            Box(
                modifier = Modifier
                    .background(
                        color = MaterialTheme.colors.subPink,
                        shape = RoundedCornerShape(2.dp)
                    )
                    .padding(horizontal = horizontalPadding),
                contentAlignment = Alignment.Center,
            ) {
                Text(
                    text = benefitLabel,
                    style = benefitStyle,
                    color = MaterialTheme.colors.onPrimaryHighEmphasis,
                )
            }
        }
    )
}
</code></pre>
<p><strong>Key implementation details:</strong></p>
<ul>
<li><code>placeholderVerticalAlign = PlaceholderVerticalAlign.Center</code> ensures perfect alignment</li>
<li><code>height = benefitStyle.lineHeight</code> matches the text height exactly</li>
<li>Dynamic width calculation ensures perfect fit for any label text</li>
</ul>
<h2>The Complete Production Implementation</h2>
<p>Here&#39;s our actual implementation from the my route app:</p>
<pre><code class="language-kotlin">@Composable
private fun TicketNameWithBenefit(
    name: String,
    benefitLabel: String,
) {
    val nameStyle = MaterialTheme.typography.body2Bold.toSpanStyle()
    val benefitStyle = MaterialTheme.typography.body3Bold
    val annotatedString =
        remember(name, benefitLabel) {
            buildAnnotatedString {
                appendInlineContent(benefitLabel)
                append(&quot; &quot;)
                withStyle(style = nameStyle) {
                    append(name)
                }
            }
        }

    val density = LocalDensity.current
    val textMeasurer = rememberTextMeasurer()
    val horizontalPadding = 4.dp

    val benefitLabelBgWidthSp =
        remember(benefitLabel) {
            val textLayoutMeasure = textMeasurer.measure(text = benefitLabel, style = benefitStyle)
            with(density) {
                (textLayoutMeasure.size.width.toDp() + (horizontalPadding * 2)).toSp()
            }
        }

    val inlineContent =
        remember(benefitLabel, benefitLabelBgWidthSp) {
            mapOf(
                benefitLabel to
                    InlineTextContent(
                        placeholder =
                            Placeholder(
                                width = benefitLabelBgWidthSp,
                                height = benefitStyle.lineHeight,
                                placeholderVerticalAlign = PlaceholderVerticalAlign.Center,
                            ),
                    ) {
                        Box(
                            modifier =
                                Modifier
                                    .background(
                                        color = MaterialTheme.colors.subPink,
                                        shape = RoundedCornerShape(2.dp),
                                    )
                                    .padding(horizontal = horizontalPadding),
                            contentAlignment = Alignment.Center,
                        ) {
                            Text(
                                text = benefitLabel,
                                style = benefitStyle,
                                color = MaterialTheme.colors.onPrimaryHighEmphasis,
                            )
                        }
                    },
            )
        }

    Text(
        text = annotatedString,
        inlineContent = inlineContent,
    )
}
</code></pre>
<p><strong>Result:</strong>
![Result of inlineContent approach: label and text perfectly aligned](/assets/blog/authors/ahsan_rasel/inline-content-compose/WithInlineContent.png =250x)</p>
<p>As you can see, this implementation perfectly aligns with our design goal.</p>
<h2>Why We Switched from External Library</h2>
<p>When we couldn&#39;t achieve our design using Row, FlowRow and even AnnotatedString approach, we tried using an third party library:</p>
<pre><code class="language-kotlin">val extendedSpans = remember {
  ExtendedSpans(
    RoundedCornerSpanPainter(…),
  )
}

Text(
  modifier = Modifier.drawBehind(extendedSpans),
  text = remember(text) {
    extendedSpans.extend(text)
  },
  onTextLayout = { result -&gt;
    extendedSpans.onTextLayout(result)
  }
)
</code></pre>
<p>By using a third-party library, we finally achieved our design goal. But we don&#39;t want to ship another library with our app just for this single Text UI item and reconsidered other approaches.</p>
<p><strong>Problems we encountered:</strong></p>
<ul>
<li>Additional dependency for a simple feature</li>
<li>Less control over styling and behavior</li>
<li>Potential compatibility issues with future Compose versions</li>
</ul>
<p><strong>The native solution proved to be better:</strong></p>
<ul>
<li>No external dependencies</li>
<li>Full control over styling and behavior</li>
<li>Better performance</li>
<li>Future-proof with Compose updates</li>
</ul>
<h2>Advantages of InlineContent Approach</h2>
<ol>
<li><strong>Natural Text Flow</strong>: Labels wrap with text naturally, maintaining proper line breaks</li>
<li><strong>Perfect Alignment</strong>: Consistent baseline alignment with surrounding text</li>
<li><strong>Flexible Styling</strong>: Full support for complex text formatting (bold, colors, sizes)</li>
<li><strong>Dynamic Sizing</strong>: Adapts to content automatically while maintaining design consistency</li>
<li><strong>Performance</strong>: Efficient rendering as part of the text layout engine</li>
<li><strong>Accessibility</strong>: Screen readers handle it as part of the text flow</li>
</ol>
<h2>Common Pitfalls and Solutions</h2>
<h3>Pitfall 1: Hardcoded Dimensions</h3>
<p>❌ <strong>Wrong:</strong></p>
<pre><code class="language-kotlin">Placeholder(width = 60.sp, height = 16.sp) // Fixed dimensions
</code></pre>
<p>✅ <strong>Correct:</strong></p>
<pre><code class="language-kotlin">Placeholder(
    width = calculatedWidth, // Dynamic based on content
    height = textStyle.lineHeight, // Match text height
)
</code></pre>
<h3>Pitfall 2: Incorrect Unit Conversion</h3>
<p>❌ <strong>Wrong:</strong></p>
<pre><code class="language-kotlin">textLayout.size.width.toSp() // Direct conversion loses density information
</code></pre>
<p>✅ <strong>Correct:</strong></p>
<pre><code class="language-kotlin">with(density) {
    textLayout.size.width.toDp().toSp() // Proper density-aware conversion
}
</code></pre>
<h3>Pitfall 3: Missing Performance Optimization</h3>
<p>❌ <strong>Wrong:</strong></p>
<pre><code class="language-kotlin">// Recalculated on every composition
val inlineContent = mapOf(...)
val annotatedString = buildAnnotatedString { ... }
</code></pre>
<p>✅ <strong>Correct:</strong></p>
<pre><code class="language-kotlin">val inlineContent = remember(dependencies) { mapOf(...) }
val annotatedString = remember(dependencies) { buildAnnotatedString { ... } }
</code></pre>
<h2>Use Cases Beyond Benefit Labels</h2>
<p>This technique works excellently for:</p>
<ol>
<li><strong>Status badges</strong> in lists</li>
<li><strong>Rating stars</strong> within reviews</li>
<li><strong>Currency symbols</strong> with special styling</li>
<li><strong>Icon indicators</strong> in text</li>
<li><strong>Highlighted keywords</strong> in search results</li>
</ol>
<h2>Performance Considerations</h2>
<p>The <code>remember</code> usage is crucial for performance:</p>
<pre><code class="language-kotlin">// Expensive operations cached properly
val benefitLabelBgWidthSp = remember(benefitLabel) { /* text measurement */ }
val inlineContent = remember(benefitLabel, width) { /* composable creation */ }
val annotatedString = remember(name, benefitLabel) { /* string building */ }
</code></pre>
<p>This ensures calculations only happen when dependencies change, not on every composition.</p>
<h2>Conclusion</h2>
<p><code>InlineContent</code> is a powerful but underutilized feature of Jetpack Compose. When you need to embed custom UI within text flow, it&#39;s the superior solution compared to layout-based approaches like <code>Row</code> or <code>FlowRow</code>.</p>
<p>The dynamic width calculation technique we&#39;ve implemented solves the common problem of perfectly fitting background elements to text content. While the implementation requires understanding text measurement and unit conversion, the result is a professional, performant UI that scales well across different devices and text sizes.</p>
<p>Our experience transitioning from an external library to this native solution proved that sometimes the built-in tools, when used correctly, provide the best balance of performance, maintainability, and design flexibility.</p>
<p>Next time you encounter inline UI challenges, remember: <code>inlineContent</code> is your friend!</p>
<hr>
<p><strong>Key Takeaways:</strong></p>
<ul>
<li>Use <code>InlineContent</code> for true inline UI elements that need to flow with text</li>
<li>Implement dynamic width calculation using <code>TextMeasurer</code> for perfect fitting</li>
<li>Optimize with <code>remember</code> for performance, especially text measurements</li>
<li>Prefer native solutions over external libraries when built-in APIs suffice</li>
<li>Pay attention to unit conversion and density handling</li>
</ul>
<p>Happy coding!</p>
<hr>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ahsan_rasel/inline-content-compose/cover-image.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Vibe Coding Week参加記：画面仕様と実画面比較の自動化試行]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-22-vibe-coding-week-qa-static/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-22-vibe-coding-week-qa-static/</guid>
            <pubDate>Wed, 22 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[QAグループがVibe Coding Weekに参加し、AIを活用した3つの自動化チャレンジのうち、画面仕様と実画面比較の自動化についての取り組みを紹介します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは！KINTOテクノロジーズQAグループで主にWebフロントエンド案件を担当しているoshimaです。
弊社は全社を挙げてAI利用を推進していますが、その波に乗って先月生成AIコーディング企画”Vibe Coding Week”が行われました。この企画で私がチャレンジした内容についてご紹介いたします。
Vibe Coding Week企画そのものの説明は他の記事をご参照いただければと思います。</p>
<h2>背景</h2>
<p>私自身、通常業務でコーディングをほとんど行うことはありません。一方、私の担当する案件については一つ一つのリリースサイクルが速いことと、同じ期間に複数の案件が重複することで、効率の良いテストが求められていました。
となればテスト自動化が一つのカギになります。実際グループではPlaywrightを用いての自動化が推進されてきました。が、私自身は残念ながら「コードの書けない人」です。これまでは出来る同僚に依頼するなどの方法でしのいできましたが、いつまでも当てにし続けられない（なかなか手が空かない）という状態が続いていました。</p>
<h2>救世主！？</h2>
<p>自動化出来れば効率アップできそうな案件関連のネタは持っている。ただ、自身のスキルではどうしようもなかった。そんなもどかしい状態を打ち砕いてくれたのが生成AIです。
やりたいテスト（条件と期待値、制約条件など）がはっきりしていて、AIに正しく伝えることができれば、お望みのコードを生成してくれる。これならコーディングできない自分でも自動化に対して立ち向かっていける。業務の効率化のみならず、今まで自分が越えられなかった壁を越えてくれる（かもしれない）、救世主のような存在がAIでした。</p>
<h2>チャレンジ企画との親和性</h2>
<p>日本語のプロンプトでPlaywrightによる自動テストコードを生成していこうという取り組みは、先述のVibe Coding Week企画関係なく自然発生的なものでした。なので、企画発表があった時点で、企画意図に沿った内容の報告ができるのではないか？という感触を持っていました。</p>
<h2>適用案件の特徴</h2>
<p>通常業務での効率化の対象とした案件、そしてチャレンジ企画で取り組んだ案件が、社内では「静的資材」案件と呼ばれているものです。主にはKINTO ONEのサービスで取り扱う車種の仕様をお客様に紹介するためのページや契約内容の案内ページなどの確認を行う案件です。
例：<a href="https://kinto-jp.com/kinto_one/lineup/toyota/yaris/">トヨタ ヤリスの紹介ページ</a></p>
<h3>特徴</h3>
<p>車種紹介のページになると、KINTOで取り扱う車種が増えるごとに案件が成立しますが、ページレイアウトや項目は全車種共通。異なるのは画像と価格などのため、確認する内容は車種が変われども同じという案件の特徴がありました。</p>
<h3>通常のテストでの作業内容</h3>
<p>通常のテストで実施していた内容は以下となります。</p>
<ul>
<li>全体デザインがFigmaで作成されたデザインと比較して差異がなく、またレイアウトの崩れがないこと</li>
<li>表示されている価格・サイズ・燃費などの各種データが指定ファイルと同じであること</li>
<li>画面内のボタンやリンクの遷移先が仕様通りであること</li>
</ul>
<p>これらの確認を今までは目視確認で行ってきました。原始的なやり方ですが、ある程度不具合指摘もできていたのですが、数が重なってくるとこれでは厳しくなってきます。上記の確認3項目については、うまくやれば自動で済みそうな内容です。
幸い試験時にはFigmaでの画面デザイン、価格表や燃費・サイズの記載された諸元表の指定ファイルは提示されます。材料はあります。なので、テスト対象ページの記載内容と自動で比較は特に問題なくできるのではないかという仮説を立て、チャレンジしてみました。</p>
<h2>チャレンジ企画での取り組み</h2>
<h3>全体デザインの自動比較</h3>
<p>デザインの比較については、次の手順で取り組みました。</p>
<ul>
<li>テスト対象の該当Figmaページからコンポーネント情報を抽出してJSONに出力を試みる<ul>
<li>全部だと情報量が多く読み込みに失敗</li>
</ul>
</li>
<li>抽出する情報を位置情報に絞って相対位置比較でレイアウト崩れ確認に移行<ul>
<li>レイヤーやID情報などが複雑で、ほしい情報を整理するのに苦労</li>
<li>実画面情報との解像度の差もあって、単純な比較でエラー頻発 
残念ながらこのチャレンジ期間ではデザイン周りはうまくいきませんでした。</li>
</ul>
</li>
</ul>
<h3>価格などの表示情報の自動比較</h3>
<p>こちらについては、次の手順で取り組みました</p>
<ul>
<li>テストページの表示情報を抽出してJSONに出力を試みる<ul>
<li>通常テキストは余裕でしたが表形式は項目名指定で工夫が必要でした</li>
</ul>
</li>
<li>元データ（Excel）から情報抽出して同様にJSON出力<ul>
<li>比較をしたいので「基本性能・主要装備」と「プラン別ご利用料イメージ」部分を抽出</li>
<li>結果的にうまくいきましたが、実はいろいろ試行錯誤し、結局はセル番号指定でのベタな指令に落ち着きました。</li>
</ul>
</li>
<li>作成された2つのJSONを比較<ul>
<li>ここまでくると問題なく実行完了
テキスト情報は比較的スムーズにうまくいきました。</li>
</ul>
</li>
</ul>
<h3>リンク遷移先の確認</h3>
<p>こちらは全部終わらなかったのですが、時間かければできそうな実感がありました。</p>
<ul>
<li>ボタンに設定されたリンク先の取得は問題なし</li>
<li>目視の方が速そうなので比較の自動化までは実施せず</li>
</ul>
<h2>チャレンジ企画の成果</h2>
<p>ポイントは、単純に情報抽出してくださいではなく、テストコードをタイプスクリプト形式で実装し、Playwrightで実行したことです。このコードを生成しておくことで、他の車種や元データファイルが変わっても少しの改変で対応できるようになりました。
今まで目視確認で行っていたテスト内容を一部でも自動化出来たことは一つの成果ですが、できていない箇所や、より効率的にテストが進められる改善の余地は、残念ながらまだまだ多いです。
とはいえ、チャレンジ企画の短期間である程度できたことも確かです。地道に継続することで、出来てない部分を埋めていけると思います。</p>
<h2>未来への妄想 -まとめにかえて-</h2>
<p>自動化でできることはとことん自動化ツールでの確認に任せて、人間でしか確認できない箇所を追求する。効率化で時間的な余裕を生み、結果として人力でのテストの質の向上まで図ることができれば理想的です。
現段階ではまだまだ妄想です。ただ、コーディングできない人間でもある程度の自動化ができる時代になった今、妄想の実現は決して遠くない未来にあるのではという期待感も持っています。
チャレンジ企画で対応した案件以外での取り組みも含め、少しずつでも歩みを進めていこうと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/oshima/QA_test_automation.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Boosting IT from Osaka — the start of CO‑LAB Tech Night]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-21-colab-tech-night-start-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-21-colab-tech-night-start-en/</guid>
            <pubDate>Tue, 21 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article introduces the start of CO-LAB Tech Night]]></description>
            <content:encoded><![CDATA[<p>Hello, I&#39;m Nakamura from KINTO Technologies&#39; Osaka Tech Lab.</p>
<p>KINTO Technologies is Toyota Group&#39;s in-house development team, creating various mobility services including the car subscription service &quot;KINTO.&quot;</p>
<p>Our office relocated to North Gate Building in Umeda in June 2025, and we now host a monthly external study session called CO-LAB Tech Night. We set a theme for each session where members from various roles including development, design, data and more, share their daily efforts and insights across professional boundaries.</p>
<p>In this article, I&#39;ll introduce our journey so far and our upcoming Vol.4 session: Generative AI × Creativity.</p>
<h2>The Origins of CO-LAB Tech Night</h2>
<p>Osaka Tech Lab has always had several members with experience presenting at external study sessions and events. However, our previous office was small, and we simply didn&#39;t have the space to accommodate events. While we talked about creating a platform to share from Osaka, we couldn&#39;t quite make it happen.</p>
<p>The relocation at the end of June 2025 finally gave us the event space we&#39;d been hoping for, enabling us to nurture our own community. In Kansai, companies like <a href="https://lycorp-osaka-jp.connpass.com/">LY Corporation</a>, <a href="https://sakura-tokyo.connpass.com/">SAKURA internet Inc.</a>, <a href="https://moneyforward.connpass.com/">Money Forward, Inc.</a>, and <a href="https://sansan.connpass.com/">Sansan, Inc.</a> are actively building communities.</p>
<p>Experiencing this vibrant ecosystem firsthand, we wanted to collaborate more with companies and communities in Kansai and energize the IT industry from Osaka.</p>
<p>That&#39;s how CO-LAB Tech Night was born.</p>
<h2>Journey Through Vol.1–3</h2>
<h3>Vol.1 | Sharing the Reality of Cloud Development</h3>
<p><img src="/assets/blog/authors/nakamura/20251021/image1.png" alt="CO-LAB Tech Night Vol.1の様子"></p>
<p>Our first session focused on cloud development, with Osaka Tech Lab members presenting. We shared the real-world challenges of architecture design and IaC implementation from our in-house development perspective.</p>
<h3>Vol.2 | Practical Security Examples for Cloud Utilization</h3>
<p><img src="/assets/blog/authors/nakamura/20251021/image2.png" alt="CO-LAB Tech Night Vol.2の様子"></p>
<p>The second session centered on cloud security, sharing insights from teams using AWS, Azure, and other platforms. We discussed the practical question of how to balance innovation and security.</p>
<h3>Vol.3 | Reconsidering the Role of QA Engineers</h3>
<p><img src="/assets/blog/authors/nakamura/20251021/image3.png" alt="CO-LAB Tech Night Vol.3の様子"></p>
<p>Our third session explored QA (Quality Assurance). We discussed evolving testing practices in the generative AI era, including automation and test design as conventional wisdom shifts.</p>
<h2>Challenges and the Road Ahead</h2>
<p>Through continuing CO-LAB Tech Night, we&#39;ve realized that KINTO Technologies isn&#39;t wildely recognized in Kansai. We often hear, &quot;Oh, KINTO Technologies has an Osaka office?&quot; indicating that Osaka Tech Lab&#39;s recognition is still developing.</p>
<p>Currently, participants often join through connections with speakers or organizers, meaning our reach still heavily depends on individual employees&#39; networks and influence. However, through CO-LAB Tech Night and participation in other events, we&#39;re steadily increasing opportunities for people to learn about our company&#39;s presence in Osaka.</p>
<p>While we&#39;ve always had strong cross-team collaboration and close relationships compared to other offices, these sessions have created new moments of realization, like &quot;I didn&#39;t know you approached work from that perspective,&quot; deepening our mutual understanding.</p>
<p>We want to transform this activity, which began with members&#39; passion and initiative, into an organizational mechanism for outreach and collaboration. And together with companies and professionals working in Kansai, we want to energize the IT industry from Osaka.</p>
<h2>Next: The Potential and Current State of Generative AI × Creative</h2>
<p>Vol.4 will showcase real-world examples from Toyota Group&#39;s in-house development, including TikTok effect creation by designers, illustration workflows, image generation comparisons, and AI-powered character expression.</p>
<p>📅 October 24, 2025 (Fri) 19:00–21:30
📍 KINTO Technologies Osaka Tech Lab (directly connected to Osaka Station, North Gate Building)</p>
<p>▼ For registration and details, please check our Connpass page:
<a href="https://kinto-technologies.connpass.com/event/369528/">https://kinto-technologies.connpass.com/event/369528/</a></p>
<p><a href="https://www.youtube.com/watch?v=v1fFP5VZEF0">https://www.youtube.com/watch?v=v1fFP5VZEF0</a></p>
<h2>Closing</h2>
<p>CO-LAB Tech Night is a casual study session series born from the desire to talk and listen. We hope it will organically grow as an Osaka-based IT community where people inside and outside the company connect and learn together.</p>
<p>Engineers, designers, and PdMs active in Kansai, please feel free to drop by!</p>
<p>▼ Learn about KINTO Technologies Osaka Tech Lab&#39;s concept and people:
<a href="https://dev-www.kinto-technologies.com/company/osakatechlab/">https://dev-www.kinto-technologies.com/company/osakatechlab/</a></p>
<p>▼ Osaka Tech Lab is hiring engineers, designers, PdMs, and other roles!
<a href="https://www.wantedly.com/projects/2069536">https://www.wantedly.com/projects/2069536</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[大阪からITを盛り上げていきたい！CO-LAB Tech Nightのはじまり]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-21-colab-tech-night-start/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-21-colab-tech-night-start/</guid>
            <pubDate>Tue, 21 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[CO-LAB Tech Nightのはじまりについてを本記事で紹介します]]></description>
            <content:encoded><![CDATA[<p>こんにちは、KINTOテクノロジーズ大阪拠点「Osaka Tech Lab」所属の<a href="https://x.com/momentofudayo">かーびー</a>です。</p>
<p>KINTOテクノロジーズは、トヨタグループの内製開発部隊として、車のサブスクリプションサービス「KINTO」をはじめとしたさまざまなモビリティサービスの開発を行っています。</p>
<p>私たちの拠点は2025年6月に梅田のノースゲートビルディングへ移転し、現在は月に一度のペースで社外向けの勉強会「CO-LAB Tech Night」を開催しています。1つのテーマを設定し、開発・デザイン・データなど、職種の垣根を越えたメンバーが日々の取り組みや気づきを共有し合う勉強会となっています。</p>
<p>今回は、これまでの歩みと、次回Vol.4「生成AI × クリエイティブ」についてご紹介します。</p>
<h2>CO-LAB Tech Nightの成り立ち</h2>
<p>もともと、Osaka Tech Labには、 社外の勉強会やイベントで登壇経験のあるメンバーが何人かいました。ただ、以前のオフィスは手狭で、社内でイベントを開けるようなスペースがありませんでした。「大阪から発信する場をつくりたいね」という話は出ていたものの、なかなか形にできずにいました。</p>
<p>2025年6月末の移転をきっかけに、念願のイベントスペースが整い、 自分たちでもコミュニティを育てていけるようになりました。 関西では、<a href="https://lycorp-osaka-jp.connpass.com/">LINEヤフー株式会社様</a>をはじめ、<a href="https://sakura-tokyo.connpass.com/">さくらインターネット株式会社様</a>、<a href="https://moneyforward.connpass.com/">株式会社マネーフォワード様</a>、<a href="https://sansan.connpass.com/">Sansan株式会社様</a>など、さまざまな企業がコミュニティ活動を展開しています。</p>
<p>そうした盛り上がりを肌で感じながら、私たちももっと関西の企業やコミュニティの方々とコラボレーションして、大阪からIT業界を盛り上げていきたい。</p>
<p>そんな思いから始まったのが「CO-LAB Tech Night」です。</p>
<h2>Vol.1〜3の歩み</h2>
<h3>Vol.1｜クラウド開発のリアルを共有</h3>
<p><img src="/assets/blog/authors/nakamura/20251021/image1.png" alt="CO-LAB Tech Night Vol.1の様子"></p>
<p>初回はクラウド開発をテーマに、Osaka Tech Labメンバーが登壇。アーキテクチャ設計やIaC化の試行錯誤を中心に、内製開発の現場感をそのまま伝えました。</p>
<h3>Vol.2｜クラウド活用に役立つセキュリティ実践例</h3>
<p><img src="/assets/blog/authors/nakamura/20251021/image2.png" alt="CO-LAB Tech Night Vol.2の様子"></p>
<p>2回目はクラウドセキュリティを軸に、AWSやAzureなど各チームの知見を共有。「攻めと守りをどう両立するか」という実務的な問いを中心に議論しました。</p>
<h3>Vol.3｜QAエンジニアの役割を再考する</h3>
<p><img src="/assets/blog/authors/nakamura/20251021/image3.png" alt="CO-LAB Tech Night Vol.3の様子"></p>
<p>3回目はQA（品質保証）をテーマに開催。生成AI時代を迎えた自動化・テスト設計など、常識が変わりつつあるテストの在り方を中心に語り合いました。</p>
<h2>見えてきた課題とこれから</h2>
<p>CO-LAB Tech Nightを続ける中で、関西ではまだKINTOテクノロジーズのことがあまり知られていないと実感することが多くあります。「KINTOテクノロジーズって大阪にもあるんですね」とお声がけいただくことも多く、Osaka Tech Labの認知は、まだまだこれからだと感じています。</p>
<p>現状では、登壇者や運営メンバーの呼びかけで参加者が集まることが多く、どうしても社員個人のつながりや発信力に支えられている部分が大きいのが実情です。一方で、CO-LAB Tech Nightやその他のイベントへの出展などを通じて「大阪にもこういう会社があるんだ」と知っていただく機会は確実に増えてきました。</p>
<p>もともと他拠点に比べても交流が多く、メンバー間の距離も近い環境ですが、改めて「こういう視点で仕事をしていたんだ」と気づく場面が増え、お互いの理解をより深めるきっかけにもなっています。</p>
<p>メンバーの想いや行動をきっかけに始まったこの活動を、組織としての発信やコラボレーションの仕組みとして根付かせていきたい。そして関西の企業や、関西で働く方々とともに、大阪からIT業界を盛り上げていきたいと考えています。</p>
<h2>次回:生成AI × クリエイティブの可能性と現在地</h2>
<p>次回のVol.4は、デザイナーによるTikTokエフェクト制作、イラストワークフロー、画像生成比較、AIを使ったキャラクター表現など、実際にトヨタグループの内製開発で活用している事例を紹介します。</p>
<p>📅 2025年10月24日（金）19:00〜21:30
📍 KINTO テクノロジーズ Osaka Tech Lab (大阪駅直結・ノースゲートビルディング)</p>
<p>▼申込・詳細はこちらのconnpassページをご確認ください。
<a href="https://kinto-technologies.connpass.com/event/369528/">https://kinto-technologies.connpass.com/event/369528/</a></p>
<p><a href="https://www.youtube.com/watch?v=v1fFP5VZEF0">https://www.youtube.com/watch?v=v1fFP5VZEF0</a></p>
<h2>おわりに</h2>
<p>CO-LAB Tech Nightは、「話してみたい」「聞いてみたい」という声から企画が生まれた、Osaka Tech Labのカジュアルな勉強会シリーズです。社内外の人がつながり、学び合う場として続けていくことで、大阪発のITコミュニティとして、ゆるやかに広がっていけたらと思います。</p>
<p>関西で活動するエンジニア、デザイナー、PdMのみなさん、ぜひ気軽に遊びにきてください！</p>
<p>▼ KINTO テクノロジーズ Osaka Tech Labのコンセプトや働く人の様子
<a href="https://www.kinto-technologies.com/company/osakatechlab/">https://www.kinto-technologies.com/company/osakatechlab/</a></p>
<p>▼ Osaka Tech Labでは、エンジニア・デザイナー・PdMなど複数職種で仲間を募集中です！
<a href="https://www.wantedly.com/projects/2069536">https://www.wantedly.com/projects/2069536</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/nakamura/20251021/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Vibe Coding Week参加記：QA業務改善のための自動化チャレンジ3選]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-20-qa-vibe-coding-week-challenge/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-20-qa-vibe-coding-week-challenge/</guid>
            <pubDate>Mon, 20 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[QAグループがVibe Coding Weekに参加し、AIを活用した3つの自動化チャレンジを実施。業務効率化と品質向上を実現した取り組みを紹介します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>QAグループのMobileチームのokapiです。
「1週間、生成AIを使ってどこまで開発生産性を上げられるか？」そんなチャレンジ精神に火がついた社内イベント「Vibe Coding Week（バイブコーディングウィーク）」に、QAグループも参戦しましたので、執筆させていただきます。
なお、この記事のカバー画像もAIで作成してみました。</p>
<h2>Vibe Codingとは？</h2>
<p>Vibe Codingとは、生成AIと人間のエンジニアが協働して行う新しい開発スタイルです。
AIが「コード生成」や「調査」といった重い作業を引き受け、人間は「品質の検証・改善」に集中することで、高品質かつ効率的な開発を行えます。
「AIの力を最大限に引き出しながら、人間にしかできない価値を提供する」——それがVibe Coding Weekです。この1週間で、AI活用の限界と可能性を体験し、チーム内のAI活用に対する理解を深めます。</p>
<h2>QAは何をやるのか？</h2>
<p>「開発生産性って、QAに関係あるの？」そう思われた方もいるかもしれません。
QAグループでは日々、「自動化・業務改善・AI活用」に積極的に取り組んでいます。
日々の業務で培ったノウハウを武器に、このVibe Coding Weekに挑戦することになりました。
私はQAグループの代表として参戦。
イベント開始の1ヶ月前から週次ミーティングを重ね、「どうすれば成果を出せるのか？」を徹底的に議論しました。
その結果、QA業務を楽にする「3つの自動化チャレンジ」を実施することに決定しました！</p>
<h2>取り組んだ3つの自動化チャレンジ</h2>
<p>中古車データ作成の自動化と、画面仕様と実画面比較の自動化については、担当メンバーが詳細記事を執筆予定です。ぜひ公開を楽しみにお待ちください。</p>
<table>
<thead>
<tr>
<th>チャレンジ</th>
<th>概要</th>
<th>効果</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1. Appiumライブラリのメジャーバージョンアップ</strong></td>
<td>QA Mobileチームのテスト自動化で利用しているAppiumのライブラリを、メジャーバージョン2つ分（java-client: 7.6.0 → 9.0.0）にアップデート。AIを活用してリリースノート調査、修正タスク化、実装を高速化し、互換性のある周辺ライブラリも同時更新。</td>
<td>メンテナンス性の向上。今後自動化予定のAppiumで行う画面比較に対応したaShotライブラリ（ピクセルパーフェクト比較）が使用可能に！ <a href="https://github.com/pazone/ashot">https://github.com/pazone/ashot</a></td>
</tr>
<tr>
<td><strong>2. 中古車データ作成の自動化</strong></td>
<td>QA Webチームのテストで必要な中古車のデータ作成をPlaywrightで自動化</td>
<td>従来200台作成に約2ヶ月かかっていたが、自動化により工数を削減。これまで手作業で行っていた面倒な作業から解放！</td>
</tr>
<tr>
<td><strong>3. 画面仕様と実画面比較の自動化</strong></td>
<td>QA Webチームのテストで手動で行っていたExcel仕様書と実画面の比較をVS Codeで自動化。仕様書から文字抽出→差分判定→差分箇所表示を自動化</td>
<td>テスト実施の確認時間を削減。目視で探していた差分が一瞬でわかるように！</td>
</tr>
</tbody></table>
<h2>各メンバーの感想</h2>
<table>
<thead>
<tr>
<th>メンバー</th>
<th>感想</th>
</tr>
</thead>
<tbody><tr>
<td>大島さん</td>
<td>これまで地道に取り組んできたことを発表でき、さらに実案件への展開の可能性も見えてきたと感じました。通常業務の中でAIを活用できたのも良かったです。</td>
</tr>
<tr>
<td>呂さん</td>
<td>今回の成果物は、今後の案件業務に活用できそうです。</td>
</tr>
<tr>
<td>小林さん</td>
<td>ClaudeCode、Copilot、Devinをそれぞれ使ってみて、性能の違いをある程度把握できました。どれを使う場合でも、調査や実装のスピードが人力よりも明らかに速い。</td>
</tr>
<tr>
<td>パンヌさん</td>
<td>GitHub Copilotを使ってMCPのサーバー構築を行い、Figma関連の理解も深まり、すべての作業を完了できました。とても満足しています。</td>
</tr>
<tr>
<td>岡</td>
<td>皆さんが事前準備からしっかり取り組んでいたおかげで、3チームとも成果を出せていたので、良いイベントでした。</td>
</tr>
</tbody></table>
<h2>成果と学び</h2>
<p>AI活用により、調査・修正作業のスピードと精度が向上。
自動化によって工数を削減し、人的リソースをより重要な品質保証やテスト設計に集中できるようになった。
短期間で普段着手できなかった改善を実施でき、AI活用文化の形成にもつながりました。</p>
<h2>おわりに</h2>
<p>今回のVibe Coding Weekでは、QA業務の自動化と効率化に向けた3つのチャレンジを実施し、短期間で大きな成果を得ることができました。
事前準備に1ヶ月かけた甲斐があり、イベント期間中は全力疾走。
この経験で得たノウハウは、今後のQA業務に大きく活かされるはずです。
今後も「自動化・業務改善・AI活用」を継続的に推進するとともに、引き続き、色んなチャレンジに挑戦していきます。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/okapi/vibe-coding-week-qa/thumbnail.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Vibe Coding Week Participation Report: 3 Automation Tasks for QA Workflow Improvement]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-20-vibe-coding-week-qa-automation-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-20-vibe-coding-week-qa-automation-en/</guid>
            <pubDate>Mon, 20 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[The QA Group participated in Vibe Coding Week and implemented three automation tasks, leveraging AI. We introduce our initiatives that achieved workflow efficiency and quality improvement.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>I&#39;m okapi from the Mobile team in the QA Group.
&quot;How much can we boost development productivity using generative AI in one week?&quot; With that challenging spirit ignited, the QA Group also joined the internal event Vibe Coding Week, and I&#39;m writing this article to share our experience.
By the way, the cover image for this article was also created using AI.</p>
<h2>What is Vibe Coding?</h2>
<p>Vibe Coding is a new development style where human engineers collaborate with generative AI.
AI takes on heavy tasks like code generation and research, while humans focus on quality verification and improvement, which enables high-quality and efficient development.
Maximizing the power of AI while delivering value that only humans can provide—that&#39;s what the Vibe Coding Week is all about. During this week, we experience the limits and potential of the AI use to deepen the team&#39;s understanding of AI adoption.</p>
<h2>What Does QA Do?</h2>
<p>Does development productivity really relate to QA? Some of you might have thought such question.
The QA Group actively works on automation, workflow improvement, and AI utilization every day.
Obtaining the know-how cultivated through daily work, we decided to take on this Vibe Coding Week challenge.
I participated as a representative of the QA Group.
Starting one month before the event, we held weekly meetings and thoroughly discussed how we can deliver results.
As a result, we decided to implement three automation tasks to make QA work easier!</p>
<h2>The 3 Automation Tasks We Tackled</h2>
<p>One of the QA team members will write detailed articles about the automation to create used vehicle data and to compare a screen design on specification with an actually developed screen. Please look forward to the article release.</p>
<table>
<thead>
<tr>
<th>Challenge</th>
<th>Overview</th>
<th>Effect</th>
</tr>
</thead>
<tbody><tr>
<td><strong>1. Major version upgrade of Appium Library</strong></td>
<td>Updated the Appium library used for test automation by the QA Mobile team by two major versions (java-client: 7.6.0 → 9.0.0). Leveraged AI to accelerate release note research, modification task creation, and implementation, while updating compatible peripheral libraries simultaneously.</td>
<td>Improved maintainability. Now able to use the aShot library (pixel-perfect comparison) compatible with screen comparison planned for future Appium automation! <a href="https://github.com/pazone/ashot">https://github.com/pazone/ashot</a></td>
</tr>
<tr>
<td><strong>2. Used vehicle data creation automation</strong></td>
<td>Automated the creation of used vehicle data needed for testing by the QA Web Team using Playwright</td>
<td>Previously, creating data for 200 vehicles took about 2 months, but automation reduced the team&#39;s workload. Freed from the tedious manual task!</td>
</tr>
<tr>
<td><strong>3. Comparison automation between screen design on specification and actual screen</strong></td>
<td>Automated the comparison between screen design on specification in Excel and actually developed screen with VS Code that used to be done manually by the QA Web Team. Automated a process from extracting texts from specifications, identifying differences between design on specification and actual screen, and displaying where the differences are identified.</td>
<td>Reduced verification time during the test phase. Differences that used to be checked visually can now be identified instantly!</td>
</tr>
</tbody></table>
<h2>Team Members&#39; Impressions</h2>
<table>
<thead>
<tr>
<th>Member</th>
<th>Impression</th>
</tr>
</thead>
<tbody><tr>
<td>Oshima-san</td>
<td>I was able to present what we had been constantly working on and expect it to be applied for actual projects. It was also good to be able to utilize AI in regular work.</td>
</tr>
<tr>
<td>Lu-san</td>
<td>The deliverables from this time seem useful for future project work.</td>
</tr>
<tr>
<td>Kobayashi-san</td>
<td>By using ClaudeCode, Copilot, and Devin respectively, I was able to grasp their performance differences to some extent. Whichever I used, the speed of research and implementation is definitely faster than manual work.</td>
</tr>
<tr>
<td>Pann Nu-san</td>
<td>I used GitHub Copilot to build an MCP server, which brought me an opportunity to deepen my understanding of Figma-related topics and complete all the work. I&#39;m very satisfied.</td>
</tr>
<tr>
<td>Oka</td>
<td>Thanks to everyone&#39;s thorough preparation from the start, all three teams were able to deliver results, so it was a great event.</td>
</tr>
</tbody></table>
<h2>Results and Learnings</h2>
<p>AI utilization increased the speed and accuracy of research and modification tasks.
Automation reduced workload, allowing humans to focus on more important quality assurance and test design.
We were able to make improvements that we couldn&#39;t normally tackle in a short amount of time. This contributed to forming a corporate culture where we automate routine tasks by leveraging AI.</p>
<h2>Conclusion</h2>
<p>In this Vibe Coding Week, we implemented three tasks aimed at automating and improving efficiency of QA work, achieving significant results in a short term.
The one month of preparation paid off, and we sprinted at full speed during the event.
The know-how gained from this experience should greatly benefit our future QA work.
We will continue to promote automation, workflow improvement, and AI utilization continuously, and keep addressing various challenging tasks.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/okapi/vibe-coding-week-qa/thumbnail.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Tips for Safe AI Image Editing—Learning from Azure OpenAI Service]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-17-tips-for-safe-ai-image-editing-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-17-tips-for-safe-ai-image-editing-en/</guid>
            <pubDate>Fri, 17 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Tips for Safe AI Image Editing]]></description>
            <content:encoded><![CDATA[<h1>Tips for Safe AI Image Editing—Learning from Azure OpenAI Service</h1>
<h2>Introduction</h2>
<p>As of October 2025, AI-related news is reported daily, covering topics like how amazing the new model is or what it can do now.</p>
<p>Since technological progress is remarkable, complex images can currently be edited easily with AI tools. While taking advantage of them, we should not forget the perspective of how to use them safely.</p>
<p>We are often captivated by performance aspects such as what amazing images can be created or how naturally they can be edited, but it is equally important to consider whether generated images could potentially harm others.</p>
<h2>Learning Responsible AI from Enterprise Services</h2>
<p>Microsoft&#39;s Azure OpenAI Service (AOAI) is a very helpful reference from this perspective.</p>
<p>AOAI is designed for enterprises and government agencies. While it uses the same technology as OpenAI, it incorporates more stringently ethical standards (guardrails) based on the principles of Responsible AI.
Of course, OpenAI also upholds similar principles, but AOAI is operated more carefully.</p>
<h2>Why Does AOAI Have Stricter Policies?</h2>
<p>For example, when a corporation uses OpenAI&#39;s API, <strong>personal identity verification of the administrator</strong> is required to prevent unauthorized use. This is an approach clarifying the responsibility of the individuals who operate the API.</p>
<p>On the other hand, AOAI is a corporate cloud service, and its use is authorized under Azure contracts and authentication infrastructure (e.g., Microsoft Entra ID). It is not realistic for each employee to submit identification documents to use a service within a company. AOAI takes this into consideration and <strong>eliminates the need for individual authentication by trusting the corporation</strong>. In short, trust is placed in the organization.</p>
<p>With this approach, AOAI, as an enterprise platform, has set up particularly stringent restrictions (guardrails) on its use.</p>
<h2>Examples of Operations Blocked by AOAI</h2>
<p>So, what specific restrictions are in place?
Based on my testing experience, the following operations were blocked by AOAI. (For testing, I used the gpt-image-1 model.)</p>
<ul>
<li><strong>Instructions to edit images of minors (including context-based judgments, like someone in a school uniform)</strong></li>
<li><strong>Including insulting or discriminatory words in prompts</strong></li>
<li><strong>Instructions to swap the face of a specific individual</strong></li>
</ul>
<p>These restrictions show that AOAI focuses on at least the following three points:</p>
<h3>AOAI&#39;s Three Key Areas of Focus</h3>
<ul>
<li><strong>Protecting minors who are in socially vulnerable positions</strong></li>
<li><strong>Protecting human dignity from verbal violence such as discrimination and insults</strong></li>
<li><strong>Preventing the spread of misinformation through deepfakes and similar technologies</strong></li>
</ul>
<p>To put it simply, this isn&#39;t a wall to restrict freedom but a framework to protect others and society.</p>
<p>Such framework is extremely helpful for avoiding unintentionally hurting someone and for not accidentally becoming a perpetrator.</p>
<h2>Transparency in the AI Era: Content Credentials</h2>
<p>Recently, as image generation and editing have become easily accessible to individuals, the risk of unintentionally hurting others has also increased.</p>
<p>However, to address such challenges, images generated or edited by AI tools often have <strong>Content Credentials</strong>, metadata embedded in the images.</p>
<p>This is a new system designed to ensure transparency in AI generation. It records information such as:</p>
<ul>
<li><strong>When it was created</strong>;</li>
<li><strong>What tools were used</strong>; and</li>
<li><strong>Who edited it</strong>.</li>
</ul>
<p>It is like a <strong>digital version of a nutrition facts label</strong>.</p>
<p>The system enables the detection of content tampering and incidental modification.
In other words, it allows people who properly use AI to publish content with confidence and authenticity.</p>
<h2>Summary</h2>
<p>Instead of getting swept away by convenience, take a moment to ask yourself: &quot;Could this content cause harm to others?&quot;</p>
<p>Building up this kind of awareness is the first step toward using AI safely. I believe that, ultimately, this protects both you and your organization.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoshima/tips_for_safe_ai_image_editing/title.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Azure OpenAI Serviceに学ぶ、安全なAI画像編集のヒント]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-17-tips-for-safe-ai-image-editing/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-17-tips-for-safe-ai-image-editing/</guid>
            <pubDate>Fri, 17 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[安全なAI画像編集のヒント]]></description>
            <content:encoded><![CDATA[<h1>Azure OpenAI Serviceに学ぶ、安全なAI画像編集のヒント</h1>
<h2>はじめに</h2>
<p>2025年10月現在、「新しいモデルはどのくらいすごい」「こんなことができる」といったAI関連ニュースが毎日のように流れてきます。</p>
<p>技術の進歩は目覚ましく、今や複雑な画像編集も、AIを使えば比較的簡単に行うことができるようになりました。こうした恩恵を受けられる一方で、忘れてはならないのが「AIをどのように安全に使うか」という視点です。</p>
<p>私たちはつい「どんなすごい画像が作れるか」「どれくらい自然に編集できるか」といった性能面に目を奪われがちですが、その一方で、生成された画像が人を傷つける可能性があるかどうかという観点を持つことも大切です。</p>
<h2>企業向けサービスに学ぶ「責任あるAI」</h2>
<p>この点で非常に参考になるのが、MicrosoftのAzure OpenAI Service（AOAI）です。</p>
<p>AOAIは企業や行政機関向けに設計されており、OpenAIと同じ技術を用いながらも「責任あるAI（Responsible AI）」の原則に基づき、より厳格な倫理基準（ガードレール）が組み込まれています。
もちろんOpenAIも同様の原則を掲げていますが、AOAIのほうがより慎重に運用されています。</p>
<h2>なぜAOAIはより慎重なのか？</h2>
<p>たとえば、OpenAIのAPIを法人で利用する場合、不正利用を防ぐために管理者<strong>個人の身分証明による本人確認</strong>が求められます。これは、APIを操作する「個人」の責任を明確にするアプローチです。</p>
<p>一方、AOAIは法人向けクラウドサービスであり、Azureの契約と認証基盤（Microsoft Entra IDなど）に基づいて利用が許可されます。企業でサービスを利用する際に従業員一人ひとりが身分証明書を提出するのは、現実的ではありません。AOAIはそこを考慮し、<strong>法人を信頼することで個人認証を不要に</strong>しています。いわば契約した「法人」を信頼するアプローチです。</p>
<p>その上でAOAIでは企業向けプラットフォームという立場から、特に慎重な利用制限（ガードレール）を設けています。</p>
<h2>AOAIでブロックされる操作例</h2>
<p>では、具体的にどのような制限があるのでしょうか。
筆者が実際に検証したところ、AOAIでは以下のような操作がブロックされました。（使用したモデルはgpt-image-1）</p>
<ul>
<li><strong>未成年者（制服を着ているなど、文脈からそう判断される場合を含む）の画像を編集する指示</strong></li>
<li><strong>侮辱的・差別的な言葉をプロンプトに含めること</strong></li>
<li><strong>特定の個人の顔を入れ替えるような指示</strong></li>
</ul>
<p>これらの制限からAOAIが重視しているのは、少なくとも次の3点だと考えられます。</p>
<h3>AOAIが重視する3つのポイント</h3>
<ul>
<li><strong>社会的に弱い立場にある未成年者の保護</strong></li>
<li><strong>差別や侮辱といった言葉の暴力から人間の尊厳を守ること</strong></li>
<li><strong>ディープフェイクなどによる誤情報の拡散防止</strong></li>
</ul>
<p>これを一言で表すなら、「自由を制限するための壁」ではなく、「他者や社会を守るための枠組み」と言えるのではないでしょうか。</p>
<p>自分が誰かを傷つけないため、そして誤って「加害者」にならないためにも、こうした枠組みは非常に参考になります。</p>
<h2>AI時代の透明性：「コンテンツクレデンシャル」</h2>
<p>最近は画像生成や編集を個人でも気軽に行えるようになったため、意図せず他人を傷つけてしまうリスクも高まっています。</p>
<p>ただしこのような課題に対応するため、AIで生成・編集された画像には多くの場合「<strong>コンテンツクレデンシャル</strong>」と呼ばれるメタデータが埋め込まれています。</p>
<p>これはAI生成の透明性を担保する新しい仕組みであり、</p>
<ul>
<li><strong>いつ作られたか</strong></li>
<li><strong>どんなツールが使われたか</strong></li>
<li><strong>誰が編集したか</strong></li>
</ul>
<p>といった情報が記録されます。</p>
<p>いわば、<strong>デジタル版の栄養成分表示</strong>のようなものです。</p>
<p>この仕組みにより、コンテンツの改ざんや虚偽の検出が可能になります。
言い換えれば、「正しくAIを使った人」がその証明をしながら堂々と発信できる環境が整いつつあるということです。</p>
<h2>まとめ</h2>
<p>便利さに流されず、「この表現は誰かを傷つけないか？」と自問すること。</p>
<p>その小さな意識の積み重ねこそが、AIを安全に使うための第一歩であり、結果として自分自身や所属する組織を守ることにもつながるのだと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoshima/tips_for_safe_ai_image_editing/title.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[InnerSource Gathering Tokyo 2025 Participation Report]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-08-InnerSource-Gathering-Tokyo-2025-Report-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-08-InnerSource-Gathering-Tokyo-2025-Report-en/</guid>
            <pubDate>Thu, 16 Oct 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This is Nakanishi from the Developer Relations Group (also serving in the FACTORY E-commerce Development Group and QA Group). This article is a report on the <a href="https://gatherings.innersourcecommons.org/tokyo-2025/">InnerSource Gathering Tokyo 2025</a> held in Odaiba on September 12th.</p>
<p>The term &quot;inner source&quot; has become much more common in recent years. At our company, we continue to make small, steady efforts to promote an inner source culture through creating our tech blog and fostering an engineering culture.</p>
<p>I learned a lot from this event and it reaffirmed my belief that our efforts are truly contributing to fostering an inner source culture. I’m writing this report to share this wonderful event more widely and to help promote the culture of inner source.</p>
<h1><strong>Changing Culture Through &quot;Actions&quot;: An Field-Based Approach to Implementing Inner Source</strong></h1>
<p>Throughout the event, a few common themes consistently echoed through the venue. <strong>Breaking down silos isn&#39;t about slogans, but the accumulation of small actions</strong>. Code isn&#39;t the only contribution. And rather than loudly proclaiming what is right, <strong>starting small and gaining allies</strong> is what drives culture change. Each speaker shared insights from their distinct perspectives and different cultural contexts.</p>
<hr>
<h2>Capturing &quot;Silos&quot; Through Gradation</h2>
<p>Right from the start, the message from the organizers was clear. <strong>Inner Source is an attempt to break down internal silos through cultural transformation</strong>, but it&#39;s not something that can be described in black-and-white terms. The intensity varies by organization. With that in mind,<strong><a href="https://ja.wikipedia.org/wiki/%E3%83%81%E3%83%A3%E3%82%BF%E3%83%A0%E3%83%8F%E3%82%A6%E3%82%B9%E3%83%AB%E3%83%BC%E3%83%AB">the Chatham House Rule</a></strong> was declared, creating a space where participants could freely take away insights without linking them to specific speakers or companies. Once these essential rules were established, the discussions became lively and engaging, which was fantastic It was the moment I felt glad I had participated from the very start of the event.</p>
<p>NTT DOCOMO, which provided the venue ( <a href="https://docomo-openlab.jp/">docomo R&amp;D OPEN LAB ODAIBA</a> ), introduced its facility for &quot;creating, learning, and sharing,&quot; equipped with giant LED displays and 5G/edge computing environments. The venue is also open as a co-working hub outside of event days, and I was impressed by how it was designed as <strong>a permanent place where engineers can naturally gather</strong>.</p>
<h2><img src="/assets/blog/authors/aoi.nakanishi/2025-10-08-InnerSource-Gathering-Tokyo-2025-Report/docomo-open-lab-odaiba.png" alt="docomo R&amp;D OPEN LAB ODAIBA"></h2>
<h2>Inner Source Is Not a &quot;License&quot; but a &quot;Method&quot;</h2>
<p>The keynote speech reinterpreted inner source as a &quot;method&quot; by drawing on the history and practices of OSS (open source software). <strong>Public discussions, open access for anyone to participate, and community collaboration</strong> —it was said that <strong>bringing</strong> these OSS practices <strong>into the company</strong> represents a return to the fundamentals of inner source.</p>
<p>The discussion also significantly highlighted <strong>contributions beyond code</strong>, explicitly stating that reviews, testing, triage, translations, documentation, infrastructure operations, and public relations are all first-class contributions. In discussing review language etiquette, they introduced a principle from the networking community, which is <strong>&quot;receive with generosity, express with precision&quot;</strong>, and connected it to dialogue etiquette. <strong>A suggestion is not a command, but the start of a dialogue</strong>. Since others can learn by observing such interactions, the language itself helps shape the culture.</p>
<p>The other key factor is <strong>its compatibility with Agile</strong>. Frequent releases, self-organization, evolving requirements—what OSS has long implemented and agile practices ultimately resemble. Therefore, the method of change follows the same path. <strong>Actions change, thoughts change, and culture changes</strong>. I was reminded that despite the various terms used, such as so-called engineer culture, there is a commonality in the underlying mindset and behavior.</p>
<p>Regarding motivation, recent trends were shared, where fun and learning are joined by <strong>career growth and reputation</strong>. One example highlighted how <strong>a company’s design that supports growth and learning</strong> led to a rapid increase in internal contributors.</p>
<p>In the Q&amp;A, a practical point was raised: incentives that <strong>rely solely on money don’t last</strong>. The best approach is to design around three elements which are fun, learning, and recognition. As a strategy against burnout, a step-by-step approach was shared.  <strong>Rather than speaking to a large group from the start, begin by reaching out to individuals, achieving small wins, and gradually building a base of supporters</strong>. An answer to the question about handling difficult behavior was down-to-earth: <strong>establish a code of conduct and work to raise the community’s overall &#39;average&#39;</strong>. The concept of creating <strong>an inclusive framework,  turning others allies</strong> rather than excluding was consistently present. </p>
<p><a href="https://kdmsnr.com/slides/20250912%5C_innersource/">https://kdmsnr.com/slides/20250912\_innersource/</a></p>
<p>Many of these align with <a href="https://www.amazon.com/dp/B00TU5KTTI/">More Fearless Change: Strategies for Making Your Ideas Happen</a>, and I highly recommend them for driving new initiatives within your organization.</p>
<hr>
<h2>NRI &quot;xPalette&quot;: Circulating Capabilities</h2>
<p>Nomura Research Institute shared insights gained over four years of creating environments that empower <strong>engineers&#39; creativity and initiative</strong>. They establish reference architectures and individual guides, then circulate insights gained through experimentation in a <strong>&quot;Learn → Apply → Feedback&quot;</strong> cycle. As this cycle turns, opportunities to participate in projects increase, and <strong>new ventures</strong> combining multiple technologies begin to happen. <strong>Explaining</strong> activities <strong>in terms of business value</strong> and creating a positive spiral from budget allocation to environmental improvement is a realistic approach. Management takes the lead in <strong>praising</strong> young members for <strong>trying things out</strong>, supporting their “<strong>just give it a go</strong>” mindset with small allocations of time and budget. This kind of hands-on, tangible management approach was evident throughout.</p>
<hr>
<h2>Mitsubishi Electric OSPO/ISPO: Turning External Attention into Internal Momentum</h2>
<p>Mitsubishi Electric has established a system to run <strong>OSPO (Open Source Program Office) and ISPO (InnerSource Program Office)</strong> in parallel, starting with fostering the habit of &quot; <strong>prepare the platform → publish it openly</strong>.&quot; Drawing attention at external events and <strong>channeling outside interest back into internal recognition</strong>. That style of storytelling is characteristic of large corporations. They showcased how a series of internal events helped <strong>certain terms evolve into shared language across the organization</strong>. Furthermore, they announced plans to <strong>host a conference in Yokohama on November 13th</strong>, showing a proactive approach to establishing a <strong>platform</strong> for the movement.</p>
<p>InnerSource Summit 2025 <a href="https://innersourcecommons.org/ja/events/isc-2025/">https://innersourcecommons.org/ja/events/isc-2025/</a></p>
<hr>
<h2>Discussion: Regulations Are Also &quot;Made Together&quot; / Quality Is &quot;Value for the User&quot;</h2>
<p>The discussion was imbued with <strong>the practical insights gained from implementing inner source within a large corporation</strong>. What struck me most was the proposal that <strong>rules, such as development standards and internal regulations, are exactly the kind of things that should be created through inner source</strong>. Involve relevant parties and make the approval process transparent. When handling KPIs, <strong>do not explain them solely in terms of monetary value</strong>. Track <strong>the preliminary factors that influence cost</strong>, including team size, code reuse, and review lead time. Regarding the definition of quality, the approach emphasized <strong>&quot;value for the user&quot;</strong> as the core principle, moving away from measuring solely by the number of defects. In response to questions about how to handle generative AI, a calm and grounded answer was given: <strong>whether the creator is human or AI is not the core issue</strong>. What is needed is quality management <strong>that includes user education, operational design, and feedback loops</strong> To overcome cost allocation and budget barriers, a practical approach was shared: <strong>&quot;Start within your own team first, build a following, and let the system follow later</strong>. <strong>&quot;</strong> </p>
<hr>
<h2>KDDI &quot;KAGreement&quot;: Where Open Agreements Become Culture</h2>
<p>KDDI&#39;s presentation is an initiative to <strong>articulate &quot;Why we are here</strong>.<strong>&quot;</strong> Through <strong>weekly FigJam sessions</strong> attended by the Vice President and <strong>public sharing on Slack</strong>, <strong>they refine</strong> their working agreement (guidelines for action) <strong>together</strong>. The discussion is following a pattern very similar to inner source practices such as &quot; <strong>public sharing, archive, small and fast action, and cross-team</strong>.&quot; The design of holding <strong>random breakout sessions</strong> during company-wide meetings, mixing departments and seniority levels for dialogue, could be described as an implementation that <strong>gradually raises the organization&#39;s &quot;average</strong>. <strong>&quot;</strong> Behind the scenes, <strong>executive sponsorship</strong> is certainly providing support.</p>
<p><a href="https://www.docswell.com/s/mitsuba%5C_yu/KLVRX7-2025-09-12-163618">https://www.docswell.com/s/mitsuba\_yu/KLVRX7-2025-09-12-163618</a></p>
<hr>
<h2>teamLab: If You Can&#39;t Find It, It Might as Well Not Exist</h2>
<p>teamLab&#39;s core theme is creating <strong>mechanisms that grow through use</strong>. The larger an organization grows, the harder it becomes to see what exists and where it’s located. Therefore, they launched the <strong>&quot;InnerSource department&quot; internally</strong>, starting by visualizing interests and potential collaborators. Next, they created the <strong>InnerSource Portal <strong>. <strong>Consolidate</strong> the repository overview, owner, setup instructions, <strong>projects using it</strong>, and links to Issues/PRs <strong>onto a single page</strong>. Issue templates are also categorized into types like &quot;questions, improvements, feature requests...&quot; to</strong> enhance ease of writing</strong>.</p>
<p>Furthermore, they talked about plans to implement <strong>titles and awards</strong> such as &quot; <strong>InnerSource Champion</strong>,&quot; &quot;Top contributor,&quot; &quot;Legendary Issue,&quot; and &quot;Rookie of the Year&quot; with a playful spirit. They are also considering initiatives such as regular release sharing sessions and <strong>days where everyone collaborates to create a single internal OSS project</strong> with a deadline. The objective is singular. Increase the number of situations where it&#39;s <strong>&quot;right there when you need it</strong>. <strong>&quot;</strong> </p>
<p><a href="https://speakerdeck.com/teamlab/innersource%5C_gathering%5C_tokyo2025%5C_teamlab">https://speakerdeck.com/teamlab/innersource\_gathering\_tokyo2025\_teamlab</a></p>
<hr>
<h2>Summary: Small Successes Become a Culture.</h2>
<p>What resonated most at this year’s ISGT was the recurring theme across all presentations: <strong>“gentle pathways</strong>. A UI that makes your first PR less intimidating. The words that make the first review comforting. A title that makes you proud of your first contribution.</p>
<p><strong>The accumulation of</strong> such <strong>small successes</strong> raises the community&#39;s overall &quot;average&quot; and dissolves silos. I strongly felt that inner source is not a system name, but rather <strong>the design of</strong> daily <strong>small actions</strong>.</p>
<hr>
<h2>Extra: The Vibe at the Social Gathering</h2>
<p>Following the event, an InnerSource OST (Open Space Technology) session was held, where discussions continued in separate groups for each theme. The event naturally transitioned into a social gathering, so the atmosphere was lively from the start, with an active exchange of opinions continuing throughout. It was striking to see how many <strong>meaningful conversations</strong> emerged, not just casual exchanges, because the gathering brought together people genuinely committed to tackling the question of how to improve culture.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-10-08-InnerSource-Gathering-Tokyo-2025-Report/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Google Play配布後のFirebase FCM/Installationsエラー解決: Play署名鍵SHA登録完全ガイド]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-15-fixFirebaseFcmInstallationsPlaySigningSha/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-15-fixFirebaseFcmInstallationsPlaySigningSha/</guid>
            <pubDate>Wed, 15 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Play Storeからインストールしたアプリでのみ発生する「Firebase Installations Service is unavailable」エラーの解決方法を詳しく解説します。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>はじめまして、KINTOテクノロジーズ（KTC）でモバイルアプリ（Flutter）の開発を担当しているHand-Tomiです。</p>
<p>アプリをGoogle Play Storeに公開した後、<strong>ストアからインストールしたアプリでのみ</strong>Firebase関連のエラーが発生する経験をしたことはありませんか?</p>
<p>デバッグビルドや内部テストでは正常に動作していたのに、本番環境でのみ以下のようなエラーログが表示される場合:</p>
<pre><code class="language-text">E  Failed to get FIS auth token
java.util.concurrent.ExecutionException: ...
Caused by: Firebase Installations Service is unavailable. Please try again later.
</code></pre>
<p>この記事では、このような問題の根本原因と解決方法を段階的に解説します。</p>
<h1>問題の症状</h1>
<ul>
<li><strong>デバッグビルド/内部リリース</strong>: 正常動作</li>
<li><strong>Play Storeからインストール</strong>: Firebase初期化失敗、FCMトークン発行不可</li>
<li>一時的なサーバー障害のように見えますが、実際には<strong>リリースビルドのアプリ識別(署名/パッケージ)とFirebase/Google Cloud設定の不一致</strong>が原因です</li>
</ul>
<h1>核心原因の理解</h1>
<h2>Play Storeの最終署名メカニズム</h2>
<p>Google Playにアプリを配布すると、最終的なAPK/AABは<strong>Google Play App signing key(アプリ署名鍵)<strong>で再署名されます。これは開発時に使用する</strong>アップロード鍵</strong>とは異なる鍵です。</p>
<h2>2箇所への登録が必須</h2>
<p>Firebase/Google CloudでAndroidアプリを正しく識別するには、以下の2箇所に<strong>Play署名鍵のSHA fingerprint</strong>を登録する必要があります:</p>
<ol>
<li><strong>Firebase Console</strong>(プロジェクト設定 &gt; Android アプリ): <strong>SHA-256</strong>必須</li>
<li><strong>Google Cloud Console</strong>(API Key &gt; Android アプリ制限を設定している場合): <strong>SHA-1</strong>必須</li>
</ol>
<blockquote>
<p>💡 <strong>比喩</strong>: Firebaseは<strong>SHA-256パスポート</strong>を、API Key制限は<strong>SHA-1身分証</strong>を要求します。開発用(デバッグ/アップロード鍵)の身分証だけでは、空港(Playビルド)で通過できません。</p>
</blockquote>
<h1>解決手順</h1>
<h2>1. Play署名証明書のSHA取得</h2>
<ol>
<li><strong>Google Play Console</strong>にアクセス → 対象アプリを選択</li>
<li>左メニュー: <strong>テストとリリース → アプリの完全性</strong></li>
<li>ページを下にスクロールして<strong>Play アプリ署名</strong>セクションを見つける</li>
<li><strong>設定を表示</strong>ボタンをクリック</li>
</ol>
<p><img src="/assets/blog/authors/semyeong/2025-10-15-fixFirebaseFcmInstallationsPlaySigningSha/image_1.png" alt="Google Play Console - アプリの完全性画面"></p>
<ol start="5">
<li><strong>アプリ署名鍵の証明書</strong>タブで<strong>SHA-1</strong>と<strong>SHA-256</strong>をコピー</li>
</ol>
<p><img src="/assets/blog/authors/semyeong/2025-10-15-fixFirebaseFcmInstallationsPlaySigningSha/image_2.png" alt="アプリ署名鍵の証明書 - SHA-1とSHA-256"></p>
<p>:::message
<strong>注意</strong>: 同じ画面に「アップロード鍵証明書」タブも表示されますが、本ガイドで必要なのは<strong>アプリ署名鍵の証明書</strong>の値です。SHA-1は<strong>Google Cloud Console</strong>で、SHA-256は<strong>Firebase</strong>で使用します。
:::</p>
<h2>2. Firebase ConsoleへのSHA-256登録</h2>
<ol>
<li><strong>Firebase Console</strong> → プロジェクト選択 → <strong>プロジェクト設定</strong></li>
</ol>
<p><img src="/assets/blog/authors/semyeong/2025-10-15-fixFirebaseFcmInstallationsPlaySigningSha/image_3.png" alt="Firebase Console - プロジェクト設定"></p>
<ol start="2">
<li><strong>Your apps</strong>でAndroidアプリを選択</li>
<li><strong>SHA certificate fingerprints</strong>セクションで<strong>Add fingerprint</strong>をクリック</li>
<li><strong>Play署名鍵のSHA-256</strong>を貼り付けて保存</li>
</ol>
<p><img src="/assets/blog/authors/semyeong/2025-10-15-fixFirebaseFcmInstallationsPlaySigningSha/image_4.png" alt="SHA certificate fingerprints追加"></p>
<ol start="5">
<li>(推奨) 既存のアップロード鍵やデバッグ鍵のSHA-1/256も登録されているか確認</li>
</ol>
<p>:::message
設定保存後、数分以内に反映されますが、デバイスキャッシュのため<strong>アプリの完全削除 → 再インストール</strong>が最も確実です。
:::</p>
<h2>3. Google Cloud ConsoleでのAPI Key制限設定(Android制限を使用している場合)</h2>
<p><code>google-services.json</code>の<code>api_key.current_key</code>に対応する<strong>API Key</strong>にAndroidアプリ制限が設定されている場合は、以下の手順でPlay署名鍵のSHA-1を登録する必要があります。</p>
<ol>
<li><strong>Google Cloud Console</strong> → Firebaseと同じプロジェクトを選択</li>
<li>左メニュー: <strong>APIs &amp; Services → 認証情報</strong></li>
<li>リストから該当<strong>API Key</strong>を選択</li>
</ol>
<p><img src="/assets/blog/authors/semyeong/2025-10-15-fixFirebaseFcmInstallationsPlaySigningSha/image_5.png" alt="Google Cloud Console - API Key選択"></p>
<ol start="4">
<li><strong>アプリケーションの制限</strong>を確認:<ul>
<li><strong>「Android apps」が選択されている場合</strong>: 次のステップに進む</li>
<li><strong>「なし」または他の制限の場合</strong>: このセクションはスキップ可能</li>
</ul>
</li>
</ol>
<p><img src="/assets/blog/authors/semyeong/2025-10-15-fixFirebaseFcmInstallationsPlaySigningSha/image_6.png" alt="API Key - Androidアプリ制限設定"></p>
<ol start="5">
<li><strong>パッケージ名 + SHA-1</strong>ペアを追加:<ul>
<li>パッケージ名: <code>applicationId</code></li>
<li>SHA-1: <strong>Play署名鍵のSHA-1</strong>(アップロード鍵ではない)</li>
</ul>
</li>
<li>保存(伝播に数分かかる場合があります)</li>
</ol>
<p>:::message alert
この画面では<strong>SHA-1のみ入力可能</strong>です。SHA-256入力欄が表示されないのは正常です。
:::</p>
<h2>4. FirebaseOptions検証(推奨)</h2>
<p>リリースビルドが正しい<code>google-services.json</code>を参照しているか確認します。</p>
<h3>ビルド成果物の確認</h3>
<p><code>app/build/generated/res/google-services/&lt;variant&gt;/values/values.xml</code>で以下の値が期待通りか確認:</p>
<ul>
<li><code>gms_app_id</code>(= <code>mobilesdk_app_id</code>)</li>
<li><code>project_id</code></li>
<li><code>gcm_defaultSenderId</code>(= Project number)</li>
<li><code>default_web_client_id</code> / <code>api key</code></li>
</ul>
<h3>実行時オプションのログ出力</h3>
<pre><code class="language-kotlin">val options = FirebaseApp.getInstance().options
Log.d(&quot;FB_OPTS&quot;, &quot;&quot;&quot;
  appId=${options.applicationId}
  projectId=${options.projectId}
  apiKey=${options.apiKey}
  sender=${options.gcmSenderId}
&quot;&quot;&quot;.trimIndent())
</code></pre>
<h2>5. 動作確認</h2>
<ol>
<li>デバイスで<strong>アプリデータ削除</strong>または<strong>アプリ完全削除</strong></li>
<li><strong>Play Storeから再インストール</strong></li>
<li>アプリ起動 → <code>adb logcat</code>で確認:</li>
</ol>
<pre><code class="language-bash">adb logcat | grep -i -E &quot;Firebase|Installation|FCM|AppCheck|Gms&quot;
</code></pre>
<h3>期待されるログ</h3>
<ul>
<li><code>FirebaseInstallations</code>の<strong>FID生成成功</strong></li>
<li><code>Gms</code>の<strong>token retrieval succeeded</strong></li>
<li><code>FirebaseMessaging</code>の**onNewToken(...)**コールバック発生</li>
</ul>
<h1>よくある問題(トラブルシューティング)</h1>
<h2>1. Play署名値ではなくアップロード鍵を登録</h2>
<p>Firebase/Cloud両方で必ず<strong>アプリ署名証明書</strong>の値を使用してください。</p>
<h2>2. マルチフレーバーでリリースが異なるファイルを使用</h2>
<ul>
<li><code>app/src/&lt;flavor&gt;/google-services.json</code>の配置ミス</li>
<li><code>apply plugin: &quot;com.google.gms.google-services&quot;</code>がモジュールの最下部にない</li>
</ul>
<h2>3. API Keyが異なる鍵</h2>
<p><code>google-services.json</code>の<code>current_key</code>とCloud Consoleで編集対象のキーが同じか再確認してください。</p>
<h2>4. App Check(Play Integrity) Enforce ON</h2>
<p>統合前であれば一時的に<strong>Enforce OFF</strong> → 原因分離後に再度ONにします。</p>
<h2>5. R8/ProGuard/Resource Shrink影響</h2>
<p>一時的に<code>minifyEnabled false</code> / <code>shrinkResources false</code>でビルドして分離テストします。</p>
<h2>6. デバイス/環境問題</h2>
<ul>
<li>Google Play開発者サービスの更新が必要</li>
<li>デバイス時間の自動同期設定</li>
<li>プロキシ/セキュリティアプリによるブロック</li>
</ul>
<h1>最終チェックリスト</h1>
<ul>
<li><input disabled="" type="checkbox"> Play Console → <strong>アプリの完全性</strong> → <strong>アプリ署名証明書</strong>の<strong>SHA-1/256</strong>を取得</li>
<li><input disabled="" type="checkbox"> <strong>Firebase Console</strong> → Androidアプリ → <strong>SHA-256(必要に応じてSHA-1も)登録</strong></li>
<li><input disabled="" type="checkbox"> <strong>Cloud Console</strong> → API Key(Android apps制限設定時) → <strong>パッケージ名 + Play署名SHA-1登録</strong></li>
<li><input disabled="" type="checkbox"> ストアインストール版を<strong>完全削除 → 再インストール</strong>後、FID/FCMトークン発行確認</li>
<li><input disabled="" type="checkbox"> <code>values.xml</code>および実行時<strong>FirebaseOptions</strong>値の検証</li>
<li><input disabled="" type="checkbox"> App Check/ProGuard/ネットワーク等の追加要因点検</li>
</ul>
<h1>FAQ</h1>
<p><strong>Q1. Play Integrity APIの状態が「統合: 未開始」でもSHA登録は必要ですか?</strong></p>
<p>はい。状態に関係なく、ストアビルドは<strong>Play署名鍵</strong>で署名されます。Firebaseには<strong>SHA-256</strong>を必ず登録し、Cloud API KeyにAndroidアプリ制限を設定している場合は<strong>SHA-1</strong>も登録する必要があります。</p>
<p><strong>Q2. Cloud Console Android アプリ制限にSHA-256は登録できませんか?</strong></p>
<p>いいえ。UI上<strong>SHA-1のみ</strong>入力できます。代わりにFirebase側に<strong>SHA-256</strong>を登録してください。</p>
<p><strong>Q3. 登録後も同じエラーが発生する場合は?</strong></p>
<p>以下の順序で点検してください:</p>
<ol>
<li>アプリ完全削除/再インストール</li>
<li>リリースビルドの<code>values.xml</code>確認</li>
<li>App Check Enforce OFFで分離</li>
<li>API Keyマッチング再検証</li>
<li>R8/ProGuard影響確認</li>
</ol>
<h1>まとめ</h1>
<p>この問題は「<strong>リリースビルドはPlay署名鍵基準</strong>」という点を見落とすとよく発生します。</p>
<ul>
<li><strong>Firebaseには SHA-256</strong></li>
<li><strong>Cloud API Key(Android制限設定時)には SHA-1</strong></li>
</ul>
<p>この2つを正しい場所に登録すれば、ほとんどの場合すぐに解決します。</p>
<p>実務では登録後の<strong>再インストール</strong>と<strong>オプション検証ログ</strong>まで一緒にチェックすることで、再発を防ぐことができます。</p>
<h1>参考: ローカルkeystoreからのSHA抽出</h1>
<p>Play署名鍵はローカルにありませんが、<strong>アップロード鍵/デバッグ鍵</strong>確認用:</p>
<pre><code class="language-bash"># keystoreから証明書フィンガープリント表示 (SHA-1/256)
keytool -list -v -keystore &lt;your-keystore.jks&gt; -alias &lt;alias-name&gt;

# Androidデバッグ鍵 (macOS/Linux)
keytool -list -v -keystore ~/.android/debug.keystore \
  -alias androiddebugkey -storepass android -keypass android
</code></pre>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/semyeong/2025-10-15-fixFirebaseFcmInstallationsPlaySigningSha/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[必須！ 既存のAndroidアプリをedge-to-edge対応にするための速習ガイド]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-09-04-e2e_android-15/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-09-04-e2e_android-15/</guid>
            <pubDate>Tue, 14 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[AndroidアプリのAPI level 35以上対応が義務化された今、既存アプリを素早くedge-to-edgeに対応させるためのノウハウの紹介]]></description>
            <content:encoded><![CDATA[<p>KINTOテクノロジーズのAndroidエンジニア 山田 剛 です。
本記事では、Android API level 35以上への対応が必須化された今、既存のAndroidアプリを素早くedge-to-edge対応にするためのノウハウを紹介します。</p>
<h2>1. はじめに</h2>
<pre><code class="language-kotlin:build.gradle.kts">android {
    defaultConfig {
        targetSdk = 35

        // ...
    }

    // ...
}
</code></pre>
<p>2025年8月31日以降、Google Playストアで公開されるAndroidアプリは、API level 35以上での公開が必須となりました。
すなわち、<code>targetSdk</code> を35以上の値にしてビルドしたAndroidアプリでなければ、Google Playストアでの新規アプリの公開、および既存アプリのアップデートが受け付けられなくなりました。
しかし、<code>targetSdk</code> を35以上の値にしてビルドしたアプリを開くと、画面のステータスバーやシステムナビゲーションバーの領域、およびノッチで隠れた画面の領域が、すべてアプリの表示領域になります。</p>
<p>これに伴い、既存のアプリをedge-to-edge対応にする必要があります。すでに対応済みのアプリも多数でしょうが、開発スケジュールが確保できずまだ対応できていない、という開発者にとってはすでに猶予のない状態です。ほとんどの画面では対応済みなのだが、一部の画面でどうにも変な表示が直らない、という状況もあるでしょう。
この記事では、既存アプリを極力早くedge-to-edge対応にするためのノウハウを紹介します。
本記事のおおまかな内容は以下の通りです:</p>
<ul>
<li>View で構成された画面では、コールバックを活用しましょう。</li>
<li>Composable で構成された画面では、 WindowInsets に関係するさまざまな関数を活用しましょう。</li>
<li>View と Composable が混在するアプリでは、ViewとComposableのそれぞれの対策を組み合わせることによって生じる問題を調整するために用意された関数をうまく使いましょう。</li>
<li>リストの内部パディングなどをうまく使い、できるだけスクロール途中の表示でステータスバー、システムナビゲーションバーの領域を活用できるようにしましょう。</li>
</ul>
<h2>2. Viewで構成された画面のedge-to-edge対応</h2>
<p>![](/assets/blog/authors/tsuyoshi_yamada/2-01_2D_basic-right_KINTO-character.svg =125x)
<em>くもびぃ</em></p>
<p>以下は<a href="https://corp.kinto-jp.com/mascot/">画像</a>が縦に流れていく画面を View で構成した簡単なアプリです:</p>
<pre><code class="language-kotlin:MainActivity.kt">class MainActivity : AppCompatActivity() {
    private lateinit var binding: ActivityMainBinding
    private lateinit var imageAdapter: ImageAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        withStyledAttributes(TypedValue().data, intArrayOf(android.R.attr.colorPrimary)) {
            window.statusBarColor = getColor(0, 0)
        }
        setSupportActionBar(binding.toolbar)

        imageAdapter = ImageAdapter(layoutInflater, 2)
        binding.recyclerView.adapter = imageAdapter
        binding.fab.setOnClickListener { _ -&gt;
            imageAdapter.increment()
            binding.recyclerView.scrollToPosition(imageAdapter.length - 1)
        }
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
        R.id.action_settings -&gt; {
            val editText = layoutInflater.inflate(R.layout.item_edit_text, null) as EditText
            val dialog = AlertDialog.Builder(this)
                .setView(editText)
                .setTitle(R.string.image_count)
                .setNegativeButton(R.string.cancel) { dialog, _ -&gt;
                    dialog.dismiss()
                }
                .show()
            editText.setOnEditorActionListener { _, actionId, _ -&gt;
                if (actionId != EditorInfo.IME_ACTION_DONE) return@setOnEditorActionListener false
                editText.text.toString().toIntOrNull()?.let { imageAdapter.length = it }
                dialog.dismiss()
                return@setOnEditorActionListener true
            }
            true
        }
        else -&gt; super.onOptionsItemSelected(item)
    }
}

class ImageAdapter(private val inflater: LayoutInflater, initialLength: Int)
: RecyclerView.Adapter&lt;ImageAdapter.ViewHolder&gt;() {

    var length: Int = initialLength
        set(value) {
            val incremental = value - field
            if (incremental == 0) return
            field = value
            if (incremental &lt; 0) {
                notifyItemRangeRemoved(value, -incremental)
            } else {
                notifyItemRangeInserted(value - incremental, incremental)
            }
        }

    override fun getItemViewType(position: Int) = position % IMAGE_LIST.size

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ) = ViewHolder(ItemImageBinding.inflate(inflater, parent, false).apply {
        image.setImageResource(IMAGE_LIST[viewType])
    })

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val bias = (position * 0.3F).rem(2F).let { if (it &lt; 1F) it else 2F - it }
        holder.binding.spaceStart.let { spaceStart -&gt;
            (spaceStart.layoutParams as? LinearLayout.LayoutParams)?.let {
                it.weight = bias
                spaceStart.layoutParams = it
            }
        }
        holder.binding.spaceEnd.let { spaceEnd -&gt;
            (spaceEnd.layoutParams as? LinearLayout.LayoutParams)?.let {
                it.weight = 1F - bias
                spaceEnd.layoutParams = it
            }
        }
    }

    override fun getItemCount() = length

    fun increment() {
        length = length + 1
    }

    class ViewHolder(val binding: ItemImageBinding) : RecyclerView.ViewHolder(binding.root)

    companion object {
        private val IMAGE_LIST = listOf(
            // ...
        )
    }
}
</code></pre>
<p>レイアウトファイルは以下の通りです:</p>
<pre><code class="language-xml:res/layout/activity_main.xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;com.google.android.material.appbar.AppBarLayout
        android:id=&quot;@+id/layout_appbar&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:background=&quot;?colorPrimary&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;&gt;

        &lt;com.google.android.material.appbar.MaterialToolbar
            android:id=&quot;@+id/toolbar&quot;
            style=&quot;@style/Widget.MaterialComponents.Toolbar.Primary&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;?attr/actionBarSize&quot; /&gt;

    &lt;/com.google.android.material.appbar.AppBarLayout&gt;

    &lt;androidx.recyclerview.widget.RecyclerView
        android:id=&quot;@+id/recycler_view&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;0dp&quot;
        android:background=&quot;?colorBackgroundFloating&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toBottomOf=&quot;@id/layout_appbar&quot;
        app:layoutManager=&quot;androidx.recyclerview.widget.LinearLayoutManager&quot;
        android:orientation=&quot;vertical&quot; /&gt;

    &lt;com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id=&quot;@+id/fab&quot;
        android:layout_width=&quot;wrap_content&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:layout_gravity=&quot;bottom|end&quot;
        app:shapeAppearanceOverlay=&quot;@style/ShapeAppearance.App.Circle&quot;
        app:backgroundTint=&quot;?colorSecondary&quot;
        app:srcCompat=&quot;@android:drawable/ic_input_add&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot; /&gt;

&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;
</code></pre>
<p>これを <code>targetSdk</code> を34に設定してビルドした場合と35に設定してビルドした場合とで、 Android OS 10 以上で実行した画面を比較します:</p>
<table>
<thead>
<tr>
<th align="center"><code>targetSdk = 34</code></th>
<th align="center"><code>targetSdk = 35</code> （対策なし）</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_view_34.webp" alt="View: API level 34"></td>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_view_35.webp" alt="View: API level 35: no-countermeasure"></td>
</tr>
</tbody></table>
<p><code>targetSdk</code> が34なら何も問題なく表示されますが、35だとステータスバーがタイトルバーに吸収されて見づらくなり、タイトル自体もカットアウト（カメラなどのセンサーをディスプレイ上に収めた部分の切り欠き）の穴が空き、システムナビゲーションバーの領域もアプリの表示領域に含まれてしまいます。</p>
<p><code>targetSdk = 35</code> における<a href="https://developer.android.com/about/versions/15/behavior-changes-15#edge-to-edge">エッジ ツー エッジの適用</a>とは、アプリの表示領域を否応なしにステータスバーやシステムナビゲーションバー、カットアウトの領域にまで拡げてしまうということなのです。
さすがにこの状態のアプリをアプリストアなどにリリースするのはつらいものがあります。もう時間がありませんが、何とかしなければなりません。</p>
<h3>2.1. ViewCompat.setOnApplyWindowInsetsListener</h3>
<p><a href="https://developer.android.com/develop/ui/views/layout/edge-to-edge">ビューでコンテンツをエッジ ツー エッジで表示する</a>にて、Viewをedge-to-edgeに対応させるさまざまな方法が紹介されています。
そのうち、汎用性の高い手段として<a href="https://developer.android.com/reference/androidx/core/view/ViewCompat#setOnApplyWindowInsetsListener(android.view.View,androidx.core.view.OnApplyWindowInsetsListener)">ViewCompat.setOnApplyWindowInsetsListener(View, OnApplyWindowInsetsListener)</a> を使用する方法があります:</p>
<pre><code class="language-kotlin:MainActivity.kt">    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // window.statusBarColor = getColor(0, 0) // edge-to-edgeではstatusBarColorの設定は不要（無効）

        setSupportActionBar(binding.toolbar)

        imageAdapter = ImageAdapter(layoutInflater, 2)
        binding.recyclerView.adapter = imageAdapter
        binding.fab.setOnClickListener { _ -&gt;
            imageAdapter.increment()
            binding.recyclerView.scrollToPosition(imageAdapter.length - 1)
        }

        applyWindowInsetsForE2E()
    }

    private fun applyWindowInsetsForE2E() {
        ViewCompat.setOnApplyWindowInsetsListener(binding.layoutAppbar) { v, windowInsets -&gt;
            val insets = windowInsets.getInsets(
                WindowInsetsCompat.Type.systemBars()
                        or WindowInsetsCompat.Type.displayCutout()
            )
            v.updatePadding(left = insets.left, top = insets.top, right = insets.right)
            WindowInsetsCompat.CONSUMED
        }

        ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { v, windowInsets -&gt;
            val insets = windowInsets.getInsets(
                WindowInsetsCompat.Type.systemBars()
                        or WindowInsetsCompat.Type.displayCutout()
            )
            v.updatePadding(left = insets.left, right = insets.right, bottom = insets.bottom)
            WindowInsetsCompat.CONSUMED
        }

        ViewCompat.setOnApplyWindowInsetsListener(binding.fab) { v, windowInsets -&gt;
            val insets = windowInsets.getInsets(
                WindowInsetsCompat.Type.systemBars()
                        or WindowInsetsCompat.Type.displayCutout()
                        or WindowInsetsCompat.Type.ime()
            )
            v.updateLayoutParams&lt;ViewGroup.MarginLayoutParams&gt; {
                val margin = resources.getDimensionPixelOffset(R.dimen.fab_margin)
                bottomMargin = insets.bottom + margin
                rightMargin = insets.right + margin
            }
            WindowInsetsCompat.CONSUMED
        }
    }
</code></pre>
<p>listener の中で <a href="https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat#getInsets(int)">WindowInsets$getInsets(Int)</a> に <a href="https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat.Type">WindowInsetsCompat.Type</a> から適切な関数を組み合わせて引数に与え、インセット（上、左、右、下の空間）の値を得ます。
<code>MainActivity$applyWindowInsetsForE2E()</code> の中で、 AppBarLayout, RecyclerView, FloatingActionButton のそれぞれに対して <code>ViewCompat.setOnApplyWindowInsetsListener(...)</code> を呼び出して設定している点に注意が必要です。
AppBarLayout ではステータスバーとカットアウトの領域分だけ上部のパディングが必要ですが、RecyclerView では上部を気にする必要がないので <code>top</code> のパディングは設定していません。
FloatingActionButton では、end と bottom のマージンを加算して LayoutParams のマージンを変更しています。
他方、RecyclerView では、以下のように <code>android:clipToPadding=&quot;false&quot;</code> を追加して、 RecyclerView の表示領域にパディングをするのではなく、 RecyclerView のコンテンツ全体の左右と下部へのパディングとすることによって、下部のシステムナビゲーションバーの部分も表示領域として活用しつつ、スクロールの下端ではコンテンツとシステムナビゲーションが重複しなくて済むようにできます:</p>
<pre><code class="language-xml:res/layout/activity_main.xml">    &lt;androidx.recyclerview.widget.RecyclerView
        android:id=&quot;@+id/recycler_view&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;0dp&quot;
        android:background=&quot;?colorBackgroundFloating&quot;
        android:clipToPadding=&quot;false&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toBottomOf=&quot;@id/layout_appbar&quot;
        app:layoutManager=&quot;androidx.recyclerview.widget.LinearLayoutManager&quot;
        android:orientation=&quot;vertical&quot; /&gt;
</code></pre>
<p>edge-to-edge対応で求められているのは、画面の端から端までを表示に活用しつつ、アプリUIがステータスバーやシステムナビゲーションバーの邪魔にならないように、カットアウトがアプリUIの邪魔にならないように両立させることです。
対策の時間が限られている状況では、ひとまずインセットの部分を背景色のみで潰しておく、という対応で時を稼がざるを得ないこともあるでしょう。それでも、できる限りはedge-to-edgeの長所を活かすように対応したいものです。</p>
<table>
<thead>
<tr>
<th align="center"><code>targetSdk = 35</code>（対策あり、スクロール上端）</th>
<th align="center"><code>targetSdk = 35</code> （対策あり、スクロール下端）</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_view_35_scroll-top.webp" alt="View: API level 35 scroll top"></td>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_view_35_scroll-bottom.webp" alt="View: API level 35 scroll bottom"></td>
</tr>
</tbody></table>
<p>listener の末尾で <code>WindowInsetsCompat.CONSUMED</code> を返すことで、各Viewのパディングもしくはマージンとして領域を<strong>消費する</strong>ことをAndroidのシステムに伝えています。 つまり、AppBarLayout の上部のパディング部分は AppBarLayout に属しているので、パディング部分も含めてXMLレイアウトファイルの <code>android:background=&quot;?colorPrimary&quot;</code> という記述によって背景色が設定されます。
この性質によって、edge-to-edgeでは「ステータスバーの背景色」という概念がなくなっているので、API level 35以上では <code>window.statusBarColor</code> (<a href="https://developer.android.com/reference/android/view/Window#setStatusBarColor(int)">Window$setStatusBarColor(Int)</a>) は何も効果がない非推奨の関数に変わっています。</p>
<p>システムナビゲーションバーの高さは3ボタンナビゲーション（下の左画像）か、ジェスチャーナビゲーション（下の右画像）かで変化します。この変化にアプリUIが追随できるように実装する必要があります。</p>
<p><code>ViewCompat.setOnApplyWindowInsetsListener(...)</code> を活用する方法の優れた点は、記述が非常にわかりやすく、かつアプリやAndroid端末の事情が絡み合う複雑な状況に対応しやすいことです。
<code>MainActivity$applyWindowInsetsForE2E()</code> は、3つのViewに対してそれぞれに必要なパディングの値を <a href="https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat#getInsets(int)">WindowInsetsCompat$getInsets(Int)</a> で取得し、その値をパディングもしくはマージンとして設定しています。
そのうち FloatingActionButton のマージンの値の取得時に <code>WindowInsetsCompat.Type.ime()</code> のフラグの指定を含めています。 これで、下の右画像のように、ソフトウェアキーボードなどが表示されているときにその高さの分だけ FloatingActionButton が持ち上がるように実装できます（ボタンをこのように持ち上げる必要があることは多くはないでしょうが、例として挙げてみました）。
このようにアプリの状態変化に追随させるためには、画面の初期設定が終わった後もリスナーのunsetは行わずにに状態を監視させ続ける必要があります。</p>
<table>
<thead>
<tr>
<th align="center"><code>targetSdk = 35</code> （対策あり、音声入力UI表示時）</th>
<th align="center"><code>targetSdk = 35</code>（対策あり、ジェスチャーナビゲーション）</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_view_35_voice-input.webp" alt="View: API level 35 voice input"></td>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_view_35_gesture-nav.webp" alt="View: API level 35 gesture navigation"></td>
</tr>
</tbody></table>
<p>画面の回転を可能にしているアプリでは、 <code>ViewCompat.setOnApplyWindowInsetsListener(...)</code>はさらに強力です。
画面が回転するアプリだと、カットアウトは回転ごとに常に移動します。回転なしポートレイト（縦長）のときは上端でステータスバーと重なるので上端ではステータスバーとカットアウトのどちらか高い方のパディングを設定すればよいですが、それ以外の状態ではステータスバーとカットアウトの両方のパディングが必要になります。
システムナビゲーションバーについては、ボタンナビゲーションの場合はランドスケープ（横長）のとき左端または右端に移動、ジェスチャーナビゲーションの場合は常にシステムナビゲーションバーが下端。またタイトルバーは常に上端…と、多数の複雑な組み合わせが存在します。
このときも <code>ViewCompat.setOnApplyWindowInsetsListener(...)</code>は常に妥当なパディング・マージンの値を提供します。</p>
<table>
<thead>
<tr>
<th align="center"><code>targetSdk = 35</code>（対策あり、左90°回転ランドスケープ、ボタンナビゲーション）</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_view_35_left90.webp" alt="View: API level 35 gesture navigation"></td>
</tr>
</tbody></table>
<table>
<thead>
<tr>
<th align="center"><code>targetSdk = 35</code> （対策あり、右90°回転ランドスケープ、ジェスチャーナビゲーション）</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_view_35_right90.webp" alt="View: API level 35 right90"></td>
</tr>
</tbody></table>
<h3>2.2. ItemDecoration などを使う</h3>
<p>Viewの構成によっては、多くのViewに新たにコールバックを追加しにくいようなケースもあるかもしれません。そのような場合には、1箇所で取得したインセットの値を共有するような方法でもよいでしょう。アプリがポートレイトのみ対応で画面回転を考慮しなくてよい場合など、インセットの動的な変化があまり起こらないアプリでは、できるだけ静的に設定する処理にするのがエンバグの危険が少ないとも考えられます。Activityで取得したインセットの値をFragmentと共有するようにしてもよいでしょう。</p>
<pre><code class="language-kotlin:MainActivity.kt">    private var insetBottom = 0
    override fun onCreate(savedInstanceState: Bundle?) {

        // ...

        ViewCompat.setOnApplyWindowInsetsListener(binding.layoutAppbar) { v, windowInsets -&gt;
            // アプリがポートレイトのみ対応で画面回転を考慮しなくてよい場合
            val insets = windowInsets.getInsets(
                WindowInsetsCompat.Type.systemBars()
                        or WindowInsetsCompat.Type.displayCutout()
            )
            insetBottom = insets.bottom
            binding.recyclerView.addItemDecoration(createListBottomSpacingItemDecoration(insetBottom))
            binding.fab.updateLayoutParams&lt;ViewGroup.MarginLayoutParams&gt; {
                val margin = resources.getDimensionPixelOffset(R.dimen.fab_margin)
                bottomMargin = insets.bottom + margin
                rightMargin = insets.right + margin
            }
            v.updatePadding(left = insets.left, top = insets.top, right = insets.right)
            WindowInsetsCompat.CONSUMED
        }
    }
</code></pre>
<p>たとえば、 RecyclerView の場合は <a href="https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView#addItemDecoration(androidx.recyclerview.widget.RecyclerView.ItemDecoration)">RecyclerView$addItemDecoration(RecyclerView.ItemDecoration)</a> を使った以下のような関数で RecyclerView のスクロール下端にedge-to-edge対応のパディングを設定することができます。ItemDecorationは主に区切り線を設定するために使われますが、このように決まった位置に空白を設定するだけの目的にも使えます。 アプリの構成によっては、 <code>android:clipToPadding=&quot;false&quot;</code> を使ってパディングを設定するよりも少ない変更で済みそうです:</p>
<pre><code class="language-kotlin:ViewUtils.kt">fun createListBottomSpacingItemDecoration(insetBottom: Int) = object : RecyclerView.ItemDecoration() {
    override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
        outRect.set(0, 0, 0, if (parent.getChildAdapterPosition(view) &lt; state.itemCount - 1) 0 else insetBottom)
    }
}
</code></pre>
<h2>3. Composableで構成された画面のedge-to-edge対応</h2>
<p>Jetpack Compose 1.0 が公開されて4年経過した今、既存のプロジェクトではViewからComposableへの移行がかなり進んだ、もしくは最近立ち上がったプロジェクトでは最初から大半のUIをComposableで構成している、という開発プロジェクトも多いでしょう。
先ほどのViewベースのアプリと同様のComposeベースのアプリは、以下のような記述になるでしょう。なお本記事では、compose-bom 2024.06.00以降、Material3を用いるものとします:</p>
<pre><code class="language-kotlin:MainActivity.kt">class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            E2ESampleTheme {
                val showsDialog = remember { mutableStateOf(false) }
                val isFabClicked = remember { mutableStateOf(false) }

                Scaffold(
                    topBar = {
                        Row(
                            modifier = Modifier
                                .fillMaxWidth()
                                .heightIn(64.dp)
                                .background(MaterialTheme.colorScheme.primary),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            Text(
                                stringResource(R.string.app_name),
                                modifier = Modifier
                                    .padding(start = 16.dp)
                                    .weight(1F, true),
                                fontSize = 20.sp,
                                fontWeight = FontWeight.W600,
                                maxLines = 1,
                                overflow = TextOverflow.Ellipsis
                            )

                            TextButton(
                                modifier = Modifier.padding(end = 8.dp),
                                onClick = { showsDialog.value = true },
                                colors = ButtonDefaults.buttonColors(contentColor = MaterialTheme.colorScheme.onPrimary)
                            ) {
                                Text(
                                    stringResource(R.string.image_count),
                                    fontSize = 16.sp,
                                    fontWeight = FontWeight.W600
                                )
                            }
                        }
                    },
                    floatingActionButton = {
                        FloatingActionButton(
                            modifier = Modifier
                                .padding(dimensionResource(R.dimen.fab_margin)),
                            shape = CircleShape,
                            containerColor = MaterialTheme.colorScheme.secondary,
                            contentColor = MaterialTheme.colorScheme.onSecondary,
                            onClick = { isFabClicked.value = true }
                        ) {
                            Icon(Icons.Filled.Add, &quot;One more&quot;)
                        }
                    },
                    content = { innerPadding -&gt;
                        ImageColumn(Modifier.padding(innerPadding), showsDialog, isFabClicked)
                    }
                )
            }
        }

        withStyledAttributes(TypedValue().data, intArrayOf(android.R.attr.colorPrimary)) {
            window.statusBarColor = getColor(0, 0)
        }
    }
}
</code></pre>
<pre><code class="language-kotlin:ImageColumn.kt">@DrawableRes
private val IMAGE_LIST = listOf(
    // ...
)

@Composable
fun ImageColumn(modifier: Modifier = Modifier, showsDialog: MutableState&lt;Boolean&gt;, isFabClicked: MutableState&lt;Boolean&gt;) {
    var length by remember { mutableIntStateOf(2) }

    Box(modifier = modifier.fillMaxSize()) {
        val lazyListState = rememberLazyListState()
        LazyColumn(state = lazyListState, modifier = Modifier.fillMaxSize()) {
            items(length, key = { it }) { index -&gt;
                Box(Modifier.fillParentMaxWidth()) {
                    val bias = (index * 0.6F).rem(4F).let { if (it &lt; 2F) it - 1F else (4F - it) - 1F }
                    Image(
                        modifier = Modifier
                            .padding(8.dp)
                            .align(BiasAlignment(horizontalBias = bias, verticalBias = 0F))
                            .animateItemPlacement(), // If compose 1.8.0 or upper, use .animateItem()
                        painter = painterResource(IMAGE_LIST[index % 4]),
                        contentDescription = null
                    )
                }
            }
        }

        val coroutineScope = rememberCoroutineScope()
        LaunchedEffect(isFabClicked.value) {
            if (isFabClicked.value) {
                length += 1
                isFabClicked.value = false
                coroutineScope.launch {
                    lazyListState.animateScrollToItem(length - 1)
                }
            }
        }

        if (showsDialog.value) {
            var numberText by remember { mutableStateOf(&quot;&quot;) }
            AlertDialog(
                onDismissRequest = { showsDialog.value = false },
                title = { Text(stringResource(R.string.image_count)) },
                confirmButton = {},
                dismissButton = {
                    TextButton(onClick = { showsDialog.value = false }) {
                        Text(stringResource(R.string.cancel))
                    }
                },
                text = {
                    TextField(
                        numberText,
                        { text -&gt; numberText = text.filter { it.isDigit() } },
                        keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
                        keyboardActions = KeyboardActions {
                            length = numberText.toIntOrNull() ?: 0
                            showsDialog.value = false
                        },
                        singleLine = true
                    )
                }
            )
        }
    }
}
</code></pre>
<p>Viewベースの場合と同様、 <code>targetSdk</code> を34に設定してビルドした場合と35に設定してビルドした場合で比較します。
やはりタイトルバーでは問題が起きていますが、Viewのときとは異なり FloatingActionButton はシステムナビゲーションバーとは重なっていません。これは <a href="https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#Scaffold(androidx.compose.ui.Modifier,kotlin.Function0,kotlin.Function0,kotlin.Function0,kotlin.Function0,androidx.compose.material3.FabPosition,androidx.compose.ui.graphics.Color,androidx.compose.ui.graphics.Color,androidx.compose.foundation.layout.WindowInsets,kotlin.Function1)">Scaffold(...)</a> の <code>content</code> パラメータに設定している関数オブジェクトの引数 <code>innerPadding</code> を参照してパディングを設定しているためです。
また、 <code>Scaffold(...)</code> の <code>topBar</code> パラメータに設定している関数オブジェクトの中で <a href="https://developer.android.com/reference/kotlin/androidx/compose/material3/package-summary#TopAppBar(kotlin.Function0,androidx.compose.ui.Modifier,kotlin.Function0,kotlin.Function1,androidx.compose.ui.unit.Dp,androidx.compose.foundation.layout.WindowInsets,androidx.compose.material3.TopAppBarColors,androidx.compose.material3.TopAppBarScrollBehavior)">TopAppBar(...)</a> を使えば、ステータスバーの領域にパディングが自動的に設定されます。
Viewベースでの開発と比べてJetpack Composeの各関数では、このようにedge-to-edgeへの対応が考慮されている部分が多く、<em>宣言的UI</em>の概念によってより直感的な記述によるedge-to-edge対応が可能になっています。
もっとも、現時点では <code>TopAppBar(...)</code> を使うには <code>@ExperimentalMaterial3ExpressiveApi</code> が必要です。ここでは、experimental API がプロジェクトの制約等で使えず、またシステムナビゲーションバーの部分に縦スクロールのコンテンツを表示させる必要がある、という場合の実装を考えてみましょう。</p>
<table>
<thead>
<tr>
<th align="center"><code>targetSdk = 34</code></th>
<th align="center"><code>targetSdk = 35</code> （対策なし）</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_composable_34.webp" alt="Composable: API level 34"></td>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_composable_35.webp" alt="Composable: API level 35: no-countermeasure"></td>
</tr>
</tbody></table>
<h3>3.1. WindowInsets のプロパティ</h3>
<p>(androidx.compose.foundation.layout.)<a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/WindowInsets">WindowInsets</a>のプロパティおよび関数を使って、ステータスバーやシステムナビゲーションバー、カットアウトの領域のインセットを取得できます。
<a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/WindowInsets#(androidx.compose.foundation.layout.WindowInsets).asPaddingValues()">WindowInsets.asPaddingValues()</a> を使ってインセットをパディングの値に変換し、 Modifier などに適用することで、Composableとエッジとの間に適切な距離をとらせることができます:</p>
<pre><code class="language-kotlin:MainActivity.kt">    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            E2ESampleTheme {
                val showsDialog = remember { mutableStateOf(false) }
                val isFabClicked = remember { mutableStateOf(false) }

                val safeDrawingInsets = WindowInsets.safeDrawing.asPaddingValues()
                val direction = LocalLayoutDirection.current

                Scaffold(
                    topBar = {
                        Row(
                            modifier = Modifier
                                .background(MaterialTheme.colorScheme.primary)
                                .padding(
                                    start = safeDrawingInsets.calculateStartPadding(direction),
                                    top = safeDrawingInsets.calculateTopPadding(),
                                    end = safeDrawingInsets.calculateEndPadding(direction)
                                )
                                .fillMaxWidth()
                                .heightIn(64.dp),
                            verticalAlignment = Alignment.CenterVertically
                        ) {
                            // この部分は不変 ...
                        }
                    },
                    floatingActionButton = {
                        FloatingActionButton(
                            modifier = Modifier.padding(end = safeDrawingInsets.calculateEndPadding(direction)),
                            shape = CircleShape,
                            containerColor = MaterialTheme.colorScheme.secondary,
                            contentColor = MaterialTheme.colorScheme.onSecondary,
                            onClick = { isFabClicked.value = true }
                        ) {
                            Icon(Icons.Filled.Add, &quot;One more&quot;)
                        }
                    },
                    content = { innerPadding -&gt;
                        ImageColumn(
                            Modifier.padding(top = innerPadding.calculateTopPadding()),
                            showsDialog,
                            isFabClicked
                        )
                    }
                )
            }
        }

        // window.statusBarColor = getColor(0, 0) // edge-to-edgeではstatusBarColorの設定は不要（無効）
    }
</code></pre>
<pre><code class="language-kotlin:ImageColumn.kt">@Composable
fun ImageColumn(modifier: Modifier = Modifier, showsDialog: MutableState&lt;Boolean&gt;, isFabClicked: MutableState&lt;Boolean&gt;) {
    var length by remember { mutableIntStateOf(2) }

    val direction = LocalLayoutDirection.current
    val navigationBars = WindowInsets.navigationBars.asPaddingValues()
    val verticalBars = WindowInsets.displayCutout.union(navigationBars).asPaddingValues()
    Box(modifier = modifier
        .padding(
            start = verticalBars.calculateStartPadding(direction),
            end = verticalBars.calculateEndPadding(direction)
        )
        .fillMaxSize()
    ) {
        val lazyListState = rememberLazyListState()
        val bottomPadding = navigationBars.calculateBottomPadding()
        LazyColumn(
            state = lazyListState,
            modifier = Modifier.fillMaxSize(),
            contentPadding = PaddingValues(bottom = bottomPadding)
        ) {
            items(length, key = { it }) { index -&gt;
                Box(Modifier.fillParentMaxWidth()) {
                    val bias = (index * 0.6F).rem(4F).let { if (it &lt; 2F) it - 1F else (4F - it) - 1F }
                    Image(
                        modifier = Modifier
                            .padding(8.dp)
                            .align(BiasAlignment(horizontalBias = bias, verticalBias = 0F))
                            .animateItemPlacement(), // If compose 1.8.0 or upper, use .animateItem()
                        painter = painterResource(IMAGE_LIST[index % 4]),
                        contentDescription = null
                    )
                }
            }
        }

        // ...
    }
}
</code></pre>
<p>各Composableの Modifier に対して <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#(androidx.compose.ui.Modifier).windowInsetsPadding(androidx.compose.foundation.layout.WindowInsets)">Modifier.windowInsetsPadding(WindowInsets)</a> を使えればもう少し簡潔に書けますが、画面の回転に応じて上、左右、下のそれぞれで異なる処理を行わせるため、細かく設定を計算しています。
要領は<a href="https://developer.android.com/reference/androidx/core/view/WindowInsetsCompat">WindowInsetsCompat</a>の使い方とよく似ています。 <code>WindowInsets</code> の方が少し簡略化されて直感的にわかりやすくなっています。
<a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/WindowInsets.Companion#(androidx.compose.foundation.layout.WindowInsets.Companion).statusBars()">WindowInsets.Companion.statusBars</a> でステータスバー、 <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/WindowInsets.Companion#(androidx.compose.foundation.layout.WindowInsets.Companion).navigationBars()">WindowInsets.Companion.navigationBars</a> でシステムナビゲーションバーのインセットを表します。
また、複数のインセットを組み合わせたインセットを考えることもできます。<a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#(androidx.compose.foundation.layout.WindowInsets.Companion).systemBars()">WindowInsets.Companion.systemBars</a>は <code>WindowInsets.statusBars.union(WindowInsets.captionBar).union(WindowInsets.navigationBars)</code>と同じ、 <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#(androidx.compose.foundation.layout.WindowInsets.Companion).safeDrawing()">WindowInsets.Companion.safeDrawing</a> は <code>WindowInsets.systemBars.union(WindowInsets.ime).union(WindowInsets.displayCutout)</code> と同じです。
ここで <a href="https://developer.android.com/reference/kotlin/androidx/compose/foundation/layout/package-summary#(androidx.compose.foundation.layout.WindowInsets).union(androidx.compose.foundation.layout.WindowInsets)">WindowInsets.union(WindowInsets)</a> は上、左、右、下のそれぞれで各インセットの最大値をとる関数です。たとえば回転なしポートレイトのときは上端にはステータスバーとカットアウトがあり、両者のうち高い方の高さを上端のインセットとする、という演算を行います（高さの最大値の距離をとることでステータスバーとカットアウトの両方を避けられるため）。
インセットの値を得て、 <code>Scaffold(...)</code> の <code>topBar</code> と <code>floatingActionButton</code> 、 <code>LazyColumn(...)</code> のそれぞれに適切なパディングの値を与えます。Viewベースの場合にはコールバックという手続き的なコードでパディングやマージンを設定しましたが、<em>宣言的UI</em>概念のComposeではパラメータのように与えることができ、より直感的にわかりやすくなっているように思います。
<code>floatingActionButton</code> に対しては、ランドスケープ時の対策としてインセットの end の値のみにパディングを設定しています。
<code>LazyColumn(...)</code> では、Viewベースの RecyclerView にて <code>android:clipToPadding=&quot;false&quot;</code> を設定したのと同様にスクロールするコンテンツの全体に対して下端のパディングを与えるため、 <code>Modifier.padding(Dp)</code> ではなく <code>contentPadding</code> パラメータに <code>safeDrawingInsets.calculateBottomPadding()</code> を与えています。</p>
<table>
<thead>
<tr>
<th align="center"><code>targetSdk = 35</code> （対策あり、ボタンナビゲーション）</th>
<th align="center"><code>targetSdk = 35</code> （対策あり、ジェスチャーナビゲーション）</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_composable_35_button-nav.webp" alt="Composable: API level 34"></td>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_composable_35_gesture-nav.webp" alt="Composable: API level 35: no-countermeasure"></td>
</tr>
</tbody></table>
<h3>3.2. レイアウトの内側にパディングを設定する</h3>
<p><a href="#2.2.-itemdecoration-%E3%81%AA%E3%81%A9%E3%82%92%E4%BD%BF%E3%81%86">2.2.</a>のItemDecorationのように、LazyLayoutの内側に空白を設定することでedge-to-edgeにすることもできます:</p>
<pre><code class="language-kotlin:ImageColumn.kt">fun ImageColumn(modifier: Modifier = Modifier, showsDialog: MutableState&lt;Boolean&gt;, isFabClicked: MutableState&lt;Boolean&gt;) {
    var length by remember { mutableIntStateOf(2) }

    val direction = LocalLayoutDirection.current
    val navigationBars = WindowInsets.navigationBars.asPaddingValues()
    val verticalBars = WindowInsets.displayCutout.union(WindowInsets.navigationBars).asPaddingValues()
    Box(
        // ...
    ) {
        val lazyListState = rememberLazyListState()
        val bottomPadding = navigationBars.calculateBottomPadding()
        LazyColumn(
            state = lazyListState,
            modifier = Modifier.fillMaxSize()
        ) {
            items(length, key = { it }) { index -&gt;

                // ...

            }

            item {
                Spacer(modifier = Modifier.height(bottomPadding)) // &lt;-
            }
        }

        // ...

    }

    // ...

}
</code></pre>
<p><code>LazyColumn(...)</code> の末尾に <code>item { Spacer(...) }</code> を追加するだけです。仮に、タイトルバーがなくステータスバーとカットアウトの分のパディングが必要であれば、<code>LazyColumn(...)</code> の先頭に同様の <code>item</code> を追加すればよいでしょう。<code>contentPadding</code> よりも<em>宣言的</em>でわかりやすいと言えるかもしれません。
このように、レイアウトの内側にパディングを設定することも有力な手段です。Jetpack Compose には、このような柔軟な対応がしやすいという利点があります。</p>
<h2>4. ViewとComposableが混在する画面のedge-to-edge対応</h2>
<p>Jetpack Compose の公開後、ViewベースのアプリのComposable化を鋭意進めているが、残っているViewのすべてはすぐにはComposable化できない、というプロジェクトも多いでしょう。実際、WebViewなど、pure composable なソリューションがまだ存在しないUI部品も残っており<a href="Composable%E3%81%A8%E3%81%97%E3%81%A6%E4%BD%BF%E3%81%88%E3%82%8B%5BWebView%E3%81%AE%E3%83%A9%E3%83%83%E3%83%91%E3%83%BC%5D(https://google.github.io/accompanist/webview/)%E3%81%8C%5BAccompanist%5D(https://google.github.io/accompanist/)%E3%81%AB%E5%90%AB%E3%81%BE%E3%82%8C%E3%81%A6%E3%81%84%E3%81%BE%E3%81%99%E3%81%8C%E3%80%812025%E5%B9%B48%E6%9C%88%E6%99%82%E7%82%B9%E3%81%A7deprecated%E3%81%A8%E3%81%AA%E3%81%A3%E3%81%A6%E3%81%8A%E3%82%8A%E3%80%81%E4%BB%8A%E3%81%AE%E3%81%A8%E3%81%93%E3%82%8DWebView%E3%82%92%E7%BD%AE%E3%81%8D%E6%8F%9B%E3%81%88%E3%82%8BComposable%E3%81%AE%E6%B1%BA%E5%AE%9A%E7%89%88%E3%81%AF%E5%AD%98%E5%9C%A8%E3%81%97%E3%81%AA%E3%81%84%E3%82%88%E3%81%86%E3%81%A7%E3%81%99%E3%80%82">^1</a>、Viewとの完全なお別れはまだ非現実的、というプロジェクトが多いことと推察いたします。
そんなアプリでは <a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/platform/ComposeView">ComposeView</a> などを使ってViewとComposableを混在させて画面を構成していると思われますが、これに対して上記で紹介したようなテクニックを使う際、状況が複雑化し、思わぬ問題が起きることがあります。</p>
<pre><code class="language-kotlin:MainActivity.kt">class MainActivity : AppCompatActivity() {

    private lateinit var imageAdapter: ImageAdapter

    val showsDialog = mutableStateOf(false)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById&lt;ComposeView&gt;(R.id.compose_view).apply {
            setContent {
                setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
                E2ESampleTheme {
                    ImageColumnScaffold(showsDialog)
                }
            }
        }

        withStyledAttributes(TypedValue().data, intArrayOf(android.R.attr.colorPrimary)) {
            window.statusBarColor = getColor(0, 0)
        }
        setSupportActionBar(findViewById(R.id.toolbar))
    }

    override fun onCreateOptionsMenu(menu: Menu): Boolean {
        menuInflater.inflate(R.menu.menu_main, menu)
        return true
    }

    override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
        R.id.action_settings -&gt; {
            showsDialog.value = true
            true
        }
        else -&gt; super.onOptionsItemSelected(item)
    }
}

@Composable
fun ImageColumnScaffold(showsDialog: MutableState&lt;Boolean&gt;) {
    val isFabClicked = remember { mutableStateOf(false) }
    Scaffold(
        floatingActionButton = {
            FloatingActionButton(
                shape = CircleShape,
                containerColor = MaterialTheme.colorScheme.secondary,
                contentColor = MaterialTheme.colorScheme.onSecondary,
                onClick = { isFabClicked.value = true }
            ) {
                Icon(Icons.Filled.Add, &quot;One more&quot;)
            }
        },
        content = { innerPadding -&gt;
            ImageColumn(Modifier.padding(innerPadding), showsDialog, isFabClicked)
        }
    )
}
</code></pre>
<p>レイアウトXMLファイルには <code>ComposeView</code> が含まれます:</p>
<pre><code class="language-xml:res/layout/activity_main.xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:clipToPadding=&quot;false&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;com.google.android.material.appbar.AppBarLayout
        android:id=&quot;@+id/layout_appbar&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;wrap_content&quot;
        android:background=&quot;?colorPrimary&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toTopOf=&quot;parent&quot;&gt;

        &lt;com.google.android.material.appbar.MaterialToolbar
            android:id=&quot;@+id/toolbar&quot;
            style=&quot;@style/Widget.MaterialComponents.Toolbar.Primary&quot;
            android:layout_width=&quot;match_parent&quot;
            android:layout_height=&quot;?attr/actionBarSize&quot; /&gt;

    &lt;/com.google.android.material.appbar.AppBarLayout&gt;

    &lt;androidx.compose.ui.platform.ComposeView
        android:id=&quot;@+id/compose_view&quot;
        android:layout_width=&quot;0dp&quot;
        android:layout_height=&quot;0dp&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintEnd_toEndOf=&quot;parent&quot;
        app:layout_constraintStart_toStartOf=&quot;parent&quot;
        app:layout_constraintTop_toBottomOf=&quot;@id/layout_appbar&quot; /&gt;

&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;
</code></pre>
<p>ImageColumn.kt は<a href="#3.-composable%E3%81%A7%E6%A7%8B%E6%88%90%E3%81%95%E3%82%8C%E3%81%9F%E7%94%BB%E9%9D%A2%E3%81%AEedge-to-edge%E5%AF%BE%E5%BF%9C">3.</a>と同じものを使います。</p>
<p>スクロール部分やフローティングアクションボタンはComposeで実装しているが、タイトルバーとメニューの処理は <code>AppCompatActivity$setSupportActionBar(Toolbar)</code> を使っている、という例です。この例は該当しませんが、たとえばFragmentで多くの画面を作っているアプリでは、このような構成になることが多そうですね。
このアプリで <code>targetSdk</code> を34→35としてビルドすると:</p>
<table>
<thead>
<tr>
<th align="center"><code>targetSdk = 34</code></th>
<th align="center"><code>targetSdk = 35</code> （対策なし）</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_v-and-c_34.webp" alt="Composable: API level 34"></td>
<td align="center"><img src="/assets/blog/authors/tsuyoshi_yamada/kumobis_v-and-c_35.webp" alt="Composable: API level 35: no-countermeasure"></td>
</tr>
</tbody></table>
<p>ステータスバー・カットアウトとタイトルが重なるのは承知の上ですが、それに加えてタイトルバーとスクロール部分の間に奇妙な隙間が生じています。
これは <code>Scaffold(...)</code> の <code>content</code> に渡している <code>innerPadding</code> がステータスバーとシステムナビゲーションバーの領域のパディングの値を持っているために起こっています。
この画面では、ステータスバー・カットアウトの領域を確保するのは <code>AppBarLayout</code> の責務、すなわちViewの責務であり、Composableは余計なことをしてはいけません。この画面に限って対策するなら <code>Scaffold(...)</code> の <code>content</code> に渡すパディングを 0dp にすればよいのですが、 <code>Scaffold(...)</code> を使うComposableが大きな関数で、ある画面では純粋なComposableからなる画面から呼び出され、ある画面では ComposeView を含むViewベースの画面から呼び出され…というように多くの画面で共通に使われていて、、多岐にわたる処理を引き受けていたりすると、修正が大変です。
こういった場合に対処するため、Jetpack Composeには便利な関数が用意されています。</p>
<h3>4.1. Modifier.consumeWindowInsets(WindowInsets)</h3>
<p>以下のコードで解決します:</p>
<pre><code class="language-kotlin:MainActivity.kt">class MainActivity : AppCompatActivity() {

    val showsDialog = mutableStateOf(false)

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById&lt;ComposeView&gt;(R.id.compose_view).apply {
            setContent {
                setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
                E2ESampleTheme {
                    ImageColumnScaffold(
                        Modifier.consumeWindowInsets(WindowInsets.systemBars), // &lt;-
                        showsDialog
                    )
                }
            }
        }

        // window.statusBarColor = getColor(0, 0) // edge-to-edgeではstatusBarColorの設定は不要（無効）
        setSupportActionBar(findViewById(R.id.toolbar))

        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.layout_appbar)) { v, windowInsets -&gt;
            val insets = windowInsets.getInsets(
                WindowInsetsCompat.Type.systemBars()
                        or WindowInsetsCompat.Type.displayCutout()
            )

            v.updatePadding(
                left = insets.left,
                top = insets.top,
                right = insets.right,
            )
            WindowInsetsCompat.CONSUMED
        }
    }

    // ...

}

@Composable
fun ImageColumnScaffold(modifier: Modifier = Modifier, showsDialog: MutableState&lt;Boolean&gt;) {
    val isFabClicked = remember { mutableStateOf(false) }
    Scaffold(
        modifier,
        floatingActionButton = {
            FloatingActionButton(
                modifier = Modifier.padding(WindowInsets.safeDrawing.asPaddingValues()),
                shape = CircleShape,
                containerColor = MaterialTheme.colorScheme.secondary,
                contentColor = MaterialTheme.colorScheme.onSecondary,
                onClick = { isFabClicked.value = true }
            ) {
                Icon(Icons.Filled.Add, &quot;One more&quot;)
            }
        },
        content = { innerPadding -&gt;
            ImageColumn(Modifier.padding(innerPadding), showsDialog, isFabClicked)
        }
    )
}
</code></pre>
<p>ステータスバーの問題は、Viewベースの方法<a href="#2.1.-viewcompat.setonapplywindowinsetslistener">ViewCompat.setOnApplyWindowInsetsListener</a>で解決します。
隙間の問題は、 <code>Scaffold(...)</code> の <code>modifier</code> 引数に <a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/modifier/package-summary#(androidx.compose.ui.Modifier).consumeWindowInsets(androidx.compose.foundation.layout.WindowInsets)">Modifier.consumeWindowInsets(WindowInsets)</a> を追加したものを渡すことで解決します。これは、Composableが <code>systemBars</code> の領域、すなわちステータスバーとシステムナビゲーションバーの領域を<strong>消費</strong>することを宣言するものです。
これで隙間だった部分にも <code>Scaffold(...)</code> のコンテンツが表示されて正常な表示に戻りますが、そのかわりに <code>FloatingActionButton(...)</code> のようなUIも <code>systemBars</code> の領域に入り込むようになるため、 <code>FloatingActionButton(...)</code> の <code>modifier</code> に <code>Modifier.padding(WindowInsets.safeDrawing.asPaddingValues())</code> を追加して、システムナビゲーションバーやカットアウトの領域を避けるように明示する必要が生じます。
<code>ImageColumn(...)</code> には <a href="#3.1.-windowinsets-%E3%81%AE%E3%83%97%E3%83%AD%E3%83%91%E3%83%86%E3%82%A3">3.1.</a>と同じものが使えます。このときと同じく、画面の左右の障害物を避けつつ、下端には <code>LazyColumn(...)</code> の <code>contentPadding</code> を設定し、スクロールコンテンツの表示をシステムナビゲーションバーなどの領域にも許す一方、スクロールの下端がシステムナビゲーションバーなどの領域に重ならないようにしています。</p>
<h2>5. まとめ</h2>
<p>Android 15 対応が必須となった今、次のアプリリリース・アプリアップデートの日までに大急ぎでedge-to-edgeに対応しなければならない、という開発者の皆さんにも助けになることを願って、Viewベース、Composableベース、そしてViewとComposableが混在する場合のedge-to-edge対応の方法を紹介しました。非常に単純な画面例をサンプルにしていますが、それでも状況によってはかなり複雑な思考を要する場合があります。開発プロジェクトによっては、ほとんどの問題は解決しているが一部の画面がまだ解決していない、ということもあるでしょう。
edge-to-edge対応にはさまざまなツールが提供されています。ここで紹介したテクニックのうち試していないものがあれば、手を替え品を替えて試してみてください。また筆者自身、上記のような簡単なサンプルでもさまざまな対策を組み合わせて試すことで多くの学びがありました。何らかの形で本記事が皆さんの助けになれば幸いです。</p>
<h2>6. 参考文献</h2>
<ul>
<li><a href="https://developer.android.com/reference">Android API reference</a></li>
<li><a href="https://developer.android.com/about/versions/15/behavior-changes-15">Behavior changes: Apps targeting Android 15 or higher</a></li>
<li><a href="https://developer.android.com/develop/ui/views/layout/edge-to-edge">Display content edge-to-edge in views</a></li>
<li><a href="https://medium.com/androiddevelopers/insets-handling-tips-for-android-15s-edge-to-edge-enforcement-872774e8839b">Insets handling tips for Android 15’s edge-to-edge enforcement</a></li>
<li><a href="https://kaleidot.net/2025/04/android-modifier-consumewindowinsets%E3%81%A8modifier-windowinsetspadding%E3%81%AE%E5%8B%95%E4%BD%9C%E3%81%BE%E3%81%A8%E3%82%81/">[Android] Modifier.consumeWindowInsetsとModifier.windowInsetsPaddingの動作まとめ｜kaleidot.net</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/tsuyoshi_yamada/cover_android-e2e.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[OpenAI vs Google Image Editing Showdown – Testing the "Consistency" of gpt-image-1 and Gemini 2.5 Flash Image]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-09-openai-vs-google-image-edit-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-09-openai-vs-google-image-edit-en/</guid>
            <pubDate>Thu, 09 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Testing the "Consistency" of gpt-image-1 and Gemini 2.5 Flash Image]]></description>
            <content:encoded><![CDATA[<h1>OpenAI vs Google Image Editing Showdown</h1>
<h2>Testing the Consistency of gpt-image-1 and Gemini 2.5 Flash Image</h2>
<p>In recent years, OpenAI&#39;s ChatGPT, Google&#39;s Gemini, and Anthropic&#39;s Claude have emerged as major players in generative AI. Among these, OpenAI and Google offer AI models with image generation and editing capabilities.</p>
<p>This article focuses on OpenAI&#39;s gpt-image-1 (as of July 2025) and Google&#39;s Gemini 2.5 Flash Image (nicknamed Nano Banana, Web UI as of August 2025) to compare them through actual output examples with an emphasis on image consistency and Japanese text handling.</p>
<h2>1. What Are gpt-image-1 and Flash Image?</h2>
<ul>
<li><p><strong>gpt-image-1 (OpenAI)</strong><br>The underlying model for ChatGPT&#39;s 4o ImageGeneration. It has powerful generation and editing capabilities and supports inpainting (mask-based editing).<br>Note that full functionality requires API access.</p>
</li>
<li><p><strong>Gemini 2.5 Flash Image (Google)</strong><br>The model has a fast, lightweight image generation feature that supports generation using reference images. One of the notable characteristics of the model (nicknamed Nano Banana) is its accessibility with a free user account.</p>
</li>
</ul>
<h2>2. A Weakness of Image Generation AI: Consistency</h2>
<p>When repeatedly generating and editing images with AI tools, you may often face a problem where the image appearance gradually drifts from the original one.</p>
<p>This so-called lack of consistency results in changes in faces, body shapes, clothing textures, and background structures in an image as the number of generation iterations increases.</p>
<ul>
<li>Flash Image (August 2025) is relatively stable in this regard and became a topic of discussion on social media immediately after its release.</li>
<li>gpt-image-1 also improved editing consistency through the input_fidelity parameter introduced in July 2025.</li>
</ul>
<h2>3. Output Comparison 1: Editing Human Poses</h2>
<p>Task: Naturally seat the people from a family photo on the right side in a vehicle&#39;s back seat in an image on the left side.</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image_edit/01.jpg" alt="シート画像"></th>
<th><img src="/assets/blog/authors/aoshima/image_edit/02.jpg" alt="家族写真"></th>
</tr>
</thead>
</table>
<h3>3-1. gpt-image-1</h3>
<p>Since gpt-image-1 cannot directly reference multiple images (placing people from image B into image A), preprocessing with a rough composite was performed beforehand.<br>The family photo was roughly cut out and overlaid on the seat image. Of course, <code>input_fidelity = high</code> was set.</p>
<p>![雑な合成画像](/assets/blog/authors/aoshima/image_edit/03.jpg =50%x) </p>
<h4>Prompt I Used</h4>
<pre><code>Make this family photo look natural and realistic:
- Fix lighting to match car interior lighting
- Add natural shadows under people
- Adjust color temperature to match
- Make people look naturally seated
- Blend edges smoothly
- Keep faces unchanged but make them fit the scene
- Add subtle reflections on windows if visible
</code></pre>
<p><strong>Output Example</strong><br>![gpt-image-1の出力画像01](/assets/blog/authors/aoshima/image_edit/04.png =50%x) </p>
<p>Impressions: While there are differences in clothing details and vehicle interiors, the sitting posture and shadow placement looked sufficiently natural only in a single generation. However, the color tone appears to have become yellowish. Facial consistency does not seem to be well maintained.</p>
<h3>3-2. Gemini 2.5 Flash Image</h3>
<p>Using the <strong>reference image feature</strong>, the seat image and family photo were specified.<br>I entered the following prompt for the same purpose of image generation using gpt-image-1.</p>
<h4>Prompt Used</h4>
<pre><code>In the image of the car&#39;s back seat, place the three people from the provided family photo, making it look natural as if they are sitting together.
- Fix lighting to match car interior lighting
- Add natural shadows under people
- Adjust color temperature to match
- Make people look naturally seated
- Blend edges smoothly
- Keep faces unchanged but make them fit the scene
- Add subtle reflections on windows if visible
- Make clothing wrinkles look natural for sitting position
</code></pre>
<p><strong>Output Examples (Selection)</strong></p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image_edit/05.png" alt="Flashimage出力画像01"></th>
<th><img src="/assets/blog/authors/aoshima/image_edit/06.png" alt="Flashimage出力画像02"></th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/aoshima/image_edit/07.png" alt="Flashimage出力画像03"></td>
<td><img src="/assets/blog/authors/aoshima/image_edit/08.png" alt="Flashimage出力画像04"></td>
</tr>
</tbody></table>
<p>Impressions: While facial consistency is high, there appears to be some variation in balance.\</p>
<h3>3-3. Summary of Human Editing</h3>
<ul>
<li><strong>gpt-image-1</strong>: Requires preprocessing effort but enables high-precision compositing.</li>
<li><strong>Flash Image</strong>: Easy to achieve high quality with the reference image feature. Sitting posture and people&#39;s size become sufficiently adjusted with a few generation retries.</li>
<li><strong>Common</strong>: Background consistency for both interiors and lighting are good.</li>
</ul>
<h2>4. Output Comparison 2: Editing with Text (Japanese)</h2>
<p><strong>Concern</strong>: In addition to consistency, generative AI often struggle with <strong>precise reproduction of Japanese text</strong>.<br>Therefore, we compared AI models by assigning them to the task of replacing the magazine cover held by a person with a Japanese title and feature text.</p>
<p>![雑誌を抱える画像](/assets/blog/authors/aoshima/image_edit/09.jpg =50%x) </p>
<h4>Prompt I Used (in Japanese)</h4>
<pre><code>手に抱えている雑誌を以下の内容に置き換えてください。
- 日本の雑誌で、「旅立ち」というタイトル
- おしゃれでモダンな方向性
- 寺院の特集で表紙はお寺の写真をフィーチャー
- 表紙にはコンテンツ紹介の文言をレイアウト
</code></pre>
<p>*Note: The prompt was <strong>written in Japanese</strong> from the beginning to the end because we wanted the magazine title to be converted into Japanese.</p>
<h3>4-1. gpt-image-1 (Using the Inpainting Feature)</h3>
<p><strong>Output Examples</strong>\</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image_edit/10.png" alt="gpt-image-1出力画像02-01"></th>
<th><img src="/assets/blog/authors/aoshima/image_edit/11.png" alt="gpt-image-1出力画像02-02"></th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/aoshima/image_edit/12.png" alt="gpt-image-1出力画像02-03"></td>
<td><img src="/assets/blog/authors/aoshima/image_edit/13.png" alt="gpt-image-1出力画像02-04"></td>
</tr>
</tbody></table>
<p>Impressions: The magazine title Tabidachi was accurately generated in Japanese. However, smaller feature text is prone to be distorted.</p>
<h3>4-2. Flash Image (Only Unsing the Above Prompt)</h3>
<p>Since inpainting is not supported, I executed image generation, <strong>specifying the entire image via prompt</strong>. The prompt content was same as the above-mentioned one.</p>
<p><strong>Output Examples</strong></p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image_edit/14.png" alt="FlashImage出力画像02-01"></th>
<th><img src="/assets/blog/authors/aoshima/image_edit/15.png" alt="FlashImage出力画像02-02"></th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/aoshima/image_edit/16.png" alt="FlashImage出力画像02-01"></td>
<td><img src="/assets/blog/authors/aoshima/image_edit/17.png" alt="FlashImage出力画像02-02"></td>
</tr>
</tbody></table>
<p>Impressions: While the appearance is reproduced, the precision of generating detailed Japanese text falls slightly behind gpt-image-1.</p>
<h2>5. Conclusions and Use Cases</h2>
<table>
<thead>
<tr>
<th>Aspect</th>
<th>gpt-image-1</th>
<th>Flash Image 2.5</th>
</tr>
</thead>
<tbody><tr>
<td>Title reproduction</td>
<td>Accurately outputs in Japanese</td>
<td>Appearance-based</td>
</tr>
<tr>
<td>Small text reproduction</td>
<td>Prone to distortion</td>
<td>Difficult</td>
</tr>
</tbody></table>
<h2>6. Results Summary</h2>
<table>
<thead>
<tr>
<th>Aspect</th>
<th>gpt-image-1</th>
<th>Flash Image 2.5</th>
</tr>
</thead>
<tbody><tr>
<td>Consistency</td>
<td>High (enhanced with input_fidelity)</td>
<td>High (stable with reference images)</td>
</tr>
<tr>
<td>Editing features</td>
<td>Supports mask editing</td>
<td>Supports reference images</td>
</tr>
<tr>
<td>Japanese text</td>
<td>Japanese titles is properly reproduced</td>
<td>Appearance-based</td>
</tr>
<tr>
<td>Convenience</td>
<td>Used via API (for advanced users)</td>
<td>Easily accessible via Web UI</td>
</tr>
</tbody></table>
<h3>Key Takeaways</h3>
<ul>
<li>Choose <strong>gpt-image-1</strong> for <strong>balance, precision, and control</strong>.</li>
<li>Choose <strong>Flash Image</strong> for <strong>convenience and speed</strong>.</li>
<li>As for <strong>Japanese text</strong>, both models can generate text that looks like Japanese, but <strong>their precision of generating Japanese small text and body sentence</strong> still needs improvements.</li>
</ul>
<p>Both are highly polished, and particularly in terms of maintaining the overall appearance without degradation, they are leagues ahead of previous generation models.</p>
<p>Continuing for our verification, we plan to share findings on prompt design, generation parameter tuning, and testing of new models in the future.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoshima/image_edit/title.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[OpenAI vs Google 画像編集対決 – gpt-image-1 と Gemini 2.5 Flash Image の“一貫性”を検証してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-09-openai-vs-google-image-edit/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-09-openai-vs-google-image-edit/</guid>
            <pubDate>Thu, 09 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[gpt-image-1 と Gemini 2.5 Flash Image の“一貫性”を検証してみた]]></description>
            <content:encoded><![CDATA[<h1>OpenAI vs Google 画像編集対決</h1>
<h2>gpt-image-1 と Gemini 2.5 Flash Image の&quot;一貫性&quot;を検証してみた</h2>
<p>近年、OpenAIの ChatGPT、Googleの Gemini、Anthropicの Claude
が生成AIの主要プレイヤーとして存在感を高めていますが、このうち画像の生成・編集を提供しているのは OpenAI と Googleの2社です。</p>
<p>本記事では、OpenAIの gpt-image-1（2025年7月時点）と、GoogleのGemini 2.5 Flash Image（通称 Nano Banana、Web UI 2025年8月時点）に焦点を当て、画像の 一貫性と日本語テキストの扱いを中心に、実際の出力例を交えて比較します。</p>
<h2>1. gpt-image-1、Flash Image とは何か</h2>
<ul>
<li><p><strong>gpt-image-1（OpenAI）</strong><br>ChatGPTの「4o ImageGeneration」の基盤モデル。強力な生成・編集能力を持ち、インペインティング（マスク編集）**に対応。<br>なお、フル機能はAPI経由での利用が前提です。</p>
</li>
<li><p><strong>Gemini 2.5 Flash Image（Google）</strong><br>高速・軽量な画像生成機能で、参照画像を用いた生成に対応。無料ユーザーでも使える点が特徴です（愛称
Nano Banana）。</p>
</li>
</ul>
<h2>2. 画像生成AIが抱える弱点：一貫性</h2>
<p>AIで画像を繰り返し生成・編集すると、<strong>「元の見た目から少しずつズレていく」問題</strong>が発生しがちです。</p>
<p>いわゆる「一貫性の欠如」で、人物の顔・体型・衣服の質感、背景の構造などが回数を重ねるほど変化してしまいます。</p>
<ul>
<li>Flash Image（2025年8月）はこの点が比較的安定しており、登場直後からSNS上でも話題に。</li>
<li>gpt-image-1も（2025年7月）に導入されたパラメータinput_fidelityにより、編集時の一貫性が向上しました。</li>
</ul>
<h2>3. アウトプット比較①：人物のポーズ編集</h2>
<p>お題：車の後部座席の画像に、家族写真の人物たちを自然に座らせる。</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image_edit/01.jpg" alt="シート画像"></th>
<th><img src="/assets/blog/authors/aoshima/image_edit/02.jpg" alt="家族写真"></th>
</tr>
</thead>
</table>
<h3>3-1. gpt-image-1</h3>
<p>gpt-image-1は複数画像の直接参照（A画像にB画像の人物を配置）ができないため、事前に簡易合成（下処理）を実施。<br>家族写真を雑に切り抜いて座席画像の上に重ねました。もちろん <code>input_fidelity = high</code> を設定しています。</p>
<p>![雑な合成画像](/assets/blog/authors/aoshima/image_edit/03.jpg =50%x) </p>
<h4>使用したプロンプト</h4>
<pre><code>Make this family photo look natural and realistic:
- Fix lighting to match car interior lighting
- Add natural shadows under people
- Adjust color temperature to match
- Make people look naturally seated
- Blend edges smoothly
- Keep faces unchanged but make them fit the scene
- Add subtle reflections on windows if visible
</code></pre>
<p><strong>出力例</strong><br>![gpt-image-1の出力画像01](/assets/blog/authors/aoshima/image_edit/04.png =50%x) </p>
<p>所感：服のディテールや内装の細部に差異はあるものの、座り姿勢や影の収まりも一度の生成で十分自然に見えるものが出力されました。ただ色味が黄色っぽくなってしまっているようです。顔の一貫性はあまり維持されていない様です。</p>
<h3>3-2. Gemini 2.5 Flash Image</h3>
<p><strong>参照画像機能</strong>を使用し、座席画像と家族写真を指定。<br>同等の意図で以下を入力しました。</p>
<h4>使用したプロンプト</h4>
<pre><code>In the image of the car’s back seat, place the three people from the provided family photo, making it look natural as if they are sitting together.
- Fix lighting to match car interior lighting
- Add natural shadows under people
- Adjust color temperature to match
- Make people look naturally seated
- Blend edges smoothly
- Keep faces unchanged but make them fit the scene
- Add subtle reflections on windows if visible
- Make clothing wrinkles look natural for sitting position
</code></pre>
<p><strong>出力例（抜粋）</strong></p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image_edit/05.png" alt="Flashimage出力画像01"></th>
<th><img src="/assets/blog/authors/aoshima/image_edit/06.png" alt="Flashimage出力画像02"></th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/aoshima/image_edit/07.png" alt="Flashimage出力画像03"></td>
<td><img src="/assets/blog/authors/aoshima/image_edit/08.png" alt="Flashimage出力画像04"></td>
</tr>
</tbody></table>
<p>所感：顔の一貫性は高いものの、バランスについてはややブレがある様です。\</p>
<h3>3-3. 人物編集のまとめ</h3>
<ul>
<li><strong>gpt-image-1</strong>：下処理の一手間はあるが、精度の高い合成が可能。</li>
<li><strong>FlashImage</strong>：参照画像機能で手軽に高品質。数回のリトライで座り姿勢・サイズ感が十分に整う。</li>
<li><strong>共通</strong>：内装や照明など背景一貫性は両者とも良好。</li>
</ul>
<h2>4. アウトプット比較②：文字を使った編集（日本語）</h2>
<p><strong>課題意識</strong>：一貫性に加え、生成AIは<strong>日本語テキストの精密再現</strong>が難しい傾向があります。<br>そこで、人物が手に持つ雑誌の表紙を「日本語タイトル・特集文言」に差し替えるタスクで比較しました。</p>
<p>![雑誌を抱える画像](/assets/blog/authors/aoshima/image_edit/09.jpg =50%x) </p>
<h4>使用したプロンプト（日本語）</h4>
<pre><code>手に抱えている雑誌を以下の内容に置き換えてください。
- 日本の雑誌で、「旅立ち」というタイトル
- おしゃれでモダンな方向性
- 寺院の特集で表紙はお寺の写真をフィーチャー
- 表紙にはコンテンツ紹介の文言をレイアウト
</code></pre>
<p>※ タイトルを日本語にしたいため、プロンプトは<strong>日本語で統一</strong>。</p>
<h3>4-1. gpt-image-1（インペインティング使用）</h3>
<p><strong>出力例</strong>\</p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image_edit/10.png" alt="gpt-image-1出力画像02-01"></th>
<th><img src="/assets/blog/authors/aoshima/image_edit/11.png" alt="gpt-image-1出力画像02-02"></th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/aoshima/image_edit/12.png" alt="gpt-image-1出力画像02-03"></td>
<td><img src="/assets/blog/authors/aoshima/image_edit/13.png" alt="gpt-image-1出力画像02-04"></td>
</tr>
</tbody></table>
<p>所感：タイトル「旅立ち」は正しく日本語で生成。ただし細かい本文テキストは崩れがち。</p>
<h3>4-2. Flash Image（プロンプトのみ）</h3>
<p>インペインティング非対応のため、<strong>全体をプロンプト指定</strong>で実行。プロンプト内容は同等。</p>
<p><strong>出力例</strong></p>
<table>
<thead>
<tr>
<th><img src="/assets/blog/authors/aoshima/image_edit/14.png" alt="FlashImage出力画像02-01"></th>
<th><img src="/assets/blog/authors/aoshima/image_edit/15.png" alt="FlashImage出力画像02-02"></th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/aoshima/image_edit/16.png" alt="FlashImage出力画像02-01"></td>
<td><img src="/assets/blog/authors/aoshima/image_edit/17.png" alt="FlashImage出力画像02-02"></td>
</tr>
</tbody></table>
<p>所感：雰囲気は再現できるものの、日本語テキストのテキストはgpt-image-1 に一歩譲る印象。</p>
<h2>5. 結論と使い分け</h2>
<table>
<thead>
<tr>
<th>観点</th>
<th>gpt-image-1</th>
<th>Flash Image 2.5</th>
</tr>
</thead>
<tbody><tr>
<td>タイトル再現</td>
<td>正確に日本語で出る</td>
<td>雰囲気重視</td>
</tr>
<tr>
<td>小文字の再現</td>
<td>崩れやすい</td>
<td>難あり</td>
</tr>
</tbody></table>
<h2>6. 結果まとめ</h2>
<table>
<thead>
<tr>
<th>観点</th>
<th>gpt-image-1</th>
<th>Flash Image 2.5</th>
</tr>
</thead>
<tbody><tr>
<td>一貫性</td>
<td>高い（input_fidelityで強化）</td>
<td>高い（参照画像で安定）</td>
</tr>
<tr>
<td>編集機能</td>
<td>マスク編集対応</td>
<td>参照画像対応</td>
</tr>
<tr>
<td>日本語テキスト</td>
<td>タイトルは良好</td>
<td>雰囲気重視</td>
</tr>
<tr>
<td>利便性</td>
<td>API経由で利用（上級者向け）</td>
<td>Web UIで手軽に利用可能</td>
</tr>
</tbody></table>
<h3>要点まとめ</h3>
<ul>
<li><strong>バランスや精度・制御力重視</strong>なら<strong>gpt-image-1</strong></li>
<li><strong>手軽さ・スピード重視</strong>なら <strong>Flash Image</strong></li>
<li><strong>日本語テキスト</strong>は両者とも「雰囲気」は出せるが、<strong>小さな文字や本文の精緻さ</strong>はまだ発展途上。</li>
</ul>
<p>どちらも非常に完成度が高く、特に「全体の雰囲気が崩れない」という点で、以前の世代とは段違いです。</p>
<p>継続的に検証を進めながら、プロンプト設計や生成パラメータのチューニングについてはもちろん、新しいモデルについての検証なども今後共有していく予定です。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoshima/image_edit/title.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[InnerSource Gathering Tokyo 2025 参加レポート]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-10-08-InnerSource-Gathering-Tokyo-2025-Report/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-10-08-InnerSource-Gathering-Tokyo-2025-Report/</guid>
            <pubDate>Wed, 08 Oct 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>技術広報Gの中西です。（FACTORY EC開発G・QAグループ兼務）こちらの記事は先日9/12にお台場で開催された<a href="https://gatherings.innersourcecommons.org/tokyo-2025/">InnerSource Gathering Tokyo 2025</a>についてのレポートになります。</p>
<p>インナーソースという言葉を耳にする機会は、ここ数年でぐっと増えてきました。弊社でもテックブログの開発やエンジニア文化の醸成を通じて、インナーソースカルチャーを推進するための小さな積み重ねを続けています。</p>
<p>今回のイベントでは学びが多く、私たちの取り組みこそがインナーソースカルチャーにつながっていると改めて確信しました。素晴らしい内容を多くの方と共有し、インナーソースという文化を広げていきたい — その思いから、このレポートを書いています。</p>
<h1><strong>「ふるまい」から文化を変える、現場発のインナーソース実装論</strong></h1>
<p>会場に流れていたのは、終始いくつかの共通トーンでした。<strong>サイロを壊すのはスローガンではなく「ふるまい」の累積だ</strong>ということ。コードだけが貢献ではないこと。そして、正しさを声高に説くより、<strong>小さく始めて仲間を増やす</strong>ことが文化を動かすということでした。登壇者それぞれの別の言葉、カルチャーの異なる現場の共有をされていました。</p>
<hr>
<h2>グラデーションで捉える「サイロ」</h2>
<p>冒頭、運営からのメッセージは明快だった。<strong>インナーソースは、文化的変化で社内のサイロを「壊す」ための試み</strong>だが、0/1で語れるものではない。組織ごとに濃淡がある。その前提で、<a href="https://ja.wikipedia.org/wiki/%E3%83%81%E3%83%A3%E3%82%BF%E3%83%A0%E3%83%8F%E3%82%A6%E3%82%B9%E3%83%AB%E3%83%BC%E3%83%AB"><strong>チャタムハウスルール(Chatham House Rule)</strong></a>を宣言し、登壇者や企業名に紐づけず内容を安心して持ち帰れる場にしていました。こういった大前提となるルールが定められていると積極的な生きた議論が出来て素晴らしいですね。イベントの始まりから参加して良かったと思えた瞬間です。</p>
<p>会場(<a href="https://docomo-openlab.jp/">docomo R&amp;D OPEN LAB ODAIBA</a>)を提供したNTTドコモさんは、巨大LEDや5G/エッジ環境まで備えた「作る・学ぶ・発信する」施設を紹介されていました。イベント日以外もコワーキングとして開放されており、「<strong>エンジニアが自然に集まれる恒常的な場</strong>」を設計していることが印象に残っています。</p>
<h2><img src="/assets/blog/authors/aoi.nakanishi/2025-10-08-InnerSource-Gathering-Tokyo-2025-Report/docomo-open-lab-odaiba.png" alt="docomo R&amp;D OPEN LAB ODAIBA"></h2>
<h2>インナーソースは「ライセンス」ではなく「やり方」</h2>
<p>基調講演は、OSS(オープンソースソフトウェア)の歴史と作法を手がかりに、インナーソースを「やり方」として捉え直した。<strong>公開された議論・誰でも参加できる入口・コミュニティでの協働</strong> ― このOSSの作法を、<strong>社内に移植する</strong>のがインナーソースだという原点回帰だということが語られていました。</p>
<p>「<strong>コード以外の貢献</strong>」に光を当てた点も大きく、レビュー、テスト、トリアージ、翻訳、ドキュメント、インフラ運用、広報までが一級の貢献だと明言されていました。レビュー言語の作法に触れたくだりでは、 <strong>「受け取りは寛容に、出す言葉は厳密に」</strong> というネットワーク界隈の原則を、対話の作法に重ねて紹介していました。<strong>指摘は命令ではなく対話の始まり</strong>。それを見て学ぶ第三者もいるからこそ、言葉が文化になるといえます。</p>
<p>もうひとつの軸は<strong>アジャイルとの親和性</strong>だ。頻繁なリリース、自己組織化、要求の進化 ― OSSが長く実装してきたものと、アジャイル実践は結局似ている。だから変え方も同じ筋道をたどる。<strong>行動を変え、考えが変わり、文化が変わる</strong>。いわゆるエンジニアカルチャーなど、言葉の選択は色々あるが根本にある考え方や行動に関して共通するものを持っていると改めて感じました。</p>
<p>動機の話では、楽しさや学びに加えて<strong>キャリアや評判</strong>が混ざる近年の傾向が紹介されていました。実例としては、<strong>会社が成長・学習を後押しする設計</strong>により、社内の貢献者が短期間で大幅に増えた事例が語られました。</p>
<p>質疑は実務的では、インセンティブは<strong>お金だけに寄ると長続きしない</strong>。楽しさと学び、そして評価の三点留めで設計するのが良い。バーンアウト対策については、<strong>最初から大勢に語らず、一人ひとりに声をかけて小さく勝ち、賛同者を増やす</strong>という手順が共有された。扱いにくい振る舞いに向き合う質問には、<strong>行動規範を用意し、コミュニティ全体の「平均値」を上げる</strong>という堅実な答え。排除ではなく、<strong>転じて味方になる構図</strong>をつくる発想が貫かれていた。</p>
<p><a href="https://kdmsnr.com/slides/20250912_innersource/">https://kdmsnr.com/slides/20250912_innersource/</a></p>
<p>これらは<a href="https://www.amazon.co.jp/dp/462108786X/">Fearless Change アジャイルに効く アイデアを組織に広めるための48のパターン</a>のパターンに当てはまるものが多く、組織の中で新たなことを推進するうえで是非オススメしたい。</p>
<hr>
<h2>NRI「xPalette」：ケイパビリティを循環させる</h2>
<p>野村総研さんは、<strong>エンジニアの創造性と主体性</strong>を解放する場づくりの四年分の知見を語った。リファレンスアーキテクチャと個別ガイドを整え、各自が試した知見を <strong>「学ぶ→現場適用→フィードバック」</strong> で循環させる。
この循環が回ると、プロジェクト参画の機会が増え、複数の技術を組み合わせた<strong>新しい事業の芽</strong>も立ち上がる。活動を<strong>事業価値で説明</strong>し、予算→環境改善へと正のスパイラルを作る筋立てが現実的だ。若手の「<strong>まず試す</strong>」を小口の時間・費用枠で支える、<strong>チャレンジを褒める</strong>のをマネジメントが率先する――こうした「手触りのある運営」が随所にあった。</p>
<hr>
<h2>三菱電機 OSPO/ISPO：社外の注目を社内の追い風に</h2>
<p>三菱電機さんは、<strong>OSPO（OSS）とISPO（InnerSource）</strong> を併走させる体制を立ち上げ、 <strong>プラットフォームを整える→オープンに載せる</strong> という習慣の普及から始めている。社外イベントで注目を集め、 <strong>外からの視線を社内の認知に逆流</strong> させる語り口は大企業ならでは。社内イベントの連鎖で <strong>用語が社内の共通語になっていく</strong> 過程が披露された。さらに、 <strong>11月13日に横浜でカンファレンスをホスト</strong> する案内もあり、ムーブメントの「<strong>場</strong>」を先に作る姿勢が印象的だった。</p>
<p>InnerSource Summit 2025
<a href="https://innersourcecommons.org/ja/events/isc-2025/">https://innersourcecommons.org/ja/events/isc-2025/</a></p>
<hr>
<h2>対談：規程も「みんなで作る」／品質とは「使い手の価値」</h2>
<p>対談は、<strong>大企業でインナーソースを実践した現場感</strong>がにじんだ。特に刺さったのは、<strong>開発標準や規程といった「ルール類」こそインナーソースで作る</strong>べきだという提案だ。関係者を巻き込み、承認フローまで公開する。
KPIの扱いでは、<strong>金額だけで説明しない</strong>。人数、再利用、レビューのリードタイムなど<strong>金額に寄与する手前の変化</strong>を測る。品質の定義については、<strong>「利用者にとっての価値」</strong> を軸に置き、欠陥数だけで測らない姿勢が示された。生成AIをどう扱うかの問いには、<strong>作り手が人かAIかは本質ではない</strong>、<strong>ユーザー教育と運用設計、フィードバックの回路</strong>まで含めた品質管理が必要だという冷静な回答。
費用配賦や予算の壁をどう越えるかには、 <strong>「まず自チームで始め、フォロワーを増やし、制度は後から追随させる」</strong> という、可能なところから現実的に進める作法が共有された。</p>
<hr>
<h2>KDDI「KAGreement」：オープンな合意が文化になる</h2>
<p>KDDIの登壇は、<strong>「なぜここにいるのか」を言葉にする</strong>取り組み。副社長も参加する<strong>週次のFigJamセッション</strong>と<strong>Slackでの公開</strong>を通じ、ワーキングアグリーメント（行動指針）を<strong>みんなで磨く</strong>。
議論は「<strong>公開・アーカイブ・小さく早く・チーム横断</strong>」といったインナーソースのパターンとよく似た挙動になっている。全社ミーティングで<strong>ランダムなブレイクアウト</strong>を行い、部門も年次も混ぜて対話する設計は、<strong>組織の「平均値」をじわじわ上げる</strong>実装と言えた。背後では、<strong>エグゼクティブ・スポンサーシップ</strong>が確かに支えている。</p>
<p><a href="https://www.docswell.com/s/mitsuba_yu/KLVRX7-2025-09-12-163618">https://www.docswell.com/s/mitsuba_yu/KLVRX7-2025-09-12-163618</a></p>
<hr>
<h2>チームラボ：見つからなければ「無い」のと同じ</h2>
<p>チームラボさんのテーマは、<strong>「使われて育つ」ための仕掛け</strong>だ。組織が大きくなるほど、「何がどこにあるのか」が見えない。そこで、<strong>社内の「インナーソース部」<strong>を立ち上げ、興味と仲間の可視化から着手した。
次に作ったのが</strong>InnerSource Portal</strong>。リポジトリの概要、オーナー、導入手順、<strong>どのプロジェクトで使われているか</strong>、Issue/PRへの導線を<strong>一枚に集約</strong>する。Issueテンプレートも「質問・改善・機能追加…」と種類を分け、<strong>書きやすさを設計</strong>。</p>
<p>さらに、「<strong>InnerSource Champion</strong>」「最多貢献」「神Issue」「新人賞」など<strong>称号と表彰</strong>を遊び心を持って運用していく構想が語られた。定期のリリース共有会や、期限を切って<strong>全員でひとつの社内OSSを作る日</strong>のような企画も視野に入れている。狙いはただ一つ。 <strong>「使いたいと思ったとき、そこにある」</strong> 状態を増やすことだ。</p>
<p><a href="https://speakerdeck.com/teamlab/innersource_gathering_tokyo2025_teamlab">https://speakerdeck.com/teamlab/innersource_gathering_tokyo2025_teamlab</a></p>
<hr>
<h2>まとめ：小さな成功体験が文化になる</h2>
<p>今年のISGTで響いたのは、どの登壇にも共通していた <strong>「やさしい導線」</strong> でした。
最初のPRが怖くないUI。最初のレビューが心地よい言葉。最初の採用が誇らしくなる称号。</p>
<p>そうした <strong>小さな成功体験の積み重ね</strong> が、コミュニティ全体の「平均値」を押し上げ、サイロを溶かしていくのです。インナーソースは制度名ではなく、日々の <strong>ふるまいのデザイン</strong> であると強く感じました。</p>
<hr>
<h2>おまけ：懇親会の熱気</h2>
<p>イベント後にはInnerSource OST（Open Space Technology）が行われ、各テーマごとに分かれてディスカッションが続きました。その流れで自然に懇親会へとつながったため、場は最初から大いに盛り上がり、活発な意見交換が続いていました。「カルチャーをどう良くしていくか」という問いに真剣に取り組む人たちが集まっていたからこそ、ただの交流ではなく、<strong>次につながる対話</strong> が数多く生まれたのが印象的です。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-10-08-InnerSource-Gathering-Tokyo-2025-Report/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[S3 Event Notifications Failing? The cause was multipart uploads.]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-30-s3-multipart-event-notification-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-30-s3-multipart-event-notification-en/</guid>
            <pubDate>Tue, 07 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[S3 Event Notification Multipart Upload Troubleshooting]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->
<h1>Introduction</h1>
<p>Hello! This is Matsuo from the Cloud Infrastructure Group.<br>Before I knew it, this August marked the start of my third year with the company.</p>
<p>This time, I encountered a minor stumbling block with S3 event notifications, so I decided to put together a brief summary as a way to share what I&#39;ve learned. I hope this serves as a helpful reference for anyone facing similar issues.</p>
<h1>How it started</h1>
<blockquote>
<p>📋 We received a request for a system where a Lambda function uploads a file to S3, and the upload triggers an SQS message. We implemented this by configuring S3 event notifications to send messages to SQS when an <strong>ObjectCreated:Put</strong> event occurs.</p>
</blockquote>
<p>However, shortly after configuration, it was pointed out that no messages were arriving in SQS, and we investigated this.</p>
<h1>What is S3 Event Notification?</h1>
<p>S3 Event Notification, in simple terms, <strong>is an S3 feature that allows you to send event-driven notifications to other AWS services</strong> when events occur within an S3 bucket. You can select( <strong>Lambda/SNS/SQS</strong> )  as the notification destinations.
<img src="/assets/blog/authors/r.matsuo/s3-multipart-event-notification/001.png" alt=""></p>
<blockquote>
<p><strong>📝 Additional Notes</strong><br>This time, we use S3 events to send notifications directly to SQS, but notifications via Amazon EventBridge are also possible. When integration with multiple services or complex configurations is required, you should also consider using EventBridge integration.</p>
</blockquote>
<h2>The Initial Hypothesis and Response</h2>
<p>In fact, the destination SQS queue had been renamed once, and the S3 event notification settings were updated accordingly. Since the requester confirmed immediately after the configuration that there were no issues, we formed the following hypothesis.</p>
<p>💭 <strong>Could it be that the SQS or S3 event notification configurations weren&#39;t updated due to an internal AWS issue?</strong></p>
<p>Therefore, we deleted and recreated the S3 event notification and SQS configuration. After uploading a sample .txt file to S3 using a PUT operation, the event was successfully received. At this point, we thought the problem had been resolved.</p>
<p><strong>Unresolved issues and new findings</strong>
After communicating the results of our investigation and the actions taken, we received a follow-up message from the requester indicating that the problem still remains unresolved. It included other information.</p>
<blockquote>
<p>💬 I tried it with a .txt file and received the message. It seems that message reception may not be working properly with .csv files.</p>
</blockquote>
<p>The requester mentioned above. I wondered what it is.  And I checked the contents of S3, and then. I found something!</p>
<table>
<thead>
<tr>
<th>File Extension</th>
<th>Size</th>
</tr>
</thead>
<tbody><tr>
<td>.txt</td>
<td>Several bytes</td>
</tr>
<tr>
<td>.csv</td>
<td>Approximately 20 MB</td>
</tr>
</tbody></table>
<p>After determining that <strong>the file size</strong> was likely the cause rather than the file extension, we investigated again!</p>
<h2>Identification of the Cause</h2>
<p>Upon reviewing the code for the Lambda function handling file uploads, I found <strong>S3.upload_file</strong>.</p>
<pre><code class="language-python">def upload_file(temp_file_path: str, S3_bucket_name: str, S3_file_name: str):
    &quot;&quot;&quot;
    ファイルアップロード
    &quot;&quot;&quot;
    logger.info(&#39;---- Upload ----&#39;)
    S3 = boto3.client(&#39;s3&#39;)
    res = S3.upload_file(temp_file_path, S3_bucket_name, S3_file_name)
    logger.info(f&quot;ファイル {S3_file_name} をアップロードしました&quot;)
</code></pre>
<p>The boto3 <code>upload_file</code> method automatically performs <strong>multipart uploads</strong> when the file size exceeds a certain threshold (8 MB).[^1]
This seems to be the cause.🔍</p>
<h3>Wrong S3 Event Type</h3>
<p>The event type configured for S3 was</p>
<pre><code>ObjectCreated:Put
</code></pre>
<p>In this configuration, only creations <code>via ObjectCreated:Put</code> will be notified.</p>
<p>However, the event triggered during a multipart upload is</p>
<pre><code>ObjectCreated:CompleteMultipartUpload
</code></pre>
<p>With multipart uploads, <strong>a completely different event</strong> is triggered, not <code>ObjectCreated:Put</code>!</p>
<p>In the case of large files, the following sequence prevented the event from being triggered:</p>
<ol>
<li>Lambda uploads the file to S3</li>
<li>During this process, the <code>upload_file</code> method automatically performs a multipart upload</li>
<li>Consequently, the event generated is <code>ObjectCreated:CompleteMultipartUpload</code></li>
<li>As a result, since the event notification configuration is set only to <code>ObjectCreated:Put</code>, it is not notified to SQS.</li>
</ol>
<p><strong>By the way, what is multipart upload?</strong>
Multipart upload is a mechanism that divides large files into multiple parts for uploading. Here are the benefits.[^2]</p>
<pre><code>High-speed: Upload multiple parts in parallel
Reliability: Resend only failed parts
Resume interrupted: Resuming from where you left off is possible even during network outages
</code></pre>
<h1>Solution</h1>
<p><strong>S3 event notification settings change</strong>
The event type settings have been changed as follows:</p>
<pre><code>All object creation events (ObjectCreated:*)
</code></pre>
<p>With this setting, all of the following events will be subject to notification.</p>
<pre><code>ObjectCreated:Put
ObjectCreated:Post
ObjectCreated:Copy
ObjectCreated:CompleteMultipartUpload
</code></pre>
<p>After changing the settings, we confirmed that S3 event notifications are successfully sent to SQS even for large CSV files!</p>
<blockquote>
<p>In this case, since it was clear that the file upload source was solely the Lambda function, we set it to <code>ObjectCreated:*</code> (all object creation events).</p>
</blockquote>
<p><strong>Other solutions are available</strong>
In addition to modifying S3 event notification settings, you can also handle this by using the put_object method in boto3 code. However, this time we chose to modify the S3 event notification configurations. The reasons:</p>
<ul>
<li>No need to modify the application code</li>
<li>Stable even against future changes</li>
<li>Other upload methods are also supported.</li>
</ul>
<p>I believe <strong>modifying S3 event configurations is the most versatile and reliable approach</strong>, but depending on the situation, handling it in code can also be effective.</p>
<h1>What We&#39;ve Learned</h1>
<p><strong>1. Testing with file size in mind</strong>
When validating with small test files, multipart uploads may not occur, potentially causing issues to be overlooked. When processing involves S3, it is crucial to conduct <strong>testing using file sizes expected in the production environment</strong>.</p>
<p><strong>2. Understanding boto3&#39;s internal behavior</strong>
While <code>upload_file</code> method in boto3 is convenient, it may automatically execute multipart uploads internally. You need to understand this behavior and configure the appropriate events.</p>
<p><strong>3. Considerations for event notification configurations</strong>
For this specific requirement, where the upload source was limited solely to Lambda functions, we selected <code>s3:ObjectCreated:*</code>.However, in general, it is recommended to narrow down the event types to only those that are strictly necessary. The key is to understand what upload method SDKs like boto3 use internally and configure the appropriate events accordingly.</p>
<h1>Conclusion</h1>
<p>This issue occurred due to the following factors overlapping.</p>
<pre><code>1. boto3 automatically performed multipart uploads
2. S3 event notifications were configured to target only ObjectCreated:Put events
3. The issue could not be reproduced with small test files
</code></pre>
<p><strong>Feeling reassured after testing with small files, only to encounter issues in actual operation</strong>, is a common pitfall.
If you&#39;re experiencing similar issues, please check the following:</p>
<ul>
<li>The size of the file uploaded to S3</li>
<li>The event type targeted by your S3 event notification</li>
</ul>
<p>I hope this blog post has been a little helpful.</p>
<p>[^1]: <a href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/customizations/S3.html#boto3.S3.transfer.TransferConfig">boto3 TransferConfig official documentation</a></p>
<p>[^2]: <a href="https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/mpuoverview.html">Multipart upload official documentation</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[S3イベント通知が失敗する？マルチパートアップロードが原因だった]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-30-s3-multipart-event-notification/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-30-s3-multipart-event-notification/</guid>
            <pubDate>Tue, 07 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[S3イベント通知 マルチパートアップロード トラブルシューティング]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->
<h1>はじめに</h1>
<p>こんにちは！
クラウドインフラグループの松尾です。<br>早いもので今年の8月で入社3年目に突入してしまいました。</p>
<p>今回は、S3イベント通知について、ちょっとした躓きがあったので
知識のアウトプットとして簡単にまとめようと思いました。
同じような問題に直面した方の参考になれば幸いです。</p>
<h1>きっかけ</h1>
<blockquote>
<p>📋LambdaがファイルをS3にアップロードし、アップロードをトリガーとしてSQSにメッセージを送信したい
とあるシステムで依頼があり、S3イベント通知を、<strong>ObjectCreated:Put</strong>の場合SQSに送信するように設定し実現しました。</p>
</blockquote>
<p>しかし、設定後しばらくしてSQSにメッセージが届いていないという指摘があり、その調査を行いました。</p>
<h1>S3イベント通知とは？</h1>
<p>そもそもS3イベント通知とは、ざっくりいうとS3バケット内でイベントが発生した際に
<strong>イベント駆動で他のAWSサービスに通知を送信できるS3の機能</strong>です。
通知先は(<strong>Lambda/SNS/SQS</strong>)が選択できます。
<img src="/assets/blog/authors/r.matsuo/s3-multipart-event-notification/001.png" alt=""></p>
<blockquote>
<p><strong>📝 補足</strong><br>今回は直接SQSへ通知するためS3イベントを採用していますが、Amazon EventBridgeを経由した通知も可能です。
複数サービスとの連携や複雑な設定が必要な場合はEventBridge連携も検討する必要があります。</p>
</blockquote>
<h2>最初の仮説と対応</h2>
<p>実は通知先のSQSは一度名前を変更しており、併せてS3イベント通知の設定も更新していました。
設定後すぐに依頼者には確認いただき問題ないと連絡をいただいていたため、以下の仮説を立てました。</p>
<p>💭 <strong>SQSやS3イベント通知の設定がAWSの内部的な問題で更新できていなかった可能性があるのではなかろうか？</strong></p>
<p>そのためS3イベント通知/SQSを一度削除し、再作成し適当なtxtファイルをS3にPUTしたところ、イベントを正常に受信できました。
この時点では「問題は解決した」と考えました。</p>
<p><strong>問題の未解決と新たな発見</strong>
調査及び対応で行ったことを伝え、しばらくすると、
再度依頼者からまだ問題が解決していないという旨の連絡がありました。
その際依頼者から</p>
<blockquote>
<p>💬拡張子.txtのファイルで試してみたところ、メッセージ受信しました。.csvでメッセージ受信できるようになっていない可能性がありそう</p>
</blockquote>
<p>とのメッセージも添えられていました。
おやおや？と思いS3の中身を確認すると、あっ！と思った箇所が！</p>
<table>
<thead>
<tr>
<th>拡張子</th>
<th>サイズ</th>
</tr>
</thead>
<tbody><tr>
<td>.txt</td>
<td>数バイト</td>
</tr>
<tr>
<td>.csv</td>
<td>約20Mバイト</td>
</tr>
</tbody></table>
<p>拡張子の問題ではなく<strong>ファイルサイズ</strong>が原因の可能性が高そうだと判断して再度調査してみました！</p>
<h2>原因の特定</h2>
<p>ファイルアップロード処理を行っているLambda関数のコードを確認したところ、<strong>S3.upload_file</strong>を見つけました。</p>
<pre><code class="language-python">def upload_file(temp_file_path: str, S3_bucket_name: str, S3_file_name: str):
    &quot;&quot;&quot;
    ファイルアップロード
    &quot;&quot;&quot;
    logger.info(&#39;---- Upload ----&#39;)
    S3 = boto3.client(&#39;s3&#39;)
    res = S3.upload_file(temp_file_path, S3_bucket_name, S3_file_name)
    logger.info(f&quot;ファイル {S3_file_name} をアップロードしました&quot;)
</code></pre>
<p>boto3の<code>upload_file</code>メソッドは、ファイルサイズが一定の閾値(8Mバイト)を超えると、自動的に<strong>マルチパートアップロード</strong>を実行します。[^1]
原因はここにありそうです🔍</p>
<h3>S3イベントタイプが違った</h3>
<p>S3の設定イベントタイプとして設定していたのは</p>
<pre><code>ObjectCreated:Put
</code></pre>
<p>この設定では、<code>ObjectCreated:Put</code>による作成のみが通知対象となります。</p>
<p>しかし、マルチパートアップロードで発生するイベントは</p>
<pre><code>ObjectCreated:CompleteMultipartUpload
</code></pre>
<p>マルチパートアップロードでは、<code>ObjectCreated:Put</code>とは<strong>全く別のイベント</strong>が発生することになります！</p>
<p>つまり大きなファイルの場合、このような流れでイベントが起こらなかったのです</p>
<ol>
<li>LambdaがS3にファイルをアップロードする</li>
<li>その際、<code>upload_file</code>メソッドにより、自動的にマルチパートアップロードを実行</li>
<li>そのため発生したイベントは<code>ObjectCreated:CompleteMultipartUpload</code></li>
<li>結果としてイベント通知設定が<code>ObjectCreated:Put</code>のみだったため、SQSに通知されない</li>
</ol>
<p><strong>ちなみにマルチパートアップロードとは？</strong>
マルチパートアップロードは、大きなファイルを複数の部分（パート）に分割してアップロードする仕組みです。以下の利点があります。<a href="%5B%E3%83%9E%E3%83%AB%E3%83%81%E3%83%91%E3%83%BC%E3%83%88%E3%82%A2%E3%83%83%E3%83%97%E3%83%AD%E3%83%BC%E3%83%89%E5%85%AC%E5%BC%8F%E3%83%89%E3%82%AD%E3%83%A5%E3%83%A1%E3%83%B3%E3%83%88%5D(https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/mpuoverview.html)">^2</a></p>
<pre><code>高速: 複数パートを並列でアップロード
信頼性: 失敗したパートのみ再送信
中断の再開: ネットワーク障害時でも途中から再開が可能
</code></pre>
<h1>解決方法</h1>
<p><strong>S3イベント通知の設定変更</strong>
イベントタイプの設定を以下のように変更しました</p>
<pre><code>すべてのオブジェクト作成イベント (ObjectCreated:*)
</code></pre>
<p>この設定により、以下のすべてのイベントが通知対象となります</p>
<pre><code>ObjectCreated:Put
ObjectCreated:Post
ObjectCreated:Copy
ObjectCreated:CompleteMultipartUpload
</code></pre>
<p>設定変更後、大きなCSVファイルでもSQSにS3イベント通知が正常に送信されることを確認できました！</p>
<blockquote>
<p>今回のケースでは、ファイルのアップロード元がLambda関数のみであることが明確だったため、<code>ObjectCreated:*</code>（すべてのオブジェクト作成イベント）に設定しました</p>
</blockquote>
<p><strong>他の解決方法もある</strong>
S3イベント通知の設定変更以外にも、boto3のコードでput_objectメソッドを使用することでも対応することは可能です。
ただし、今回はS3イベント通知の設定変更を選択しました。理由は</p>
<ul>
<li>アプリケーションコードを変更する必要がない</li>
<li>将来的な変更に対しても安定している</li>
<li>他のアップロード方法でも対応可能</li>
</ul>
<p><strong>S3イベント設定変更が最も汎用的で確実</strong>だと思いますが、状況によってはコード側での対応も有効だと思います。</p>
<h1>学んだこと</h1>
<p><strong>1. ファイルサイズを意識したテスト</strong>
小さなテストファイルでの検証では、マルチパートアップロードが発生せず、問題を見落とす可能性があります。S3を挟む処理の場合は<strong>本番環境で想定されるファイルサイズでのテスト</strong>を実施することが重要です。</p>
<p><strong>2. boto3の内部動作への理解</strong>
boto3の<code>upload_file</code>メソッドは便利ですが、内部でマルチパートアップロードを自動実行する場合があります。
この動作を理解して、適切なイベント設定を行う必要があります。</p>
<p><strong>3. イベント通知設定の考慮点</strong>
今回は要件上、アップロード元がLambda関数のみと限定されていたため、<code>s3:ObjectCreated:*</code>を選択しましたが、一般的には必要最小限のイベントタイプに絞ることが推奨されます。
重要なのは、boto3などのSDKが内部でどのようなアップロード方法を使用するかを把握し、それに応じた適切なイベント設定を行うことです。</p>
<h1>まとめ</h1>
<p>今回の問題は、以下の要因が重なって発生しました。</p>
<pre><code>1. boto3が自動的にマルチパートアップロードを実行
2. S3イベント通知が`ObjectCreated:Put`イベントのみを対象に設定されていた
3. 小さなテストファイルでは問題が再現されない
</code></pre>
<p><strong>小さなファイルでテストして安心していたら、実際の運用で問題が発覚する</strong>というのは、よくある落とし穴だと思います。
同様の問題で困っている方は、以下を確認してみてください。</p>
<ul>
<li>S3にアップロードしたファイルのサイズ</li>
<li>S3イベント通知の対象イベントタイプ</li>
</ul>
<p>このブログが少しでも参考になれば幸いです。</p>
<p>[^1]: <a href="https://boto3.amazonaws.com/v1/documentation/api/latest/reference/customizations/S3.html#boto3.S3.transfer.TransferConfig">boto3 TransferConfig公式ドキュメント</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[SRE NEXT] What's Your "NEXT"? 312 Survey Results!]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-31-sre-next-thanks-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-31-sre-next-thanks-en/</guid>
            <pubDate>Wed, 01 Oct 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[What's your "NEXT"? 312 Survey Results!]]></description>
            <content:encoded><![CDATA[<p>Hello! I&#39;m Kasai from the SRE Team.</p>
<p>KINTO Technologies Corporation participated in &quot;SRE NEXT 2025&quot; as a Platinum Sponsor!</p>
<p>Thank you to everyone who visited our booth! It was inspiring to talk with so many participants, and I gained a lot of valuable insights.</p>
<p>You can read about the roundtable discussion held by our members who attended SRE NEXT in the article linked below. Please take a look! <a href="https://blog.kinto-technologies.com/posts/2025-07-18-sre_next_look_back/">https://blog.kinto-technologies.com/posts/2025-07-18-sre_next_look_back/</a></p>
<p>At our booth, we conducted a survey with the theme: &quot;What&#39;s Your NEXT?&quot; Thank you to everyone who took part.</p>
<p>In this article, I’d like to share the survey results with you.</p>
<p><img src="/assets/blog/authors/kasai/20250731/board.png" alt=""> <em>Sticky notes on the board (left: Day 1, right: Day 2)</em> ![] (/assets/blog/authors/kasai/20250731/20250724_162209.jpeg =400x) <em>Stack of sticky notes for the two days</em></p>
<h1>Survey Results</h1>
<p>Over the two days, we received 312 responses to the survey. I categorized the responses into several themes, which I would like to share here. *The classification was done using Gemini.</p>
<ol>
<li><p>SRE &amp; Organizational Culture (60 responses) These responses were related to the practice and promotion of SRE, fostering organizational culture, recruitment, and team building.</p>
<ul>
<li>Becoming able to promote SRE</li>
<li>Spreading the SRE culture</li>
<li>Hiring engineers successfully</li>
<li>Creating an engineering organization that excites people!</li>
<li>Building a common platform for Embedded SRE</li>
</ul>
<p>Many responses touched on topics such as how to behave as an SRE, how to spread SRE-oriented thinking, and the difficulty of hiring more engineers. I can strongly relate to these points, as I also think about how to effectively communicate the benefits of SRE thinking to other teams and help them recognize its value.</p>
</li>
<li><p>AI Utilization (58 responses) These responses were related to improving operational efficiency and creating new value through the use of AI and LLMs.</p>
<ul>
<li>Applying AI to infrastructure and SRE</li>
<li>Achieving Agentic DevOps</li>
<li>Enabling incident response entirely with AI</li>
<li>Reducing toil through AI utilization</li>
<li>Achieving a seven-day weekend with AI!!</li>
</ul>
<p>Some participants were already using AI and wanted to expand its use, while others were planning to start using it. I also use generative AI, but only as an aid when writing code. For example, I have not yet reached the point where I can have AI handle all incident responses. I would like to challenge myself to find ways to use it beyond coding, such as in areas like incident response.</p>
</li>
<li><p>Technology &amp; Service Improvement (58 responses) These responses were related to improving service quality, including the introduction of SLI/SLO, enhancing performance, and resolving technical debt.</p>
<ul>
<li>Introducing SLI/SLO</li>
<li>Significantly improving performance to enhance user experience</li>
<li>Resolving technical debt</li>
<li>Fully automating operations</li>
<li>Working with eBPF</li>
</ul>
<p>Many responses focused on introducing SLI/SLO and expanding observability. I still have much to learn about SLI/SLO, and my experience with implementation is limited, so I would like to continue practicing and building my knowledge and expertise.</p>
</li>
<li><p>Business &amp; Career (41 responses) These responses were related to contributing to business, product growth, IPO, career changes, and promotions.</p>
<ul>
<li>Understanding the business side and applying it to work</li>
<li>Business Growth</li>
<li>IPO</li>
<li>Become a CTO</li>
<li>Changing jobs</li>
</ul>
<p>In addition to technical topics, there were also responses related to business. I feel that it is necessary to discuss reliability not only with the development side but also with the business side. There were others who shared the same view, as well as those who were thinking about how to grow the business. At present, I still feel somewhat distant from the business side, so I would like to close that gap and contribute to the business from an SRE perspective.</p>
</li>
<li><p>Speaking &amp; Output (30 responses) These responses were related to sharing information externally, such as speaking at conferences or writing blog posts.</p>
<ul>
<li>Preparing for a keynote talk</li>
<li>Outputting every month</li>
<li>Hosting PHP Conference 2026 Hokkaido</li>
<li>Submitting many CFPs</li>
<li>Becoming a speaker</li>
</ul>
<p>Perhaps because it was a conference setting, there were many responses related to speaking engagements. Some people even wrote that they would speak at SRE NEXT, which made me feel that it’s wonderful we are able to host events that make people want to present. I would also like to spread the word about our company through blog posts and speaking engagements so that more people can learn about who we are.</p>
</li>
<li><p>Other (65 responses) These were private goals or unique responses that did not fit into the categories above.</p>
<ul>
<li>Go to a sauna</li>
<li>See penguins in Antarctica</li>
<li>Be happy</li>
<li>Have dinner</li>
<li>Go on a trip</li>
</ul>
<p>Some participants also wrote things unrelated to work or SRE. It reminded me that maintaining health and taking time to refresh are also important for doing good work.</p>
</li>
</ol>
<p>Since we had the opportunity, we also asked Gemini to put together a summary.</p>
<blockquote>
<p>Thank you so much for the 312 responses we received. Looking over the results, it is clear that today’s engineers have a healthy, well-rounded set of interests, covering technology, organization, business, and even personal goals.</p>
<p>The most common responses were in the &quot;SRE &amp; Organizational Culture&quot; category, which shows that people see SRE not just as a technical role but as part of the culture of the team and the entire organization, and that they are strongly motivated to foster and develop this culture. This was closely followed by &quot;AI Utilization&quot; and &quot;Technology &amp; Service Improvement,&quot; indicating a balance between the desire to explore cutting-edge technology and a strong awareness of SRE’s core responsibility of improving service reliability. The fact that these three categories ranked at the top in almost equal numbers symbolizes a very balanced view of engineering.</p>
<p>There were also many responses related to &quot;Business &amp; Career&quot; and &quot;Speaking &amp; Output,&quot; which was notable because it showed that many people view their role from a broader perspective, going beyond their daily work to contribute to the business and give back to the community.</p>
<p>Finally, the &quot;Other&quot; category clearly reflected values that prioritize well-being, including health, personal life, and individual dreams, alongside work.</p>
<p>Overall, the survey results were highly insightful, painting a picture of the modern, mature engineer—someone who pursues technical excellence, contributes to the organization, people, and business, and also seeks fulfillment in their personal life.</p>
</blockquote>
<p>The above are the survey results and summary. The theme of SRE NEXT 2025 is &quot;Talk NEXT.&quot; This is why we decided to conduct this survey with a topic related to the theme, with the hope of engaging in a dialogue with people who visited our booth. By choosing a highly abstract topic, we were able to have conversations not only with SREs, but also with engineers in other roles, and even with non-engineers. I feel it turned out to be a very good topic.</p>
<p>Many of the responses touched on generative AI, which has been a recent trend, and I got the impression that many people are thinking about how to apply AI in the SRE field. As for myself, while I can collect system metrics, I am still not making full use of that data to support decision-making, influence the business, or contribute to operations. I believe that being able to do so would make my role as an SRE even more interesting, and I would like to take on that challenge. I could relate to many of the other responses as well, and I enjoyed having conversations at the booth on the day. Thank you very much!</p>
<h1>In Closing</h1>
<p>Once again, thank you very much to everyone who visited our booth! We were delighted to welcome such a large number of visitors.</p>
<p>Many thanks also to the SRE NEXT organizers for putting on a wonderful event. I believe the stamp rally and other activities played a big part in bringing so many people to our booth. I hope to be involved in some way in next year&#39;s SRE NEXT as well.</p>
<p>See you again at SRE NEXT next year!</p>
<h1>We Are Hiring!</h1>
<p>KINTO Technologies is looking for new teammates to join us! We are happy to start with a casual interview. If you’re even a little curious, please apply using the link below! <a href="https://hrmos.co/pages/kinto-technologies">https://hrmos.co/pages/kinto-technologies</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/kasai/20250731/cover_image.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[iOSDC Japan 2025にゴールドスポンサーとして協賛しました！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-09-24_iosdc2025-sponsored/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-09-24_iosdc2025-sponsored/</guid>
            <pubDate>Tue, 30 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[iOSDC Japan 2025にゴールドスポンサーとして協賛しました！]]></description>
            <content:encoded><![CDATA[<p>KINTOテクノロジーズ（以下KTC）で my route（iOS）を開発しているRyomm（<a href="https://x.com/__ryomm">@__ryomm</a>）です。
2025年9月19-21日の3日間にわたって開催されたiOSDC Japan 2025にゴールドスポンサーとして協賛しました✨</p>
<p>昨年に引き続き、2回目の協賛となります。
1年の間に様々なカンファレンスでスポンサー出展してノウハウを身につけた技術広報の力を借りつつ、iOSアプリ開発に携わるエンジニアが中心となって準備を進めてきました。社内のクリエイティブ室と協力して作成した、こだわりのノベルティやブース企画を紹介します。</p>
<h2>「アプリのひみつ アプリ内製開発ストーリー」冊子</h2>
<p><img src="/assets/blog/authors/ryomm/2025-09-24/04.jpeg" alt="アプリのひみつ アプリ内製開発ストーリー"></p>
<p>こちらはノベルティBOXに封入した特製冊子です。
昨年の反省を踏まえ、今年は紙媒体にしようと決めていました。
しかし、ただのチラシというのも味気なくつまらないと思ったので、絵本のような質感で手に取りたくなる冊子を目指して制作しました。</p>
<p>予算との戦いがありつつも、検討を重ねた末にやわらかい質感の「ヴァンヌーボVG スノーホワイト」という用紙を採用しました。KTCで開発しているアプリのストーリーを楽しく紹介しています。
ぜひご一読いただき、KTCのアプリ開発に触れてみてください。</p>
<h2>ブース企画</h2>
<p>今回のブースのテーマは「KTCを知ってもらう」でした。
今年度のKTCでは <a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">AIファースト</a> が注力テーマのひとつに据えられています。
そこから、案出しのマンダラートにて「VTuberのようなキャラクターが喋っていたらインパクトがあるのではないか？」というアイディアが生まれ、紆余曲折を経てKTCのAI広報「るぴあ」がブースでじゃんけんをすることになりました。</p>
<p>今回るぴあをブースに立たせるにあたって、クリエイティブ室の桃井さん（<a href="https://x.com/momoitter">@momoitter</a>）に全面協力いただきました。
るぴあ誕生秘話についてはこちらの記事をご覧ください。
<a href="https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/">https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/</a></p>
<p>るぴあをブースに立たせるにあたって等身大になる大型のサイネージを購入したのですが、会場でストリーミング再生をするのは品質に不安があったためオフラインで動画をシームレスに再生できるようにMacアプリを作成しました。
こちらのMacアプリはヒロヤさん（<a href="https://x.com/TRAsh___">@TRAsh___</a>）との共作です。</p>
<p>プロトタイプ版はGemini CLIを利用して1時間半ほどで完成しました。</p>
<p><img src="/assets/blog/authors/ryomm/2025-09-24/01.gif" alt="プロトタイプ版"></p>
<p>プロトタイプ版では <a href="https://developer.apple.com/documentation/avkit/videoplayer"><code>VideoPlayer</code></a> に <a href="https://developer.apple.com/documentation/AVFoundation/AVPlayer"><code>AVPlayer</code></a> を渡し、AVPlayerItemを差し替えて動画を再生しています。
待機・呼び込み動画はそれぞれ何種類かあり、全てデフォルトポジションで始まり、デフォルトポジションに戻るようにすることで動画をなめらかに繋げられるようにしました。
ただこちらの実装では動画間のチラつきが激しく、シームレスな再生とは言えません。</p>
<p>そこで <code>AVPlayer</code> から <a href="https://developer.apple.com/documentation/avfoundation/avqueueplayer"><code>AVQueuePlayer</code></a> に変更し、次の動画まであらかじめキューに詰めておくことで事前にロードし、シームレスに動画が再生されるように改修しました。</p>
<p><img src="/assets/blog/authors/ryomm/2025-09-24/02.gif" alt="プロトタイプ版"></p>
<p>じゃんけんモードへの移行/勝ち負け動画への移行等に関してはキューを割り込ませる必要があり、若干のチラつきは発生しますが、その他の部分ではきれいにつながるようになったと思います。</p>
<p>さて、そんなじゃんけんを勝ち抜いた猛者の方々にはトミカ、残念ながら負けてしまった方々にはありがとうめぐリズムをプレゼントしていました。
遊びにきていただいたみなさま、ありがとうございました！またどこかでパワーアップしたるぴあと遊んでくださいね。
<a href="https://x.com/KintoTech_Dev/status/1969236820270420032">https://x.com/KintoTech_Dev/status/1969236820270420032</a></p>
<p>さらに、じゃんけんだけではコミュニケーションに不安があったため、話のタネとして「iOSアプリ開発で楽しいことは？」というテーマでアンケートも実施していました。</p>
<p><img src="/assets/blog/authors/ryomm/2025-09-24/05.jpeg" alt="アンケート"></p>
<p>こちらにもたくさんご回答いただき、ありがとうございました！</p>
<p>最後に、参加したKTCのメンバーで記念写真を撮りました📸 
<img src="/assets/blog/authors/ryomm/2025-09-24/03.jpg" alt="みんなでパシャ"></p>
<h2>登壇情報</h2>
<p>弊社から2名登壇しておりました🎉</p>
<ul>
<li><p>iPhoneを用いたフライトシム用ヘッドトラッカーの自作事例 by Felix Chon
<a href="https://fortee.jp/iosdc-japan-2025/proposal/dfe5819b-6eb7-4880-a89e-411a839b794c">https://fortee.jp/iosdc-japan-2025/proposal/dfe5819b-6eb7-4880-a89e-411a839b794c</a></p>
</li>
<li><p>QRコードの仕様ってn種類あんねん by Ryomm
<a href="https://fortee.jp/iosdc-japan-2025/proposal/a1ddb24c-ecc2-4db7-8bce-5a002a1489e1">https://fortee.jp/iosdc-japan-2025/proposal/a1ddb24c-ecc2-4db7-8bce-5a002a1489e1</a></p>
</li>
</ul>
<p>ぜひニコ生タイムシフトやYoutubeからご覧ください。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ryomm/2025-09-24/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Attending the 10x Innovation Culture Pitch Practice Session at Google]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-23-google-10x-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-23-google-10x-en/</guid>
            <pubDate>Mon, 29 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Attending the 10x Innovation Culture Pitch Practice Session at Google]]></description>
            <content:encoded><![CDATA[<p>Hello, this is Hoka winter.</p>
<p>For about a year, KINTO Technologies (KTC) has been running the 10X Innovation Culture Program <a href="https://x.com/googlejapan/status/1698591724984275216">announced by Google Cloud Japan G.K. in September 2023</a> to foster an innovation-driven organizational environment.</p>
<p>This time, aside from the usual 10X, I will talk about the 10x Innovation Culture Pitch practice session we attended.</p>
<p><strong>What is the 10x Innovation Culture Pitch Practice Session?</strong> The purpose of this training is to develop the facilitation skills necessary to implement the &quot;10X Innovation Culture Program&quot; within your company. To do this, you need a deep understanding of the 10X Innovation Culture Program. This training is designed to deepen that understanding.</p>
<p>This was our second time taking part in the training. Last time, the training was mainly attended by managers. Since then, the progress of 10X has been dramatic, so this time, volunteer members—mainly team leaders—took part.</p>
<p>The 10x Innovation Culture Pitch practice session is broadly divided into two parts. One part involves “learning the six elements for creating innovation,” and the other focuses on “expressing the six elements in our own words.”</p>
<p>![](/assets/blog/authors/hoka/20250714/image6.png =600x)</p>
<p><strong>Preparation for the Training</strong></p>
<p>What I&#39;ve learned about 10X from Google people is that the difficulty level for KTC gradually increases.</p>
<p>In the first training session, all KTC employees “merely participated,” but in the second session, KTC employees took on the role of presenters for the culture session. In other words, they play an important role as presenters who convey the six elements for creating innovation to other participants.</p>
<p>![](/assets/blog/authors/hoka/20250714/image3.png =600x)</p>
<p>Thankfully, the presentation slides were prepared by the people at Google, so all we at KTC had to do was read out the six elements.</p>
<p>Even though it was just that simple, it was incredibly difficult!!!</p>
<p>The six elements contain many of Google&#39;s ideas and examples of how to be an innovative organization. However, simply reading them will not reach the hearts of participants. We practiced many times until we could speak in our own words, incorporating episodes from KTC and our own experiences.</p>
<p>In particular, we remembered the Google presenters from the first training session and focused on speaking confidently and at an easy-to-follow pace.</p>
<h3>** On the Day of the Training**</h3>
<p>The day has finally arrived. 27 people gathered at the Google office in Shibuya. Participants once again joined from Osaka, Nagoya, and Tokyo.</p>
<p>The day kicked off with an opening talk by Google’s Kota-san. Many thanks to Kota-san, as always.</p>
<p>Next, Kissy, the manager who is the main leader of 10X, shared an encouraging message online from the Nagoya office.</p>
<p>Amid an atmosphere of “Huh? What’s starting now? What is this training?” we, the presenters, took turns announcing themes one by one.    Can we get the participants to understand 10X?</p>
<p>Awacchi presented with an original story, I was overly nervous, Yukiki appeared online, Nabeyan was calm like a teacher, Mizuki gave the best performance on the actual day as usual, and Otake had the composure to make others laugh. Everyone performed their best on this very day (if we do say so ourselves).</p>
<p>In the post-event survey, as many as 10 participants chose &quot;The culture session was great.” I was also happy to hear comments like &quot;It was just as great as the last Google presenters&quot; and &quot;The talk flowed so naturally—I could follow everything just by looking at the slides and listening to the presentations.&quot;  </p>
<p><img src="/assets/blog/authors/hoka/20250714/image5.png" alt=""></p>
<p>Next was the output session.</p>
<p>Each team, consisting of six people plus one Googler, moved to their assigned room, and just like the earlier presenters, each person took turns giving a presentation. It was an intensive output time of 20 minutes × 6 people, totaling 120 minutes.</p>
<p><img src="/assets/blog/authors/hoka/20250714/image2.png" alt=""></p>
<p>The participants used the same slides that the presenters had used earlier, and each gave a 10-minute presentation. There was a 5-minute preparation time before each presentation.</p>
<p>While listening to the presentations, other members filled out feedback sheets with points they liked and points requiring improvement, and then provided feedback after each presentation.</p>
<p>![](/assets/blog/authors/hoka/20250714/image1.png =600x)</p>
<p>I was part of Team D, and they were so good that I couldn’t help but wonder, &quot;Did they practice at home?&quot;  During the feedback time, we naturally discussed the good points of the presentations, and the discussion became lively. For example, the following comments were made:</p>
<ul>
<li>Speak while summarizing, without being bound by slides or a script.</li>
<li>Speak in your own words.</li>
<li>Stories of failure tend to resonate with the audience.</li>
<li>Be empathetic to the audience and avoid imposing too much of a lecturing tone.</li>
<li>Catchy phrases like &quot;Motivation Switch” are effective and make things easy to understand.</li>
</ul>
<p>![](/assets/blog/authors/hoka/20250714/image7.png =600x)</p>
<p>In the post-presentation survey, satisfaction with the program was very high, with an average score of 4.7.</p>
<p>The following points were selected as “positive aspects of the program content.”: (n=22, multiple answers allowed)</p>
<ul>
<li>It was good to be able to listen to other participants&#39; presentations: 20 people  </li>
<li>I was glad to have the opportunity to practice myself: 17 people  </li>
<li>It was great to receive feedback from others: 21 people</li>
</ul>
<h3><strong>Closing</strong></h3>
<p>After the presentations, we held a wrap-up session in the original seminar room. While I wondered how the other groups were doing, Google people summarized the earlier feedback sheets for us using generative AI, &quot;Gemini.&quot;  </p>
<p>![](/assets/blog/authors/hoka/20250714/image4.png =600x)</p>
<p>I was planning to check the feedback sheets from the other groups later, but they were instantly converted to text via Gemini and shared with everyone on the spot. It truly was a “Feedback is a gift!” moment.</p>
<p>Not only did we learn the training content itself, but we also gained a lot of tips on how to be more efficient—such as how to install tools quickly, how to make use of feedback sheets, and how to share information from other groups.</p>
<p>Thank you so much to everyone at Google.</p>
<h3><strong>Looking Ahead</strong></h3>
<p>Through this training, we found that the high-level 10x Innovation Culture Pitch practice sessions are effective even for non-managerial members. So, we plan to implement them at KTC in FY2025.</p>
<p>KTC’s challenge to foster innovation is far from over.</p>
<p>![](/assets/blog/authors/hoka/20250714/image8.png =600x)</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Hardening Designers Conference 2025 Event Report]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-01-HardeningConference2025-Report-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-01-HardeningConference2025-Report-en/</guid>
            <pubDate>Fri, 26 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA["Divides" in strengthening security and in-house efforts]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello! This is Otaka from the Cloud Security Group in KINTO Technologies. On a daily basis, I work on setting up guardrails for our cloud environments and keeping them safe through monitoring and improvements with CSPM and threat detection.</p>
<p>To stay up to date on the latest security trends, I joined the <a href="https://wasforum.jp/2025/06/hardening-designers-conference-2025/">Hardening Designers Conference 2025</a>. Here&#39;s my report from the event.</p>
<h1>What is the Hardening Project?</h1>
<p>The Hardening Project is a competitive event aimed at improving practical cybersecurity skills. Participants run vulnerable systems while defending against, recovering from, and improving them in response to external attacks, building real-world incident response skills in the process. What sets it apart is that it evaluates not only technical skills but also overall incident response capabilities, including teamwork, documentation readiness, and the establishment of an operational system.</p>
<p>The Hardening Designers Conference 2025 that I participated in this time is a hands-on and conference event themed &quot;invisible divide.&quot; It served as preparation for the competitive event in October.</p>
<h1>Day1 Hands-on Program</h1>
<p>In the hands-on session, we experienced an attack technique called &quot;Living off the Land.&quot; This technique involves attackers abusing legitimate, built-in tools and functions already in a system to gain access and carry out harmful activities. For example, in a Windows environment, they use PowerShell, WMI, etc. to conduct the attack.</p>
<p>The key is that they use built-in tools and functions, not files brought in from outside. This makes it hard to tell their activities apart from normal operations, and difficult for security tools to detect.</p>
<p>Some of the commands used in the attack were ones I&#39;d relied on back in my days as a system administrator. If a tool or command isn&#39;t used in normal operations, disabling it might help. But for those frequently used and hard to turn off, the only real option may be to log and monitor them closely.</p>
<p>The workshop was a real eye-opener, showing just how sophisticated server attacks have become, and how blurred the line is between malicious activities and legitimate operations.</p>
<h1>Day2 &amp; 3 Conference Program</h1>
<p>On Day 2 and 3, a variety of lightning talks and sessions delved into the theme of &quot;divide&quot; in the context of cybersecurity, with speakers sharing the latest technologies, introducing new initiatives, and hosting lively discussions.</p>
<p>In the security field, there are often &quot;divides&quot; between different stakeholders, and these can become obstacles that hinder Hardening (security fortification) efforts. For example, divides like the following often arise in actual operations:</p>
<p><img src="/assets/blog/authors/k.otaka/Techblog-Hardening-1.png" alt="セキュリティ分断の概要図"></p>
<h3>Divide between Development, Operations, and Security</h3>
<p>Sometimes, the focus on implementing features and improving operational efficiency can push security to the back seat. For instance, poor password management or weak account controls can create security vulnerabilities. To avoid this, think of security not as a &quot;restriction&quot; but as part of overall &quot;quality,&quot; and make sure security requirements are built in from the very start through security-by-design and shift-left practices.</p>
<h3>Divide between System Users and Developers/Operators</h3>
<p>While system users desire ease of use, they may not fully grasp the importance of security. Engineers, on the other hand, often find themselves caught in the middle, trying to balance user requests for features with the need to keep the system secure. To bridge this gap, it is necessary to educate users and maintain careful communication with them during system development and operation, fostering their understanding of security.</p>
<h3>Divide between Rule Makers and Implementers</h3>
<p>Security personnel who set rules often draw on a range of guidelines from public bodies and specialist organizations to define ideal baselines and rules. However, for those on the ground such as system development and operations teams, system constraints and operational workload can make it hard to implement them as intended. To put this into practice, it is important to take into account constraints and operational loads and take a flexible approach so that security can be implemented properly.</p>
<h3>Divide between Attackers and Defenders</h3>
<p>While attackers use technological innovation and teamwork to launch increasingly sophisticated attacks, defenders can end up reacting too slowly because of costs or a lack of understanding among stakeholders. Companies hit by cyberattacks are often reluctant to disclose details, which means valuable knowledge that could prevent similar incidents is not shared in many cases. The defense side would also like to strengthen information sharing and cooperation, but things are not going as smoothly as expected.</p>
<h3>Divide between AI and Humans</h3>
<p>Efforts to utilize generative AI are spreading in the IT field, from writing program code to upgrading SOC operations. But in reality, AI often can&#39;t take security into account without clear and specific instructions, . Generative AI has come a long way, yet there still seems to be a gap between what humans and AI can do. To utilize AI properly, we still need human know-how, like designing effective prompts and setting up guardrails.</p>
<p>When we think about it again, we can see just how many kinds of divides exist. I had never looked at security from this angle before, so this was genuinely insightful.</p>
<h1>Overcoming the Divides—KINTO Technologies&#39; Approach</h1>
<p>At the Cloud Security Group, our basic policy is &quot;security for business,&quot; and we believe that security should accelerate business operations, not slow them down.  </p>
<p>We work on security from the following two key angles: </p>
<ul>
<li>Preventative guardrails: We provide security preset account environments with the minimum required security settings already implemented before handing them to development teams. This helps support secure design from the very beginning.</li>
<li>Detective guardrails: We use SOC monitoring with tools such as Sysdig, AWS Security Hub, and Amazon GuardDuty to detect and respond to threats in real time. Through regular Posture management, we also conduct <i>kaizen</i> activities to improve problematic configurations.</li>
</ul>
<p>Through these security measures and operations, we are working to create an environment where developers can focus on their work with peace of mind, while adhering to our company&#39;s security guidelines and ensuring the necessary security. In short, this is an effort to bridge the divide between rule-makers and implementers, and between development, operations, and security teams.</p>
<p>We have also begun taking gradual steps toward AI security (see details <a href="https://blog.kinto-technologies.com/posts/2025-06-16-aispm/">here</a>). However, with technology and trends evolving so rapidly, it feels as though we are currently a little on the back foot. Within the company, the use of generative AI in business operations and its implementation into products are advancing actively, and the challenge ahead will be determining how to implement effective controls while overcoming the divide with AI.  </p>
<p>Furthermore, we are working to review our mindset toward system development projects at KINTO Technologies, drawing on the IPA&#39;s key points for requirement clarification with a house-building analogy as a reference. This is not limited to system construction; from a security perspective as well, it is an effort to be mindful of the divide between system users and engineers, and to foster better relationships and results. For more information about IPA house-building, please see <a href="https://www.ipa.go.jp/archive/files/000065172.pdf">here (in Japanese)</a>.</p>
<h1>Summary</h1>
<p>Through the Hardening Designers Conference 2025, I had the valuable opportunity to learn about security trends from the perspective of divide, something I had not consciously considered before. By looking at my own organization&#39;s security through the same lens of divide, I was also able to reaffirm our current initiatives.</p>
<p>Going forward, I hope to continue and refine our efforts to overcome divides and achieve better security.</p>
<h1>Lastly</h1>
<p>Our Cloud Security Group is looking for people to work with us. We welcome not only those with hands-on experience in cloud security but also those who may not have experience but have a keen interest in the field. Please feel free to contact us.</p>
<p>For additional details, <a href="https://hrmos.co/pages/kinto-technologies/jobs?category=2037393016523087875">please check here (in Japanese).</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[A Report (Management Perspective) on Participation in iOSDC Japan 2023]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-09-14-iOSDCreport-operation-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-09-14-iOSDCreport-operation-en/</guid>
            <pubDate>Thu, 25 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I participated in iOSDC Japan 2023! This is a report written from the perspective of event management by a non-engineer.]]></description>
            <content:encoded><![CDATA[<p>Hello everyone, I’m Mori from the Global Development Division and the Tech Blog team. I usually work as a web product manager (PdM) in the Global Development Division. I, along with several colleagues from my company, attended <strong>iOSDC Japan 2023</strong> held from September 1 to 3, 2023!</p>
<p>Since an iOS engineer who attended the iOSDC Japan 2023 will cover the content of the interesting sessions, I’ll share my impressions from an event management perspective😎 (#iwillblog is putting pressure on me lol)</p>
<p><a href="https://iosdc.jp/2023/">https://iosdc.jp/2023/</a></p>
<h1>Reason for Participation</h1>
<p>Why did I participate in iOSDC even though I&#39;m not an iOS engineer?</p>
<p>In fact, recently the Tech Blog team has been supporting the operation of study sessions for external audiences and planning internal events. Recently, we have supported the operation of events such as the <a href="https://blog.kinto-technologies.com/posts/ktc-meet-up-support/">KINTO Technologies Meetup!</a> hosted by the Corporate IT team and the <a href="https://blog.kinto-technologies.com/posts/2023-09-05-%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88-DBRE-Summit-2023/">DBRE Summit 2023</a> organized by the DBRE team. </p>
<p>We are still a relatively new team, so even during planning and events, we are full of concerns like,
“How can we make the participants enjoy more?”
“How can we facilitate the event better?” 🤔  </p>
<p>Hearing the rumor that iOSDC is incredibly exciting, I infiltrated the conference for three days to learn about its planning ideas and how to create such a lively atmosphere!!</p>
<p>Our iOS engineers reported on the 2022 iOSDC, so please be sure to check it out as well 👍</p>
<p><a href="https://blog.kinto-technologies.com/posts/2022-10-13-iosdc2022_participation_report/">https://blog.kinto-technologies.com/posts/2022-10-13-iosdc2022_participation_report/</a></p>
<h1>Reception</h1>
<p>Upon entering the venue, we first checked in and received name cards. These cards contain QR codes and NFC chips for entrance and exit management. QR codes are used for managing entrance and exit at the venue, while NFC chips allow attendees to collect name badges via an app. This idea is great. I better take notes.📝</p>
<p><a href="https://blog.iosdc.jp/2023/09/01/name-card-nfc-tag-exchange-2023/">https://blog.iosdc.jp/2023/09/01/name-card-nfc-tag-exchange-2023/</a></p>
<p><img src="/assets/blog/authors/M.Mori/20230926/iOSDC_card.jpg" alt="iosdc_namecard"></p>
<p>In fact, on the second day, I completely forgot my name card, but even then, they smoothly gave me a new one, kindly saying, “You forgot your name badge.” I was impressed by their fine consideration. I better take notes.📝</p>
<h1>Sessions</h1>
<p>The sessions were held across four rooms: two large and two small ones. Attendees were able to participate in the sessions they wanted.</p>
<p>🔻Venue layout: I listened to the talks on the second floor. The first floor was for booths for communication with sponsors. Drinks were available. <img src="/assets/blog/authors/M.Mori/20230926/iOSDC_map.jpg" alt="会場図"></p>
<p>I had confirmed the <a href="https://fortee.jp/iosdc-japan-2023/timetable">timetable for the day</a> before the event, and I was impressed by how much effort went into deciding which talks to give at which times and in which rooms.  With four rooms running sessions simultaneously, the time for each session would vary depending on its content, and the days the speakers could attend would also differ. This coordination skill is something I want to acquire.</p>
<p>The title and speaker name of each session were read aloud via recording by <a href="https://osawa-inc.co.jp/men/tachikifumihiko/">Fumihiko Tachiki</a>, a famous voice actor known for narrations in TV programs such as The Quest a.k.a. “Sekai no Hate Made Itte Q!” Both the audience and the speakers got excited🥳</p>
<h1>Online Distribution</h1>
<p> By purchasing an on-site attendance ticket, attendees could also watch the sessions online. (There were also tickets available for online viewing only.)</p>
<p> The online sessions were streamed via Niconico Live. I watched online the sessions I couldn&#39;t attend in person, and what surprised me was that they were streamed almost in real time! It may depend on the device or environment, but the time lags were probably less than five seconds. I was able to chat with on-site participants in real time via Slack.</p>
<p>What was impressive was that, during the venue changeover, the streaming screen not only showed commercials from sponsors but also footage of the staff working during the preparation period. We also started hybrid streaming from the August event, so I was watching it while thinking that it might be interesting to stream something like this during the break in our next event.</p>
<h1>Lightning Talk (LT) Session</h1>
<p>Even before attending, I was curious about the schedule, which had six or seven 5-minute lightning talks. <img src="/assets/blog/authors/M.Mori/20230926/iODSC_LT_TT.png" alt=""> </p>
<p>Since I also usually facilitate internal events and meetings, I was thinking, “Is it really possible to pull off this kind of agenda?” — but they did it!😂 </p>
<p>First of all, I thought the most difficult thing would be to finish the presentation on time. Their ingenuity on this point is amazing…! When the 5-minute limit approached, music played to create a sense of urgency for each speaker, and the audience was instructed to wave <strong>penlights</strong>. It was a great idea to encourage the speakers while ensuring they stick to their allotted time. Plus, it’s fun for those waving penlights…! (It was a production style different from last year.)</p>
<p> <img src="/assets/blog/authors/M.Mori/20230926/iOSDC_penlight.jpg" alt="iOSDC_penlight"> *They cheered for the speakers with penlights in their signature colors. *</p>
<p>To ensure each 5-minute speech proceeded smoothly, no time was allocated for Q&A; instead, a system was set up for attendees to approach the speakers later at other booths. The only preparation needed between speakers was setting up their presentation materials. During this time, the audience are informed of the next speaker’s “signature color” so that they can prepare their penlights accordingly. Of course, that alone would have left some extra time, so the emcee skillfully introduced booths and shared behind-the-scenes stories about the speakers, making the waiting time feel short for the audience. 👏</p>
<p>Of course, the production was great, but this Lightning Talk (LT) Session was also very interesting in terms of content.  Since the session was supposed to be cut off after five minutes, it was impressive to see how each speaker came up with creative ways to summarize it.  While some speakers probably didn&#39;t get to say everything they wanted to say, their time management skills were impressive, and it didn&#39;t really feel that way from the audience.</p>
<p>I think most people who have experience with presentations or speeches would feel that it’s incredibly difficult to concisely summarize what they want to say in a short time. 😭 I myself tend to talk at length because I’m quite chatty. Structuring a talk with a clear beginning, middle, and end—while also adding a touch of humor—makes this kind of event an incredibly valuable opportunity for a great presentation!</p>
<p>Also, giving a short presentation helps sharpen time management skills. Watching them glance at the remaining time and instantly decide things like “I’ll cut this part,” all while effectively conveying their main points, made me think, “They must also be really good at facilitation.”</p>
<p>As the Tech Blog team also aims to improve the employees’ output skills, I’d like to incorporate opportunities like this within the company as well.😎✨ </p>
<p><img src="/assets/blog/authors/M.Mori/20230926/iOSDC_penlight_2.jpg" alt="iOSDC_penlight_team"> <em>Tech blog members enjoying penlights</em></p>
<h1>Doing What I Want to Do = Maybe Everyone Will Have Fun…!?</h1>
<p>This is my simple impression from attending iOSDC, but overall, it was a very meaningful conference even for non-engineers like me. Of course, I don’t understand technical details, but my desire to improve our company’s products is the same as that of the engineers. I participated to learn about event management, but from a PdM perspective, I also realized, “So this is what engineers are thinking,” and thought, “If we incorporate these ideas into our products, they might improve even more!” From the perspective of my original purpose—event management—it was an extremely valuable conference. 🤩</p>
<p>During the social gathering, I fortunately had the opportunity to talk with <a href="https://twitter.com/tomzoh">Mr. Hasegawa</a>, the chairperson of the executive committee. When I asked him about overall event planning and efforts to make an event exciting, he said, <strong>“I’m just embodying what I want to do myself.”</strong> I thought, this is really a profound truth. Even now, after the event has ended, it really resonates with me.</p>
<p>Before participating, I was worried about “How could I make everyone enjoy our event?” but then I came up with a new idea: “If I try doing what I find interesting, maybe everyone else will enjoy it too.”  </p>
<p>Of course, whether it resonates with the audience or not is another matter, but gaining this new perspective has ignited my passion to plan and manage various events going forward. 🔥🔥🔥</p>
<p>At KINTO Technologies, we will continue planning events for external audiences. We will provide information as needed through <a href="https://kinto-technologies.connpass.com/">Connpass (in Japanese)</a> and other channels, so please feel free to join if you’re interested. ✨</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/M.Mori/20230926/iOSDC_2023_logo.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[My Experience Trying Out Strands Agents]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-18-try-strands-agent-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-18-try-strands-agent-en/</guid>
            <pubDate>Thu, 25 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[What happened when a converse API user tried out strands agents]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello!   </p>
<p>I’m Uehira from the DataOps Group in the Data Strategy Division at KINTO Technologies.<br>I’m mainly responsible for the development, maintenance, and operation of our internal data analytics platform and an in-house AI-powered application called &quot;cirro.&quot;   </p>
<p>&quot;cirro&quot; uses Amazon Bedrock for its AI capabilities, and we interact with it via the AWS Converse API.<br>In this article, I’ll share how I tested Strands Agents in a local environment to explore tool integration and multi-agent functionality for potential use in &quot;cirro.&quot;</p>
<h2>Intended Audience</h2>
<p>This article is intended for readers who have experience using Amazon Bedrock via the Converse API or Invoke Model API.</p>
<h2>What Is Strands Agents?</h2>
<p>Strands Agents is an open-source SDK for building AI agents, released on May 16, 2025, on the AWS Open Source Blog.<br>The diagram below is from the official Amazon Web Services blog:</p>
<p><img src="/assets/blog/authors/kamihira/202508-try-strands-agents/AWS-agentic-loop.png" alt="AWSブログからの引用図"></p>
<p>As shown in the diagram, implementing an AI that can use tools requires a processing structure known as an Agentic Loop.<br>This loop allows the AI to determine whether its response should go directly to the user or whether it should take further action using a tool.   </p>
<p>With Strands Agents, you can build AI agents that include this loop without needing to implement it manually.</p>
<p><a href="https://aws.amazon.com/jp/blogs/news/introducing-strands-agents-an-open-source-ai-agents-sdk/">Source: Introducing Strands Agents – An Open-Source AI Agent SDK</a> </p>
<h2>Running Strands Agents in a Local Environment</h2>
<p>*This section assumes that you have prior experience using Bedrock via the Converse API or similar tools.<br>Therefore, basic setup steps such as configuring model permissions are omitted. Exception handling is also skipped, as this is a sample implementation.</p>
<h3>Setup</h3>
<ul>
<li>Libraries Install the required libraries with the following command:</li>
</ul>
<p><code>pip install strands-agents strands-agents-tools</code></p>
<h3>Execution ①</h3>
<p>If you&#39;re lucky, the following minimal code might work:   </p>
<pre><code>from strands import Agent
agent = Agent()
agent(&quot;こんにちは！&quot;) 
</code></pre>
<p>This code appears in many blog posts, but it didn&#39;t work in my environment. 😂<br>Which makes sense—after all, it doesn’t specify the model or the Bedrock region...</p>
<h3>Execution ②</h3>
<p>To call the model properly, you need to explicitly specify the model and region like this:<br>In this example, we assume an environment similar to ours, where you log in via SSO and obtain permissions through a switch role.   </p>
<h4>【Point】</h4>
<ul>
<li>Make sure the model and region you specify are accessible with the assumed role.<br>Example: Model: anthropic.claude-3-sonnet-20240229-v1:0 Region: us-east-1 *The region is specified in the profile when creating the session.</li>
</ul>
<pre><code>import boto3
from strands import Agent
from strands.models import BedrockModel

if __name__ == &quot;__main__&quot;:
   
    # セッション作成
    session = boto3.Session(profile_name=&#39;&lt;スイッチ先のロール&gt;&#39;)

    # モデル設定
    bedrock_model = BedrockModel(
        boto_session=session,
        model_id=&quot;us.amazon.nova-pro-v1:0&quot;,
        temperature=0.0,
        max_tokens=1024,
        top_p=0.1,
        top_k=1,
        # Trueにするとストリーミングで出力される。
        # ストリーミングでツール利用がサポートされないモデルがあるため、OFF
        streaming=False 
    )

    # エージェントのインスタンスを作成
    agent = Agent(model=bedrock_model)

    # 質問を投げる
    query = &quot;こんにちは！&quot;
    response = agent(query)
    print(response)
</code></pre>
<p>Now you can call Bedrock with parameters like temperature, just like you would with the Converse API. 🙌<br>But if you&#39;re using Strands Agents, of course you&#39;ll want to <strong>call a tool</strong>!   </p>
<h3>Execution ③</h3>
<p>If you define a tool as shown below, the agent will use the appropriate tool based on the question and return a response after executing the Agentic Loop.</p>
<h4>【Point】</h4>
<ul>
<li>The function you want to use as a tool is decorated with &quot;@tool&quot;.</li>
<li>Tools are passed as a list of functions, like this: <code>Agent(model=bedrock_model, tools=[get_time])</code></li>
</ul>
<pre><code>import boto3
from strands import Agent
from strands.models import BedrockModel

#------ツール用に読み込んだライブラリ------------
from strands import tool
from datetime import datetime


# ツールの定義
@tool(name=&quot;get_time&quot;, description=&quot;時刻を回答します。&quot;)
def get_time() -&gt; str:
    &quot;&quot;&quot;
    現在の時刻を返すツール。
    &quot;&quot;&quot;
    current_time = datetime.now().strftime(&quot;%Y-%m-%d %H:%M:%S&quot;)
    return f&quot;現在の時刻は {current_time} です。&quot;

if __name__ == &quot;__main__&quot;:

    # セッション作成
    session = boto3.Session(profile_name=&#39;&lt;スイッチ先のロール&gt;&#39;)

    # モデル設定
    bedrock_model = BedrockModel(
        boto_session=session,
        model_id=&quot;us.amazon.nova-pro-v1:0&quot;,
        temperature=0.0,
        max_tokens=1024,
        top_p=0.1,
        top_k=1,
        # Trueにするとストリーミングで出力される。
        # ストリーミングでツール利用がサポートされないモデルがあるため、OFF
        streaming=False 
    )

    # ツールを使用するエージェントのインスタンスを作成
    agent = Agent(model=bedrock_model, tools=[get_time])

    # 質問を投げる。 ツールを使用しないとAIは時刻が判別できない。
    query = &quot;こんにちは！ 今何時？&quot;
    response = agent(query)
    print(response)
</code></pre>
<p>In my environment, I got the following response:</p>
<pre><code>&lt;thinking&gt; 現在の時刻を調べる必要があります。 そのためには、`get_time`ツールを使用します。 &lt;/thinking&gt;

Tool #1: get_time
Hello! 現在の時刻は 2025-07-09 20:11:51 です。 Hello! 現在の時刻は 2025-07-09 20:11:51 です。
</code></pre>
<h2>Advanced Use</h2>
<p>Regarding the tool, in the previous example, it simply returned logic-based output.<br>However, if you create an agent within the tool and incorporate additional logic, such as having the agent verify a response,<br>you can easily build a <strong>multi-agent</strong> system where one AI calls another.   </p>
<p>Here’s a modified version of the tool that returns not only the current time, but also a trivia fact provided by a child agent:   </p>
<h4>【Point】</h4>
<ul>
<li>We’re reusing the <code>session</code> object declared in the global scope under <code>if __name__ == &quot;__main__&quot;:</code>.<br>If you don&#39;t do this, model setup takes about a minute in my environment.<br>This is probably due to the time required to allocate resources.</li>
</ul>
<pre><code>@tool(name=&quot;get_time&quot;, description=&quot;現在日時と、日時にちなんだトリビアを回答します。&quot;)
def get_time() -&gt; str:
    &quot;&quot;&quot;
    現在の時刻を返すツール。

    注意：この関数では boto3.Session を使った BedrockModel の初期化に
    グローバルスコープで定義された `session` 変数が必要です。
    `session` は `if __name__ == &quot;__main__&quot;:` ブロックなどで事前に定義しておく必要があります。
    &quot;&quot;&quot;

    current_time = datetime.now().strftime(&quot;%Y-%m-%d %H:%M:%S&quot;)
    
    # モデル設定
    bedrock_model = BedrockModel(
        boto_session=session,
        model_id=&quot;us.anthropic.claude-sonnet-4-20250514-v1:0&quot;,
        temperature=0.0,
        max_tokens=1024,
        top_p=0.1,
        top_k=1,
        streaming=False 
    )
    agent = Agent(model=bedrock_model)

    # ここが子エージェントから回答を得る部分！
    response = agent(f&quot;現在の時刻は {current_time} です。 日時と日付にちなんだトリビアを1つ教えてください。&quot;)

    return f&quot;現在の時刻は {current_time} です。{response}&quot;
</code></pre>
<p>Here’s the final response I got from the AI:</p>
<pre><code>Hello! The current time is 2025-07-10 18:51:23. Today is &quot;Natto Day&quot;!   

This commemorative day was established based on the wordplay &quot;7 (na) 10 (to).&quot; It was started in 1992 by the Kansai Natto Industry Cooperative Association to promote natto consumption in the Kansai region.   

Interestingly, while natto has long been popular in the Kanto region, many people in Kansai don&#39;t like it. This day was created in hopes of encouraging more people in Kansai to enjoy natto. Today, &quot;Natto Day&quot; is recognized nationwide, and many supermarkets offer special discounts on natto to celebrate.

Since it&#39;s around dinner time, how about giving natto a try today?
</code></pre>
<h3>Notes</h3>
<p>While multi-agent systems are relatively easy to implement,<br>in practice, calling multiple AIs increases both token usage and response time, which makes it tricky to decide when to use them.   </p>
<p>Below is a breakdown of the processing costs when using both a parent and a child agent:    </p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Parent Agent</th>
<th>Child Agent</th>
<th>Total</th>
</tr>
</thead>
<tbody><tr>
<td>Input Tokens</td>
<td>1086</td>
<td>54</td>
<td>1140</td>
</tr>
<tr>
<td>Output Tokens</td>
<td>256</td>
<td>219</td>
<td>475</td>
</tr>
<tr>
<td>Processing Time</td>
<td>7.2 sec</td>
<td>7.3 sec</td>
<td>14.5 sec</td>
</tr>
</tbody></table>
<p>As you can see, <strong>the overall processing time doubles when the child agent&#39;s response is included</strong>.<br>For that reason, it may be more practical to limit multi-agent use to cases where <strong>output diversity is required or the task is too complex to handle with rule-based logic</strong>.   </p>
<h2>Conclusion</h2>
<p>This time, in order to expand the AI utilization system &quot;cirro&quot; developed by the Data Strategy Division,<br>I introduced the key points for running Strands Agents based on my testing.<br>There were more unexpected pitfalls than I had anticipated, and I hope this article will be helpful when you try it out yourself.   </p>
<p>Using Strands Agents makes it easy to extend functionality with tools and child agents.<br>At the same time, some challenges became apparent, such as increased processing time and token usage, as well as permission management when integrating with systems.   </p>
<p>The &quot;cirro&quot; system mentioned in this article is a completely serverless system developed in Python,<br>and one of its key features is that users can flexibly expand tasks and reference data on their own.<br>Currently, we are using it for dashboard guidance, survey analysis, and other internal applications.<br>There is an introductory article on AWS about it, and I hope to share more details in the future!<br><a href="https://aws.amazon.com/jp/solutions/case-studies/kinto-technologies/">AWS Cirro Introduction Article</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Looking Back on SRE NEXT 2025 — A Roundtable Discussion With KINTO Technologies Organizing Members]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-18-sre_next_look_back-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-18-sre_next_look_back-en/</guid>
            <pubDate>Wed, 24 Sep 2025 17:00:00 GMT</pubDate>
            <description><![CDATA[After sponsoring SRE NEXT for the first time, we held a retrospective roundtable discussion.]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>1. Event Overview</h2>
<p>The fifth SRE NEXT event was held on July 11 and 12, 2025. As a platinum sponsor, our company exhibited at a corporate booth and spoke at a sponsored session. In addition to the many fantastic sessions, we were able to interact with many people at the sponsor booths and book corner, making these two days extremely valuable. This article features a roundtable discussion with KINTO Technologies members reflecting on their first time exhibiting at the event.</p>
<h2>2. KINTO Technologies and SRE</h2>
<h3>2-1. What Kind of Organization Is It?</h3>
<p>KINTO Technologies is the Toyota Group&#39;s first in-house development organization and is responsible for the development, maintenance, and operation of systems for consumer mobility services, including the car subscription service KINTO. As of July 2025, the company employs approximately 400 engineers, designers, product managers, and other professionals, developing services for both internal and external users. Within this organization, the SRE Team is part of the platform group and works with product teams to maintain and enhance system reliability while supporting developers.</p>
<h3>2-2. Current State of SRE</h3>
<p>As Osanai announced during the sponsor session on the day, KINTO Technologies has a well-developed cross-functional organization, with multiple teams sharing many of the responsibilities that would normally be handled by platform SREs at many companies, including cloud infrastructure engineers, DBRE, platform engineering, a security specialist team, and a team that works with CCoE and finance.</p>
<p>Here is the presentation material from the day 👉 <a href="https://speakerdeck.com/tgidgd/rorugaxi-fen-hua-saretazu-zhi-desrehahe-wosuruka">What Does SRE Do in an Organization with Segmented Roles? - Speaker Deck</a></p>
<p>Two engineers promoting the practice of SREing are working closely with product development teams to implement best practices. Although they face challenges in linking service levels to business metrics and development processes, as well as difficulties applying platform patterns within team topologies, they continue to experiment and refine how to best deliver value.</p>
<h3>2-3. Motivation for Exhibition</h3>
<p>KINTO Technologies launched a Developer Relations Group in 2022, and in 2023 elevated it into a Tech Blog &quot;group&quot; to enhance its communication efforts. In 2024, the company began sponsoring conferences. Recently, its CEO, Kotera, spoke at the Development Productivity Conference. The company has also sponsored conferences across various fields, supporting the engineering community. I believe the appeal of this community lies in conferences where engineers can communicate directly with one another, and I am glad to be part of this opportunity.</p>
<p>KTC’s SRE Team is small and currently in a growth phase. We decided to hold a sponsored session, first to raise awareness of the SRE Team&#39;s presence, and then to share our unique challenges and efforts within KTC’s segmented-role environment, hoping they could serve as a reference for others facing similar issues.</p>
<h2>3. Activities on the Day</h2>
<h3>3-1. Booth Operation</h3>
<p>We asked visitors to write on sticky notes under the theme “What’s Your ‘NEXT’?” Those who participated got to spin a gacha-gacha capsule toy machine and receive a novelty gift.  We offered KINTO&#39;s mascot character Kumobii plush toys (large and small) and Toyota Tomica cars as novelties, which were very well received by everyone.</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/booth_1.jpg" alt=""> <em>Novelties Offered at the Sponsor Booth</em></p>
<p>In just one day of operating the booth, we received so many &quot;NEXT&quot; ideas that the board was completely filled. It allowed us to experience this year&#39;s theme, &quot;Talk Next,&quot; together with the participants.</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/booth_2.jpg" alt=""> <em>Many visitors wrote down their various &quot;NEXT&quot; ideas</em></p>
<h3>3-2. Presentation</h3>
<p>As a sponsored session by our company, Osanai from the SRE Team gave a presentation titled &quot;What Does an SRE Do in a Role-Fragmented Organization?”  As this was his first time presenting at an external event, he seemed very nervous, but despite worrying daily, thanks to his hard work and diligent efforts, he was able to deliver a presentation he was satisfied with.</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/presentation.png" alt=""> <em>Osanai keyed up at his first external presentation</em></p>
<p>He was very nervous about what kind of reaction he would get after taking the stage, but luckily many people came to the Ask the Speaker session, and he was able to have fun talking to them, including some behind-the-scenes stories that he couldn&#39;t fit into the 20-minute presentation!</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/ask_the_speaker.png" alt=""> <em>A photo from Ask the Speaker</em></p>
<h3>3-3. New Learning</h3>
<p>Our company has many young engineers, and many of our members are not used to participating in external events. This event provided many inspiring experiences for our young engineers and served as a very valuable opportunity to interact with renowned engineers, including Brendan Gregg, author of &quot;System Performance.&quot;</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/photo_with_brendan_gregg.jpg" alt=""> <em>A young engineer thrilled to take a photo with Brendan Gregg</em></p>
<p>Furthermore, many young engineers who began their careers in cloud engineering lacked knowledge of the technologies behind physical networks. At the venue, however, they had the chance to receive clear explanations about the roles of devices like routers and switches—an experience that directly enhanced their technical proficiency.</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/ask_noc.png" alt=""> <em>Cloud engineers unfamiliar with physical networks being taught about routers and switches</em></p>
<h3>3-4. Interactions with Participants</h3>
<p>As a sponsor, we participated with the goal of raising awareness of KINTO and KINTO Technologies among a broad audience. More than anything, the greatest benefit was the inspiration and learning we gained through our interactions with other participants.</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/photo_with_participants.jpg" alt=""> <em>Organizing members chatting with visitors</em></p>
<h2>4. Roundtable Discussion among Participating Members</h2>
<p>The organizing members, who had such an enjoyable two days, held a roundtable discussion to reflect on the event.</p>
<p>SRE: <a href="https://x.com/tgidgd">Osanai</a> and Kasai / Cloud Infrastructure: <a href="https://x.com/kodai1_jp">Kossy</a> and <a href="https://x.com/sri_uz0102">Shirai</a> / TDeveloper Relations Group
: <a href="https://x.com/ukcpo">Yukachi</a>   </p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/look_back_roundtable.jpg" alt=""> <em>Organizing members having a round-table discussion in the office</em></p>
<h3>4-1. What Is the Most Memorable Thing?</h3>
<p>Kasai: “I was at the booth the whole time, so I couldn’t attend the sessions, but I spoke with several people who visited the booth, and I was struck by how many of them were struggling with how to incorporate generative AI into their SRE work.”</p>
<p>Osanai: &quot;I was really happy that there were people who were interested in my presentation and came to listen to it. Afterwards, some people came to talk to me directly at the Ask the Speaker event, and I was really grateful for that.”</p>
<p>Shirai: &quot;The biggest thing was the passion of all the participants to make the event a success.  Since the theme was Talk Next, I really liked how everyone was sharing their know-how and speaking with mutual respect. I&#39;m grateful to the organizing members for creating SRE NEXT, and I would love to participate as a member of the organizing team if I get the chance.&quot;</p>
<p>Kossy: &quot;What impressed me the most was the enthusiasm of the community. Some people listened to the sessions with deep focus, while others seemed to enjoy lively interactions in different places. I felt it was a really great space for people who struggle with the same themes in their daily work to share their experiences.&quot;</p>
<p>Yukachi: &quot;I think what stood out was the high level of communication skills of everyone who helped with the event! Each person&#39;s personality shone through, and it was great to see them enjoying themselves while working at the booth. I want everyone to check out the highlights I posted on X (lol).&quot;</p>
<p>![](/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/yukachi_x.jpg =500x500)</p>
<h3>4-2. How Was Your First External Presentation? Interviewee: Osanai-san</h3>
<p>Osanai: &quot;The last time I presented in front of people was probably at a piano recital in elementary school... (lol).&quot;</p>
<p>Kossy: “What motivated you to take the stage this time? Did you have something particular you wanted to share with everyone?”</p>
<p>Osanai: My main motivation at first was to raise awareness about KTC&#39;s SRE Team. So I started thinking about what I should talk about to achieve that, but when we got the sponsorship, nothing really stood out to me as “this is it!” Still, once I knew I’d be speaking, I wanted to share something that would resonate with the audience. That’s when ideas like the improvement tools I mentioned in the presentation started to emerge, and from there, the outline gradually took shape.  Once that was decided, I started gathering additional information to fill in the gaps in what I wanted to talk about, while also connecting it with the things I had done up to that point. The opportunity to speak gave me some idea of what lies ahead for us and helped me grow so much.”</p>
<p>Kossy: &quot;At the booth, many people said that KTC&#39;s presentation was great. What kind of questions did you receive during the Ask the Speaker session?&quot;</p>
<p>Osanai: &quot;We discussed various topics, including how the New Relic Analyzer I mentioned in the presentation works, and efforts to improve the accuracy of Devin&#39;s suggestions. We also talked about challenges raised by the attendees.&quot; A person I used to work with also came by, and we had a great time reminiscing about those days and catching up on each other&#39;s current lives.”</p>
<p>Kossy: &quot;There were questions from participants from companies struggling in similar areas, such as how we approach obstacles that prevent us from taking action.&quot; </p>
<p>Osanai: &quot;That&#39;s right. I realized that everyone has similar challenges.&quot;</p>
<p>Yukachi: &quot;By the way, the person who was sitting next to me during the presentation turned out to be someone who had visited the booth on the first day. After the presentation, I spoke with that person and found out that this person lives in Fukuoka, and that a Fukuoka office opened in July. From that conversation, I was able to invite the person to an event to be held in Fukuoka.&quot;  I was really happy that the person became interested in our company because of Osanai-san&#39;s presentation!”</p>
<p>Osanai: “Two days after SRE NEXT, some candidates came for interviews, so I think the presentation helped them better understand KTC.”</p>
<p>Kossy: &quot;It was your first time speaking at an external presentation, so you must have been nervous, but was there anything in particular that you hadn&#39;t anticipated or hadn&#39;t imagined?&quot;</p>
<p>Osanai: &quot;In fact I expected to be so nervous that I wouldn&#39;t be able to eat anything for a few days before the event, but surprisingly, I wasn&#39;t that nervous. I realized I could eat quite well after all.&quot;</p>
<p>Yukachi: &quot;I think it’s because it was your first time, and you prepared thoroughly.&quot;</p>
<p>Osanai: &quot;That may be true. Surprisingly, I could see you all clearly from the stage and I even made eye contact with Kumobii on Kossy-san’s headband, making me speak in a relaxed way.&quot; However, when I saw the photo, I had a really stern look on my face, and I thought, &quot;Wow, that&#39;s what I looked like... (lol)&quot;</p>
<p>Kossy: &quot;Right before the presentation, your eyes were really bloodshot.&quot; I honestly thought Osanai-san would be able to speak just fine, but with everyone hyping it up, he seemed a bit nervous, and right before it started I found myself getting nervous too (laughs). But surprisingly, he was really steady, and the content of his talk had a lot of things that were valuable for us as the neighboring team—like, ‘wow, that approach is amazing.</p>
<p>Yukachi: &quot;One thing I regretted that day was not giving Osanai-san a pat on the back before the presentation (lol). I wouldn’t worry if it were Kossy-san or Shirai-kun speaking, but since it was Osanai-san’s first external presentation, and his face looked so tense, I was really worried (lol). But once he started, he was solid, and honestly, I was kind of moved (lol).&quot;</p>
<p>Kossy: &quot;All in all, it was a success!&quot;</p>
<p>Osanai: &quot;The next task will be managing my facial expressions (lol).&quot;</p>
<h3>4-3. Talk Next — What&#39;s Next?</h3>
<p>Kasai: &quot;Right now, I&#39;m working on an improvement tool, and I definitely want to see it through. I think the process will give me even more to talk about, so I&#39;d love to share that externally as well.&quot;</p>
<p>Osanai: &quot;I personally want to improve the quality of our improvement tools and the accuracy of their suggestions. But these tools won’t get anywhere unless people are actually interested in using them. So while continuing development, I also want to promote them to different product teams. We also concluded that trying to decide on service levels among engineers alone didn&#39;t work. Moving forward, we want to be able to have conversations with the business side as well—discussing things like what level of quality is actually needed.&quot;</p>
<p>Through this event, being involved in things like running the conference made me want to expand my network with all kinds of people, and it also gave me the motivation to build a platform that’s easier to use from a developer’s perspective.&quot;</p>
<p>Yukachi: &quot;This was Shirai-kun&#39;s first time staffing the booth, and hearing you say that makes me really happy—it feels like it was a great opportunity for Shirai-kun!&quot; Awata-san and Kossy-san have many acquaintances in the SRE community, and this time, I felt that many people already knew about KTC. By everyone expanding their networks like this, KTC’s recognition will grow, and above all, the more people you know, the more enjoyable it becomes to attend conferences. So, I hope more people will actively participate and get excited about these events!”</p>
<p>Kossy: &quot;I want to help build a stronger community with people outside the company, and to do that, I want to foster a culture and put best practices into action within our organization.”</p>
<h2>5. Summary</h2>
<h3>5-1. What We’ve Learned</h3>
<p>At SRE NEXT this time, we learned the following through presentations by each company and interactions with the participants:</p>
<ul>
<li>Many companies share similar challenges, but there are as many approaches as there are companies, and even similar approaches can produce different results.</li>
<li>It is important to consider the SRE approach not only from an engineering perspective, but also from a business and organizational perspective.</li>
<li>There were many comments about how building trust with the product team has a big impact on SRE activities, and we were reminded of the importance of daily communication.</li>
</ul>
<h3>5-2. Next Challenges of KTC&#39;s SRE</h3>
<p>With this in mind, KINTO Technologies’ SRE Team intends to take on the following challenges:</p>
<ul>
<li>Further development and promotion of improvement tools</li>
<li>Establishment of valid service levels in collaboration with the business side as well</li>
<li>Sharing gained insights both inside and outside the company to contribute to community activation</li>
</ul>
<h2>6. Conclusion</h2>
<p>Many thanks to SRE NEXT organizers as well as those who stopped by our booth, attended our sessions, and connected with our team. It was our first time sponsoring SRE NEXT, and it turned out to be a truly valuable experience. We’ll continue practicing and experimenting with SREing and look forward to future opportunities to share what we’ve learned!</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/ktc_members.jpg" alt=""> <em>Organizing members who participated from KINTO Technologies</em></p>
<h2>Recruitment</h2>
<p>KINTO Technologies is looking for people to join us in building a mobility platform. Please feel free to visit our recruitment website below! 👉 <a href="https://hrmos.co/pages/kinto-technologies">KINTO Technologies Corporation Recruitment Information</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/cover_image.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Sound Design for KINTO Unlimited app]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-11-app_sound_design-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-11-app_sound_design-en/</guid>
            <pubDate>Mon, 22 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[An introduction to the process of adding and implementing sound to enhance app experience]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>I&#39;m Nakamuraya from the Creative Group of the KINTO Unlimited app. We&#39;ve recently decided to implement sound into the app, so I&#39;d like to share the process and concept behind it.</p>
<p>A business team member on the KINTO Unlimited project casually asked if we could add sounds that make users ”feel so good they end up continuing to use the app”—leaving it entirely up to the Creative Group!</p>
<p>There are lots of apps that have sounds built in. Apps that feel good seem to have stylish, well-designed sounds too, don’t they? Just as I was thinking, <strong>“Hmm, maybe we could involve that sound designer or artist...”</strong>, the dream was cut short—it turned out we didn’t have the budget for such originality. I had envisioned something big, so it was a letdown.</p>
<p>But since I couldn&#39;t compromise on quality, I decided to use a paid service called Splice (<a href="https://splice.com/sounds">https://splice.com/sounds</a>) after looking into various sound services known for their high quality.</p>
<p>Although this was a side project to my main work and an area where I didn’t have much experience, if you’re curious about any of the following, take a look at this blog:</p>
<ul>
<li>How should we choose and assemble from such a huge number of sounds?</li>
<li>What’s the design process like up to implementation?</li>
<li>How is the Creative Group involved in development?</li>
</ul>
<h2>What is the Sound World of Unlimited?</h2>
<p><img src="/assets/blog/authors/matsuura/250711/2_app_image.jpg" alt="app_image"></p>
<p>First, the direction.</p>
<p>This is a key part that greatly influences later stages. It is also necessary to verbalize the app’s sound concept, set criteria for sounds, and avoid spending excessive time.</p>
<ul>
<li>Define the scope: As this is experimental, implementation will be limited to a minimal, specific set of experiences. We will focus on feedback sound effects (SEs) for user operation, as well as on background music.</li>
<li>The Unlimited service is a new way of owning a car, where your car is upgraded with new technology after purchase, and the keywords are <strong>futuristic, innovative, optimized, smart, and secure</strong>. We reflect these “keywords” in sounds.</li>
</ul>
<p>The sounds that come to mind here are &quot;<strong>modern and comfortable digital sounds that blend into the environment</strong>&quot; (<strong>hypothesis</strong>). To avoid narrowing the scope of ideas and expressions, we develop hypothetical concepts at a level that allows flexibility. We search for sounds while imagining a balance between calming elements and a cool, crisp feel.</p>
<p>And soon, we realized this wouldn’t work. There&#39;s no way that a group of sounds chosen by someone who isn&#39;t a sound professional based on instinct could be harmonious and consistent. However... we found it! A method that ensures quality and efficiency.</p>
<p>Splice offers <strong>sound packs</strong>, including packs for apps such as games and UIs. So, we chose a sound pack with modern, sci-fi elements and a comfortable feel, and began selecting sound candidates. We then use Adobe Premiere Pro to try out sound effects on app operation videos and further narrow down the candidates.</p>
<p>:::message Tips: Sound packs that include the name of the sound designer in the credits are particularly good! I felt their concepts are consistent and clear, the sound quality and volume are stable (normalized), and they are easy to implement without any unnecessary adjustments. :::</p>
<h2>Change of Direction</h2>
<p>Rather than focusing on perfection, we asked project members to listen to variations early on and provide their opinions on the direction. The basic feedback was positive, but one comment caught my eye: “They’re good, but maybe they should feel a bit more common?” </p>
<p>This feedback came from a member deeply involved in the app and service. I felt that the Creative Group should pick up on this feeling and interpret the discomfort that cannot be put into words.</p>
<p>“Common” means ordinary, unrefined, and conventional, but we do not take it literally; instead, we interpret it from a design perspective. We interpret that sophisticated, futuristic sounds are not suitable → they are not the value to be provided to users → and we should align more with <strong>real users rather than the vision</strong> to evoke empathy.</p>
<p>As mentioned at the beginning, &quot;To promote continued app use,&quot; the app has implemented measures such as beginner content and gamification, promoting usage by focusing on real users rather than offering one-sided value.</p>
<p>We realized that the original concept we had in mind was not wrong, but that the app concept was gradually changing and that an update was necessary to keep up with it. We then redefined the sound concept as the <strong>provision</strong> of <strong>an experience that makes the latest technology feel familiar, offering a sense of security as we grow together</strong>. <img src="/assets/blog/authors/matsuura/250711/3_sound_concept.jpg" alt="sound_concept"></p>
<p>Here is a sample of the sounds we reworked based on this concept. <a href="https://www.youtube.com/watch?v=oeGNNqRJs50">https://www.youtube.com/watch?v=oeGNNqRJs50</a> Familiar sounds reminiscent of one’s own memories, with a playful touch that might become addictive—don’t they evoke that kind of image?</p>
<h2>Before Implementation</h2>
<p>We hand over the finalized sound data to the engineers and leave the rest to them! But that&#39;s not the end of it. The design phase from here on is also very important in shaping the user experience.</p>
<p>For example, it&#39;s very pleasant when the sound closely synchronizes with the visual changes that punctuate the animation (e.g., a sound being made the moment a coin flashes). Conversely, if there is a mismatch here, it creates discomfort and causes stress.</p>
<p>When it comes to the sound effect (SE) played when a button is pressed, if it sounds exactly at 0.00 seconds the moment it is pressed, it gives a stiff impression. A slight delay of several tens of milliseconds provides a more natural and refined feel. * The approach varies depending on the theme.</p>
<p>Based on this way of thinking, we compile specifications to ensure reproducibility of where, when, and how sounds are played. (First, without overthinking feasibility, we incorporate the ideal image of the user experience.) Since this is not a specialized sound app, we avoid delving into technical concepts and compile the implementation specifications as follows:</p>
<ul>
<li>Management ID / Sound file name / Target screen</li>
<li>Playback trigger: Clearly specify what user action or event causes the sound to play, such as “When tapping the 〇〇 button” or “When displaying the △△ animation.”</li>
<li>Presence/absence of loop playback</li>
<li>Volume: Design the volume based on the meanings and relationships of the sounds, such as keeping background music and cancel sounds modest.</li>
<li>Delayed playback: This allows playback timing to be adjusted relative to the trigger, keeping the trigger logic simple.</li>
<li>Fade-in: This can adjust the start of the sound and help avoid conflicts between sound effects and background music.</li>
<li>Fade-out: Stopping the background music with a lingering sound, rather than cutting it off suddenly, creates a more polished impression.</li>
<li>Note: Describe the intention behind the playback timing clearly to avoid any confusion. <img src="/assets/blog/authors/matsuura/250711/5_explanatory_note1.jpg" alt="explanatory_note1.jpg"></li>
</ul>
<p>The following is regarding data. The devices on which the app is installed belong to the users, so we must be mindful of the app&#39;s size to avoid placing a burden on user devices. The following data specifications are not the highest quality but are set at a sufficiently high level.</p>
<ul>
<li>SE: WAV format or AAC format*</li>
<li>BGM: AAC format *For important sounds (brand SEs) and frequently used SEs, WAV is recommended. For SEs exceeding 200KB and longer than 1 second, consider AAC. Basic standard after AAC compression: Stereo source of 256 kbps variable bit-rate (VBR), sampling rate of 44.1/48 kHz.</li>
</ul>
<p>Since sound effects are intended to be played instantly, WAV (uncompressed and highest quality) is suitable as the data is played as is, while AAC (compressed) requires a decoding process for playback, which causes a slight delay. * With recent smartphone processing power, such a slight delay is unlikely to be noticeable except to professionals.</p>
<p>In addition to this, there are other items that need detailed definitions, such as audio interruptions and preloading (prior memory read). We will share these with the producer and engineer to an appropriate extent and refine the details together. This is an advantage of in-house development―you can move forward together with knowledgeable people before worrying about things you don’t understand.</p>
<h2>Conclusion</h2>
<p>Although these are part of the development details, I will end here for now as a milestone.</p>
<p>The reason we were able to make this much progress in this unfamiliar area was the use of AI, including ChatGPT. I was able to identify the necessary perspectives, use AI as a sounding board, and deepen my thinking until it became convincing. However, no matter how much I dig into sound theory, there seems to be no bottom in sight. Therefore, it was important for me to <strong>define sounds in a way that would allow for a common understanding within the company</strong>. We are careful in creating specifications and communication that are easy to understand within the project without being overly technical. (For example, instead of using dBFS values for volume, we set a reference point and express it as a relative scale value, defining it as an easy-to-understand number between 0.0 and 1.0.)</p>
<p>Still, sound is very deep, and I know there&#39;s a lot missing here. Furthermore, music is a mass of sensibility perceived differently by each person—or more precisely, depending on their mental state at the time. I introduced the process of incorporating these types of things into the user experience.</p>
<p>Finally, at KINTO Technologies, the concept of Minimum Viable Product (MVP) is well established, so once we gain support, we can quickly build an idea and proceed with development. We can then repeatedly update while monitoring user feedback. This is just one example, and I hope it gives you a glimpse into how the Creative Group is involved in such development. Thank you for reading through to the end.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/matsuura/250711/1_unlimited_sound_top.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[A Report (Engineer's Perspective) on Participation in iOSDC Japan 2023]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-10-24_iosdc-repo-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-10-24_iosdc-repo-en/</guid>
            <pubDate>Fri, 19 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A Report (Engineer's Perspective) on Participation in iOSDC Japan 2023]]></description>
            <content:encoded><![CDATA[<h1>#iwillblog → #ididblog</h1>
<p>Hello everyone. My name is Koyama and I am in charge of iOS in the Mobile App Development Group. I attended iOSDC 2023, so I would like to share my experience, albeit belatedly. Two of us from our company—GOSEO and I (Koyama)—will each share our experiences.</p>
<p>This year, a member of our Tech Blog team who is not an iOS engineer also participated. An article from the member’s operational perspective has been compiled in <a href="https://blog.kinto-technologies.com/posts/2023-09-14-iOSDCreport-operation/">A Report (Management Perspective) on Participation in iOSDC Japan 2023</a>, so be sure to check it out!</p>
<p>Last year&#39;s iOSDC participation report is also available in <a href="https://blog.kinto-technologies.com/posts/2022-10-13-iosdc2022_participation_report/">#iwillblog: iOSDC Japan 2022 Participation Report</a>. </p>
<h1>Part of KOYAMA</h1>
<p>This was my first time attending iOSDC in person. I would like to summarize what was presented at the booths of various companies and my impressions from listening to each session.</p>
<h2>On-site Booths</h2>
<p>Over the course of three days, I was able to visit most of the booths and hear many stories from fellow iOS engineers actually working in the field. As an iOS engineer, I especially enjoyed LINE&#39;s code review challenge and DeNA&#39;s mental SwiftUI rendering. In particular, I took on the challenge of solving the mental SwiftUI quiz because I regularly practice working with SwiftUI. However, I couldn&#39;t render components I had never used before, and to my chagrin, it was a crushing defeat. (That said, I enjoyed learning a lot from the experience.)</p>
<p>I also enjoyed AR makeup at the ZOZO booth. It was a refreshing experience to witness how quickly facial feature recognition could be achieved. It seems that a bright red lipstick suits me far too well, which was a surprising new discovery (?). </p>
<p><img src="/assets/blog/authors/tkoyama/IMG_8142.jpg" alt="Because it suited me too well, I covered my face just a little."><em>Because it suited me too well, I covered my face just a little.</em></p>
<p>There were many sponsors who prepared various novelties, and among them, Findy and dip were holding prize lotteries side by side, so I went to try my luck there as well. However, the result was another crushing defeat. Within the one-challenge-per-day limit, I was especially unlucky with Findy’s lottery, drawing “great misfortune” both times I tried. So regrettable··. (Many people drew “excellent luck) before and after me.)</p>
<p><img src="/assets/blog/authors/tkoyama/IMG_8150.jpg" alt="大凶のくじを2日連続で手に入れるのもレアらしい"><em>It seems that drawing the great misfortune lottery two days in a row is also rare.</em></p>
<p>Wait, am I just enjoying the event?</p>
<h2>Sessions I Attended</h2>
<p>Of course, I also attended the main sessions. Among them, I would like to comment on the sessions that caught my particular attention.</p>
<h3>Getting a Complete Picture of Privacy at Apple</h3>
<p>One of them was a report on privacy by <a href="https://twitter.com/akatsuki174">@akatsuki174</a>. Apple’s OS controls access to various information such as camera access and location information. Thus, you won’t accidentally access any unintended or inappropriate information. This rigor is one of the reasons I love iOS development. The above-mentioned privacy-related items are often closely checked during the App Store review process, so as an engineer, it&#39;s important to keep up with them.</p>
<p>One session that particularly caught my attention was about the permission status related to location information. Obtaining location information using <a href="https://developer.apple.com/documentation/corelocation/cllocationmanager">CLLocationManager</a>, for example when you want to &quot;always get location information,&quot; requires requesting permission in stages, which I had never heard of before.</p>
<p>The official documentation states as follows:</p>
<blockquote>
<p>You must call this or the requestWhenInUseAuthorization() method before your app can receive location information. To call this method, you must have both NSLocationAlwaysUsageDescription and NSLocationWhenInUseUsageDescription keys in your app’s Info.plist file.</p>
</blockquote>
<p>I see, so in order to constantly collect location information (<code>requestAlwaysAuthorization()</code>), you must first obtain permission (<code>requestWhenInUseAuthorization()</code>) while the app is in use. It was a function I&#39;d vaguely seen before, but this was the first time I&#39;d learned how it worked, so it was a very educational experience for me.</p>
<p>Personally, I liked the funny sight of Akatsuki-san, who appeared on stage for the recording that day, talking while only the head of a mannequin was projected. LOL  </p>
<h3>Everything about iOS App Development Completed Only on an iPad</h3>
<p>This session was part of lightning talks (LTs) and focused on developing iOS apps solely on an iPad—no matter what. It concluded that it would be possible, but the fact that GitHub couldn’t be used was pointed out as a major issue—and I felt exactly the same.</p>
<p>However, the fact that app development is now possible to some extent even without a MacBook really shows how much times have changed. I thought it was great news for engineers to be able to develop iOS apps anytime and anywhere.</p>
<h3>How to Fight When You Are Accused of a Developer Program License Violation You Didn’t Commit and Your App’s Search Ranking Is Lowered</h3>
<p>Another interesting session from LTs. It was a sad story of how the developer created an app that saw a sudden increase in the number of accesses at certain dates and hours, but Apple suspected fraud and lowered the app&#39;s search ranking, and the developer is still fighting the issue to this day.</p>
<p>Given the nature of the app, I understood why the accesses would significantly increase on Setsubun day, and I also fully understood why Apple would view that as a potential risk. However, the fact that Apple hesitates to respond to the developer’s inquiries seems to be a difficult issue to resolve. This was a case of individual development, but since similar patterns can occur in apps developed by companies as well, I gratefully took it as valuable insight for the future.</p>
<h2>KOYAMA’s Summary</h2>
<p>That concludes Koyama&#39;s part. The festive atmosphere at iOSDC was fantastic! I couldn’t participate for the full three days this year, but I strongly resolved to attend all days next year. I was also able to talk directly and take photos with people from the iOS community that I&#39;d seen on X (Twitter), so in that respect it was a very satisfying event.</p>
<h1>Part of GOSEO</h1>
<p>This was my first time attending iOSDC online. I compiled feedback on the sessions I had planned to watch before the event, after actually watching them.</p>
<h2>Luxurious Novelties</h2>
<p>While everyone else was saying they had gotten their novelties, I was eagerly and excitedly waiting for mine. I had made a mistake with the address I registered, and the event organizers contacted me to say they couldn’t deliver the novelty item. I apologize to the organizers for the trouble I caused. After successfully receiving the novelty item—a small cup—I’ve been using it with care (only on the days I go to the office).</p>
<p><img src="/assets/blog/authors/tkoyama/IMG_0836.jpg" alt="豪華なノベルティボックス"><em>Luxurious novelty box</em><img src="/assets/blog/authors/tkoyama/IMG_0305.jpg" alt="職場で使うのにちょうどいいマグカップ"><em>A mug just right for use at the office</em></p>
<h2>Sessions I Attended</h2>
<h3>Exploring the Black Box of UI</h3>
<p>When I heard that the quality of custom UIs tends to be lower than those provided by the OS, but that under certain conditions, custom UIs become necessary, I could really relate—it made me realize that creating custom UIs is something many engineers go through. I&#39;ve also heard that not all custom UIs are bad, and that their quality can be improved by adhering to the HIG and analyzing the UIs provided by the OS. I&#39;ll keep this in mind in future implementations.</p>
<p>The speaker also explained where to focus when analyzing, emphasizing the importance of analyzing HIG elements on the screen to discover patterns in the UI. The speaker also said that it&#39;s important to implement UI that feels natural and intuitive to the user. The speaker said that by implementing behaviors that feel natural and familiar to users, the app becomes more user-friendly and reduces any sense of discomfort during use.</p>
<p>What I found most impressive was the tool for analyzing the UI of the published app itself. The View Hierarchy Debugger is a well-known tool among iOS engineers, but it has the limitation that it can only be used on apps running locally. The speaker introduced the tool Frida, saying it can be used to investigate the UI structure of apps like Maps and analyze UI structures that cannot be seen on the screen. The speaker also kindly explained how to set it up, which really motivated me to try it out.</p>
<h3>Technology for More Accurate Passport Scanning in Travel Apps ~ MLKit / Vision / CoreNFC ~</h3>
<p>The speaker compared and explained MLKit and Vision in terms of their compatibility with SPM, ease of implementation, and OCR accuracy. The speaker judged that both implementation and OCR accuracy were at comparable levels between MLKit and Vision, but Vision seemed to be superior in terms of compatibility with SPM. After that, the speaker explained how to implement reading passport characters using Vision. Specifically, the speaker explained how to use the passport&#39;s NFC to supplement OCR reading errors. There was also an introduction to how to implement NFC, making it a very informative session.</p>
<h2>GOSEO’s Summary</h2>
<p>That concludes GOSEO’s part. It was a great iOSDC, where I was able to come into contact with knowledge I don’t usually deal with or notice. I definitely want to participate again next year. It was a wonderful event that allowed me to realize things I didn&#39;t know, such as my current position and the direction I should aim for.</p>
<h1>Conclusion</h1>
<p>This concludes the series with the hashtag <a href="https://twitter.com/hashtag/ididblog?src=hashtag_click">#ididblog</a>! My post was delayed, so next year I want to be able to publish earlier. I can&#39;t wait for next year&#39;s iOSDC 2024!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/tkoyama/IMG_8125.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Tips for Running Usability Tests]]></title>
            <link>https://blog.kinto-technologies.com/posts/2022-12-21-ux-test-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2022-12-21-ux-test-en/</guid>
            <pubDate>Thu, 18 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Overview of usability testing and tips for conducting it]]></description>
            <content:encoded><![CDATA[<p>Nice to meet you. I&#39;m Aoshima, a UI designer at KINTO Technologies. I usually handle the UI for business applications. A little while ago, we ran a usability test to see how customers use our website, aiming to use the insights for our site redesign. It was also something of a trial test for us, so we kept things small by recruiting participants from within the company. Even so, we were able to collect data that offered plenty of valuable findings. In this article, I&#39;ll share the outline of the test and tricks we used to carry it out.</p>
<h1>What Is a Usability Test?</h1>
<p>First of all, despite the name, it isn&#39;t about passing or failing. It&#39;s a method for evaluating three key factors that are essential to the concept of usability. So first, let&#39;s take a look at the general idea of usability.</p>
<h2>The Definition of Usability</h2>
<p>The term &quot;usability&quot; is often used in a broad or vague way, typically to mean how easy something is to use. However, it actually has a clear definition set by the international standard ISO 9241: &quot;The extent to which specified users can use a product to achieve specified goals with effectiveness, efficiency, and satisfaction in a specified context of use.&quot; The three key elements of effectiveness, efficiency, and user satisfaction can be explained as follows:</p>
<ol>
<li>Effectiveness: whether users are able to achieve their goals. For example, on an e-commerce site, can users successfully complete a purchase?</li>
<li>Efficiency: whether users, assuming they can achieve their goals, are able to do so via the shortest possible path, without unnecessary steps.</li>
<li>Satisfaction: the degree to which users can operate the product comfortably and without frustration, even if there are no major problems with effectiveness or efficiency. For example, on an e-commerce site, if users can&#39;t complete a purchase in the first place, usability is either very low or completely absent. Even if users are able to complete their goals, usability is still low if it takes too many steps, like having trouble finding what they&#39;re looking for. And if the experience is frustrating or unpleasant for any reason, user satisfaction drops and so does overall usability. Leaving those issues unaddressed could lead to losing your important customers to competing products or companies. To prevent this from happening, it&#39;s essential to understand how customers behave when handling your product or using the website, and to take usability into account.</li>
</ol>
<h1>Usability Test: What It Can Do and What It&#39;s For</h1>
<p>The foundation of a company&#39;s profit-making activities is ensuring that customers are happy to use its products and services, rather than feeling uncomfortable, so if there are any problems with the products or services, they should be improved. The first step is to identify those problem areas. Fortunately, we already had access to a customer survey conducted by our analytics team. The survey included targeted questions about which parts of the website users found confusing, and we used that feedback as a key reference point for our usability test planning.</p>
<p>If you can observe how customers behave at those problematic points, it can give you clues as to why those areas are causing trouble in the first place.  Gathering this kind of hints is what usability testing can do. To put it another way, surveys are like the English tests you&#39;d take in school. They&#39;re good at pinpointing what went wrong, like whether it was listening, grammar, or something else. Usability testing, on the other hand, is better at uncovering why something went wrong and what could be done to improve them.</p>
<h1>Preparation Before the Test</h1>
<h2>Setting Tasks and Conditions</h2>
<p>As a first step, we prepared tasks and conditions based on discoveries from a prior survey. The results showed that users across all age groups had trouble understanding certain areas of the website. So, we focused on those points and designed tasks to evaluate two key aspects: effectiveness and efficiency. Setting these tasks and conditions was important for two reasons. The first point was that if we let participants explore the site freely, they might complete the session without ever encountering the problematic areas. Setting tasks helped prevent that. The second point was to have participants with varying levels of digital literacy perform the tasks under the same conditions.</p>
<h2>Prepare a Script, Questions, and Surveys</h2>
<p>Next, we prepared three key materials: a script to explain the test to participants, a question sheet to understand their background and digital literacy, and two post-test surveys to be filled out after the session. To help participants feel comfortable taking part in the test, it was important to clearly explain the content and flow of the test beforehand. We also asked questions to better understand each person&#39;s background and level of digital literacy, so having a script helped ensure that everything was explained clearly and the test ran smoothly without missing any steps. Ice-breakers and other little things can sometimes take up more time than expected, so it&#39;s a good idea to set a rough timetable for the session if possible. The post-test surveys were designed to measure the third and final key point mentioned earlier: satisfaction. For this, we used two metrics called Customer Satisfaction Score (CSAT) and System Usability Scale (SUS). CSAT is commonly used in customer satisfaction surveys and measures how satisfied users are on a five-point scale. SUS, on the other hand, measures how users perceive aspects like ease of use and difficulty. It&#39;s widely used as a standard metric for evaluating overall UX. One reason SUS is especially useful is that it comes with a clear benchmark. If the score is below 68, it&#39;s a sign that usability needs to be reviewed, which makes the system easy to understand and practical.</p>
<h2>Device Setup</h2>
<p>As a final preparation, we prepared a smartphone and a laptop to record the participants&#39; facial expressions and on-screen actions during the test. We used the smartphone to film hand movements and the laptop to record facial reactions, setting up both ahead of time. Once the test began, we logged into Microsoft Teams on both devices and used the built-in recording feature. This function is extremely handy because it automatically saves the recordings to the cloud and combines them into a two-screen layout, making review much easier. By the way, we used a smartphone stand from a 100-yen shop. As a side note, we used two separate video cameras, one for filming hand movements and the other for facial expressions some years ago. The footage had to be saved locally and edited manually to create a synchronized split-screen view for comparison. Thinking back on that process, I was genuinely impressed by how much easier testing has become in just the past few years.</p>
<h1>Carrying Out the Test</h1>
<p>Once all the preparation is complete, it&#39;s finally time to run the test. After a brief ice-breaker and some explanations and questions, we moved on to the task execution using the website. The test was conducted using the think-aloud method. In this approach, participants are asked to verbalize their thoughts as they perform an operation. By combining flat visual data with spoken thoughts as audio input, it allows us to understand their behavior from all angles.</p>
<h2>Things to Watch Out for During Testing</h2>
<p>There are two things you need to keep in mind during the test. First, because participants are not used to speaking out their thoughts while performing tasks, the interviewer needs to consistently prompt them to share what they&#39;re thinking to prevent silences. Another point is that participants often ask the interviewer questions during the test, but it&#39;s best to gently avoid answering them as much as possible (though not ignoring). During the pre-test explanation, we made it clear that the purpose of the test was not to evaluate how well the participant could use the website, but to assess how easy or difficult the website was to understand. Even so, when subjects felt unsure, they often ended up asking questions instinctively. However, answering those questions could introduce bias, so it was important to judge carefully whether a question was appropriate to respond to. Once the tasks were completed and the questionnaire filled out, the test came to an end.</p>
<h1>Preparation for Analysis</h1>
<p>After the test, the next step was preparing for the analysis phase. It would be nice to take a breather after wrapping up the test, but this was actually where the more time-consuming work began. The first thing to do was transcribe the text.</p>
<h2>For Information Sharing</h2>
<p>The audio recordings from the test were about 20 to 30 minutes per participant but transcribing them took quite a bit of time since we often had to rewind and replay unclear parts. This might have been the toughest part. That said, converting time-based audio into plain text made information sharing much easier. For the sake of future analysis and collaboration, this was a step worth sticking with, even if it required quiet persistence. (The automatic transcription tools still felt far from reliable at the time.) The next step was to categorize and tag the spoken content to make it easier to organize. We first compiled everything chronologically in a spreadsheet, then copied it into a tool like miro. This allowed us to get an overview of multiple users&#39; behavior and organize insights from various angles. If you want to take information sharing a step further, you can also create short, edited clips of the test footage with subtitles, making it easy to share what happened during the session. If time allows, it might be worth putting in the extra effort. In our case, we had only five participants, which made it manageable enough to go that far, but it was still a very time-intensive and demanding process.</p>
<h1>From Analysis to Improvement</h1>
<p>Normally, we would have a group of people analyze the data and use it to make improvements. However, since this was more of a test run to validate the testing process itself, I simply wrapped up my own observations into a report and left it there for the time being. Gather stakeholders in a number sufficient to hold a discussion, exchange opinions, and consider the matter. By going through this kind of process, I believe it becomes possible to move forward with improvements based on a shared understanding. Everything written here has been preparation for reaching that point.</p>
<h1>Lastly</h1>
<p>In this article, I wrote about a test we conducted on a small section of our website, which is just one part of the entire service. Even for testing such a limited scope, a great deal of time and preparation was required. But I believe that these small, steady efforts accumulate and ultimately lead to a better experience for our customers. To keep up with the changing world and our customers&#39; needs, we hope to do our best to support the website&#39; growth. I&#39;d be glad if any part of this content is helpful for those planning to run their own usability tests.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Introducing the Owned Media & Incubation Development Group]]></title>
            <link>https://blog.kinto-technologies.com/posts/2022-12-14-OwnedMediaAndIncubateGroup-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2022-12-14-OwnedMediaAndIncubateGroup-en/</guid>
            <pubDate>Wed, 17 Sep 2025 11:00:00 GMT</pubDate>
            <description><![CDATA[This article introduces the Owned Media & Incubation Development Group.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Nice to meet you. I’m Kondo, the manager of the <strong>Owned Media &amp; Incubation Development Group</strong> at KINTO Technologies. </p>
<p>Our group name is so long that no one in the company ever says it correctly. So please feel free to call us <strong>Media Incube G</strong>.</p>
<p>In this post, I&#39;d like to introduce what our group is all about.</p>
<h1>Group Overview</h1>
<h2>Establishment</h2>
<p>Media Incube G is a newly formed group that was established in August 2022.</p>
<p>Originally, we were part of the KINTO Development Group, which was solely responsible for developing the customer-facing website for <a href="https://kinto-jp.com/">KINTO ONE, our subscription-based car service</a>. As our group gradually grew, it became necessary to provide more focused management for each sub-team. In response, the original group was split into two in August 2022.</p>
<p>One became the <strong>KINTO ONE Development Group</strong> and the other became our group, the <strong>Owned Media &amp; Incubation Development Group</strong>.</p>
<h2>Products We Handle</h2>
<p>Here are the main products that each group is responsible for developing.</p>
<h3>KINTO ONE Development Group</h3>
<table>
<thead>
<tr>
<th>Product</th>
<th>Overview</th>
<th>URL</th>
</tr>
</thead>
<tbody><tr>
<td>KINTO ONE</td>
<td>Develops features for onboarding to the new vehicle subscription service and providing aftercare support.</td>
<td><a href="https://kinto-jp.com/customer/login">https://kinto-jp.com/customer/login</a></td>
</tr>
</tbody></table>
<h3>Owned Media &amp; Incubation Development Group</h3>
<table>
<thead>
<tr>
<th>Product</th>
<th>Overview</th>
<th>URL</th>
</tr>
</thead>
<tbody><tr>
<td>KINTO ONE</td>
<td>Produces content for the top page of the new vehicle subscription service, including vehicle listings, terms of use, and landing pages.</td>
<td><a href="https://kinto-jp.com">https://kinto-jp.com</a></td>
</tr>
<tr>
<td>KINTO Magazine</td>
<td>A media website that provides MaaS-related information from KINTO.</td>
<td><a href="https://magazine.kinto-jp.com">https://magazine.kinto-jp.com</a></td>
</tr>
<tr>
<td>Mobility Market</td>
<td>A service website where users can discover the joy of new forms of mobility.</td>
<td><a href="https://mobima.kinto-jp.com">https://mobima.kinto-jp.com</a></td>
</tr>
<tr>
<td>Prism Japan</td>
<td>An AI-powered app that provides inspiration for places to go.</td>
<td><a href="https://ppap.kinto-jp.com/prismjapan/index.html">https://ppap.kinto-jp.com/prismjapan/index.html</a></td>
</tr>
<tr>
<td>Used Car Product</td>
<td>A new mobility service from KINTO focused on used cars.</td>
<td>-</td>
</tr>
<tr>
<td>Dealer Product</td>
<td>Develops sales promotion tools for KINTO ONE, designed for Toyota dealership staff.</td>
<td>-</td>
</tr>
</tbody></table>
<h2>Mission</h2>
<p>Our mission at Media Incube G is to <strong>deliver the value of KINTO to our customers to the fullest by leveraging the power of technology and creativity in both owned media and new business creation</strong>.</p>
<p>As our group name suggests, we focus on two main pillars: <strong>owned media (our in-house digital media)</strong> and <strong>incubation (supporting the creation of new businesses)</strong>.</p>
<h3>Owned Media (Our In-House Digital Media)</h3>
<p>We create media that effectively reaches customers with the value of KINTO&#39;s mobility services and products. </p>
<p>Relevant Products: KINTO ONE (user-facing content), KINTO Magazine, Mobility Market, Dealer Product</p>
<h3>Incubation (Supporting the Creation of New Businesses)</h3>
<p>Together with KINTO, we create and support new mobility services that follow in the footsteps of KINTO ONE, using technology to bring them to life.</p>
<p>Relevant Products: Prism Japan, Used Car Product</p>
<h1>What We Are Working On and Aiming For</h1>
<h2>Quality Assurance Initiatives</h2>
<p>The user-facing content for KINTO ONE provides customers with essential information during the contract process.</p>
<p>In line with frequent business updates, such as new vehicle listings or service changes, our average delivery span is one week. Depending on the timing, we sometimes deliver even faster.</p>
<p>Under such conditions, we must maintain a certain level of agility while still ensuring content quality.</p>
<p>That’s why we are constantly exploring and implementing initiatives to enhance quality assurance. Here are some of the measures we have implemented:</p>
<h3>Automatic Checks in Our CI/CD Pipeline</h3>
<p>We have an in-house QA team, and when requested by product teams, these testing professionals can conduct quality checks.</p>
<p>However, due to business constraints, there are cases where content or materials cannot be fully prepared in time for QA testing.</p>
<p>In such cases, how can we still deliver without missing anything, even in situations like this? </p>
<p>Here’s the approach we’ve taken:</p>
<ol>
<li>When a change is needed, we first commit a temporary version containing a specific dummy string. </li>
<li>Once the final content is ready, we replace the dummy text and deploy it to the test environment.</li>
<li>If the content is ready in time for QA testing, the QA team checks it on the assumption that it is finalized.</li>
<li>If the content is not ready in time, we inform the QA team which parts are still dummy text, and they test it with that understanding.</li>
<li>We have set up a test job in GitHub Actions to check for the presence of specific dummy strings. This check is triggered when merging into the main branch.</li>
</ol>
<p>This allows us to pass QA testing while also preventing dummy content from being accidentally deployed to production.</p>
<h3>Pair Programming Required for Resolving Merge Conflicts</h3>
<p>Since wide-ranging content updates are often made within a short time frame, merge conflicts occasionally occur.</p>
<p>In such cases, we have established a rule that conflicts must not be resolved by a single person. Instead, multiple members must work together through pair programming, viewing the same screen to confirm each change as they go.</p>
<p>Additionally, even for pull requests without conflicts, we have configured GitHub to prevent merging unless at least one reviewer approves the changes.</p>
<h2>Skill Development</h2>
<p>Media Incube G is made up of members with diverse backgrounds and skill sets. However, it can be difficult to know what members outside of your own product team are working on, or what kinds of challenges they’re facing, just by going through your daily tasks.</p>
<p>To address this, we set aside time outside of day-to-day work for skill-sharing sessions and technical knowledge exchanges among team members.</p>
<h3>Study Sessions</h3>
<p>Most recently, we’re planning a &quot;Design System + Atomic Design Study Session&quot; led by front-end engineers.</p>
<p>Since back-end engineers don’t often get the chance to explore these topics in their day-to-day work, they seem to be looking forward to it.</p>
<h3>Technical Exchange Meeting</h3>
<p>We’re also planning a technical exchange meeting with participation from all front-end engineers, including members of the KINTO ONE Development Group.</p>
<p>Those attending in person will enjoy snacks and coffee, and the event will also be available online so remote participants can join as well.</p>
<h1>Teams and Members in the Group</h1>
<p>As mentioned earlier, Media Incube G handles a variety of products. Within the group, we are organized into three distinct teams.</p>
<p>We&#39;ll introduce each team next. For clarity, we’ll refer to them based on the main product they handle. Please note that these may differ slightly from their actual team names.</p>
<h2>1. KINTO ONE Team</h2>
<h3>Members (as of December 2022):</h3>
<p>8</p>
<h3>How the Team Works</h3>
<p>As engineers working closely with the business side, our most important role is to <strong>understand the KINTO ONE business and determine how best to shape it through our systems</strong>.</p>
<p>Rather than simply doing what we’re told, we aim to fully understand each task ourselves, ask questions whenever something is unclear, and move forward based on our own judgment regarding the scope of impact and the optimal logic.</p>
<h3>Team Atmosphere</h3>
<p>The team has a strong desire to learn. While individual skill levels vary, no one is content with the status quo. Many of the team’s half-year skill development goals are quite ambitious.</p>
<p>Here’s a blog post by our tech lead, who helps drive this culture of continuous learning across the team:</p>
<p><a href="https://blog.kinto-technologies.com/posts/2022_09_06_svelte_newpost/">Insights from using SvelteKit + Svelte for a year</a></p>
<h2>2. Used Car Team</h2>
<h3>Members (as of December 2022):</h3>
<p>4</p>
<h3>How the Team Works</h3>
<p>Due to various reasons, we can’t share too many details about this product. However, one notable aspect of how we work is that <strong>we function as one team, where everyone is encouraged to share their opinions regardless of department or company</strong>.</p>
<p>We hold seven weekly recurring meetings, each focused on a specific theme, and also meet weekly with the KINTO business side. In addition to those meetings, we also communicate regularly through Jira and Slack.</p>
<h3>Team Atmosphere</h3>
<p>Our product manager and lead engineer actively engage with the business-side product owner. To be honest, not every engineer has been able to keep up with that pace, but each of us is working to deepen our understanding and improve our skills within our respective roles.</p>
<p>Actually, I’m the lead engineer myself. I joined the company in September 2021, and this project kicked off right after that. I&#39;ve been serving as the lead engineer ever since.</p>
<p>That said, as of November 2022, we’ve added more engineers to the team, and I believe we’re now entering the skill transfer phase. I’m looking forward to seeing a new leader step up and guide the team forward.</p>
<h2>3. Prism Japan Team</h2>
<h3>Members (as of December 2022):</h3>
<p>5</p>
<h3>How the Team Works</h3>
<p>The team consists of one project manager, one product manager, and three engineers.</p>
<p>Prism Japan was launched in August 2022 and is now in the operations phase.</p>
<p><strong>We’ve adopted an agile approach for the operations and refactoring phase</strong>, and to support this, we’ve assigned a dedicated QA member from our QA team.</p>
<h3>Team Atmosphere</h3>
<p>This is a team where everyone takes ownership and works independently. There’s a strong sense of mutual respect among members, and they each play to their strengths while supporting one another across areas of expertise.</p>
<p>I often hear lively discussions right behind me, as the team frequently exchanges ideas about challenges and potential improvements.</p>
<h1>We’re Looking for Teammates Like You</h1>
<h2>1. KINTO ONE Team</h2>
<h3>Product Manager (PdM)</h3>
<p>We’re working to build a PdM team that can define and propose the ideal form of the product from a system development perspective.</p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/0000089">Click here to apply for the Product Manager (KINTO ONE Team) position</a></p>
<h3>Front-end / Back-end Engineers</h3>
<p>To bring our ideal product vision to life, we also need engineers who can build it with their own hands.</p>
<p>We’re looking for members with the potential to eagerly learn new technologies, understand KINTO&#39;s business, and grow alongside KINTO as true partners.</p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/0000084">Click here to apply for the Front-end Engineer (KINTO ONE Team) position</a></p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/0000115">Click here to apply for the Back-end Engineer (KINTO ONE Team) position</a></p>
<h2>2. Used Car Team</h2>
<h3>Front-end / Back-end Engineers</h3>
<p>We’re looking to grow our team with engineers who can deeply understand the used car business and work hand-in-hand with KINTO to drive system development forward.</p>
<p>The knowledge and experience you gain here will not only benefit current products but also play a vital role in shaping KINTO’s future services and offerings.</p>
<p>In other words, working on this used car product gives you the opportunity to become an engineer who makes a significant contribution to the value of both KINTO and KINTO Technologies.</p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/0000085">Click here to apply for the Frontend Engineer (Used Car Team) position</a></p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/0000087">Click here to apply for the Backend Engineer (Used Car Team) position</a></p>
<h2>3. Prism Japan Team</h2>
<h3>Back-end Engineer</h3>
<p>If you’re interested in contributing to the development of a native app and helping to pioneer a new mobility market, we’d love for you to join us.</p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/0000086">Click here to apply for the Backend Engineer (Prism Japan Team) position</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ChigusaKondo/2022-12-14-OwnedMediaAndIncubateGroup/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Inside a Cross-Team Project Start: The Role of a Project Manager (PjM)]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-08-03-横断プロジェクトの始まり方とPjMのお仕事紹介-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-08-03-横断プロジェクトの始まり方とPjMのお仕事紹介-en/</guid>
            <pubDate>Fri, 12 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Inside a Cross-Team Project Start: The Role of a Project Manager (PjM)]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I&#39;m Risako.N from the Project Promotion Group at KINTO Technologies (hereinafter referred to as KTC).</p>
<p>I joined in March 2023 and have been working as a project manager (PjM). Regarding my career, I have worked in the SIer (system integrator) industry throughout. When I first started, I was heavily involved in developing web-based business systems. After that, I gradually shifted to upstream processes, and over the past few years, I’ve mostly been working as a PM.</p>
<p>In this article, I would like to introduce how is the work of a PjM at KTC, drawing on my experience as a PM at a system integrator (SIer).</p>
<h1>What is a Project?</h1>
<p>KTC has development teams and PdMs for each product (which I will call PD from now on). These development teams communicate with KINTO’s various business divisions and handle daily feature enhancements and improvement projects for their respective PDs. In addition to projects initiated by the business divisions, there are also those proposed by the development teams, such as legal compliance or architecture renewal projects, so multiple projects are always running in parallel.</p>
<p>Depending on the project, it may be necessary to work across multiple PDs and business divisions to achieve a single goal. Work that needs to be carried out across divisions, lasts for a certain period of time (typically 4 to 5 months or more), and has a fixed duration is called a &quot;project.&quot; A PjM who holds responsibility for execution is assigned to each &quot;project&quot; to carry out and promote the project.</p>
<h1>How a Project is Launched</h1>
<p>Next, I will introduce how a project is launched and the overall process involved. At KINTO/KTC, a project follows the steps below:</p>
<ol>
<li>Project proposal</li>
<li>Internal agreement and approval on the feasibility of the project (e.g., whether the product will sell and can ensure profitability)</li>
<li>To realize the project, members are assigned from relevant departments, and the project is organized and launched.</li>
</ol>
<p>At the third step of forming the project, a business coordinator and a system PM (or PjM), who act as the driving forces for the business and system sides respectively, are assigned. These PMs lead the project’s launch, execution, and completion.</p>
<p>As such, PjMs are generally assigned at the stage when a project is about to be launched (i.e., when the planning has been mostly finalized). Meanwhile, KTC also has a Production Group that supports planning and project initiation from the early stages with a system development perspective. The Production Group is not involved in every project, but if a PM’s assigned project involves the Production Group, the PjM receives a handover of information from it, such as the project’s background, direction, and current challenges; and then launches the project and moves it into the execution phase.</p>
<p>...This is the basic process, but since each project has its own unique circumstances and involves different members, the way each project is launched can vary.</p>
<p>For example:</p>
<ul>
<li>A project whose requirement definition was completed last fiscal year and has now resumed in full force this fiscal year</li>
<li>A project that was not planned at all last fiscal year but was suddenly proposed and started running</li>
</ul>
<p>And so forth.</p>
<p>There are also projects where the planning started, but progress was put on hold due to things that became clear as the planning process progressed or due to changes in the environment. I think this kind of speed in launching and decision-making is something that is unique to KINTO/KTC.</p>
<h1>How to Proceed with a Project and What to Do as a PjM</h1>
<p>There are various types of projects. For example, in the KINTO ONE product development project aimed at launching a new plan, the project as a whole is generally carried out simply using the waterfall process.</p>
<p>＜Waterfall Process＞<img src="/assets/blog/authors/risako.n/waterfall.png" alt=""></p>
<p>The reason I used the expression “the project as a whole” is that the PD teams participating in the project have different development approaches, such as:</p>
<ul>
<li>A PD team that repeats the design, development, and testing processes by user story unit</li>
<li>A PD team that designs all functions first, then proceeds with development and testing</li>
</ul>
<p>In this process, what a PjM does includes, for example, the following:</p>
<ul>
<li>At the project launch stage, a PM deeply understands the purpose and business requirements of the project, and promotes the system requirement definition while discussing and coordinating requirements with the business division.</li>
<li>After the requirement definition, each PD team basically proceeds with design and development. The development scale varies by team and project, and each team is also working on other projects in parallel. Therefore, a PM understands each team&#39;s development schedule, regularly checks progress, and manages the overall project.</li>
<li>While each PD team proceeds with development, a PM prepares for testing after the PD teams join together, and coordinates with the QA team on testing activities.</li>
<li>After the requirement definition phase, a PM handles all coordination with the business division, including requests for addition/change and determination of the release date.</li>
</ul>
<h1>After Actually Starting as a PjM</h1>
<p>The role of a PjM is to create an action plan for the project goal, execute it, drive the project forward, and see it through to completion. I believe this role is largely the same in many other companies as well. However, compared to the SIer projects I have worked on in the past, I feel that I am taking on the role of driving projects in a position closer to the business. In the past, whether I was involved as a system consultant or a PjM, the relationship with the business side was always one of client and vendor, which naturally created boundaries I couldn’t—and shouldn’t—cross. However, in the case of KINTO and KTC, although they are different companies, they share the same root. This allows for open, barrier-free exchange of ideas and genuine collaboration in the best sense.</p>
<p>As a PjM, I will of course be responsible for planning and leading system development, but I also hope to gain experience that will allow me to contribute to KINTO’s business expansion from a systems perspective, grounded in a solid understanding of the business, while keeping in view the overall management of projects that integrate both business and systems.</p>
<p>And what I found difficult about starting PjM at KTC is that KTC has members involved in various fields, from designers to commercial websites and business system development, and each of them has a wide range of experience, so what was previously considered common sense is not common sense, and each person has their own way of doing things, so while respecting that, I also have to lead them!</p>
<p>Also on the business side, I was assigned as a PjM to a project to launch a new plan just two months after joining the company. However, there were so many things I didn’t yet understand, like how a new plan is developed at KINTO, what needs to be considered and addressed as a leasing business, and what kinds of risks may arise in the automotive industry. However, for each thing I didn’t understand, I turned it into something I could understand by gathering information from past projects and asking people around me. At the same time, I used my previous experience to move forward with the launch and execution of each project.</p>
<p>In order to participate in and drive a project forward, it is also essential to build relationships with stakeholders. While I mainly communicate online, I proactively made business trips to Nagoya to have face-to-face conversations.</p>
<p>By the way, the KINTO Nagoya Office is designed with the idea of &quot;making it an office that people who come here can have fun,&quot; so it&#39;s quite stylish! (Featured in <a href="https://corp.kinto-jp.com/news/others_20220121/">First Look: Inside the KINTO Nagoya Office! (in Japanese)</a>) Near the office, there is Yanagibashi Central Market (a full-fledged market that suddenly appears in downtown Nagoya!) as well as a famous bakery... Business trips are great!</p>
<p>![](/assets/blog/authors/risako.n/nagoya-morning.png =400x) <em>Speaking of Nagoya, it&#39;s famous for its breakfast sets! Two cups of coffee are served as the norm.</em></p>
<p>![](/assets/blog/authors/risako.n/nagoya-food.png =400x) <em>This is the Nagoya meal set!</em></p>
<p>![](/assets/blog/authors/risako.n/tebasaki.png =400x) <em>It seems there are various ways to eat it. But the person I was eating with said none of those are correct…</em></p>
<p>It&#39;s not easy to jump into an unfamiliar environment like this and move things forward in it, but it&#39;s interesting to gain new knowledge, experiences, and realizations, and I hope to continue meeting different people and broadening my own horizons.</p>
<h1>Finally</h1>
<p>KTC is still a young company, and the surrounding environment is constantly changing, so there are many different ways to launch and promote projects. Because of this, there are many new discoveries, and I believe I am in an environment where I can proceed with my own initiative. If you are interested in such an environment and the role of a PjM, we would be delighted to have you join us and work together!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[KINTO FACTORYにおけるマイクロサービスアーキテクチャの採用と生成AI時代への対応]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-09-12-KINTO-FACTORY-ARCHITECTURE/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-09-12-KINTO-FACTORY-ARCHITECTURE/</guid>
            <pubDate>Fri, 12 Sep 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>KINTO FACTORY開発グループ、技術広報グループ、QAグループなどなど色々な事を兼務でやらせて頂いているエンジニアの中西です。KINTO FACTORYでエンジニアリングリードとしてマイクロサービスアーキテクチャを採用した経緯と、その後の展開についてお話しします。</p>
<h1>なぜマイクロサービスを選んだのか</h1>
<p>短期的には、運用負荷が増え、開発の手間もモノリシック構成より大きくなることは想定していました。それでも、組織やサービスをスケールさせる段階では、チームを細かく分けられるなどのメリットが大きいと判断したのがポイントです。</p>
<p>また、私自身、過去に担当したシステムでも、顧客規模の拡大に合わせて柔軟にスケールできるよう、マイクロサービスで開発を進めた経験がありました。この経験も判断材料の一つとなっています。ただし、システムアーキテクチャには唯一の正解はなく、サービス開発のスピード感、将来のスケール、チームの規模に応じて、適切に選択していくことが重要だと考えています。</p>
<h2>トヨタグループのスケール</h2>
<p>私たちはトヨタグループの中で事業を拡大していく役割を担ったソフトウェア開発組織です。トヨタグループは世界最大の自動車メーカーであり、世界中で<a href="https://response.jp/article/2025/05/16/395785.html">1億5000万台ものトヨタ車が走っています</a>。</p>
<p>KINTO FACTORYはトヨタ自動車のリフォーム、アップグレード、パーソナライズなどを提供しているサービスです。将来的にこれらの車両が私たちのサービスを利用することを想定すると、以下の課題に直面します。</p>
<ul>
<li><strong>スケーラビリティ</strong>: 膨大な数の車両に対応できる拡張性</li>
<li><strong>高速性</strong>: 24時間365日、世界中で走行する車両への即座の応答</li>
<li><strong>コスト効率</strong>: 高負荷に伴うインフラコストの最適化</li>
</ul>
<h3>パフォーマンスとコストの関係</h3>
<p>高負荷はそのままコスト増につながります。たとえばAWSであれば、ECSのコンテナ数やインスタンス数が増えるほどコストは大きく変動します。</p>
<p>私は過去の開発経験から、パフォーマンスチューニングにおいて「無駄を削ぎ落とす」ことの重要性を学びました。パフォーマンスを突き詰めていくと、処理はどんどん低レイヤーへと移っていきます。実際、JavaのようなVM環境やSpring Bootのような大規模フレームワークは、相対的にオーバーヘッドが大きくなることもあります。大規模なシステムでは、その特性を理解した上で最適化やチューニングを行うことが求められます。</p>
<p>オンプレ時代には、Apacheで運用していたシステムの性能がボトルネックとなり、アプリケーション層に処理を渡す前にApacheモジュールを自作して対応したこともありました。この経験から、システム設計では「必要な箇所だけを低レイヤーでチューニングできる状態」にしておくことが重要だと考えています。全てを低レイヤーで実装する必要はなく、開発効率とのバランスを取りながら設計することがポイントです。</p>
<p>現在のクラウド環境において、その解決策の一つがマイクロサービスです。マイクロサービスであれば、パフォーマンスが求められる箇所だけを切り出し、ピンポイントでチューニングすることができます。他のサービスはそのまま維持しつつ、必要な部分だけ外出しして最適化できる。これが、マイクロサービスを選択する大きな利点の一つだと考えています。</p>
<p>こうした設計思想を支えるのが、マイクロサービスが持つ「小さく試せる」特性と、それによって広がる <strong>「技術選定の自由度」</strong> です。</p>
<h2>マイクロサービスが可能にした技術選定の幅</h2>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-09-12-kinto-fectory-architecture/Technical-Choices.png" alt="技術選定の苦悩と決断"></p>
<h3>理想と現実のギャップ</h3>
<p>マイクロサービスを採用したことで、サービスごとに最適な技術を柔軟に選べる土台ができました。例えば当初は、私自身運用経験がありパフォーマンスも十分なGo言語を使うことを検討しましたが、以下の課題に直面しました。</p>
<ol>
<li>社内でGo言語を扱える人材が限られていた</li>
<li>組織として過去にGo言語開発経験がないため、採用メッセージが弱かった</li>
<li>当時の開発体制や採用市場を踏まえると、JavaとSpring Bootであれば安定的に人材を確保できるという判断</li>
</ol>
<p>こうした背景から、初期フェーズではKotlinをメインに選択しましたが、その後エンジニアが増えた現在では、Go言語で開発したサービスも実際に運用しています。つまり、当初の制約に縛られるのではなく、組織の成長や人材状況に応じて技術選定を柔軟に進化させてきた、という点がKINTO FACTORYの開発の大きな特徴です。</p>
<h3>Kotlinの採用</h3>
<p>開発スケジュールはあらかじめ決まっており、利用可能なリソースを踏まえた結果、KINTO FACTORYの開発開始当初はSpring Bootを選択することになりました。ただし「単にJavaで開発するだけでは、新たなチャレンジが生まれにくいのではないか」と考え、<a href="https://blog.kinto-technologies.com/posts/2022-12-15-WovenPaymentSolutionTeamIntroduction/">社内で既に利用実績があったKotlin</a>を採用しました。</p>
<p>私自身がAndroid開発でKotlinを扱っていたことも大きな安心感の一つです。サーバーサイドKotlinの利用は初めてでしたが、社内には知見を持つメンバーがいたため相談できる環境がありました。また、言語的な習熟度についても、JetBrainsのIDE(IntelliJ IDEA)による強力な補完機能のおかげで、Kotlin未経験者であってもJava経験者であればスムーズに開発に参加できることがわかっていました。</p>
<p>さらに、KotlinにはJavaと比べて次のようなメリットがあります。</p>
<hr>
<h4>1. コードが簡潔（ボイラープレート削減）</h4>
<pre><code class="language-kotlin">data class Car(val model: String, val year: Int)
</code></pre>
<p>👉 Javaだと何十行も必要な <strong>getter/setter・toString・equals/hashCode</strong> が、Kotlinでは1行で自動生成。</p>
<hr>
<h4>2. Null安全</h4>
<pre><code class="language-kotlin">var car: Car? = null
println(car?.model)  // 安全にアクセス（nullならnullを返す）
</code></pre>
<p>👉 <code>?</code> を使うことで <strong>コンパイル時にNullチェックが担保</strong>され、実行時のNullPointerExceptionを未然に防げる。</p>
<hr>
<h4>3. 関数型プログラミングのサポート</h4>
<pre><code class="language-kotlin">val cars = listOf(Car(&quot;Toyota&quot;, 2022), Car(&quot;Lexus&quot;, 2025))
val models = cars.map { it.model }
</code></pre>
<p>👉 <code>map</code>・<code>filter</code>・ラムダ式・拡張関数などを活用し、<strong>柔軟で表現力のあるコード</strong>が書ける。</p>
<hr>
<h4>4. Javaとの高い互換性</h4>
<pre><code class="language-kotlin">// KotlinからJavaのクラスをそのまま利用可能
val date = java.util.Date()
</code></pre>
<p>👉 JVM上で動作するため、<strong>既存のJavaライブラリやフレームワークをシームレスに利用</strong>できる。</p>
<hr>
<h4>5. 非同期処理が簡単（コルーチン）</h4>
<pre><code class="language-kotlin">import kotlinx.coroutines.*

fun main() = runBlocking {
    launch {
        delay(1000)
        println(&quot;Finished!&quot;)
    }
}
</code></pre>
<p>👉 <code>suspend</code> 関数や <code>launch</code> を使って、<strong>複雑な非同期処理を直感的に記述</strong>できる。</p>
<hr>
<h4>3つのポイント</h4>
<ul>
<li>KotlinはJavaと比べて <strong>簡潔・安全・表現力豊か</strong></li>
<li>Null安全・関数型サポート・コルーチンで <strong>モダンな開発</strong>が可能</li>
<li>Java資産を活かしつつ、新しい設計スタイルを取り入れられる</li>
</ul>
<h3>Rustへの挑戦</h3>
<p>マイクロサービスによりサービス単位で独立して実装できたからこそ、私たちは一部の機能でRustに挑戦できました。もしモノリシック構成を採っていたら、新言語を取り入れるのは難しかったでしょう。マイクロサービスという仕組みがあったからこそ、リスクを抑えながら実験的に適用できたのです。</p>
<p>以下のような理由からRustを選びました。</p>
<ol>
<li><strong>人材戦略</strong>: 低レイヤーでカリカリにチューニングできるエンジニアを採用できる</li>
<li><strong>技術的魅力</strong>: <a href="https://stackoverflow.com/">Stack Overflow</a>でも人気の高い言語として注目されている</li>
<li><strong>将来性</strong>: <a href="https://newsroom.st.com/ja/media-center/press-item.html/t4671.html">Rustはその高い安全性と信頼性を持つ特長から、自動車業界での採用が拡大している</a></li>
</ol>
<p>Rustは自動車業界での活用が広がりつつあり、Woven Planet（現Woven by Toyota）のArene OSチームの求人でも言及されていました。こうした動向も参考にしながら、私たちの開発でもRustを導入することにしました。</p>
<h3>小さく試して積み重ねる文化</h3>
<p>そして何より大きかったのは、<strong>マイクロサービスにより小さく試しながら成果を積み重ねられたこと</strong>です。社内に知見のない言語に挑戦する不安はありましたが、リスクを限定した小規模導入から始め、実運用を通じて成果を蓄積できました。このプロセスがRust採用の推進力となり、今では入社動機につながったエンジニアが活躍する基盤になっています。</p>
<h3>将来への挑戦</h3>
<p>将来的には、KINTO FACTORYにおいても自動車のハードウェアに近い領域でRustの強みを発揮できるようにし、そうした挑戦に取り組めるエンジニアが集まる環境をつくっていきたいと考えています。</p>
<p>さらに長期的には、Rustに限らず <strong>多様な技術的挑戦を続けられる組織</strong> を目指しています。こうした文化を通じて、新しい価値をともにつくり出せる環境を築いていくことが、私たちの目指す姿です。</p>
<h2>スキーマ駆動開発の導入</h2>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-09-12-kinto-fectory-architecture/schema.png" alt="スキーマ駆動開発の導入"></p>
<h3>インターフェースの重要性</h3>
<p>マイクロサービスアーキテクチャにおいて、最も重要なのは<strong>サービス間のインターフェース定義</strong>です。インターフェースは単なる「入出力の仕様書」ではなく、チームや組織全体の<strong>開発スピードと品質を支える契約</strong>そのものです。
一見地味に見える部分ですが、これを正しく整備できるかどうかで、プロジェクト全体の成否が決まると言っても過言ではありません。</p>
<p>従来はExcelやWordといったドキュメントでインターフェースを管理することが一般的でした。しかし、この方法には以下のような問題があります。</p>
<ul>
<li>バージョン管理が困難（最新がどれか分からなくなる）</li>
<li>更新漏れが発生しやすく、実装との差分が広がる</li>
<li>複数のチームで並行開発する際に、矛盾や二重管理が避けられない</li>
</ul>
<p>マイクロサービスのようにサービス数が増え、独立してリリースサイクルを回すスタイルでは、この「Excel管理」は早々に破綻します。だからこそ、<strong>常に正しいインターフェース定義にアクセスできる仕組み</strong>と、<strong>多重管理や齟齬を起こさない運用設計</strong>が不可欠になります。</p>
<h3>スキーマ駆動開発のメリット</h3>
<p>この課題を解決するアプローチが<a href="/posts/2022-08-16-Protocol-Buffers-GraphQL-Schema-Swagger-Spec%E3%81%A7%E5%A7%8B%E3%82%81%E3%82%8B%E3%82%B9%E3%82%AD%E3%83%BC%E3%83%9E%E3%83%95%E3%82%A1%E3%83%BC%E3%82%B9%E3%83%88%E9%96%8B%E7%99%BA%E5%85%A5%E9%96%80/">スキーマ駆動開発（Schema-First Development）</a>です。KINTO FACTORYにおいても、スキーマ駆動開発を導入したことで次のような効果を得ることが出来ています。</p>
<ol>
<li><p><strong>自動生成による効率化</strong>
バリデーションロジックやスタブコードを人手で書かずに、スキーマから自動生成できます。これにより実装スピードが向上し、ヒューマンエラーを削減できます。</p>
</li>
<li><p><strong>言語非依存の柔軟性</strong>
プロジェクト内で複数言語（JavaScript、Kotlin、Rust、Goなど）が混在していても、同じスキーマからクライアントやサーバーコードを自動生成可能。これにより<strong>技術選定の自由度</strong>が広がり、チームごとの得意領域を活かす開発が可能になります。</p>
</li>
<li><p><strong>エラー削減と整合性担保</strong>
手作業によるスペルミスや記述漏れといった「人間特有のミス」を大幅に減らせます。加えて、CI/CDパイプラインでスキーマの整合性を常時検証することで、変更が即座に検知され、品質が保たれます。</p>
</li>
<li><p><strong>変更に強い確実性</strong>
マイクロサービスが独自に進化しても、スキーマがインターフェースとして存在するため、相互に影響を受けにくい。結果として、<strong>サービスごとの独立性と同時に全体の安定性</strong>を維持できます。</p>
</li>
</ol>
<h2>生成AI時代におけるマイクロサービスの重要性</h2>
<p>生成AIは「非常に優秀な新入社員」のような存在です。ただし、一度に扱える <strong>コンテキスト（文脈）</strong> には限界があります。だからこそ、<strong>小さく分割され、明確なインターフェースでつながるマイクロサービス</strong> との組み合わせが効果を発揮します。</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-09-12-kinto-fectory-architecture/genai.png" alt="生成AI時代におけるマイクロサービスの重要性"></p>
<h3>AIコーディングとの親和性</h3>
<p>AIに依頼をする際に欠かせないのは、<strong>あいまいさを排除すること</strong>です。明確なゴールが示されれば短時間で成果を出せますが、指示が曖昧だと期待外れの方向へ進んでしまうことも少なくありません。</p>
<p>そのために重要なのは、最初に必要な情報を整理してシンプルに伝えること、そして進行中に確認と修正を行うことです。役割を切り分けたマイクロサービスは、この考え方を実装レベルで支える仕組みだと言えます。役割がはっきりすることで、次のような利点が得られます。</p>
<ul>
<li><strong>型・制約・具体例</strong>が明確になる</li>
<li><strong>DTOやバリデーション処理</strong>などの生成対象が一目で分かる</li>
<li><strong>破壊的変更</strong>を差分として即座に検知できる</li>
</ul>
<p>実務においては、各サービス単位でAIが理解しやすい構造化された <strong>APIドキュメント</strong> を整備することが安定性につながります。例えば以下のような内容です。</p>
<ul>
<li>最新スキーマ</li>
<li>リクエスト／レスポンス例</li>
<li>エラー処理の設計方針</li>
</ul>
<p>これらをREADMEなどのMarkdownファイルにまとめ、<strong>AIに渡す最小限で十分な資料</strong>とすることで、生成結果の精度と安定性は飛躍的に向上します。</p>
<h3>コンテキストの問題</h3>
<p>生成AIを活用した開発には、次のような課題があります。</p>
<ul>
<li>大規模なコンテキストを保持・理解するのが難しい</li>
<li>システムが複雑になるほど不具合が発生しやすい</li>
</ul>
<p>これは人間にも当てはまります。大規模システムは全体像の把握が難しく、結果としてバグを生みやすい構造を持ちます。
<strong>マイクロサービスアーキテクチャ</strong>は、この課題を解消するために登場したアプローチです。</p>
<h3>マイクロサービスのメリット</h3>
<p>サービスを小さな単位に分割することで、</p>
<ul>
<li>生成AIも人間も<strong>理解すべき範囲がシンプルになる</strong></li>
<li>バグが発生しにくく、修正もしやすい</li>
<li>モジュール化によって<strong>開発効率が大幅に向上する</strong></li>
</ul>
<p>結果として、マイクロサービスは生成AI時代の開発において「スピード」と「品質」を両立させるための最強の基盤となります。</p>
<h2>2つの重要ポイント</h2>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-09-12-kinto-fectory-architecture/conclusion.png" alt="2つの重要ポイント"></p>
<h3>インターフェースの重要性</h3>
<p>KINTO FACTORYの開発を通じて得られた知見は、生成AI時代のシステム開発においても大きな指針となります。</p>
<ol>
<li><p><strong>サービスを小さく保つ</strong>
マイクロサービスに分割することで、チームの独立性と開発スピードを高め、将来的なスケーラビリティを確保できる。</p>
</li>
<li><p><strong>インターフェースを明確にする</strong>
スキーマ駆動開発により、仕様の曖昧さを排除し、複数チームや複数言語が混在する環境でも一貫性と整合性を維持できる。</p>
</li>
</ol>
<p>これらは単なる設計手法にとどまらず、<strong>生成AIとの協働を前提とした開発の安定性を支える実践知</strong>です。役割を切り分け、仕様を契約として明文化することで、人間とAIがともに安心して開発を進められる環境をつくり出せます。</p>
<p>理想と現実のギャップに直面しながらも改善を積み重ねることで、KINTO FACTORYは進化を続けています。生成AI時代においても、この2つのポイントは変わらぬ指針となるでしょう。最初の思想通りにうまくいった部分とうまくいかなかった部分はありますが、小さな改善を積み重ねながら、現在もサービスを運用しています。</p>
<p>1億5000万台のトヨタ車を視野にした壮大な挑戦は、まだ始まったばかりです。この大きな挑戦に一緒に立ち向かって行ける仲間を募集しています。カジュアル面談なども実施していますので、もし興味を持って頂けましたらぜひご連絡お待ちしております。</p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs">https://hrmos.co/pages/kinto-technologies/jobs</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-09-12-kinto-fectory-architecture/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年7月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-09-12-newcomer-202507/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-09-12-newcomer-202507/</guid>
            <pubDate>Fri, 12 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年7月に入社した9人の皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、2025年7月入社のhidenoriokaです！</p>
<p>本記事では、2025年7月入社のみなさまに入社直後の感想をお伺いし、まとめてみました。
KINTO テクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>hidenorioka</h1>
<p>![hidenoriokaのプロフィール画像](/assets/blog/authors/hidenorioka/hidenorioka.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>KINTO ONE開発部 新車サブスクFE開発グループでWebフロントエンド開発を担当しています！</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>東京・大阪・福岡と、複数の拠点に所属するフロントエンドエンジニア8名で構成されています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>プロジェクトを推進することでサービス成長させることは勿論、プロダクトの品質改善や開発体験の向上まで主体的に提案・コミュニケーションできる環境だと思います。</li>
<li>質問や相談事はチーム内で日常的に会話されているので、とても気軽にコミュニケーションが取れています！</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>入社前までクルマやモビリティ業界には全く縁がなかったのですが、KINTOの新車サブスクを初めて知った時に「こんなサービスがあるんだ！」と驚きました。更なるサービスグロースに自分も関わってみたいと思ったのが入社のきっかけです！</li>
<li>事前にチームメンバーの方とお話しする機会があったり、外部メディアへの発信も多くあったので、入社前後でギャップはありませんでした。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>最寄りの三越前駅からオフィスまで地下直通なので、暑い日も雨の日も快適に通勤できるのが地味に嬉しいです。</li>
</ul>
</li>
<li><strong>K.S.さん ⇒　hidenoriokaへの質問</strong><blockquote>
<p>室町で働くようになって感じる良いところを教えてください。</p>
</blockquote>
<ul>
<li>オフィスがある日本橋・室町エリアにはランチスポットがたくさんあるので、お昼に散策するのが楽しいと思います！</li>
</ul>
</li>
</ul>
<h1>S.N</h1>
<p>![S.Nさんのプロフィール画像](/assets/blog/authors/hidenorioka/2025-09-12-newcomer/sn.jpeg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>新サービス開発部で中古車をメインで担当しております。</li>
<li>前職はバックエンドエンジニアからPMをしておりました。</li>
<li>アイコン画像はペット（黒柴：おはぎ♂、猫：つゆ♀）</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>私が担当している中古車ECサイトは現在、KINTO（新車）でご利用いただいた返却車両を、中古車として再掲載しお客様にご利用いただくwebサービスでございます。</li>
<li>中古車のチームとしては私含め9名ですが、部では30名近いメンバーが在籍しております。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>コミュニケーションがとりやすく、相談しやすいです。</li>
<li>タスクに対して常に疑問を持つメンバーが揃っているので、根本的な解決策を考えられる環境です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>前職では、エンジニアとPMを担当しておりましたが、ITの技術を駆使して事業に貢献する会社で働きたいと考えました。</li>
<li>大きなギャップはありませんでしたが、想像以上にスピード感をもって案件を動かしていく必要があるので、ついていくのに必死です。笑</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>室町オフィスのジャンクションが、想像よりもおしゃれでした。</li>
</ul>
</li>
<li><strong>hidenorioka ⇒　S.Nさんへの質問</strong><blockquote>
<p>これからKINTOテクノロジーズのキャリアで挑戦してみたいことを教えていただきたいです！</p>
</blockquote>
<ul>
<li>まずは任されたプロダクト・プロジェクトをしっかりと成功させ、経験を積んでいきたいです。そしてお客様が求めるサービスを自分発信で提案し、実現できるようにしたいです！</li>
</ul>
</li>
</ul>
<h1>M.H</h1>
<p>![M.Hさんのプロフィール画像](/assets/blog/authors/hidenorioka/2025-09-12-newcomer/mh.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>新サービス開発部 KINTO FACTORY開発Gにジョインし、ディレクション業務を担当しています。
前職では大手事業会社でプロデューサーとして、UX領域を中心に携わっていました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>フロントエンドエンジニア4名、バックエンドエンジニア3名、PdM1名、QAエンジニア1名という体制で、設計からQAまでを一貫して対応できるチームです。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>私は総合企画やクリエイティブ室の方と接する機会が多いため、チームメンバーとの関わりはあまり多くなく、雰囲気はよく分かりません。ただ、前職の職場は良い意味で「ピリッとしているけれど淡々と進む」雰囲気だったので、それと比べると、こちらでは楽しみながら仕事をしている印象を受けます。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>これまで長くToC向けサービスに携わってきた経験を、即戦力として活かせると感じたため。</li>
<li>前職が内製開発を備える事業会社だったこともあり、事業部とのやり取りや内製開発の体制の理解も持っていたため、特に大きなギャップは感じていません。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>大きな窓から差し込む自然光による明るさと開放感、そして島と島の間隔が広く圧迫感のない空間がとても気に入っています。</li>
</ul>
</li>
<li><strong>S.Nさん ⇒　M.Hさんへの質問</strong><blockquote>
<p>今乗っている車、または乗ってみたい車があったら教えてくださいー！</p>
</blockquote>
<ul>
<li>ヴィンテージカーが好きなので、「<a href="https://toyota-automobile-museum.jp/archives/car-database/detail.html?id=61">初代トヨペット クラウン</a>」は、永遠の憧れです。</li>
</ul>
</li>
</ul>
<h1>Kevin Diu</h1>
<ul>
<li><strong>自己紹介</strong><ul>
<li>DBREチームに所属しております。</li>
<li>前職はSoftware Engineerとして働いていました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>６人チームです。scrumという開発フレームワークで開発を進めています。</li>
<li>組織横断でDatabaseに特化したエンジニアチームです</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>開発言語：Goはメイン</li>
<li>AWSは結構使っている</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>入社動機：自分の技術力で自動車業界に貢献してみたい</li>
<li>入社前後のギャップ：あまりないです</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>正直あまりない。。</li>
</ul>
</li>
<li><strong>M.Hさん ⇒　Kevin Diuさんへの質問</strong><blockquote>
<p>香港と日本の働き方の違いについてあれば教えてください。
  実際日本で働いてみてどう感じているか教えてください。</p>
</blockquote>
<ul>
<li>香港人は、「時間はお金だ」という思いが常にあり、仕事や議論には常に時間を意識して進めるので、結論や成果物はささっと出ることが多いですが、日本ですと議論や原因まで究明することが多いです。どれも一長一短だと思います。</li>
<li>実際日本で働いてみて、想像よりみなさん優しく思っています。昔は日本のドラマをよくみていて、「半沢直樹」みたいに働かないといけないという思いがありましたが、実際は全然違いますw</li>
</ul>
</li>
</ul>
<h1>H.Y</h1>
<p>![H.Yさんのプロフィール画像](/assets/blog/authors/hidenorioka/2025-09-12-newcomer/hy.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>いままではSierでインフラ系のシステム構築/移行/運用を実施していましたが、2025年7月からKTCにジョインました。初めての事業会社となるので、より一層、自分事として意識して業務できればと思っています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>自社サービスとは別となりますが、主にTMC(トヨタ自動車)案件に携わっております。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>案件によりさまざまですが、アサインされているプロジェクトでは、M365を使用しています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>KTC社内のプロジェクトは、モダンスタイルなので、いわゆるJTCのような雰囲気がないところが良い意味でのGAPですね。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>神保町：最近リニューアルしていて、全体的にフレッシュなところがいい感じです！</li>
</ul>
</li>
<li><strong>Kevin Diuさん ⇒　H.Yさんへの質問</strong><blockquote>
<p>最近ハマっているものは？</p>
</blockquote>
<ul>
<li>ここ最近は<a href="https://www.zwift.com/ja">Zwift</a>やってます!</li>
</ul>
</li>
</ul>
<h1>H.H</h1>
<p>![H.Hさんのプロフィール画像](/assets/blog/authors/hidenorioka/2025-09-12-newcomer/hh.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>QAグループでWebのQAを担当しています。拠点は大阪(OsakaTechLab)です。</li>
<li>前職でも同様にWebのQAをしておりました。(某宿泊予約サイト、某車買取サイト、などなど…)</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>QAグループ全体は12名で、自身が所属しているWebチームは5名体制です。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>どなたも優しく、質問もちょっとした雑談もしやすい雰囲気です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>入社動機：テスト自動化やAI活用といった最新技術に触れたく、KTCには既に導入事例があったため。</li>
<li>入社前後のギャップ：毎週のようにITに関する勉強会やイベントが社内で開催されており、いい意味で驚きました。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>「Park」と呼ばれているオープンスペースがとても素敵です。
タイヤを使用したテーブル、車の形をした椅子、横断歩道がデザインされたマット、など細部までこだわりを感じます。</li>
</ul>
</li>
<li><strong>H.Yさん ⇒　H.Hさんへの質問</strong><blockquote>
<p>オフィス周辺（大阪）で、おすすめのお店おしえてください！</p>
</blockquote>
<ul>
<li>OsakaTechLabと同じビルの10階にある「浪花ろばた 頂鯛」というお店が近くて個人的におすすめです！
OsakaTechLabにいらっしゃった時はぜひ！</li>
</ul>
</li>
</ul>
<h1>ばんぶー</h1>
<p>![ばんぶーさんのプロフィール画像](/assets/blog/authors/hidenorioka/2025-09-12-newcomer/bamboo.jpeg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>新卒以来、ずっと福岡で働いてます。1社目ではガラケーの開発に始まりいろんなプロジェクトに携わってました。前職の銀行では、スクラムマスターやアジャイル浸透なんかをやってました。</li>
<li>過去、私が入社した直後に、リーマンショックやらコロナショックやらが起きてるので、投資家の皆様は警戒しておいてください(笑)</li>
<li>何事も楽しく、がモットーです！技術広報として(?)非公式につぶやいてますのでフォローお願いします！<ul>
<li><a href="https://x.com/shell_in_bamboo">ばんぶー@KINTOテクノロジーズ（@shell_in_bamboo）さん / X</a></li>
</ul>
</li>
<li>アイコンはAIに適当に指示しすぎて原形がなくなったものです。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>新たな拠点・Fukuoka Tech Labで立ち上げをしてます！上司の新田さんと2人でしたが、8月に新たなメンバーが加わってくれて盛り上がってます！今後も続々と増えるはず！</li>
<li>技術広報も兼任していて、社内・社外のイベント活動やこのテックブログの運営などを学ばせてもらってます。前向きで活発なメンバーばかりで刺激をもらってます！</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>福岡では3人で濃密な時間を過ごしています。和やかで、楽しい雰囲気で仕事してます。たまに出張者が来ると嬉しくてみんなでソワソワしてます。</li>
<li>技術広報は、みんな優しくてビビります。仕事も早いしビビります。ビビるって死語？</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>入社動機は「天下のトヨタグループなのに圧倒的ベンチャー感」と「社長、副社長のメッセージやカジュアル面談で感じた組織文化」</li>
<li>入社後のギャップは「オフィスめっちゃいい・・・！」です。東京、名古屋、大阪のオフィスはめちゃくちゃオシャレで快適ですし、福岡は↓に記載のとおり。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>福岡オフィス内はまだお披露目できないんですが、窓からの眺めが最高！です。</li>
</ul>
</li>
</ul>
<table>
<thead>
<tr>
<th align="center"><img src="/assets/blog/authors/hidenorioka/2025-09-12-newcomer/fukuoka-tech-lab-001.jpg" alt="Fukuoka_Tech_Lab画像1"></th>
<th align="center"><img src="/assets/blog/authors/hidenorioka/2025-09-12-newcomer/fukuoka-tech-lab-002.jpg" alt="Fukuoka_Tech_Lab画像2"></th>
</tr>
</thead>
</table>
<ul>
<li><strong>H.Hさん ⇒　ばんぶーさんへの質問</strong><blockquote>
<p>Fukuoka Tech Labを今後どのような拠点にしていきたいか、目標や希望があれば教えてください！</p>
</blockquote>
<ul>
<li>まだまだ人数の少ない拠点なので、いい意味で実験や新しい取り組みができたらいいなと思ってます。他の拠点から知見を取り入れ、福岡で試したことを他拠点に展開し、相互作用を生んでKTCや地域に貢献できると嬉しいです。</li>
</ul>
</li>
</ul>
<h1>youhei</h1>
<p>![youheiさんのプロフィール画像](/assets/blog/authors/hidenorioka/2025-09-12-newcomer/youhei.jpg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>入社以来 Fukuoka Tech Lab の立ち上げ責任者として奔走しています。</li>
<li>前職では開発組織のマネージャーをしていました。前職と前々職でも組織の立ち上げ期を経験しているので、その経験を今回も活かせればと考えています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>まったくのゼロベースから拠点の立ち上げに挑戦できる環境です。拠点所属のメンバーは現在３名で、採用も活発化していますしどんどん拡大していく予定です。</li>
<li>他の拠点のメンバーも福岡拠点の立ち上げに積極的に協力してくれるので、裁量を持って多くのプロジェクトを進行できる環境です。拠点間連携を深めることも今後していきたいですね。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>立場上全ての拠点に行ったことがあり、それぞれの特徴を掴んだつもりです。そんな中で福岡の拠点は開放的で気兼ねなく会話できる雰囲気が最大の特徴ですね。出張者も普段の責任ある立場から少しリラックスしてオープンマインドで接してくれますね。少人数でも賑やかなところがとても良いのでこの空気を今後も大切にしたいです。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>今このタイミングで福岡に新しい開発拠点を立ち上げることに意義を感じたことが大きかったですね。トヨタグループの内製開発という大きな仕事を小さなチームで推進できることにKTCの可能性を感じました。詳しい話は<a href="https://event.shoeisha.jp/devsumi/20250926/timetable#session8">今度登壇するイベント</a>で話す予定です。</li>
<li>入社して一番のギャップはこれまで在籍した企業で最も技術的にフラットな点です。特定の技術にロックインすることがないので、自分が無意識に制約していた箱の外にある技術も選択肢に入れて良いんだと自分のバイアスに気付けたのが良かったです。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>眺望ですね。自分の住む街の美しさを感じられます。福岡空港、博多と天神の市街地、博多湾、福岡タワー、眺めているだけで癒されますし、福岡という街をより好きになりました。</li>
</ul>
</li>
<li><strong>ばんぶーさん ⇒　youheiさんへの質問</strong><blockquote>
<p>趣味でPodcastを配信されてますが、もし誰でも呼べるとしたら、ゲストに誰を呼びたいですか？？理由も教えてください！</p>
</blockquote>
<ul>
<li>ご紹介ありがとうございます（笑）。<a href="https://www.hotto.tech/">ほっとテック</a>というPodcastを３年ほどやっています。ゲストを呼ぶ機会があって、いつもテック系の文脈でお声がけしてます。その制約をとって誰でも良いなら自分が好きなミュージシャンの誰かを呼んで彼らの創作に対する感謝を伝えられたら最高ですね。</li>
</ul>
</li>
</ul>
<h1>K.S.</h1>
<p>![K.S.さんのプロフィール画像](/assets/blog/authors/hidenorioka/2025-09-12-newcomer/ks.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>データ戦略部のデータサイエンティストです。これまで金融機関やコンサルティング会社で、クオンツやデータサイエンティストなど、定量分析の仕事をしてきました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>プロダクト開発、データアナリスト、データエンジニア、データサイエンスの4つのグループがあり、データサイエンスではKINTO事業部やトヨタグループからの依頼を受けてデータ探索やモデル開発を実施しています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>グループごとに異なりますが、データサイエンスは自身で考えて動くことが求められます。事業寄りのグループほどよく話している印象です。</li>
<li>社内の雰囲気がフラットで、上席者が話を聞く姿勢で居てくれるためありがたいです。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>KTCのクライアントにはしっかりとした事業があり、その成果にデータ分析で関われる環境があります。単に数字を分析して終わりではなく、その結果が事業にどう影響したかを実際に確認できると考えて入社しました。</li>
<li>会社の規模が少しずつ拡大しているので変化も多いですが、想定の範囲内でしたので、入社前後のギャップはないです。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>開放的なオフィスかつ、駅近で通勤しやすいです。リモートと出社を組み合わせた柔軟な働き方ができるのが気に入っています。</li>
</ul>
</li>
<li><strong>youheiさん ⇒　K.S.さんへの質問</strong><blockquote>
<p>モビリティの世界に来てこれまでの世界におけるデータサイエンスと比べて同じところ、違うところを一つずつ教えてください。</p>
</blockquote>
<ul>
<li>大きな違いは、位置情報やセンサー情報といった、これまで金融やコンサルではあまり扱わなかった種類のデータが豊富に存在することで、分析の視点や手法にも新しい発想が求められます。その未知のデータに触れること自体が、日々の知的好奇心を強く刺激してくれます。</li>
<li>一方で、データを正しく理解するためには、背景となる業務知識が欠かせないという点が同じです。数値や項目の意味を把握し、文脈と照らし合わせることで初めて価値ある示唆を導き出せる点は変わらないなと感じます。</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Digging Deeper into Why Web Components and Tailwind CSS Don't Play Well Together]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-14-web-components-and-tailwind-css-dont-mix-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-14-web-components-and-tailwind-css-dont-mix-en/</guid>
            <pubDate>Thu, 11 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article explains why combining Web Components' Shadow DOM with Tailwind CSS is technically difficult and generally not recommended. The core issue lies in the fundamental conflict between the style encapsulation provided by the Shadow DOM and the global CSS generation approach of Tailwind CSS.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello! My name is Kameyama, and I&#39;m a web engineer in the Project Promotion Group at KINTO Technologies. I&#39;m currently studying frontend development.</p>
<p>In modern web development, **component-oriented ** architecture has become the standard. By breaking the UI into reusable components, efficiency and maintainability can be improved. <strong>Web Components</strong> and <strong>Tailwind CSS</strong> are both powerful tools that support component-based frontend development.</p>
<p>Web Components is a technology that&#39;s been gaining attention in recent years. It allows you to create reusable, encapsulated custom elements based on web standards. Tailwind CSS, on the other hand, is a CSS framework that offers fast UI styling through a utility-first approach. The recent release of Tailwind CSS v4 has brought even better performance, and it continues to receive active updates.</p>
<p>At first glance, these technologies may seem like a good match. The idea of &quot;encapsulated markup and logic per component (with Web Components)&quot; combined with &quot;easy styling with utility classes (with Tailwind CSS)&quot; sounded ideal, I thought.</p>
<p>But once I started developing, things wouldn&#39;t work out the way I expected. As I dug deeper, I found that the <strong>Shadow DOM</strong>, a core part of Web Components, and the styling mechanism of Tailwind CSS are fundamentally at odds with each other. In this article, I&#39;ll share what I&#39;ve learned about why these two don&#39;t work well together, especially from the perspective of the Shadow DOM, and why combining them is generally discouraged.</p>
<h2>About Web Components</h2>
<h3>What is Shadow DOM?</h3>
<p>First of all, Web Components are mainly made up of the following three technologies:</p>
<ul>
<li><strong>Custom Elements:</strong> An API for defining your own HTML elements (e.g., <code>&lt;my-button&gt;</code>) </li>
<li><strong>Shadow DOM:</strong> A technology that isolates (encapsulates) a component&#39;s internal DOM tree and styles from the outside world.</li>
<li><strong>HTML Templates:</strong> <code>&lt;template&gt;</code> and <code>&lt;slot&gt;</code> elements to hold reusable markup fragments</li>
</ul>
<p>Among these, the existence of <strong>Shadow DOM</strong> plays a key role in why Web Components and Tailwind CSS don&#39;t work well together.</p>
<p>When you attach Shadow DOM to an element, that element becomes a <strong>Shadow Host</strong> and contains a hidden internal DOM tree called <strong>Shadow Tree</strong>. As a general rule, styles applied to elements inside a shadow tree are not affected by anything outside of it, such as the main document or a parent shadow tree. Likewise, styles defined outside the shadow tree generally won&#39;t apply to its internal elements either.</p>
<p>This mechanism provides powerful <strong>style encapsulation</strong>, preventing a component&#39;s styles from being affected by external CSS and keeping internal styles from leaking outside. This helps ensure that your components won&#39;t break or look inconsistent, even in environments where multiple CSS design approaches are used.</p>
<p>Here is an example of source code showing how the two are used together.</p>
<pre><code class="language-typescript">class MyStyledComponent extends HTMLElement {
  constructor() {
    super();
    // Shadow DOMをアタッチ（openモードで外部からアクセス可能に）
    const shadowRoot = this.attachShadow({ mode: &#39;open&#39; });

    // Shadow DOM内部のHTML構造
    const template = document.createElement(&#39;template&#39;);
    template.innerHTML = `
      &lt;div class=&quot;container mx-auto p-4 bg-blue-200&quot;&gt; // Tailwindクラスを使用
        &lt;p class=&quot;text-xl font-bold text-gray-800&quot;&gt;Hello from Shadow DOM!&lt;/p&gt; // Tailwindクラスを使用
        &lt;button class=&quot;bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded&quot;&gt;
          Click me
        &lt;/button&gt;
      &lt;/div&gt;
    `;
    shadowRoot.appendChild(template.content.cloneNode(true));

    // ★ ここが問題 ★ Shadow DOM内部にスタイルを適用するには...？
    // 外部のスタイルシートは原則届かない
  }
}
customElements.define(&#39;my-styled-component&#39;, MyStyledComponent);
</code></pre>
<p>As shown in the example above, even if you use Tailwind classes like <code>&lt;div class=&quot;container mx-auto p-4 bg-blue-200&quot;&gt;</code> inside the Shadow DOM of <code>MyStyledComponent</code>, those styles won&#39;t be applied by default.</p>
<h2>About Tailwind CSS</h2>
<h3>Utility-First and Global CSS</h3>
<p>Tailwind CSS takes an approach to building UIs quickly by writing low-level utility classes such as <code>flex</code>, <code>pt-4</code>, and <code>text-blue-500</code> directly into HTML.</p>
<p>During the build process, Tailwind scans your project&#39;s HTML, JavaScript, TypeScript files, etc. and generates CSS rules for any utility classes used. The generated CSS is typically output as a <strong>single global stylesheet</strong>, which is then loaded into the HTML document, often in the <code>&lt;head&gt;</code>.</p>
<p>For example, if your HTML contains something like <code>&lt;div class=&quot;flex pt-4&quot;&gt;</code>, Tailwind will generate the following CSS rules and include them in the global stylesheet:</p>
<pre><code class="language-css">/* Tailwindによって生成されるCSS（の例） */
.flex {
  display: flex;
}
.pt-4 {
  padding-top: 1rem;
}
</code></pre>
<p>A key point about Tailwind&#39;s styling mechanism is that CSS rules are defined in the <strong>global scope</strong>.</p>
<h2>A Hopeless Incompatibility</h2>
<h3>Shadow DOM Encapsulation vs. Tailwind&#39;s Global Styles</h3>
<p>Here&#39;s the crux of the problem.</p>
<ul>
<li><strong>Shadow DOM</strong> <strong>encapsulates</strong> internal elements to prevent external styles from being applied to them.</li>
<li><strong>Tailwind CSS</strong>, on the other hand, generates CSS rules <strong>in the global scope</strong> based on the utility classes you use.</li>
</ul>
<p>There is a clear contradiction between the two. CSS rules like <code>.flex { display: flex; }</code> generated globally by Tailwind do not cross the Shadow DOM boundary to reach elements inside the Shadow Tree. </p>
<p>In the earlier TypeScript example, the reason Tailwind styles do not apply to <code>&lt;div class=&quot;container mx-auto p-4 bg-blue-200&quot;&gt;</code> is because the corresponding CSS rules exist outside the Shadow DOM, in the global scope of the main document. The Shadow DOM blocks those rules from being applied inside the component.</p>
<p><strong>A Note on Tailwind CSS v4:</strong> Tailwind CSS v4 boasts better performance thanks to a new engine. However, its core styling mechanism remains the same: it scans project files and generates global CSS based on the utility classes used. As a result, even with v4, the incompatibility with Shadow DOM remains unresolved.</p>
<h2>Is There Any Way Around This?</h2>
<p>While researching ways to solve this issue, I did come across a few possible workarounds. However, all of them either compromised the strengths of Web Components or Tailwind CSS, or required a high implementation cost. In the end, I couldn&#39;t find any solution that truly solves the problem. Still, here are some workarounds, even if they feel like desperate measures.</p>
<h3>Copy and Paste the Built Tailwind CSS into the Shadow DOM.</h3>
<p>This method involves manually or using a build tool to extract the CSS rules corresponding to the Tailwind classes used in each Web Component and embedding them as a <code>&lt;style&gt;</code> tag inside the component&#39;s Shadow DOM.</p>
<p><strong>Disadvantages:</strong></p>
<ul>
<li>Very time-consuming and difficult to maintain</li>
<li>Leads to duplicated CSS for each component, increasing file size</li>
<li>The advantages of Tailwind&#39;s JIT compiler, which only generates the styles you use, cannot be fully utilized.</li>
<li>Deviates from the Tailwind operational workflow, including config files and plugins</li>
</ul>
<h3>Don&#39;t Use Shadow DOM</h3>
<p>This approach places elements in the Light DOM instead of using Shadow DOM in your Web Components. In this case, the elements are considered part of the main document&#39;s DOM tree, so global Tailwind styles are applied to them.</p>
<p><strong>Disadvantages:</strong></p>
<ul>
<li>The biggest benefit of Web Components, which is style encapsulation, is lost in this case. As a result, external CSS may affect the component, and the component&#39;s styles may leak out as well, compromising the independence of the component.</li>
</ul>
<p>As you can see from these approaches, it is clear that the strong encapsulation provided by Shadow DOM and Tailwind CSS&#39;s reliance on global style sheets are basically different in design philosophy. Trying to combine the two tends to undermine the benefits of one or the other.</p>
<h2>Conclusion: Web Components and Tailwind CSS Should Not Be Used Together</h2>
<p>As we&#39;ve seen, combining Web Components, especially when using Shadow DOM, with Tailwind CSS should generally be avoided since the advantages of each technology tend to cancel each other out.</p>
<p>This is because the two technologies have <strong>fundamentally different approaches to styling, which end up clashing with each other</strong>.</p>
<ul>
<li><strong>Web Components (Shadow DOM)</strong> are designed to completely encapsulate component styles and isolate them from the outside.</li>
<li><strong>Tailwind CSS</strong>, on the other hand, generates CSS corresponding to utility classes as a <strong>global stylesheet</strong>, assuming it will be applied across the entire page.</li>
</ul>
<p>Because of this, the convenient utility class styles generated by Tailwind cannot cross the strict boundaries of the Shadow DOM and therefore do not apply inside the component.</p>
<p>Although there are workarounds, they often sacrifice component independence and increase development complexity. In many cases, these solutions become counterproductive. To maximize the benefits of each technology, it would be wise to avoid this combination.</p>
<p>I hope this article serves as a useful reference for anyone considering combining Web Components and Tailwind CSS.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/soju.kameyama/20250714/wordcloud_coverimage_web-components-and-tailwind-css-dont-mix.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Entering a New Stage! Osaka Tech Lab 2.0 Begins]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-10-OsakaTechLab2-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-10-OsakaTechLab2-en/</guid>
            <pubDate>Wed, 10 Sep 2025 09:00:00 GMT</pubDate>
            <description><![CDATA[Entering a New Stage! Osaka Tech Lab 2.0 Begins]]></description>
            <content:encoded><![CDATA[<p>Hello! My name is Oka, and I work in recruitment for Osaka at KINTO Technologies Corporation. Our Osaka Tech Lab has recently moved to a new office. In this article, I&#39;ll take you behind the scenes and show you what&#39;s so great about the new office!</p>
<h2>What is Osaka Tech Lab?</h2>
<p>Osaka Tech Lab, located in Shinsaibashi, opened in 2022 as an engineering hub in western Japan. We recently moved to a building with direct access to JR Osaka Station, making our location even more convenient.</p>
<p>Engineers from a variety of fields, including software development, cloud infrastructure, and data analysis, gather here to develop and improve our in-house products.</p>
<h2>&quot;Osaka Tech Lab 2.0&quot; Built Together by Everyone</h2>
<p><strong>How the concept was born</strong></p>
<p>The office relocation marks the start of the &quot;Osaka Tech Lab 2.0&quot; project! This project wasn&#39;t something pre-planned by anyone. It all began with team members sharing ideas about what kind of space they wanted to create and we built it together.</p>
<p>From this the concept came up: &quot;Get Together! Spark Ideas! CO-LAB&quot;</p>
<p>&quot;We didn&#39;t want just a place to work; we wanted a space that reflects Osaka&#39;s vibe and culture, and where we could co-create new value together.&quot; With that mindset, we reflected on our past activities and named it together as a team.</p>
<p>![](/assets/blog/authors/oka/osakarenewal/1.png =600x) </p>
<p><strong>The &quot;Who&#39;s in?&quot; Culture</strong></p>
<p>Another phrase that naturally emerged at Osaka Tech Lab. That&#39;s &quot;Who&#39;s in?&quot; When someone has an idea or something they want to try, they simply speak up. Others respond with &quot;Sounds good!&quot; or &quot;Let&#39;s do it together,&quot; and a group naturally forms around it. Such scenes are common around us here.</p>
<p>We call this the &quot;Who&#39;s in?&quot; style.</p>
<p>![](/assets/blog/authors/oka/osakarenewal/2.png =600x)  </p>
<p>In fact, the office relocation project followed this style. One person called out, and others came together to help build. Our new office is filled with that spirit. Now, let me show you a glimpse of what it looks like!</p>
<h2>Highlights of Our New Office!</h2>
<p>![](/assets/blog/authors/oka/osakarenewal/3.png =600x)<br>The office floor is decorated with road-like lines that guide you toward the meeting room.</p>
<p>**🛝 PARK Area | Take off your shoes and have a break ** ![](/assets/blog/authors/oka/osakarenewal/4.png =600x)  </p>
<p>We created a relaxing space where you can take off your shoes and unwind in comfort. It&#39;s the perfect for casual meetings or short breaks. It has already become a popular spot during our all-hands meetings, where everyone naturally gathers.</p>
<p>![](/assets/blog/authors/oka/osakarenewal/5.png =600x)  </p>
<p><strong>🚗 The names of the meeting rooms are also Osaka Tech Lab style</strong></p>
<p>The meeting rooms are named with themes inspired by garages and pits. Some of them even have uniquely Osaka-style names that tie in with mobility like &quot;Motor Pool.&quot;</p>
<p>*Motor Pool: A term commonly used in Osaka meaning &quot;parking lot.&quot;</p>
<p>![](/assets/blog/authors/oka/osakarenewal/6.png =600x)<br>The name emerged naturally from casual conversation during repeated brainstorming sessions on Slack. We all had fun deciding on a name that suited us.</p>
<p>![](/assets/blog/authors/oka/osakarenewal/7.png =600x)<br>(And the final names were chosen through passionate discussions with a touch of Osaka humor!)</p>
<p>![](/assets/blog/authors/oka/osakarenewal/8.png =600x)  </p>
<p><strong>🛣️OSAKA JCT</strong></p>
<p>Like KINTO&#39;s Muromachi Office, a communication space called &quot;OSAKA JCT&quot; has also been created. The wall design is a creative piece that Osaka Tech Lab&#39;s designers are very proud of, having embodied a concept that everyone shaped together.</p>
<p>![](/assets/blog/authors/oka/osakarenewal/9.png =600x)<br>We planned and held the opening ceremony in the JCT space, using our &quot;Who&#39;s in?&quot; approach to form a volunteer-led committee. The relocation event itself was also led by team members, and we invited the managers and held an internal kickoff session. From start to finish, it was a fully handmade event, built by everyone.</p>
<p>![](/assets/blog/authors/oka/osakarenewal/10.png =600x)<br>We&#39;ve received lots of feedback from members about the new office:</p>
<ul>
<li>I feel naturally more motivated to work. It&#39;s the kind of space that makes you sit up straighter.</li>
<li>The shared space &quot;PARK&quot; has an open feel and is a comfortable place where even larger groups can gather naturally.</li>
<li>Mobility-themed ideas can be found throughout the office. The area is filled with playful details, such as the names and signs of the locations, road-patterned flooring, tire-shaped tables, and even car-shaped mobile benches, making it exciting just to walk around.</li>
</ul>
<p>During interviews at the opening ceremony, many team members said things like, &quot;It feels great to see our voices being reflected in the office,&quot; and &quot;We feel a sense of attachment to this place as &#39;ours.&#39;&quot;</p>
<h2>What I Felt at Osaka Tech Lab</h2>
<p>To be honest, this atmosphere of &quot;creating together&quot; has been around since our previous office.</p>
<p>![](/assets/blog/authors/oka/osakarenewal/11.jpeg =600x)  </p>
<p>When we closed down that old space, we held a warm and casual farewell party. Everyone just showed up with drinks and toasted together. Regardless of department or title, people just gather together and before you knew it, a relaxed get-together had started. This kind of culture has taken root naturally at Osaka Tech Lab.</p>
<p>As a recruiter, I believe that this closeness and the culture where everyone&#39;s voice is heard and valued are what make Osaka Tech Lab so appealing. Even now in the new office, that atmosphere hasn&#39;t changed.</p>
<p>We hope that this will continue to be a place where people can casually chat about the future.</p>
<h2>Why don&#39;t we &quot;Get Together! Spark Ideas! CO-LAB&quot;</h2>
<p><strong>Event Information</strong></p>
<p>At Osaka Tech Lab, we regularly host events where you can experience our culture. If our &quot;Who&#39;s in?&quot; culture resonates with you, we&#39;d love for you to drop by. One of our core initiatives is CO-LAB Tech Night, an event where Osaka Tech Lab members share insights and know-how from their daily development work.</p>
<p><strong>CO-LAB Tech Night vol.1: Full in-house cloud development in Osaka! #1</strong></p>
<ul>
<li>Date and time: Thursday, July 10, 2025 / 19:00-21:30</li>
<li>Overview: Under the theme of cloud development, Osaka Tech Lab members will share their current initiatives and key insights on cloud infrastructure, SRE, and data analytics.</li>
<li>Details: <a href="https://www.kinto-technologies.com/news/20250702">https://www.kinto-technologies.com/news/20250702</a></li>
</ul>
<p><strong>CO-LAB Tech Night vol.2 , Cloud Security Night #3</strong></p>
<ul>
<li>Date and time: Thursday, August 07, 2025 / 19:00-21:30</li>
<li>Overview: This event will focus on cloud security in multi-cloud environments, including AWS, Google Cloud, and Azure, and will deepen knowledge of cloud security through case studies from various companies. We&#39;re excited to bring the third &quot;Cloud Security Night&quot; to Osaka which is typically held in Tokyo!</li>
<li>Details: <a href="https://www.kinto-technologies.com/news/20250709">https://www.kinto-technologies.com/news/20250709</a></li>
</ul>
<p>![](/assets/blog/authors/oka/osakarenewal/12.png =600x)  </p>
<p>**For the latest information, check the Osaka Tech Lab&#39;s special website! **</p>
<p>Osaka Tech Lab will continue to share information on a variety of topics, including engineering, cloud computing, and data analysis through events and the Tech Blog. Event information will be updated regularly on the Osaka Tech Lab special website. If you are interested, check out the event list (CO-LAB events)!</p>
<p>▼ Osaka Tech Lab special website here: <a href="https://www.kinto-technologies.com/company/osakatechlab/">https://www.kinto-technologies.com/company/osakatechlab/</a></p>
<p>![](/assets/blog/authors/oka/osakarenewal/13.png =600x)  </p>
<p><strong>(The Osaka Tech Lab special website was also born from our &quot;Who&#39;s in?&quot; culture)</strong></p>
<p>Launched alongside this article, the Osaka Tech Lab special website is yet another initiative that came to life through our &quot;Who&#39;s in?&quot; culture.</p>
<p>It all started with members saying things like &quot;We want to share more!&quot; or &quot;Let&#39;s show what Osaka really feels like.&quot; People naturally came together to plan, design, write, and launch the site by hand. We also collaborated with members from the Creative Office in Tokyo, making this a true CO-LAB effort and Osaka-style initiative. This special website is also packed with our unique culture. Come and take a look!</p>
<h4>Open for Casual Chats</h4>
<p>If you&#39;d like to hear more about what Osaka Tech Lab is like, feel free to sign up using the link below!</p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/1859151978603163665">https://hrmos.co/pages/kinto-technologies/jobs/1859151978603163665</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/oka/osakarenewal/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[An Approach to Improving the Quality of In-App Ad Creatives]]></title>
            <link>https://blog.kinto-technologies.com/posts/2022-12-23-ad-creative-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2022-12-23-ad-creative-en/</guid>
            <pubDate>Tue, 09 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[An Approach to Improving the Quality of In-App Ad Creatives]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>I’m Kai Yamamoto, a designer for my route by KINTO at KINTO Technologies. I mainly handle advertising design for my route by KINTO, including landing pages and banners, as well as organizing app screen flows and developing design guidelines. my route by KINTO is a multimodal mobility service that provides a complete set of travel features—all in one app—from searching and booking transportation to making payments. It also enhances urban mobility by offering information on event spots and local shops that help “bring the city to life.”</p>
<h1>Challenges to Solve</h1>
<p>Aiming to boost ticket sales and weekly usage, we worked together with representatives from Toyota Financial Services Corporation as part of our initiatives. my route by KINTO includes a content section called Feature Articles. Users are guided to these articles through in-app pop-ups, designed to encourage outings and increase app engagement. The design task this time was to improve the quality of the in-app pop-up.</p>
<p><img src="/assets/blog/authors/KaiYamamoto/20221223/1.png" alt=""></p>
<h1>Creative Improvement Approaches</h1>
<p>&quot;Improving creative quality&quot; is a fairly vague goal, so we focused on narrowing the gap between the current state and the intended outcome. We started by identifying the issues, and from there, extracted three key challenges we needed to address in this design.</p>
<ol>
<li><p><strong>Take an approach that fits the target audience.</strong> By building a shared understanding of the target user among the team, it became easier to make design decisions—such as choosing a color tone or adjusting corner roundness—that matched the user.</p>
</li>
<li><p><strong>Ensure consistency between the article and the design.</strong> As an extreme example, even if the pop-up has a stylish design, users might lose interest and exit without reading it if the article is about something like &quot;going out to see autumn leaves and visiting a cute café.&quot; That&#39;s why it was important to read the article thoroughly beforehand and imagine expressions that aligned with the content.</p>
</li>
<li><p><strong>Deliver a sense of excitement that makes users want to go out.</strong> This may sound abstract, but unlike typical ads that highlight clear benefits like &quot;Limited time offer!&quot; or  &quot;Only 1000 yen!&quot; this pop-up simply asks, &quot;Why not go out and have some fun?&quot; With that in mind, we focused on creating an excitement factor that would resonate when users saw the ad.</p>
</li>
</ol>
<p><img src="/assets/blog/authors/KaiYamamoto/20221223/2.png" alt=""></p>
<h1>Pop-Up Presentation Approaches</h1>
<p>In addition to design-related challenges, we also considered how to present the pop-up, as this was another important factor. We wanted to run A/B tests to identify the most effective presentation, so we explored several layer patterns.</p>
<p><img src="/assets/blog/authors/KaiYamamoto/20221223/3.png" alt=""></p>
<p>Pattern 1 had a strong advertising feel but tended to be effective when the content shown was relevant to the user. This approach was also seen in ad displays on major video platforms. Pattern 2 felt more friendly and thoughtful, as it included detailed descriptions. However, if the text was too long, users might lose interest. Pattern 3 was a versatile type, commonly seen in carrier pop-ups. We wondered whether it would be suitable for this particular campaign.We tried multiple approaches with these considerations in mind.</p>
<h1>Results</h1>
<p>As you can see, we explored various directions to upgrade the design. Three months after introducing weekly pop-ups, daily active users (DAU) increased by 1,100. While this figure also reflects the impact of other campaigns, the click-through rate for the pop-ups improved from 7 % to nearly 19 % each time, marking an 11 % increase. This positive result was certainly supported by the strong problem-solving efforts on the business side by the Toyota Financial Services team. However, I believe the key factor was the bi-weekly communication, which helped us deepen our shared understanding of the business goals, article content, and design direction.</p>
<h1>Future Challenges</h1>
<p>There is still room for improvement. Going forward, we aim to share this initiative with regional partners, deepen our understanding of effective advertising design, and develop design guidelines to maintain consistent quality. We will continue to refine our designs through the PDCA cycle.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Findy Team+ Award 2025 Best Practice Awardを受賞しました]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-09-05-FindyTeamAward/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-09-05-FindyTeamAward/</guid>
            <pubDate>Tue, 09 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[開発生産性が優れたエンジニア組織を表彰する「Findy Team+ Award 2025 Best Practice Award」の受賞理由、KINTOテクノロジーズ株式会社の「リリースファースト」の実現状況についてご紹介します。]]></description>
            <content:encoded><![CDATA[<p>開発生産性が優れたエンジニア組織を表彰する<a href="https://award.findy-team.io/2025">「Findy Team+ Award 2025」</a>にて、KINTOテクノロジーズ（以下、KTC）を「Best Practice Award」に選出いただきました。</p>
<p>表彰式ではKTCの取り組みについて、ご紹介の時間もいただきました。</p>
<p>今回はその内容を中心に、KTCの「リリースファースト」の実現状況についてご紹介します。</p>
<h2>リリースファースト推進と部署紹介</h2>
<p>「リリースファースト」は、今年できたばかりのEngineering Officeという組織の活動の一環で推進しています。</p>
<p>Engineering Officeはいわゆる横断型のEnablingチームと呼ばれるような形でプロダクト開発に関わり、組織力・開発力向上に取り組むチームです。チームの詳細は以前「<a href="https://blog.kinto-technologies.com/posts/2025-02-12-engineering-office/">KINTOテクノロジーズの開発組織を強くしたい Engineering Office のご紹介</a>」という記事でも紹介されていますので、ご覧ください。</p>
<p>今回受賞のテーマにもなっている「リリースファースト」は、2025年の展望として副社長の景山より発信された「<a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">2024年の振り返りと2025年の展望</a>」で最初に触れられた、注力テーマの一つです。</p>
<blockquote>
<p><strong>リリースファースト</strong>（最短リリース） は、われわれが開発するプロダクトをいかに最短でリリースするか、知恵と技術を使ってここにこだわっていきたいと思っています。</p>
</blockquote>
<p>（中略）</p>
<blockquote>
<p>自分たちでプロダクトの価値について深く考え、最短リリースを実現することが内製開発部隊の強みであり、これが最大の事業貢献だと思っています。</p>
<p>来年は徹底的にこれを突き詰めていきます。</p>
</blockquote>
<p>このメッセージを受け、現在は各プロダクトがリリースファーストの実現に向け、邁進している最中です。</p>
<p>Engineering Officeでは、開発生産性の観点を軸に各プロダクトの活動のサポートを行っています。</p>
<h2>Findy Team+導入の状況とKTCの現状</h2>
<p>Findy Team+は、KTCでは約20チーム／解析対象160人に導入されています（2025年8月現在）。Four Keysでの分析や定量的な現状把握が浸透しつつある状況で、チーム内で定期的な改善への話し合いを進められるチームが増えてきたところです。</p>
<p>改善活動が進み、Four Keysの向上も見られる中、リリースが早くなっている「実感がない」ことが次の課題となりました。</p>
<p><img src="/assets/blog/authors/naito/2025-09-FindyTeamAward/2025-09-FindyTeamAward_img02.png" alt="Findy Team+導入の状況：改善活動はしているし、ちょっとずつFour Keysが向上しているチームもあるが、リリースが早くなっている実感がない"></p>
<p>現状把握を行うために、<strong>情報</strong>×<strong>思考</strong>×<strong>行動</strong>のサイクルを粘り強く続けることが重要です。</p>
<ul>
<li>（情報）Findy Team+を導入し、チームの現状を定量的に把握する</li>
<li>（思考）収集した情報をもとにチームで話し合い、アイデアを出し合う</li>
<li>（行動）アイデアをもとに行動を起こし、フィードバックを得る</li>
</ul>
<p>と並べてみると、一見できているように見受けられます。</p>
<p>それでも「実感」につながらないのはなぜか？</p>
<p>情報×思考×行動サイクルを重ねることで、解像度は上がります。より解像度を上げるためには、</p>
<ol>
<li>深さ：原因・要因・方法等を具体的に掘り下げる</li>
<li>広さ：考慮する原因・要因、アプローチの多様性</li>
<li>構造：「深さ」「広さ」で見えた要素を分類し、要素間の関係性等を把握する</li>
<li>時間：経時変化や因果関係、プロセスや流れを捉える​</li>
</ol>
<p>という4つの観点を組み込むことが重要です。（この考え方は、馬田隆明氏著『解像度を上げる』​​で紹介されています。）</p>
<p><a href="https://eijipress.co.jp/products/2318">https://eijipress.co.jp/products/2318</a></p>
<p>開発生産性の視点で分析し、「情報」の不足に起因し課題の解像度が低くなっている、という仮説を立てました。</p>
<ul>
<li>Four Keys以外に指標がないこと</li>
<li>Findy Team+に新しい機能（プロジェクト投資分析／プロジェクトアウトカム分析／プロジェクトプロセス分析）が提供されても、概ね計測ができない状況にある</li>
</ul>
<p>この2点を踏まえ、「情報の不足」が「思考」と「行動」の幅を狭めてしまっていると考えました。定量が不足しているならば、定性情報を増やす・獲得する行動を起こそうと、ケイパビリティ調査とValue Stream Mapping（以下VSM）に取り組みました。</p>
<h2>ケイパビリティ調査</h2>
<p>ケイパビリティがパフォーマンスに影響を与える、という調査結果がGoogle Cloudの<a href="https://dora.dev/research/?view=detail">DevOps Research and Assessment（DORA）チームから提供</a>されています。また、<a href="https://cloud.google.com/architecture/devops?hl=ja">技術・プロセス・組織文化の計27項目のケイパビリティも公開</a>されており、項目に沿って計測・評価することが可能です。</p>
<p><img src="/assets/blog/authors/naito/2025-09-FindyTeamAward/2025-09-FindyTeamAward_img01.png" alt="DORA Core model v2.1.0のサマリー図とケイパビリティの一覧の図"></p>
<ol>
<li>インタビューシートの作成</li>
<li>インタビューの実施</li>
<li>Engineering Officeによる分析とレポート</li>
</ol>
<p>という流れで実行しました。</p>
<p>このプロセスの中では、インタビュー形式の対話が特に重要となります。</p>
<p>アンケート形式で渡して回答を収集することもできますが、あえてインタビューにすることで「なぜその回答になったのか」「今の仕事の進め方」など、チームの現状について深掘りが可能です。</p>
<p>開発チーム自身も、インタビューを通じ観点を得て言語化することで、自分たちの現状を再認識することになります。</p>
<p>インタビュー実施後は、Engineering Officeで結果に対して分析を行います。そうすることでインタビュー結果やFour Keysが構造化され、より解像度を高められます。</p>
<p>ケイパビリティ調査と分析は、チームごとに行い、それぞれの現状の報告と改善提案を合わせてレポートしています。</p>
<h3>ケイパビリティ調査後の行動</h3>
<p>調査後は、技術カテゴリーやチーム単体でできるケイパビリティ強化・獲得の行動が、それぞれ着実に増えているように感じています。</p>
<p>一方で、チームを超えて取り組む必要があるところに関しては鈍化する傾向にあります。</p>
<p>［バリューストリームでの作業の可視化］項で、「他のロール・チームの仕事の進め方についてはよくわからない」という意見が上がり、結果的に思考や行動も自分のチームの中に留まる、という傾向が見えました。</p>
<p>そこで、プロダクト全体でのVSMにチャレンジすることにしました。</p>
<h2>Value Stream Mapping（VSM）</h2>
<p>Value Stream Mapping^[（引用）By DanielPenfield - Own work, CC BY-SA 3.0, <a href="https://commons.wikimedia.org/w/index.php?curid=28553995%5D%E3%81%A8%E3%81%AF%E3%80%81%E8%A3%BD%E5%93%81%E3%81%8C%E3%83%A6%E3%83%BC%E3%82%B6%E3%83%BC%E3%81%AB%E5%B1%8A%E3%81%8F%E3%81%BE%E3%81%A7%E3%81%AE%E3%80%8C%E3%82%82%E3%81%AE%E3%81%A8%E4%BB%95%E4%BA%8B%E3%81%AE%E6%B5%81%E3%82%8C%E3%80%8D%E3%82%92%E8%A6%8B%E3%81%88%E3%82%8B%E5%8C%96%E3%81%99%E3%82%8B%E6%89%8B%E6%B3%95%E3%81%A7%E3%81%99%E3%80%82">https://commons.wikimedia.org/w/index.php?curid=28553995]とは、製品がユーザーに届くまでの「ものと仕事の流れ」を見える化する手法です。</a></p>
<p><img src="/assets/blog/authors/naito/2025-09-FindyTeamAward/2025-09-FindyTeamAward_img03.png" alt="DanielPenfieldのValueStreamMapの図：製品がユーザーに届くまでのものと仕事の流れを見える化する"></p>
<p>各チームから数人ずつ参加して、現在のチームの「ものと仕事の流れ」を可視化します。</p>
<p>その後、開発チーム全体を集めてVSMの共有をして認識合わせをし、課題感や理想像についてのディスカッションを行います。その結果をインプットに、チーム体制の改善計画を立てていきます。</p>
<p>この取り組みで大事なのは全体での認識合わせと課題のディスカッションです。</p>
<p>全員が全体像を理解し、思考する。それが一人ひとりの行動変容につながります。</p>
<p>解像度の視点では、広さ・時間・深さ・構造ともに高まります。</p>
<h3>VSM実行後の変化</h3>
<p>VSMを受け、プロダクト全体でのチーム活動の改善計画・改革を絶賛実行しています。</p>
<p>曖昧だった役割、会議体、コミュニケーションを可視化し、新しい仕事の進め方に合わせて見直しました。また、拠点が分かれていたメンバーが1拠点に集結し、物理的なコミュニケーション強化も行いました。</p>
<p><img src="/assets/blog/authors/naito/2025-09-FindyTeamAward/2025-09-FindyTeamAward_img04.png" alt="VSMの結果、他のチームからの受け渡し待ちや、未解決による作業開始タイミングの遅れ、ムダな時間、ムリ・ムラの可視化が行われました"></p>
<p>さらにJIRA（Atlassian社開発のプロジェクト管理ツール）も、新しい仕事の進め方に合わせて改修を行っています。今後、Findy Team+で工程ごとのリードタイムが計測できるようになる見込みです。</p>
<h2>試みのまとめと今後の課題</h2>
<p>情報×思考×行動のサイクルを実行しても「実感」につながらないという現状解決に、</p>
<ul>
<li>（情報）取得量を上げるため、Findy Team+の他にケイパビリティ調査、VSMを追加</li>
<li>（思考）Engineering Officeが収集した情報の分析サポートを行い、プロダクト全体で話し合いを実行<ul>
<li>さまざまなロールの観点が合わさり思考を深めることにつながった</li>
</ul>
</li>
<li>（行動）チームを超え、プロダクト全体でリリースファーストを目指し行動を増やす</li>
</ul>
<p>という取り組みを行っています。</p>
<p>それでもまだ課題は残っています。</p>
<p>VSMで得た「新しい仕事の進め方」が本格的に始まるのはこれからです。フィードバックも得ながら、チームに定着するまでは継続的な議論と改善が必要となります。</p>
<p>また、ケイパビリティ調査やVSMは一定の効果が見えているものの、社内の他のチームへの展開はできていません。深く、広く、時間軸を考慮した情報を得る活動や、上手に思考する方法は、質の高い結果を得られます。分析・改善することで、他のチームの「リリースファースト」推進にも効果が見込めるので、順次展開を進める予定です。</p>
<p>発表スライドの全編は、Speaker Deckからご覧いただけます。</p>
<p><a href="https://speakerdeck.com/kintotechdev">https://speakerdeck.com/kintotechdev</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/naito/2025-09-FindyTeamAward/2025-09-FindyTeamAward_title.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[パパママエンジニア必見！KTCパパエンジニアの１日]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-09-08-factory-engineer-with-child/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-09-08-factory-engineer-with-child/</guid>
            <pubDate>Mon, 08 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KTCの30代パパエンジニアのとある在宅勤務をまとめました。]]></description>
            <content:encoded><![CDATA[<h2>ご挨拶</h2>
<p>こんにちは！KINTO FACTORYチーム、フロントエンジニアで二児の父の三上です。</p>
<p>今回は自分の在宅勤務のとある１日を紹介します。
同じ境遇のパパ・ママエンジニアの方へ「KTCではこんな働き方ができるんだ」というイメージを掴んでいただけると幸いです。</p>
<h2>とある一日の流れ</h2>
<table>
<thead>
<tr>
<th>時間</th>
<th>内容</th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td>07:00</td>
<td>起床</td>
<td>子供より少し早く起きて朝ごはん準備</td>
</tr>
<tr>
<td>07:30</td>
<td>子供の朝食 ~ 保育園送迎</td>
<td>パパとママで協力体制</td>
</tr>
<tr>
<td>08:00</td>
<td>在宅勤務開始</td>
<td>大体いつもチームで2番目に早いです。（1番早く出社するメンバーもパパ）</td>
</tr>
<tr>
<td>11:00</td>
<td>保育園から電話</td>
<td>次女、38度の発熱。</td>
</tr>
<tr>
<td>11:15</td>
<td>一度業務を中断してお迎えへ</td>
<td>チームに連絡をし、保育園へ</td>
</tr>
<tr>
<td>11:45</td>
<td>業務再開</td>
<td>子供の様子を見つつ抱っこしながら</td>
</tr>
<tr>
<td>12:30</td>
<td>昼ご飯</td>
<td>この日のご飯はサクッと</td>
</tr>
<tr>
<td>14:00</td>
<td>ミーティング</td>
<td>抱っこ紐で抱えながらミーティング参加</td>
</tr>
<tr>
<td>15:00</td>
<td>子供の通院</td>
<td>またチームへ連絡して病院へ</td>
</tr>
<tr>
<td>16:00</td>
<td>業務再開</td>
<td>薬をもらったりで再開までに１時間</td>
</tr>
<tr>
<td>18:00</td>
<td>ママ &amp; 長女帰宅</td>
<td>次女を託し集中モード</td>
</tr>
<tr>
<td>19:30</td>
<td>業務終了</td>
<td>お疲れ様でしたー</td>
</tr>
</tbody></table>
<p>この日は子供が熱を出してしまいお迎え+通院と色々盛りだくさんの１日でした。
見ていただけるとわかりますがKTCではこれだけフレキシブルな働き方が可能です！</p>
<p>![在宅勤務の様子](/assets/blog/authors/y.mikami/2025_09_08_001.png =300x)</p>
<h2>KTCの働きやすい環境</h2>
<h3>フルフレックス制&amp;ハイブリッドワーク</h3>
<p>KTCではフルフレックス制を採用しており、コアタイムがありません。子供の送り迎えや急な発熱などに合わせて、柔軟に勤務時間を調整できます。</p>
<p>この日も8時から勤務を開始し、子供の体調不良で一時中断しましたが、19:30まで働くことで必要な業務時間を確保できました。「9時〜18時」という固定的な勤務時間に縛られないため、家庭の事情に合わせた働き方が可能です。</p>
<p>また、ハイブリッドワークにより在宅勤務とオフィス勤務を選択できます。子供の体調が心配な日は在宅勤務を選択し、すぐに対応できる環境で仕事ができるのは大きな安心感につながっています。</p>
<h3>パパ・ママ向けの制度充実</h3>
<p>KTCでは育児休業制度や時短勤務制度はもちろん
看護休暇や家族手当制度など子育て世代をサポートする制度が充実しています</p>
<blockquote>
<p>※最新の情報はコーポレートサイトの<a href="https://www.kinto-technologies.com/recruit/environment/">福利厚生</a>をご確認ください</p>
</blockquote>
<h3>突発的な出来事への寛容な文化</h3>
<p>この日のように子供の急な発熱でお迎えが必要になることは、子育て世代にとって避けられない出来事です。</p>
<p>KTCでは「家族優先」の文化が根付いており、チームメンバーに連絡すると「お大事に！」「子供さん優先で！」という温かい言葉が返ってきます。決して「会議があるのに...」「締切が...」というプレッシャーを感じることはありません。</p>
<p>また、急な離席があってもチーム全体でカバーし合う体制が整っています。普段からドキュメント化やペアプログラミングを推進しているため、誰かが急に離席しても業務が滞らないよう工夫しています。</p>
<p>私のチームメンバーの半数以上が子育て中のパパ・ママエンジニアです。お互いの状況を理解し合える環境があることも、働きやすさにつながっています。</p>
<h2>まとめ</h2>
<p>子育てをしながらエンジニアとして働くことは、時に大変なこともあります。しかし、KTCの柔軟な働き方と理解ある環境があれば、家族との時間を大切にしながらキャリアを積むことが可能です。</p>
<p>フルフレックス制、ハイブリッドワーク、充実した子育て支援制度、そして何より「家族優先」を理解してくれる文化。これらがKTCで働く大きな魅力です。</p>
<p>今後もチーム・会社全体でしっかりカバーし合える環境で子育てとエンジニアリングの両立を実践していきます！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/y.mikami/2025_09_08_cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[CIO Office HR Recruitment Team Introduction]]></title>
            <link>https://blog.kinto-technologies.com/posts/2022-12-18-IntroductionOfHRteam-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2022-12-18-IntroductionOfHRteam-en/</guid>
            <pubDate>Fri, 05 Sep 2025 11:00:00 GMT</pubDate>
            <description><![CDATA[CIO Office HR Recruitment Team Introduction]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>My name is Iwamoto, and I lead the HR recruitment team in the CIO Office at KINTO Technologies. My career includes working as a hotel front desk staff member, as a career adviser and HR staff member at a foreign-owned dispatch agency, and as an HR staff member in a mega venture’s business division and also setting up the HR operations at a startup. I’m responsible for a wide range of HR-related work these days, too. I’m usually surrounded by four dogs and two cats, so I my everyday is a bit like Mutsugoro-san’s, the well-known Japanese animal lover.  In this article, I’m going to introduce the HR Team.</p>
<h1>What Does the Team Do?</h1>
<p>The HR Team is involved in recruitment and organization building at KINTO Technologies, based on the values below.</p>
<p><img src="/assets/blog/authors/mamikoiwamoto/image_1.jpg" alt="altテキスト"></p>
<p>Specifically, the details are as follows.</p>
<h2>Recruitment</h2>
<p>This is mid-career recruitment of engineers and creators to lead the development work at KINTO Technologies. The organization had just under 160 employees when I joined it in November 2021, but as of December 1, 2022, that figure has grown to around 280. So, the number of employees has gone up by a factor of about 1.5 in around a year. Once again, let me thank all you employees for choosing to join the KINTO Technologies family! Going forward, we’re going to work on improving the recruitment experience, spreading information internally, and more, with the aim of becoming a company that other companies use as a reference for all things recruitment.</p>
<h2>Employee Interviews</h2>
<p>As a measure to gather information about the company&#39;s issues and make it a better organization, we have been conducting post-employment interviews, interviews with all employees, interviews with employees on leave, and interviews with employees leaving the company since February 2022. The company was only founded three years ago. Partly due to its rapid organizational expansion, it’s facing many challenges. What we’re doing is building up a picture of the actual situation through communication and interviews with employees, then planning and running activities like organizational development initiatives to create organizations that will be strong and appealing for both the company itself and for its employees, too. Of course, if you have any concerns, then please feel free to discuss them with us anytime and as often as you like, not just during interviews! :-) </p>
<h2>Management Training</h2>
<p>In cooperation with engineering education and training project members, we’re planning and running training for group managers.</p>
<h2>Updating Working Environments</h2>
<p>We’re also involved in creating environments that will enable employees to feel highly motivated as they work. We’re making a conscious effort to create experiences that will make working at KINTO Technologies fun. Examples include an office snack station where employees can buy food when feeling a little bit hungry, displaying TOYOTA miniature cars, and installing vending machines wrapped in a KINTO Technologies-themed design!</p>
<p><img src="/assets/blog/authors/mamikoiwamoto/image_3.jpeg" alt="altテキスト"></p>
<h2>Other Miscellaneousr Things</h2>
<ul>
<li>Orientation support upon joining the company, and follow-ups afterward</li>
<li>Cultivating culture and putting missions, visions, and values into words</li>
<li>Support for engineering events</li>
</ul>
<p>And lots more. Anyway, we’re working hard every day to provide HR support—and sometimes even lead—for everyone working at KINTO Technologies, both carrying the balls we’re handed, and picking up ones that have been dropped, too!</p>
<h1>What Kinds of Members are There?</h1>
<p>Actually, until recently, I’d been doing the recruitment and organizational adjustment work all on my own. However, a team was launched with the addition of more members this April, and there are now seven of us including myself.</p>
<p>Our respective areas of expertise are as below, but we don’t draw lines in the sand about anything (which is why we’re able to spot and pick up dropped balls), so we’re often working on multiple projects in a cross-disciplinary fashion.</p>
<p><img src="/assets/blog/authors/mamikoiwamoto/image_2.jpg" alt="altテキスト"></p>
<p>&quot;Instead of thinking about what you can&#39;t do, think about how you can do it.&quot;
&quot;When someone is in trouble, think about how you can help them together.&quot;</p>
<p>I feel that kind of mindset is well-rooted. They’re heartening members have on board!</p>
<h1>What Lies Ahead for the Team</h1>
<p>I want us to go on valuing the things the HR Team should cherish that I covered at the beginning of the article, and to aim to be a team that employees can rely on, and one that exceeds their expectations.</p>
<p>I’d also like to start rolling out strategic measures to improve the company and its organizations, and from an HR perspective, I believe that KINTO Technologies is going to become an even more unique and interesting organization than it already is. So, I really hope you all watch this space.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Introducing the Mobile App Development Group]]></title>
            <link>https://blog.kinto-technologies.com/posts/2022-12-23-MobileAppG-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2022-12-23-MobileAppG-en/</guid>
            <pubDate>Wed, 03 Sep 2025 11:00:00 GMT</pubDate>
            <description><![CDATA[Introducing the Mobile App Development Group]]></description>
            <content:encoded><![CDATA[<h1>Introduction and Summary</h1>
<p>Hi, I&#39;m Iwamoto. Last month, I had the opportunity to write an introductory post as the group manager of the My route Development Group. As it happens, I also serve as the manager of the Mobile App Development Group. In this article, I&#39;d like to introduce the group and share a bit about how I came to manage both.</p>
<h1>About the Mobile App Development Group</h1>
<h2>What is the Mobile App Development Group?</h2>
<p>Formed in early 2022, the Mobile App Development Group is a relatively new team made up of engineers who, as the name implies, specialize in mobile app development. With pride as a mobile app development specialist, we take full ownership of all mobile app projects involving KINTO Technologies, transcending the boundaries between individual services. Basically, we create mobile apps in collaboration with the development groups in charge of each service (mainly those responsible for backend API development), but our involvement doesn&#39;t end at delivery. We stay actively involved in post-launch development and enhancements, working closely with each service group to take shared responsibility in driving the service forward.</p>
<h2>Our Journey So Far</h2>
<p>Let’s explore how the Mobile App Development Group was formed.</p>
<h3>The Early Days of KINTO Technologies</h3>
<p>KINTO Technologies has its roots in the development department of KINTO Corporation. At the time, the service primarily handled applications via the web and had no mobile app. So, the engineering team was made up almost entirely of web engineers, with very few members having any experience in mobile app development.</p>
<h3>my route Development</h3>
<p>Amid these circumstances, KINTO Technologies was assigned to develop <strong>my route</strong>, a MaaS app that originally began as a pilot project by Toyota Motor Corporation. At the time, My Route was being developed under contract by a partner company, and our first mission was to bring that development in-house. Although the in-house development of backend APIs progressed smoothly, mobile app development remained heavily reliant on partner companies for quite some time due to a shortage of specialized engineers. To turn things around, we gradually built up in-house expertise by reassigning engineers with mobile app experience and actively hiring new talent. By fall 2021, we had a solid internal foundation in place and were ready to kick off our in-house mobile app development project.</p>
<h3>Development of Apps Other Than my route</h3>
<p>As the mobile app engineering team within the My Route Development Group grew more capable, we started receiving an increasing number of inquiries about mobile app development from teams outside of My Route. By that time, KINTO Technologies had started handling several mobile apps besides my route. However, due to the ongoing shortage of mobile app engineers, much of the development work was still outsourced to partner companies. I wouldn&#39;t say that was the sole reason, but it&#39;s true that many of those projects faced challenges and didn&#39;t go very well. As we continued offering support and assisting with these external projects, our involvement in services beyond my route gradually increased, even though we were still technically part of the my route Development Group.</p>
<h3>The Birth of Mobile App Development Group</h3>
<p>Before we knew it, nearly half of our members were working on services other than my route. At that point, it no longer made sense from an organizational or operational perspective for us to remain a team within the my route Development Group. As a result, the Mobile App Development Group was established as an independent team in January 2022. We also considered embedding mobile app engineers directly into the development teams for each individual service. However, since the team had only just been formed—bringing together engineers with diverse backgrounds and skill sets—there were concerns that spreading them out too soon would make it difficult for KINTO Technologies to establish a consistent standard for mobile app development. This led to the decision to create a dedicated mobile app development group. Thanks to that, about a year later, we have established a number of development standards, and the group has grown into an organization of engineers capable of delivering high-quality mobile applications.</p>
<h1>Features of the Mobile App Development Group</h1>
<h2>Team Composition</h2>
<p>As of the end of 2022, the group&#39;s size has grown to about 25 people. The number of Android and iOS engineers is roughly half and half, and some members are skilled in both platforms or have multi-platform experience using tools like Flutter and Unity. We also have a few members called &quot;producers,&quot; who take on PM-like roles. Currently, our development work is centered on native apps, so the team is broadly divided into Android and iOS groups. However, we are considering reorganizing into service-based teams in the future. This would allow us to strengthen ties to each service and make better use of engineers who have the skills to work across both operating systems.</p>
<h2>International Diversity</h2>
<p>Compared to other groups, our team has a notably high ratio of non-Japanese members. In particular, around 80% of our Android specialists are global talent. Our group includes members from a wide range of countries, including Korea, China, Taiwan, Myanmar, Poland, and Germany, making for a truly international environment. Many of our members are fluent in English, but since English has not yet been adopted as the company&#39;s official language, all members are able to communicate in Japanese well enough to perform their daily work. With people from many different cultural backgrounds, the team enjoys a lively atmosphere and open, energetic communication on a daily basis. The communication style can feel much more direct compared to all-Japanese teams, which can be surprising at times. Even so, we aim to be an organization that embraces and enjoys these differences.</p>
<h2>What kind of People Work Here?</h2>
<p>When it comes to hiring, technical skills in mobile app development are a given. What we value even more is a strong interest in the field and a proactive mindset toward staying up to date with the latest trends. That&#39;s because the mobile app development landscape is constantly evolving, with new technologies and techniques emerging every day. Relying solely on existing knowledge quickly leads to obsolescence in such a fast-paced environment. As a result, the team is made up of members who are truly passionate about mobile app development. Many go beyond their daily work to improve their skills, and study sessions are held regularly.</p>
<h1>To Our Future Members</h1>
<p>If you&#39;re looking to grow as a mobile app specialist and want to work with the latest technologies, this is the perfect environment for you. Even if you don&#39;t have much hands-on experience yet, we warmly welcome those who bring passion and a willingness to learn. Let&#39;s work together to create mobile applications that support Toyota Group!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[とあるBEエンジニアの一日 〜 KINTO FACTORY 編 〜]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-09-03-factory-be-one-day/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-09-03-factory-be-one-day/</guid>
            <pubDate>Wed, 03 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTO FACTORYのBEエンジニアのとある一日]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは。KINTO テクノロジーズで <a href="https://factory.kinto-jp.com/">KINTO FACTORY</a>(以下FACTORY) というサービスのバックエンド開発・運用をしているS.Itoです。
今回は、FACTORY開発チームで執筆祭りということで、私のとある一日を紹介したいと思います。
技術的な話というよりも、私の一日のスケジュールを中心に、KINTO FACTORY の BE チームの雰囲気を感じてもらえたらと思います。</p>
<h2>KINTO FACTORY の BE 開発チームについて</h2>
<p>KINTO FACTORY の BE チームは、現在3名 + 協力会社さんで開発しています。
私以外の2名は大阪勤務で、私は東京で勤務をしています。 物理的に離れているため、バーチャルオフィスツールを活用してコミュニケーションをとっています。
最近では Devin や Claude Code などのAIツールも活用しつつ、日々開発を進めています。</p>
<h2>一日のスケジュール</h2>
<table>
<thead>
<tr>
<th>時間</th>
<th>内容</th>
<th>詳細</th>
</tr>
</thead>
<tbody><tr>
<td>9:50</td>
<td>出社</td>
<td>エンジニアの朝は早い。</td>
</tr>
<tr>
<td>10:00</td>
<td>朝会 (全体)</td>
<td>全体の進捗や困りごとの共有、全体へのお知らせ事項の確認。</td>
</tr>
<tr>
<td>10:30</td>
<td>朝会 (BE)</td>
<td>バーチャルオフィス上の会議室に集まり BE のメンバーで、状況の詳細な確認。設計の方針の相談などの込み入った話もあったり。<br/>![](/assets/blog/authors/s_ito/factory-be/meeting_be.png =250x)</td>
</tr>
<tr>
<td>11:00</td>
<td>レビュータイム</td>
<td>生成AIツールを使い始めたことでPRの量が爆増。チームで時間を決めて一気にレビューを片付けます。</td>
</tr>
<tr>
<td>12:00</td>
<td>ランチ</td>
<td>FACTORY開発Gのメンバーとオフィス近くでランチ。BE メンバー以外とも仲良しです。</td>
</tr>
<tr>
<td>13:00</td>
<td>開発タイム</td>
<td>生成AIツールを活用しつつ開発。相談事などはバーチャルオフィス上で話しかけて即解決。</td>
</tr>
<tr>
<td>随時</td>
<td>運用作業</td>
<td>データ抽出など運用作業。地味かもしれないけどサービスにとってはとても大事。<br/>当番の時はリリース準備なども。</td>
</tr>
<tr>
<td>随時</td>
<td>問い合わせ対応</td>
<td>他チームからの問い合わせなどに対応。即レスを心がけます。</td>
</tr>
<tr>
<td>20:00</td>
<td>終業</td>
<td>一日の振り返り。翌日の予定を確認して帰宅。</td>
</tr>
</tbody></table>
<h2>それ以外にも</h2>
<p>上記のスケジュール以外にも、月次での定例会議や振りかえりなども実施しています。
ときには大阪に出張して、直接会って話すこともあります。普段のオンライン上でのコミュニケーションでも困ったことはないですが、やはり直接会って話すのは良いものです。</p>
<h2>BEチームの魅力</h2>
<ul>
<li>少人数で意思決定が早い</li>
<li>バーチャルオフィス常時接続で即相談可能</li>
<li>出張でのリアルな交流も確保</li>
<li>新しいツールや働き方に積極的にチャレンジ</li>
</ul>
<p>物理的な距離に関係なく、 <strong>「すぐ聞ける、すぐ助け合える」</strong> のが私たちの強みです。</p>
<h2>おわりに</h2>
<p>今回は、私のとある一日を紹介しました。
KINTO FACTORY の BE チームは、少人数ながらも密にコミュニケーションを取りながら、日々サービスの開発・運用に取り組んでいます。
興味を持っていただけた方は、ぜひ KINTO テクノロジーズの採用情報もチェックしてみてください！
最後まで読んでいただき、ありがとうございました。    </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/s_ito/factory-be/coverimage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[For Those Visiting the KINTO Technologies Osaka Tech Lab]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-09-office-access-osaka-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-09-office-access-osaka-en/</guid>
            <pubDate>Mon, 01 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[For Those Visiting the KINTO Technologies Osaka Tech Lab]]></description>
            <content:encoded><![CDATA[<h2>Howdy from Osaka ( º∀º )/</h2>
<p>My name is Yukachi from the Event Team on the Developer Relations Group.</p>
<p>In July 2025, we finally got our long-awaited event space at the Osaka Tech Lab!<br>This time, I would like to give you a quick and easy guide on how to get to our new venue, <strong>Osaka Tech Lab JCT</strong>!</p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/jct.png =600x)<br><em>The Osaka Tech Lab JCT has around 40 seating</em></p>
<h4><strong>Address: 20th floor, North Gate Building, 3-1-3 Umeda, Kita-ku, Osaka City, Osaka 530-0001</strong></h4>
<ul>
<li>JR Osaka Station: 2 minutes from either the Central Gate (1F) or the Bridge Gate (3F)</li>
<li>Osaka Metro Umeda Station: 5 minutes from the North Gate</li>
<li>Hankyu Railway Umeda Station: 7 minutes from the 2nd Floor Central Gate</li>
<li>Hanshin Railway Umeda Station: 7 minutes via the connecting bridge</li>
</ul>
<p>The location is right next to LUCUA 1100. ** Look for signs that say LUCUA 1100 or Office Tower: that’s where you&#39;re headed! ** If you’re coming via Hankyu or Hanshin Railway, first make your way toward JR Osaka Station! [accessosaka1](/assets/blog/authors/uka/accessosaka/0.png =600x)<br>*Use this as a landmark! *</p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/2.png =600x)<br><em>From JR Osaka Station, exit the Central Gate or Bridge Gate and head to the Office Tower</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/1.png =600x)<br><em>From Osaka Metro Umeda Station, exit the North Gate and head toward the Office Tower</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/3.png =600x)<br><em>For those coming from the 1st floor, go this way</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/4.png =600x)  </p>
<ul>
<li>For those coming from the 3rd floor, go this way *</li>
</ul>
<p>:::message alert
The entrance to the office area from the 3rd floor in front of LUCUA 1100 is currently under construction.
![Distant view near the entrance automatic doors](/assets/blog/authors/uka/accessosaka/underconstruction01.jpg =600x)
![Photo of the entrance automatic doors](/assets/blog/authors/uka/accessosaka/underconstruction02.jpg =600x)
Please note that the entrance may be difficult to find.
:::</p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/5.png =600x)<br><em>Take the escalator up to the 4th floor for connecting passage. It’s even quicker if you hop on from the 3rd floor!</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/6.png =600x)<br><em>Go straight ahead to the automatic door</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/front.jpg =600x)<br><em>Enter through the main entrance and turn right</em> :::message **Please note that due to building security, the main entrance may be locked during certain hours. **<br>In that case, please contact us using the &quot;information board with QR code&quot; located at the front entrance! A staff member will come to escort you shortly. :::</p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/7.png =600x)<br><em>Please take the nearest elevator to the 20th floor</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/8.png =600x)<br><em>KINTO Technologies is right in front of you after you get off the escalator! Welcome！</em></p>
<h2>In Closing</h2>
<p>We hope you enjoy your time at the Osaka Tech Lab! Thank you so much for visiting! We look forward to seeing you again soon! (^_^)/</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uka/accessosaka/accessosaka.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[FACTORYチームのQA活動スタート！QAの作業風景とこれからの挑戦]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-09-01-FACTORYチームのQA活動スタート！QAの作業風景とこれからの挑戦/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-09-01-FACTORYチームのQA活動スタート！QAの作業風景とこれからの挑戦/</guid>
            <pubDate>Mon, 01 Sep 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[FACTORYチームでのQA活動のスタートにあたり、作業風景や今後の挑戦について紹介します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは。KINTOテクノロジーズで <strong>KINTO FACTORY</strong> サービスのQAを担当している遠藤です。<br>2025年5月から「インプロセスQA」として活動を始めました。今回は、QAの視点からFACTORYの開発風景をご紹介します。</p>
<p>このブログを通じて、FACTORYというサービスやKINTO全体の開発に、少しでも興味を持っていただけたら嬉しいです。</p>
<hr>
<h2>インプロセスQAとして開発チームに溶け込む - KINTO FACTORYの品質文化</h2>
<h3>インプロセスQAとは</h3>
<p>「インプロセスQA」とは、開発チームの一員として日々の開発プロセスに参加し、その中で品質保証活動を行うQA（品質保証）エンジニアのことを指します。
従来のQAは、リリース直前に独立した立場でテストを行い、不具合を洗い出す「最後の砦」の役割を担ってきました。
一方、インプロセスQAでは、開発初期から仕様検討や設計レビューに関わり、早い段階で課題を発見・改善します。これにより、手戻りを減らし、品質と開発スピードの両立を実現します。</p>
<p>KINTO FACTORYでは、このインプロセスQAを2025年5月から導入しました。
まずは従来の最終QAプロセスを維持しつつ、開発チームの中に入り込み、「一緒に品質を作り込む」文化を育てています。
この取り組みにより、仕様段階から品質の視点を取り入れることができ、ユーザーにより安心して使っていただけるサービス提供を目指しています。</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>従来QA</th>
<th>インプロセスQA</th>
</tr>
</thead>
<tbody><tr>
<td>関わるタイミング</td>
<td>開発後半（リリース前）</td>
<td>開発初期から継続的に</td>
</tr>
<tr>
<td>主な役割</td>
<td>最終テスト・不具合検出</td>
<td>仕様検討・設計レビュー・早期改善</td>
</tr>
<tr>
<td>メリット</td>
<td>リリース前の品質担保</td>
<td>手戻り削減・品質とスピードの両立</td>
</tr>
<tr>
<td>関係性</td>
<td>開発と別組織的</td>
<td>開発チームの一員として活動</td>
</tr>
<tr>
<td>課題</td>
<td>後工程での不具合修正コストが高い</td>
<td>文化として根付くまで時間がかかる</td>
</tr>
</tbody></table>
<h3>FACTORYの開発チームに加わる意味</h3>
<p>これまでQAチームは、リリース直前に不具合を洗い出し、障害の防止や納期遵守、安定稼働に大きく貢献してきました。<br>その経験と知見を活かし、FACTORYチームでは、従来の最終QA工程を続けながら、少しずつ開発の前工程にも関わる取り組みを始めています。</p>
<p>今回はチーム内にQAが一人から参画し、まずは仕様検討や設計の場に顔を出し、早期に課題を共有できる環境づくりを進めています。<br>まだ加速的に進められる段階ではありませんが、日々のやり取りの中で開発とQAの距離を縮め、自然に協力し合える体制を目指しています。</p>
<p>こうした地道な取り組みが、やがては後工程の手戻り削減や品質向上につながると考えています。<br>では、この体制の中でどのようなQA作業を行っているのか、次の章で具体的にご紹介します。</p>
<hr>
<h2>FACTORYチームのQA作業とは？</h2>
<p>基本的には、従来の各サービスで行ってきたQA業務と大きな違いはありません。<br>主な作業内容は以下の通りです。</p>
<ul>
<li>中・長期的な案件対応</li>
<li>短期的な改善対応</li>
<li>テスト自動化対応</li>
</ul>
<h3>中・長期案件対応</h3>
<p>管理ツールの新規開発や、販売商流の新規追加に伴う機能開発では、短期間では終わらない丁寧な準備が欠かせません。
まず関係者と時間をかけて仕様を確認し、その内容をもとにテスト計画を練り上げます。
そして、仕様確認からテスト計画、実施までを数カ月かけて丁寧に進め、品質を確保したうえでリリースにつなげています。こうしたプロセスを踏むことで、リリース後のトラブルを防ぎ、安心して使っていただけるサービスを提供しています。</p>
<h3>短期改善対応</h3>
<p>画像の見栄えや一時的な表示変更など、軽微な修正や改善は1〜2週間程度で対応します。
着手前にはチケット内容の認識合わせを行い、必要に応じて3日ほどでQAを実施することもあります。
短期間であっても品質を犠牲にせず、ユーザーに素早く価値を届けられるよう心がけています。</p>
<h3>テスト自動化対応</h3>
<p>Autifyを活用し、2週間ごとのリリースサイクルに合わせて自動テストシナリオの追加・更新・メンテナンスを行っています。
これにより、繰り返し発生するテストを効率化しつつ、リリースごとの品質を安定的に確保しています。
将来的には、PlaywrightとAIを組み合わせ、エンジニアに依存せずQA主導でテストコードをメンテナンスできる仕組みづくりも進めています。</p>
<hr>
<h2>長期案件と短期案件の違い</h2>
<table>
<thead>
<tr>
<th>項目</th>
<th>長期案件</th>
<th>短期案件</th>
</tr>
</thead>
<tbody><tr>
<td>期間</td>
<td>数カ月</td>
<td>1〜2週間</td>
</tr>
<tr>
<td>対象</td>
<td>新サービス / 大規模開発</td>
<td>軽微なUI修正 / 表示改善</td>
</tr>
<tr>
<td>QA期間</td>
<td>約1カ月</td>
<td>約3日</td>
</tr>
<tr>
<td>主な課題</td>
<td>仕様の複雑さ、関係者調整</td>
<td>認識齟齬によるミス防止</td>
</tr>
</tbody></table>
<hr>
<h2>QAとして意識していること</h2>
<p>私の信念は、<strong>「QA作業は開発スケジュールを阻害しない」</strong> ということです。<br>時間と品質の両立を常に意識して取り組んでいます。</p>
<p>例えば、計画段階でリリース日から逆算して開発スケジュールを立て、QA期間を2週間と想定していたとします。
その後、見積もり依頼を受けた結果、実際には1カ月かかると判明した場合、QA作業を優先するとリリースが遅れます。
リリース遅延は、開発メンバーだけでなく、マーケティングや営業、経営陣、さらには利用を待っているお客様にも影響します。例えば、広告出稿やキャンペーンの開始日を合わせて準備していた場合、それらが無駄になったり、季節やイベントに合わせた需要のピークを逃したりします。さらに、競合他社が先に類似サービスをリリースすることで、市場での優位性やユーザー獲得のチャンスを失う可能性もあります。
このように、リリース時期の後ろ倒しは、関係者全員が予定していた成果を得られなくなるだけでなく、事業成長に直結する重要な機会を逃すリスクを伴います。  </p>
<p>そのような場合は、関係者と再度確認を行い、<strong>「適切なタイミングで」「必要な範囲のQAを」</strong> 行うことを提案します。<br>大事なのは、QAだけの都合でスケジュールを動かすことではありません。
まずはプロジェクト全体のゴールをはっきりさせて、そのゴールを叶えるために一番いい方法を、プロジェクトとして判断することが大切です。</p>
<p>もちろん、緊急で不具合や障害の改修が必要なときもあります。そんなときは、スケジュールを守りつつ、できるだけ早く対応します。
ただし、スピードを優先するあまり品質を犠牲にすることはありません。短期対応であっても、ユーザーが安心して使えるレベルまでしっかり仕上げてからリリースするのが基本姿勢です。</p>
<hr>
<h2>FACTORYチームの一員としてのQA</h2>
<p>従来のQAに加え、FACTORYサービスに特化した新たな取り組みも進めています。<br>ここでは「シフトレフト」の考え方を取り入れています。<br>シフトレフトとは、テストや品質の確認をできるだけ開発の早い段階（＝左側）で行い、手戻りや修正コストを減らすアプローチのことです。  </p>
<p>具体的には、<strong>チケット起票段階からQAの観点を盛り込み</strong>、開発メンバー側が手の回らない部分の品質もサポートできるよう、まずはできる範囲から早めに着手しています。</p>
<p>理想は、「痒いところに手が届くQA」。<br>開発だけでは見落としがちなポイントや、通常はQAが対応しない結合テストなど、品質面で不安が残る領域も積極的にカバーしていきます。</p>
<hr>
<h2>実際の作業風景</h2>
<p>案件対応では、仕様資料をもとにテスト計画や観点をまとめます。<br>複雑な内容は事前説明を受けることもありますが、基本は資料をベースに進行します。</p>
<p>早期に仕様を理解し、開発メンバー側へ質問しながら進めることで、作業をスムーズに進行できます。<br>短期対応の場合は、変更点が明確なため、具体的なテストケースを作成し、認識違いがあれば即時修正します。</p>
<p>また、質問だけでなく、雑談レベルの不安も積極的にヒアリングします。<br>「申し込みできるか不安」「システム連携が正しくできているか」など、細かな不安も解消できるよう努めています。</p>
<p>開発メンバーの方々は、QAの質問や対応依頼など積極的にかつ前向きにとらえて実行してもらえています。
そのため、QA作業もスムーズに進行し、開発メンバー側との連携も良好です。
この環境に甘えることなく、QAとしてできることを常に考え、開発メンバーの皆様と一緒に品質向上に取り組んでいます。</p>
<hr>
<h2>これからの活動</h2>
<p>Autifyに加え、これまで開発メンバー側主導で使用してきた <strong>Playwright</strong> を活用した自動化にも取り組みます。
AIを活用してテストコードをエンジニアに頼らずメンテナンスできるようにし、QAが主体的に改善・運用できる体制を整えていきます。</p>
<p>さらに、シフトレフトの推進やテスト範囲の拡大も計画中です。<br>上流工程での品質作り込み、結合テスト、データフローの確認などにも積極的に取り組み、開発メンバーとQAの連携をより強化していきます。</p>
<p>QAは品質の最後の砦であると同時に、開発メンバーの伴走者でもあります。<br>チーム内に加わったことで、これまで以上に早い段階から伴走者として関わり、<br>同じゴールを目指しながら品質を作り込み、必要な対応は即時に行えるよう日々取り組んでいます。</p>
<hr>
<h2>さいごに</h2>
<p>今回は、FACTORYチームのQA活動についてご紹介しました。<br>チーム内の一体感や品質へのこだわりが、少しでも伝われば幸いです。</p>
<p>私自身、お客様視点とサービス視点を忘れず、開発メンバーと協力しながら、みなさまに笑顔になっていただける品質を追求していきます。<br>もし興味を持っていただけた方は、ぜひ <strong><a href="https://kinto-technologies.com/recruit">弊社の採用ページ</a></strong> もご覧ください。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/endo/2025-09-01/coverimage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[KINTO Technologies Will Be a Platinum Sponsor of SRE NEXT 2025]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-03-sre-next-2025-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-03-sre-next-2025-en/</guid>
            <pubDate>Fri, 29 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTO Technologies Will Be a Platinum Sponsor of SRE NEXT 2025]]></description>
            <content:encoded><![CDATA[<p>Hello! I&#39;m Kasai from the SRE Team.</p>
<p>KINTO Technologies Corporation (hereinafter referred to as KTC) will be a platinum sponsor of &quot;SRE NEXT 2025&quot; which will be held at TOC Ariake on Friday and Saturday, July 11 and 12, 2025. This will be KTC’s first time sponsoring SRE NEXT.</p>
<p>Our SRE Team got a fresh start last year. While we face the daily challenges of practicing SRE, we continue to work on improving the reliability of our services. I’m sure many of you are also going through the similar process of trial and error. We decided to become a sponsor because we wanted to support a space where SREs can come together and connect.</p>
<h1>What is SRE NEXT?</h1>
<blockquote>
<p>SRE NEXT is a conference for engineers with a strong interest in reliability practices. It is organized and hosted primarily by members of &quot;SRE Lounge,&quot; another community-based SRE study group. The theme of SRE NEXT 2025 is &quot;Talk NEXT.&quot; Building on the values introduced at SRE NEXT 2023— Diversity, Interactivity, and Empathy—the event will provide a space for discovering new insights through discussions and communication on a wide range of SRE-related topics, including technology, organizational design, and talent development.</p>
</blockquote>
<p><a href="https://sre-next.dev/2025/">Home | SRE NEXT 2025</a></p>
<h3>Event Overview</h3>
<p><strong>Date: July 11 (Fri) and 12 (Sat), 2025</strong> <strong>Venue: TOC Ariake and Online</strong> <strong>Official Website: <a href="https://sre-next.dev/2025/">https://sre-next.dev/2025/</a></strong></p>
<h1>There Will Be a Sponsored Session!</h1>
<p>On Day 2 (July 12), from 13:00 to 13:20 on Track B, Osanai will be holding a sponsored session titled: &quot;What Does an SRE Do in a Role-Fragmented Organization?&quot; </p>
<p>In a fragmented organization where roles often overlap, questions naturally arise, such as &quot;What should we be doing?&quot; and &quot;Where does the value of SRE lie?&quot; This session will share how our SRE Team has faced and worked through questions like these.</p>
<p><img src="/assets/blog/authors/kasai/20250703/session.png" alt="セッション"> <em>Details: <a href="https://sre-next.dev/2025/schedule/#slot081">https://sre-next.dev/2025/schedule/#slot081</a></em></p>
<h1>We’ll Also Have a Booth!</h1>
<p>We’ll have a short and easy questionnaire available at our booth.</p>
<p>If you participate, you can spin a gachapon machine for a chance to win original novelty items. Be sure to stop by and give it a try!</p>
<p>Our SRE Team members will also be at the booth, so come and talk with us about SRE.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/kasai/20250703/cover_image.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Automating i18n File Alignment with GitHub Actions]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-29-Automating-i18n-File-Alignment-with-GitHub-Actions/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-29-Automating-i18n-File-Alignment-with-GitHub-Actions/</guid>
            <pubDate>Fri, 29 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We built a GitHub Action to automatically flag i18n file changes, helping our localization team keep GitHub and Lokalise in sync.]]></description>
            <content:encoded><![CDATA[<p>Hello, I am Maya from KINTO Technologies and today I’d like to quickly share a <em>kaizen</em> the Localization team implemented along with our Product Development team.
Keeping internationalization (i18n) files aligned across systems is a challenge every growing product faces. For our team, revising the data of a specific project, together with the development team revealed how quickly inconsistencies can sneak in, and how painful manual fixes can be.</p>
<h2><strong>The Problem</strong></h2>
<p>When working across multiple GitHub repositories, both teams discovered a mismatch in one of the i18n files we were relying on for a product. We huddled together one day and created a problem framing grid to visualize the issue to product managers and team leads alike in the hopes to gain support (which we quickly did!) so we could work on implementing a solution.</p>
<table>
<thead>
<tr>
<th>Category</th>
<th>Details</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Who</strong></td>
<td>The Product Developers and Localization team</td>
</tr>
<tr>
<td><strong>What</strong></td>
<td>We found that i18n file data diverged between GitHub and <a href="https://lokalise.com/">Lokalise</a></td>
</tr>
<tr>
<td><strong>Where</strong></td>
<td>Issues were detected in both GitHub repositories and Lokalise projects</td>
</tr>
<tr>
<td><strong>When</strong></td>
<td>During cross-checking of all i18n files for a certain product project</td>
</tr>
<tr>
<td><strong>Why it matters</strong></td>
<td>・Unnecessary or outdated data can accumulate.<br>・If left unresolved, the Single Source of Truth (SSOT) breaks down.<br>・Manual verification means going line by line, which is time-consuming and error-prone.</td>
</tr>
</tbody></table>
<h2><strong>The Impact</strong></h2>
<p>By not having ways to detect data discrepancies, developers risk working with broken keys, outdated strings, or misaligned naming conventions. On the localization side, we sadly wasted effort working on content that did not exist in production.
We learned the hard way that these misalignments may seem so small and harmless at the beginning, but if left unchecked, could end up creating and accumulating unnecessary data that clutters the files and leads to inefficiency. This can also contribute to higher costs and cause inconsistent user experiences in the long run.</p>
<h2><strong>From Idea to Solution</strong></h2>
<p>We needed a way to automatically keep track of i18n files, detect changes in GitHub as soon as they occurred, and have an alert that lets us know (the Localization side) so we could work on reconciling differences between GitHub and Lokalise before the files drifted too far apart.</p>
<p>To achieve this, we built a GitHub Action that runs whenever a Pull Request (PR) touches any i18n file. The action inspects the files in the PR and, if changes are detected, sends an alert to a dedicated Slack channel. The Localization team is instantly notified and can decide whether a change is a legitimate key rename or removal, and whether the same update should be reflected in Lokalise.</p>
<p>This small automation keeps development and localization in sync and sparks quick conversations before issues pile up.</p>
<p>The following diagram illustrates how it works:
1.	Repositories A to E each contain i18n files.
2.	The GitHub Action monitors these files and triggers whenever a PR modifies one.
3.	The Action sends an alert to a dedicated Slack channel we called #notice-localization-watcher.
4.	The Localization team reviews and syncs updates with Lokalise if needed.</p>
<p><img src="/assets/blog/authors/Maya.s/20250829/Automation.png" alt="Diagram"></p>
<p>The alert looks like this and is sent to me, the Localization team and the Product Development team we added to the channel:</p>
<p><img src="/assets/blog/authors/Maya.s/20250829/Notification_Screenshot.png" alt="Alert"></p>
<h2><strong>Benefits</strong></h2>
<p>Using GitHub Actions to watch i18n files turned a tedious manual job into a simple, scalable process. It’s already making work smoother and saving both teams time and effort. We accomplished:</p>
<p>•	Faster detection of unnecessary data and avoiding surprises during cross-checks.
•	Cleaner data, keeping GitHub and Lokalise in sync as a Single Source of Truth.
•	Quicker team collaboration prompted by the alert, which allows us to do small check-ins via Slack between teams to check which data is needed and which isn&#39;t.</p>
<h2><strong>Drawbacks &amp; Next Steps</strong></h2>
<p>Like any automation, it has its ups and downs. Although we haven&#39;t experienced it yet, frequent alerts could cause noise and fatigue, especially if every PR triggers a notification. Alerts also provide limited context as they flag that “something changed” but don’t show which keys were added, removed, or renamed at a glance unless you access the full PR. Without an even clearer way to define and maintain the source of truth, file drift can continue despite the above efforts.</p>
<p>To improve, we plan to add diff snippets to alerts so changes are immediately visible, and filter to focus on meaningful updates like key additions, deletions, or renames. Another idea is to batch alerts into daily or weekly summaries to reduce noise. We’re excited to see how these refinements make it even smoother!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[開発チームに生成AIを導入して変化したこと 〜KINTO FACTORYでの活用事例〜]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-29-claude-code-productivity/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-29-claude-code-productivity/</guid>
            <pubDate>Fri, 29 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[PR-Agent、GitHub Copilot、Devin、Claude Codeと段階的に生成AIを導入し、開発効率を大幅に改善した実践事例を紹介します]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、KINTO FACTORYのバックエンドエンジニアをしている西田です。</p>
<p>今回は、7/24にYOUTRUSTさん×KINTOテクノロジーズで共催させていただいた「<a href="https://youtrust.connpass.com/event/362295/">関西エンジニアプライド、ここに集結。Tech Boost LT</a>」で登壇した「開発チームに生成AIを導入して変化したこと」について、発表内容を記事としてまとめてみました。
KINTO FACTORY で取り組んでいる生成AIの活用を知っていただくきっかけになれば幸いです。</p>
<h1>KINTO FACTORYについて</h1>
<p><a href="https://factory.kinto-jp.com/">KINTO FACTORY</a>は、「安心安全なカーライフを愛車で長く楽しめるアップグレードサービス」です。</p>
<p>従来の新車を売って終わりではなく、販売後もお客様の幸せを量産できるように、クルマを進化させ、クルマの一生に寄り添うライフタイムサービスの提供を目指しています。</p>
<h2>チーム体制</h2>
<p>開発チームの体制としては、エンジニアだけでなく、PdM（プロダクトマネージャー）、デザイナー、マネージャー、ディレクターなど多様なメンバーが協力し合いながら開発を進めています。</p>
<p>また、東京、大阪とそれぞれの拠点にメンバーが在籍していてリモートでコミュニケーションをとりながら開発しているのも特徴です。</p>
<p><img src="/assets/blog/authors/m.nishida/20250829/organization.jpg" alt="体制"></p>
<h1>開発チームで抱えていた課題</h1>
<p>ローンチしてから、約2年が経過し、サービスが成長する中で、以下のような課題が表面化してきました。</p>
<ul>
<li>限られたリソースでの開発</li>
<li>複数の案件を開発することでコンテキストスイッチ負荷の増大</li>
<li>技術負債の蓄積</li>
</ul>
<p>組織の拡大を見据えつつ採用活動といった人材獲得を積極的に進めつつもすぐに改善できることでもないため、
この<strong>限られたリソースでどう戦うか</strong>が重要になってきます。</p>
<h1>なぜ開発チームに生成AIを導入したか</h1>
<p>ここからは、開発チームに生成AIを導入した理由についてお話しします。</p>
<p>前述の課題を解決するための期待値と会社の環境が揃ったことが、生成AI導入の大きな理由です。</p>
<h3>期待</h3>
<ul>
<li>増加するタスクへの効率化</li>
<li>開発負荷の軽減</li>
<li>少人数でも戦える体制作り</li>
</ul>
<h3>環境</h3>
<ul>
<li>会社として今期の開発テーマに「<a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">AIファースト</a>」を掲げており、AI活用の推進が積極的に行われていた</li>
</ul>
<p>これらの要素が揃ったことで、生成AIの導入を積極的に進めることができました。</p>
<h1>生成AI導入の歩み</h1>
<p>これまでの生成AI導入の歩みを振り返ると、コードレビュー自動化から始まり、コーディング支援、タスク実行まで生成AIツールの進化によって対応領域を広げてきました。</p>
<h3>2024年〜</h3>
<ul>
<li><strong>PR-Agent</strong>: レビューの自動化</li>
<li><strong>GitHub Copilot (Agent)</strong>: コーディング支援</li>
</ul>
<h3>2025年〜</h3>
<ul>
<li><strong>Devin</strong>: リポジトリ横断的な調査や開発タスクの実行</li>
<li><strong>Claude Code</strong>: より高度な思考力で設計、開発タスクを実行</li>
</ul>
<h1>Devinを使った活用事例</h1>
<p>その中でも、特にDevinを使った運用事例として実際に起きたインシデント対応を紹介したいと思います。</p>
<h2>インシデント対応</h2>
<p>KINTO FACTORYではインシデントが発生した際にSlackで検知する仕組みを導入しています。</p>
<p><img src="/assets/blog/authors/m.nishida/20250829/devin_1.jpg" alt="devinを使ったインシデント対応1"></p>
<p>通常であれば、エンジニアが手動で調査を行い、修正を行う必要がありますが、今回はDevinを使って対応することにしました。</p>
<p>はじめに、Slackから通知されたエラー内容を元に、Devinに調査を依頼します。</p>
<p><img src="/assets/blog/authors/m.nishida/20250829/devin_2.jpg" alt="devinを使ったインシデント対応2"></p>
<p>調査結果から原因が特定できたらそのままコード修正も依頼します。
細かい修正もSlack上で行うことができます。</p>
<p><img src="/assets/blog/authors/m.nishida/20250829/devin_3.jpg" alt="devinを使ったインシデント対応3"></p>
<p>その後、PRを作成し、approveされるとDevinから通知を受け取りマージします。</p>
<p><img src="/assets/blog/authors/m.nishida/20250829/devin_4.jpg" alt="devinを使ったインシデント対応4"></p>
<p>調査、修正、PR管理までをDevinに任せることで、修正までの時間を大幅に短縮することができました。
今回の対応ではこれまで2日程度かかるところを、Devinを使うことで1日で対応できました。</p>
<p><img src="/assets/blog/authors/m.nishida/20250829/devin_5.jpg" alt="devinを使ったインシデント対応5"></p>
<h2>Devinでタスク実行する前にやっていたこと</h2>
<p>Devinにタスク実行を依頼する前に、以下のような準備を行っていました。</p>
<h3>Knowledge機能</h3>
<ul>
<li>デフォルトで設定しておきたいプロンプトを定義</li>
<li>呼び出し時にインプットされた状態でタスク実行できる</li>
</ul>
<h3>Devin&#39;s Machine機能</h3>
<ul>
<li>開発環境のセットアップを事前に行うことでタスク実行時のwaitingが短縮される</li>
</ul>
<p>これらの機能を活用することで、Devinにタスク実行を依頼する際の準備がスムーズになり、実行までの時間を短縮することができました。</p>
<h1>生成AI導入後の成果</h1>
<p>KINTOテクノロジーズでは、生産性を可視化するためのツールとしてFindy Team+を導入しています。
今回、こちらのツール上でも生成AI導入前後で変化が起きていることが確認できました。</p>
<p><img src="/assets/blog/authors/m.nishida/20250829/findy.jpg" alt="Findy Team+による生産性指標のグラフ"></p>
<ul>
<li><strong>コミットからオープンまでの時間</strong>の改善</li>
<li>生成AIによるPR作成数が増え、トータルの<strong>PR数も増加</strong></li>
<li><strong>デプロイ頻度</strong>の改善</li>
</ul>
<p>生成AIを導入することで、開発の効率化が進み、チーム全体の生産性が向上していることが数値としても確認できました。</p>
<h1>学び</h1>
<p>今回、生成AIの導入を進めることで、開発チームの生産性向上に大きく寄与しました。
特に以下の点が大きな学びとなりました。</p>
<ul>
<li>定型作業の効率化</li>
<li>既存コードから挙動を横断的に調査（そのまま修正も）</li>
<li>イメージ通りになるまでプロンプトを叩く → 技術負債の蓄積を減らす</li>
</ul>
<p>一方で注意点もあります。</p>
<ul>
<li>生成AIの提案を鵜呑みにすると想定しない挙動（バグ）が仕込まれることも</li>
<li>コンテキスト不足だと的外れな改修（ハルシネーション）が発生する</li>
</ul>
<p><strong>コンテキストの解像度を高めて、最後はヒトの目を通せる仕組みが重要</strong>であることを実感しました。</p>
<h1>まとめ</h1>
<p>今回、生成AIツールを段階的に導入することで活用できる領域を広げ、
生産性向上の改善に繋げることができました。</p>
<p>生成AIは開発者を置き換えるのではなく、開発者をエンパワーする存在です。
限られたリソースで戦う開発チームにとって、生成AIは強力な味方になることを実感しています。</p>
<p>私たちの経験が、同じような課題を抱えるチームの参考になれば幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/m.nishida/20250829/thumbnail.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[内製デザイン組織だからできた！コンセプトからストーリーを綴る新オフィス移転]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-29-osaka-tech-lab-story/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-29-osaka-tech-lab-story/</guid>
            <pubDate>Fri, 29 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[大阪オフィス「Osaka Tech Lab」の移転を機に、メンバーの想いを起点にコンセプトと合言葉をつくり、デザイン組織ならではのストーリーで新しい空間を形にした取り組みを紹介する記事です。]]></description>
            <content:encoded><![CDATA[<p>弊社の大阪オフィス「Osaka Tech Lab」が7月に引越ししました。（<a href="https://www.kinto-technologies.com/company/osakatechlab/">新オフィスの様子はこちらをチェック</a>）このオフィスでは「イノベーション・新しいもの・実験を加速させること」「Osaka Tech Lab発信のプロダクトをつくること」という目標がありました。その目標に賛同してくれる人を「この指と〜まれ」で集めて、どんどん巻き込んでいこうという動きが自主的に生まれ、東京支社にいるクリエイティブ室の私のもとへも依頼がきました。</p>
<p>大阪オフィスメンバーの熱い想いにクリエイティブ視点で叶えたい！私もこの一員として実現したい！と思い、微力ながらジョインすることに（やりたいことをやる、この柔軟性も魅力ですね）。</p>
<p>まずは、新オフィスが誕生する今、どうあるべきかを可視化するために、コンセプトをつくり、オフィスのインテリアや採用プロモーションにつながるストーリーをつくることにしました。タイトルは「Osaka Tech Lab2.0 STORY」。</p>
<h1>「Osaka Tech Lab2.0 STORY」目的に向かうまでのストーリー</h1>
<p>そもそも、大阪にオフィスをつくった会社の思いからはじまる、目指すべき未来へのストーリーを社長や副社長にインタビュー。いつかの理想を叶えるために「いま、そのためにどうするのか、どんな場にするのか」という部分がこの新オフィスプロジェクトのコンセプトになってくると考えました。
<img src="/assets/blog/authors/aya_sugimoto/250829/story_01.png" alt="story_01"> </p>
<p>こちらの軸を基本に、社内のどの人が見てもわかりやすい言葉にし、みなの意識統一のための合言葉（コンセプトワード）をつくりました。この辺りが出てくる頃には、週一の定例で大阪メンバーと東京在籍の制作陣もコミュニケーションをとっていたので、「めっちゃブレークスルーしようや！」という意識が生まれていて、ポロッと話した言葉がそのまま活かされるなど、やわらかい頭のなかを言葉にできる楽しい会議でわきあいあいとできました。（ワイワイやろうや〜というプロジェクトマネージャーに感謝）</p>
<p><img src="/assets/blog/authors/aya_sugimoto/250829/story_02.png" alt="story_02"> 
<img src="/assets/blog/authors/aya_sugimoto/250829/story_03.png" alt="story_03"> </p>
<h1>そして、生まれたコンセプトワードがこちら</h1>
<p><img src="/assets/blog/authors/aya_sugimoto/250829/concept.png" alt="concept"> 
<em>コンセプト企画書時点では、「Co-LAB」の表記でしたが「CO-LAB」とCompanyの略称ではなく、
幅広い意味をもたせるため「CO」と大文字にすることになりました。</em>
集ゴウは、やってみようの意味をもたせて「GO」とし、発シンには様々な「シン」の意味をもたせたかったので「SHIN」としました。大阪オフィスのメンバーの気合いが入っているので、コンセプトという堅苦しいものではなく、あいことばとして使っていこう！となりました。
デザイナーやエンジニア、マネージャーなど、肩書きとはいっさい関係なく意見やアイデアを伝え、形にしていきたいという気持ちがこもった言葉になりました。</p>
<h1>あいことばは決まった！つぎは、社内外の人が集まる空間の壁デザイン</h1>
<p>大阪オフィスには、東京の室町オフィス同様、ジャンクションと呼ばれる空間があります。社内外の人が集まり、イノベーションを起こしていく場にしようという意味でつくられた空間に、横8400mm×高さ2500mmくらいの壁になにか意味のあるグラフィックを描こうというプランが生まれました。
<img src="/assets/blog/authors/aya_sugimoto/250829/graphic.png" alt="graphic"> 
はい、こちらが、その壁に描いたグラフィックになります。ずっとオフィスに残るものなのでクリエイターとしてはとてもやりがいのあるものになります。Osaka Tech Labに在籍しているデザイナーと東京にいるデザイナーとでアイデアを出し合い、生成AIを駆使しながら、Osaka Tech Lab所属のデザイナーが仕上げました。あいことばである、「集GO！発SHIN！CO-LAB」や、「めっちゃブレイクスルーしよう！」がつまったデザインです。</p>
<h1>おもしろいデザインの仕掛けは次の機会に！</h1>
<p>次回、コンセプト「集GO！発SHIN！CO-LAB」から生まれたデザイナーのこだわりが詰まった壁や、<a href="https://www.kinto-technologies.com/company/osakatechlab/">LP</a>についての制作ストーリーを書いてみたいと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aya_sugimoto/250829/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[About Our Front-End Study Group]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-30-frontend-study-group-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-30-frontend-study-group-en/</guid>
            <pubDate>Thu, 28 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article will share the activity report of the in-house front-end study group.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello!<br>I&#39;m high-g (<a href="https://x.com/high_g_engineer">@high_g_engineer</a>) from the New Car Subscription Development Group at the Osaka Tech Lab.<br>In this article, I&#39;d like to introduce the front-end study group that we started within the company.</p>
<h2>Background</h2>
<p>One day, I had the opportunity to show my supervisor the <a href="https://2024.tskaigi.org/talks">TSKaigi 2024 timetable</a> during a one-on-one meeting in the New Vehicle Subscription Development Group. He responded positively, saying, &quot;This covers a wide range of topics and is a great teaching material. It&#39;d be great if we could use this to hold study sessions where front-end engineers across departments to share insights and build horizontal connections.&quot;</p>
<p>Those words prompted me to reach out to front-end engineers who seemed eager to join, and I began planning an in-house study group.</p>
<h2>Purposes of the Study Group</h2>
<p>This study group has three goals:</p>
<ol>
<li><strong>Learning</strong>: Deepen our understanding by discussing and sharing presentations from external conferences and web standards</li>
<li><strong>Applying</strong>: Take what we learn and put it to use in real projects</li>
<li><strong>Sharing</strong>: Share the outcomes, challenges, and insights from real-world applications to accumulate knowledge across the organization</li>
</ol>
<p>This is a practical study group that aims not to simply acquire knowledge, but to get to the point where we can actually use it on the job. We set aside a one-hour time slot once a week and conduct the sessions in a format appropriate to the content, such as read-along, mob programming, hands-on sessions.</p>
<h2>Main Themes and Development History</h2>
<p>Since September 30, 2024, 34 events have been held to date, and the following topics have been addressed:</p>
<h3>Sharing insights from conferences and tech events (Session 1 to 17)</h3>
<p>To get things rolling, we focused on the latest trends in front-end technologies using presentations from events like <a href="https://2024.tskaigi.org/">TSKaigi 2024</a> and <a href="https://jsconf.jp/2024/">JSConf JP 2024</a> as starting points.</p>
<ul>
<li><strong>Thinking about the Future of Prettier</strong> (Session 1): The future direction of code formatters</li>
<li><strong>Improving TypeScript Performance</strong> (Session 2): Comparing with issues in actual business code</li>
<li>**This is What Happened When We Unified Everything with TypeScript! ** (Session 3): A full-stack development case study</li>
<li><strong>TypeScript Journey: Helpfeel&#39;s journey of trials and errors on the road to success</strong> (Session 5)</li>
<li><strong>Type-Safe and Efficient Routing with TanStack Router</strong> (Session 7)</li>
<li>** Storybook-Driven Development: Reproducibility and Efficiency of UI Development ** (Session 9)</li>
<li><strong>Setting Trust Boundaries Instead of Blindly Relying on TypeScript Type Declarations</strong> (Session 10)</li>
<li><strong>LAPRAS Open Performance Tuning by Mizchi-san</strong> (Sessions 12-13): Performance improvement learned from external case studies</li>
<li>** Micro-Frontends on the Top Page of Yahoo! JAPAN** (Session 15): A development example from a large organization</li>
<li><strong>JavaScript Module Resolution Interoperability</strong> (Session 16)</li>
<li><strong>You Don&#39;t Know Figma Yet - Hacking Figma with JS</strong> (Session 17)</li>
</ul>
<h3>Cross-Team Knowledge Sharing &amp; Understanding (Sessions 18-24)</h3>
<p>As more people joined, we started sharing personal skills and the status of each division&#39;s front-end team. This helped shape the future direction of the study group and gave us a chance to review different projects at the code level. It also allowed us to identify common front-end challenges across KINTO Technologies.</p>
<ul>
<li><strong>Looking Back So Far</strong> (Session 18): Discussing the direction of the study group</li>
<li><strong>Self-Introductions with Career Backgrounds</strong> (Session 19): Promoting mutual understanding among members</li>
<li><strong>Frontend Development Status Sharing Session for Each Team</strong> (Sessions 20-24): A five-session series in which each team shares their tech stack, ongoing challenges, and current initiatives in detail</li>
</ul>
<h3>Understanding and Applying Web Standards (Sessions 25-28)</h3>
<p>At the retrospective session, some expressed interest in better understanding web specifications. Therefore, we decided to dive into the Baseline project together.</p>
<p><a href="https://web.dev/baseline?hl=ja">https://web.dev/baseline?hl=ja</a></p>
<ul>
<li><strong>Dive into Baseline</strong> (Sessions 25-27): A systematic study of web standards in a three-session series</li>
<li><strong>Baseline Review and Discussion of Next Steps</strong> (Session 28): Summarizing what we learned and discussing what to explore next</li>
</ul>
<h3>Practical Performance Improvement (Sessions 29-Present)</h3>
<p>Using <a href="https://www.youtube.com/watch?v=j0MtGpJX81E">Mizchi-san&#39;s public performance tuning video</a> as a reference, we began applying performance improvements to actual products.</p>
<p><a href="https://www.youtube.com/watch?v=j0MtGpJX81E">https://www.youtube.com/watch?v=j0MtGpJX81E</a></p>
<ul>
<li>** FACTORY Performance Improvement** (Sessions 29-30): A two-session series sharing specific optimization strategies and the results</li>
<li><strong>TSKaigi 2025 Presentation Sharing Session</strong> (Session 31): Preview of presentation content by internal members</li>
<li>** KINTO ONE Performance Improvement** (Sessions 32-34): A three-session series where everyone worked on real performance improvements through mob programming, resulting in our most hands-on learning experience</li>
</ul>
<h2>The Impact of Ongoing Sessions</h2>
<h3>Increasing and Retaining Participants</h3>
<p>We kicked off the study group with just 5 members, but over time and with consistent sessions, it&#39;s grown into a group of over 10 regular members. Participants are not only from the New Vehicle Subscription Development Group but also from other groups, realizing horizontal connections. Many of the original members are still actively involved and the retention rate of new members is also high, proving that this study group has become a truly valuable space for everyone.</p>
<h3>Gradual Improvement of Organizational Technical Capabilities</h3>
<p>Knowledge gained from conferences and tech events often stays at the individual level, but by learning simultaneously with front-end engineers from each department who have different perspectives on issues, not only does it benefit the entire organization, but it also leads to insights that are hard to gain alone.</p>
<p>Members who consistently took part in the Baseline series now understand web standards at a specification level, rather than just using the technology vaguely, which helps them make more well-founded decisions when choosing tools for their work.</p>
<h3>Acquiring Practical Problem-Solving Skills</h3>
<p>In the most recent sessions, we used a mob programming format to work on improving the performance of actual products such as KINTO ONE and KINTO FACTORY.</p>
<p>By applying the knowledge gained in the workshop directly to the product and actually doing it, the participants were able to overcome their aversion to performance tuning. This was a practical and valuable initiative that also led to increased sales.</p>
<h3>Deepening Technical Collaboration Between Teams</h3>
<p>Through the development status sharing sessions, we&#39;ve had more chances to learn about the technical efforts and challenges of other teams that we hadn&#39;t fully understood before. As a result, there&#39;s been a growing number of cases where teams with similar issues follow up individually after sessions to consult and collaborate.</p>
<p>The study group is no longer just a place for learning; it&#39;s becoming a hub for solving real business challenges.</p>
<h2>Conclusion</h2>
<p>This study group, which was started with the aim of strengthening horizontal connections between front-end engineers within the company, began by sharing knowledge at external conferences and has now evolved into performance improvement based on actual products.</p>
<p>We plan to keep it going, fostering collaboration while helping raise the overall technical proficiency of the organization.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/high-g/20250630/bg_frontend_study_group.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年6月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-28-newcomer-202506/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-28-newcomer-202506/</guid>
            <pubDate>Thu, 28 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年6月に入社した7人の皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、2025年6月入社のemimです！</p>
<p>本記事では、2025年6月入社のみなさまに入社直後の感想をお伺いし、まとめてみました。
KINTO テクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>Jun</h1>
<ul>
<li><strong>自己紹介</strong><ul>
<li>業務システム開発部 業務システムGで、中古車を扱う業務システムを担当しています。</li>
<li>前々職はSIerにて受託開発案件のPMをしたり、前職ではスタートアップでB2BのSaaSプロダクトの開発をしたりしていました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>私が所属しているチームはKINTOで扱う中古車の業務で使うシステムの開発と運用を担当しています。</li>
<li>私が所属しているチームには私を含めKTCのメンバーが5人います。開発（プログラミング）の大半をパートナーさんに委託しており、KTCのメンバーは主にプロダクトマネージメント、プロジェクトマネージメント、要件定義、システム設計、運用保守を担当しています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>事業の拡大に合わせて常に業務とシステムの見直しが動いており、また、新しい技術の活用なども積極的に推進されていて飽きる暇がない雰囲気です。</li>
<li>現在担当しているシステムではバックエンドにはJava、フロントエンドはVue3を使っています。AIの活用はまだあまりできていないですが、GitHub CopilotやDevinを開発で活用しようとしています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>ITと事業が直結している会社にてシステム開発をしたくKTCへ入社しました。業務・システム運用を楽にするための開発にもっと時間を割きたいところですが、まだそこまで手が回っていません。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>日当たりが良いところに小さな休憩スペースと給茶機があり、いつもそこで昼食を食べています。特に豪華な設備があるわけではないですが、良い気分転換になります。</li>
</ul>
</li>
<li><strong>Sさん ⇒　Junさんへの質問</strong><blockquote>
<p>いろいろな車に乗りたいとおっしゃっていたので、今一番気になっている車とその理由を教えてください！</p>
</blockquote>
<ul>
<li>GRヤリスです。本格的なスポーツカーに乗ってみたいです。</li>
</ul>
</li>
</ul>
<h1>なかの</h1>
<p>![なかのさんのプロフィール画像](/assets/blog/authors/emim/2025-08-27-newcomer/nakano.jpeg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>DXソリューションGという販売店向けのDX支援を行うチームに所属しています。PdM業務や今後の計画業務などに携わっています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>PdM+デザイナーで8名で構成される、販売店DXプロダクトの設計やディレクションを担うチームです。<br>  同部門中の営業や開発チームの方と連携してプロジェクトを進めます。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>穏やか、優しい、シゴデキな方ばかり！<br>  システム開発だけでなく、複雑な販売店業務への理解・興味をみなさんがもたれている事に驚きました。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>前職が広告代理店でITは事業の一部だったので、会社の軸がIT &amp; 知見のある方が集まる会社で働きたい！と考えていました。入社後もそういう方々の集まりだ、という印象は変わりません！</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>Osaka Tech Lab所属で、自席から見える大阪一望の窓がめっちゃ気に入っています。（どの席からでも見える）<br>  山も見えて、仕事の合間で癒やされる景色があるのはありがたい...</li>
</ul>
</li>
<li><strong>Junさん ⇒　なかのさんへの質問</strong><blockquote>
<p>KTCに入社して、ここがすごい！と思ったことを1つ教えてください 🙇</p>
</blockquote>
<ul>
<li>前のめりでポジティブな方が多い所です！<br>  入社してから常に情報が動き続けていて、みなさん仕事、技術、拠点の成長、他色々…前のめりに取り組まれているんだなと感じます。（Osaka Tech Labの一体感はホントにすごい）</li>
</ul>
</li>
</ul>
<h1><a href="/authors/01985418-e96d-7bc7-b05d-90a3a0215597/">Kazuki Otaka</a></h1>
<ul>
<li><strong>自己紹介</strong><ul>
<li>6月にクラウドセキュリティグループにJOINした大高です！業務では、クラウドセキュリティ分野に携わっており、CSPMや脅威検知の仕組みを活用して、マルチクラウド環境のガードレール監視とカイゼン活動を行っています。過去にテックブログにも投稿していますので、ぜひご覧ください。（過去の投稿は、<a href="/authors/01985418-e96d-7bc7-b05d-90a3a0215597/">こちら</a>）</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>Osaka Tech Labに2名と、神保町オフィスに1名の3名体制です。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>落ち着いた印象で、フレンドリーな人が多い印象です。社内では勉強会やLT大会やBeer Bashなども頻繁に開催されています。技術力の高くプロフェッショナルな人もたくさんいるので、自分も置いていかれないように頑張りたいと思います！</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>前職では、オンプレミス環境のシステムの構築に携わる機会が多く、クラウド環境を用いたシステム構築・運用のスキルを身につけたいと考え、フルクラウドでシステムを構築しているKINTOテクノロジーズに入社を決めました。AWSを中心にIaC化が進んでいたり、生成AIの検証・活用を進めていたり、想像していた以上にクラウドインフラ運用の最前線の環境にあると感じています。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>普段は静かで落ち着いた感じですが、突然、社内に機器が設置されて、PoC（概念実証）が始まるところなどは、新しいことにチャレンジしていく雰囲気が肌で感じられて意外と気に入っています。</li>
</ul>
</li>
<li><strong>なかのさん ⇒　Kazuki Otakaさんへの質問</strong><blockquote>
<p>セキュリティ界隈の仕事のおもしろさ、難しさを教えてください！</p>
</blockquote>
<ul>
<li>セキュリティは、被害が生じるとビジネスに大きな影響を与えるリスクがありますが、逆にセキュリティを高めすぎるとビジネスや開発を阻害する可能性があります。サイバー攻撃も進化する中で、いかにビジネスの成長を妨げずに、必要なセキュリティを実装・運用していくかのバランスが難しいところでもあり、逆に面白い部分でもあるかなと思っています。</li>
</ul>
</li>
</ul>
<h1>ゆな</h1>
<p>![ゆなさんのプロフィール画像](/assets/blog/authors/emim/2025-08-27-newcomer/yuna.jpeg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>業務システム開発部 業務システムGで、販売店の方が利用する業務システムのチームに所属しているゆなです。現在は、販売店が商談時に利用するシステムのリニューアルプロジェクトを担当しています。</li>
<li>最近、KINTOで契約した車が納車され、毎週末ドライブへ出かけるのがすっかり趣味になりました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>私含めてプロパー3人のチームです。リーダーともう一人のメンバーから日々サポートを受けながら、要件定義や保守運用に取り組んでいます。新しい知識やスキルを吸収しながら、着実に貢献していけるよう取り組んでいます。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>チーム内外問わず温かく迎え入れてくださって、とても良い雰囲気です。</li>
<li>KINTOの商品や業務は複雑で、把握すべき情報も多いですが、丁寧に教えていただきながらキャッチアップし、業務を進められています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>前職に引き続き、プロダクトマネージャーとして入社しました。<br>  自身のキャリアをさらに伸ばすため、「自分がプロダクトマネージャーとしてやりたいことを実現できそうか」という観点で会社を探し、KTCに出会いました。</li>
<li>面接では現マネージャーやリーダーの方とお話しし、具体的な業務内容を確認できたこと、また社員の皆さんの雰囲気が良かったことが決め手になりました。</li>
<li>入社前から、前職よりもテック寄りの領域に関わることは理解していましたが、業務が始まり、より高いスキルが必要だと実感し、現在は日々勉強しています。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>室町オフィスの16Fで働いています。三越前駅・新日本橋駅から地下直結でアクセスできるので、雨の日も快適です。</li>
<li>ビル内にはスタバがあり、周辺には飲食店やショップも多く、とても便利な環境です。昼休みに少し散歩をすると良い気分転換になり、午後の仕事もはかどります。</li>
</ul>
</li>
<li><strong>Kazuki Otakaさん ⇒　ゆなさんへの質問</strong><blockquote>
<p>趣味のお菓子作り・パン作りで、一番お気に入りのものはありますか？私もパンにハマっていた時期があり、学芸大学付近にあるパン屋さんに通っていました！</p>
</blockquote>
<ul>
<li>夏らしく、旬のとうもろこしをたっぷりのせた「マヨコーンパン」を作るのが最近のお気に入りです。甘いとうもろこしとマヨネーズの香ばしさがふんわりしたパン生地にしみ込んで、焼き上がりを待つ時間も幸せになります。</li>
</ul>
</li>
</ul>
<h1>emim</h1>
<ul>
<li><strong>自己紹介</strong><ul>
<li>Engineering Officeという組織横断チームで、組織全体の開発力を高めるための分析や仕組みづくり、各部署やチームへの働きかけ等をデザイナー目線で行っています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>チームには私を含め3名しかおらず、一人はフロントエンド開発が本職ながら組織全体に目を向けているマネージャー、もう一人はソフトウェアプロセス改善を軸に開発生産性を見られています。私は開発組織の中でのデザイナーのプレゼンスを上げる為の様々な取り組みを行っています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>Engineering Officeが極めて特殊で、それぞれが全く違うことに取り組んでいる一方で、拠点も一箇所なわけでもないのに情報共有及び連携がものすごくしっかりしています。少し特殊な技能を持ったメンバーが集まっているからこそな気がしています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>元々クルマも好きだったのもありますが、モビリティ業界はとても遠いと考えていました。私がこれまで身につけてきたスキルを転用できる所があると気づき、応募しました。<br>  入社前にかなり解像度の高い情報共有をチームメンバーからできていたので、そこまでギャップは持っていません。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>日本橋室町という立地自体が、毎日の出社がとても楽しいです。</li>
</ul>
</li>
<li><strong>ゆなさん ⇒　emimへの質問</strong><blockquote>
<p>映画やドラマがお好きとのことだったのですが、これまでに見た映画やドラマで、“デザインの仕事に活きた”作品はありますか？</p>
</blockquote>
<ul>
<li>趣味を覚えててくださってありがとうございます！SFがとても好きで、見たことのないようなUIや端末が出てくるのに毎度ワクワクしています。デザインの道を選んだのも、あんな未知のデバイスのデザインがしたかったのがもとで、どんな情報端末でも正しく情報提供出来るような情報設計……という志向で、デザインについて学んでいる／向き合っているフシがあります。</li>
</ul>
</li>
</ul>
<h1>Taka</h1>
<p>![Takaさんのプロフィール画像](/assets/blog/authors/emim/2025-08-27-newcomer/taka.jpeg =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>n=1と統計のあわいで思考して戦略を作るのが生業だと思っています。目標達成のための定量データ分析とデプスインタビューが大好きで、ずっとやってられます。自分が人生において発露してきた重要な衝動は3つで、「Observe（観察する）」「Weave a story（物語を紡ぐ）」「Curate（再構成する）」です。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>事業会社（大企業〜スタートアップ）経験者と、優秀なデータアナリスト、優秀なエンジニアが所属しています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>カルチャーとしては目標達成マインドを大事にしているチームです。前例にとらわれずに、自発的に問題解決を行って行く姿勢が重視されています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>入社動機は、自動車業界が世界的に変革期にある今、重要なのはn=1起点のマーケティングだという仮説を持っており、それを実践し、変革を推進するため。</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>室町オフィス16Fの大きな窓から入る明るい陽射しです。窓の周囲が大きなビルで囲まれていないから、大きな空と自然光を感じながら仕事ができるのを嬉しく思っています。</li>
</ul>
</li>
<li><strong>emim ⇒　Takaさんへの質問</strong><blockquote>
<p>これまでジョブチェンジされながら色々なお仕事をされてきたと伺っていますが、もしやり残した仕事があればそれを教えていただきたいのと、現在の（最高の）スキルを持った上でのKTCでの野望を教えてください！</p>
</blockquote>
<ul>
<li>そうですね、ジョブチェンジして来ましたが、実は軸は変わっていないと捉えていまして、やって来たことは「人と人とを繋げるためのデザイン」だと思っています。やり残したことは起業してないことです。野望は入社動機に書いた通りになります！</li>
</ul>
</li>
</ul>
<h1>S</h1>
<p>![Sさんのプロフィール画像](/assets/blog/authors/emim/2025-08-27-newcomer/s.png =300x)</p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>KINTO ONE（新車サブスクサービス）のフロントエンド開発を行うチームに所属しています</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>メンバー構成はエンジニア8名（東京6名、大阪1名、福岡1名）です</li>
<li>開発の体制はスクラム開発を採用しており、1スプリントを1週間として進めています</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>私は大阪所属で1人なので基本的にオンラインでのやり取りになりますが、気軽に質問もしやすく良い雰囲気の中で仕事ができています</li>
<li>チャットで質問などをしてもすぐに返事が返ってくるので業務で困ることもありません</li>
</ul>
</li>
<li><strong>KTCへ入社したときの入社動機や入社前後のギャップは？</strong><ul>
<li>入社動機はモビリティ領域がこれからさらに発展していく分野だと考えており、その成長を支えるプロダクト開発に携わりたいと思ったからです</li>
<li>入社前にチームの状況などはしっかりと聞くことができていたので、ギャップはほとんどありませんでした</li>
</ul>
</li>
<li><strong>オフィスで気に入っているところ</strong><ul>
<li>やはり大阪駅直結で通勤しやすい・オフィスが広くて綺麗なところと、オフィス内にガレージや道路を模したところがあるのが面白いなと思います</li>
</ul>
</li>
<li><strong>Takaさん ⇒　Sさんへの質問</strong><blockquote>
<p>Sさんが、ご自身ではとても好きなことや興味深いことなのに、人に話したら少し相手に引かれたエピソードがあれば教えてください。（そこにSさんの衝動が隠れていることがあります）</p>
</blockquote>
<ul>
<li>普段の食事で完全栄養食が準備に手間がかからず楽なので、いろんなメーカーのものを試しています。冷蔵庫の中もほとんどそれで埋まっているのですが、周りの人に話すと「考えられない」とよく言われます</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[Hands-On] How I Unified a Character's Appearance Using Midjourney v7's Omni-Reference]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-13-omni-reference-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-13-omni-reference-en/</guid>
            <pubDate>Wed, 27 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This is a practical report introducing how the new Omni-Reference feature in Midjourney V7 was used to consistently recreate the appearance of the original character "KTC AI WEB" and update its visual expression.]]></description>
            <content:encoded><![CDATA[<p>Hello. My name is Momoi (<a href="https://x.com/momoitter">@momoitter</a>), and I’m a designer in the Creative Office at KINTO Technologies.</p>
<p>In this article, I’ll walk through the process of using Midjourney V7’s new Omni-Reference feature to keep the look of the original character KTC AI WEB consistent.</p>
<p>This article is for:</p>
<ul>
<li>Those who want to learn more about Omni-Reference in Midjourney V7</li>
<li>Those looking to generate character images with a consistent look</li>
<li>Those interested in creating high-quality visuals using AI tools</li>
</ul>
<p>I’ll walk through how to use Omni-Reference, along with tips for fine-tuning, using actual prompts and generated images.</p>
<h2>What is Omni-Reference?</h2>
<p><img src="/assets/blog/authors/momoi/250613/01_midjourney.jpg" alt="midjourney"> In May 2025, a long-awaited feature called Omni-Reference was introduced in Midjourney V7.</p>
<p>This feature allows you to generate new images while maintaining visual consistency by referencing a specific image. It is now possible to maintain characterization, which was previously difficult in V7, and it’s not limited to people; it works with objects and vehicles, too.</p>
<p>There’s also a parameter called Omni Strength (ow), which lets you fine-tune how closely the output follows the reference image using numerical values.</p>
<h2>About &quot;KTC AI WEB&quot;</h2>
<h3>What is KTC AI WEB?</h3>
<p><img src="/assets/blog/authors/momoi/250613/02_old_character.jpg" alt="KTC AI WEB"> In November 2024, an AI-generated character named KTC AI WEB made her debut in the opening video of KINTO Technologies’ internal event CHO All-Hands Meeting.</p>
<p>Since then, she’s served as a navigator, appearing at developer events and recruitment events to help introduce KINTO Technologies.</p>
<p>Read the story behind KTC AI WEB’s creation here.<a href="https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/">https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/</a></p>
<h3>Why Unify Her Appearance?</h3>
<p>Since the birth of KTC AI WEB, image and video generative AIs have evolved rapidly. As a result, her original visuals started to feel a bit outdated.</p>
<p>Shortly afterwards, Midjourney V7 and Runway Gen-4 were released, dramatically improving generation quality. We also decided to update KTC AI WEB’s visuals at this time.</p>
<p>See the update process.<a href="https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making/">https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making/</a></p>
<p>However, while V7’s expressive power is extremely high, it didn’t initially offer a way to keep a character’s appearance consistent. KTC AI WEB’s face changed slightly with each generation.</p>
<p><img src="/assets/blog/authors/momoi/250613/03_difference.jpg" alt="difference"></p>
<p>Furthermore, while the update improved quality, it came with a side effect—some of KTC AI WEB’s original approachable charm faded.</p>
<p>Meanwhile, in May 2025, a new feature called Omni-Reference was added to Midjourney V7.</p>
<p>I used this feature to reconstruct an &quot;evolved KTC AI WEB&quot; that combines the beauty and realism of V7 with the original character’s look and feel.</p>
<h2>Let’s Put it into Practice!</h2>
<h3>Consulting ChatGPT</h3>
<p><img src="/assets/blog/authors/momoi/250613/04_chatgpt.jpg" alt="chatgpt"></p>
<p>To start, I asked ChatGPT the following:</p>
<blockquote>
<p>I want to upload an image to Midjourney V7’s Omni-Reference to generate a front-facing image of this pink-haired female character with a clean background while keeping her look consistent. Give me some prompts for Midjourney.&quot;</p>
</blockquote>
<p>ChatGPT responded with these prompts:</p>
<blockquote>
<p>upper body portrait of a female virtual operator in a clean futuristic interior, facing camera directly, symmetrical composition, looking straight ahead, centered, gentle expression, slightly relaxed face, subtle smile, brightly illuminated face, soft front lighting, high key lighting on the face, studio-style lighting setup, clear and vivid facial features, softly lit background, minimalistic sci-fi control room, white and silver tones, crisp details</p>
</blockquote>
<h3>How to Use Omni-Reference</h3>
<h4>Step 1: Drag and drop the image</h4>
<p><img src="/assets/blog/authors/momoi/250613/05_01_omnireference.jpg" alt="omnireference"> Drag the image you want to reference into the Midjourney prompt input box. A bar labeled Omni-Reference will appear at the top of the screen, drop it there.</p>
<h4>Step 2: Adjust Omni Strength (ow)</h4>
<p><img src="/assets/blog/authors/momoi/250613/05_02_omnistrength.jpg" alt="omnistrength"> Omni Strength allows you to adjust the strength of consistency (how closely the output matches the reference image). The numerical value is specified by the parameter called ow.</p>
<h4>Step 3: Start generating</h4>
<p><img src="/assets/blog/authors/momoi/250613/05_03_start.jpg" alt="start"> Enter the prompt you got from ChatGPT and start generating!</p>
<h3>Changes by &quot;ow&quot; values</h3>
<p><img src="/assets/blog/authors/momoi/250613/06_01_ow.jpg" alt="ow"></p>
<ul>
<li>Low ow (e.g. 100-200): Less similar to the original image, but with Midjourney’s signature soft, beautiful rendering.</li>
<li>High ow (e.g. 800-1000):Closer to the original image, but with less of Midjourney’s distinctive feel.</li>
</ul>
<p><img src="/assets/blog/authors/momoi/250613/06_02_ow100.jpg" alt="ow100"> <em>ow100</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_03_ow200.jpg" alt="ow200"> <em>ow200</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_04_ow400.jpg" alt="ow400"> <em>ow400</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_05_ow600.jpg" alt="ow600"> <em>ow600</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_06_ow800.jpg" alt="ow800"> <em>ow800</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_07_ow1000.jpg" alt="ow1000"> <em>ow1000</em></p>
<h3>Finding the Right Balance</h3>
<p><img src="/assets/blog/authors/momoi/250613/07_01_2000.jpg" alt="2000"> After much trial and error, I found that setting ow between 200 and 400 worked best for updating KTC AI WEB. With these values, the output preserved her original features while still capturing Midjourney’s signature soft, beautiful rendering.</p>
<p><img src="/assets/blog/authors/momoi/250613/07_02_fix.jpg" alt="fix"> At one point, &quot;This is it!&quot; I came across an image that felt just right. It was the ideal image capturing KTC AI WEB’s feature with Midjourney’s signature fine, delicate beauty.</p>
<h3>Expanding Scenes and Building a World</h3>
<p>Using the final image as a base, I expanded the composition and backgrounds using Omni-Reference. I also consulted ChatGPT again to get scene ideas and prompts for Midjourney.</p>
<p><img src="/assets/blog/authors/momoi/250613/08_another01.jpg" alt="another01"> <img src="/assets/blog/authors/momoi/250613/08_another02.jpg" alt="another02"> Having a solid base made it easier to build out a consistent world around her.</p>
<p>The updated look for KTC AI WEB is also featured at the start of the company’s introduction video. <a href="https://www.youtube.com/watch?v=8Df_0StDAiw">https://www.youtube.com/watch?v=8Df_0StDAiw</a></p>
<h3>Application: Expanding to Other Internal Content</h3>
<p>KTC AI WEB was also featured in presentation materials for a recent internal study session. Thanks to Omni-Reference, there’s no need to stress over whether the visuals stay on-model, making it much easier to use her across slides, event visuals, and other materials. <img src="/assets/blog/authors/momoi/250613/09_application01.jpg" alt="application01"> <img src="/assets/blog/authors/momoi/250613/09_application02.jpg" alt="application02"> <img src="/assets/blog/authors/momoi/250613/09_application03.jpg" alt="application03"></p>
<h3>Tips Learned Through Practice</h3>
<p>Omni-Reference is a very powerful feature, but the reference can be too strong.</p>
<ul>
<li>Want to change the outfit -&gt; Attach an image that only shows the face</li>
<li>Want to change the hairstyle, too -&gt; Focus on just the eyes or another minimal area</li>
</ul>
<p>Fine-tuning the reference range like these allows for consistent facial features while preserving Midjourney’s signature creative flexibility.</p>
<h2>Summary: A Time When Anyone Can Create &quot;KTC AI WEB&quot;</h2>
<p>The introduction of Omni-Reference has made it possible to create high-quality scenes while maintaining consistent character visuals.</p>
<p>In other words, we’ve entered a time when anyone can create something like KTC AI WEB.</p>
<p>It feels like a waste to keep her confined to my own imagination. That’s why I hope KTC AI WEB will continue to grow through the creativity of everyone in the company.</p>
<p>I want to expand the range of expression and help her spread her wings even further. As AI technology continues to evolve, so will KTC AI WEB. <img src="/assets/blog/authors/momoi/250613/10_butterfly.jpg" alt="butterfly"></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/momoi/250613/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[KTCのPdMってどんな仕事？ 〜とあるPdMの一日〜]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-27-factory-pdm-life/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-27-factory-pdm-life/</guid>
            <pubDate>Wed, 27 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KTCにおけるPdMの仕事って実際どんな感じであるかをまとめ]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは。KINTOテクノロジーズ株式会社（以下、KTC）でプロダクトマネージャー（以下、PdM）をしているK.Ichinoseです。</p>
<p>「PdMって企画を考えるだけの仕事じゃないの？」<br/>
そんなイメージを持っている方も多いかもしれません。実際には、日々の業務は多岐にわたり、関わる人も多種多様です。<br/></p>
<p>この記事では、私が担当している 「KINTO FACTORY」 のPdMとしての一日を通して、KTCのPdMのリアルな働き方をお伝えします。<br/>
読んだ後に、「PdMって面白そう！」「KTCで働くイメージが湧いた」と感じてもらえたら嬉しいです。<br/></p>
<h1>KTCにおけるPdMの役割とは？</h1>
<p>一般的にPdMは、プロダクトの方向性を定め、企画から開発、リリース、改善までをリードする役割です。<br/>
KTCの場合、<strong>モビリティサービス</strong>に関するプロダクトを扱っています。<br/></p>
<p>私はその中で、<a href="https://factory.kinto-jp.com/">KINTO FACTORY</a>（以下、FACTORY）というサービスを担当しています。<br/>
FACTORYは、TOYOTA・LEXUS純正オプションを正規販売店で「後付け」ができるアップグレードサービスです。<br/>
これにより、購入後でも最新の機能やパーツを追加でき、クルマの価値や使い勝手を高められます。<br/></p>
<p>私は「プロダクトマネジメントトライアングル」でいうところの開発者寄りの領域を主に担当し、ビジネスと開発者の橋渡しを担っています。<br/>
データ分析も最近始めています。<br/>
<img src="/assets/blog/authors/K.Ichinose/FACTORY_PdMTriangle.jpg" alt="FACTORY_PdMTriangle.jpg">
<em>図はPdMの役割を「ビジネス」「顧客」「開発者」の3領域で表したものです。</em></p>
<p>具体的には、企画や要求をヒアリングして要件を整理し、開発スケジュールを組み、リリースまで推進します。<br/>
さらに、KTC全社で複数プロダクトを横断する開発案件では、社内調整や他チームとの連携も重要な仕事です。</p>
<h1>PdMの一日の流れ</h1>
<p>ここでは、私の一日のスケジュール例をご紹介します。<br/>
（KTCはフルフレックス制ですが、私は毎日ほぼ同じ時間に始業しています。規則正しい方が集中できるタイプです）</p>
<table>
<thead>
<tr>
<th>時間</th>
<th>活動内容</th>
</tr>
</thead>
<tbody><tr>
<td>09:30</td>
<td><strong>始業・メールチェック</strong><br/>Slackやメールを確認し、今日のタスクを整理します。<br/>「誰に確認が必要か」「今日中に決めるべきことは何か」をこのタイミングで把握します。</td>
</tr>
<tr>
<td>10:00</td>
<td><strong>朝会（デイリースクラム）</strong><br/>開発チームと進捗や課題を共有。<br/>小さな課題でも早めに共有することで、後の大きな遅延を防ぎます。</td>
</tr>
<tr>
<td>10:30</td>
<td><strong>開発チーム内レビュー</strong><br/>PRD（プロダクト要求文書）やDesignDoc（設計ドキュメント）、その他仕様書のレビューを行います。</td>
</tr>
<tr>
<td>11:00</td>
<td><strong>ビジネス担当(トヨタ or KINTO)と定例ミーティング</strong><br/>FACTORY関連の新機能要件や仕様を確認します。<br/>KTCからの提案や要望を伝えることもあります。</td>
</tr>
<tr>
<td>12:00</td>
<td><strong>昼休憩</strong><br/>オフィス周辺でランチ(会社のある室町は少々お高め・・・)。</td>
</tr>
<tr>
<td>13:00</td>
<td><strong>要件整理・提案資料作成</strong><br/>企画段階で受け取った要求を要件に落とし込み、エンジニアやデザイナーと共有できる形にまとめます。</td>
</tr>
<tr>
<td>15:00</td>
<td><strong>開発案件の実現性、工数確認</strong><br/>エンジニアリーダーと要件をすり合わせ、実現方法や課題、必要工数を確認します。<br/>ここで現実的なスケジュールを固めます。</td>
</tr>
<tr>
<td>16:00</td>
<td><strong>社内全社PJの定例ミーティング</strong><br/>複数プロダクトにまたがる案件の進捗や課題、スケジュール調整を行います。</td>
</tr>
<tr>
<td>17:00</td>
<td><strong>データ分析</strong><br/>利用データを分析してユーザー傾向を把握。改善のヒントを探ります。</td>
</tr>
<tr>
<td>随時</td>
<td><strong>問い合わせ対応</strong><br/>KTC内の仕様や設計に関する質問、運用改善、テスト改善、リリース調整などを行います。<br/>また、KINTO/トヨタからの新機能や、運用相談など多岐にわたります。</td>
</tr>
</tbody></table>
<h1>KTCのPdMとして働く魅力</h1>
<ul>
<li>モビリティ業界の未来に関われる<ul>
<li><a href="https://global.toyota/jp/newsroom/corporate/39422385.html">クルマの価値を高める</a>という新しい挑戦に直接関われます。</li>
</ul>
</li>
<li>様々なステークホルダーとのコミュニケーション<ul>
<li>トヨタ、KINTO、社内外のさまざまな立場の方と関わり、異なる視点からの意見を聞けるのは刺激的です。</li>
</ul>
</li>
<li>チームの雰囲気<ul>
<li>役職や年齢に関係なく意見を言いやすく、議論も前向きです。</li>
</ul>
</li>
</ul>
<h1>やりがい</h1>
<p>FACTORYはまだ世の中で認知度が高くない新しいサービスです。<br/>
その分、成長の余地が大きく、方向性に関わる仕事はサービスの成長をダイレクトに感じられます。<br/>
自分が関わった機能がリリースされ、実際にユーザーが申し込みしてくれる瞬間は大きな達成感があります。</p>
<h1>これからKTCのPdMを目指す方へ</h1>
<p>私が思うKTCで活躍できるPdMの条件は、次の3つです。</p>
<ul>
<li>コミュニケーション力<ul>
<li>関係者と信頼関係を築き、スムーズに情報をやり取りできる力。</li>
</ul>
</li>
<li>柔軟さ<ul>
<li>変化を前向きに受け入れ、改善につなげられる力。</li>
</ul>
</li>
<li>プロダクトへの愛着<ul>
<li>担当するプロダクトを深く理解し、こだわりを持てること。</li>
</ul>
</li>
</ul>
<p>特に3つ目は、仕様や細部へのこだわり、改善アイデアの源泉になります。<br/>
愛着を持つことでチーム全体のモチベーションにも良い影響を与えられます。</p>
<h1>おわりに</h1>
<p>KTCのPdMは、変化の激しいモビリティ領域で、まだ新しいサービスをより良い形に育てていくやりがいのある仕事です。<br/>
この記事が、「PdMって面白そう！」と思うきっかけになれば嬉しいです。<br/>
もし興味を持っていただけたなら、ぜひ一緒にKTCで未来のモビリティをつくりましょう！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/K.Ichinose/PdMimage.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Introducing new members joining in March 2025]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-12-newcomer-202503-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-12-newcomer-202503-en/</guid>
            <pubDate>Tue, 26 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We asked five new members who joined us in March 2025 to share their impressions after joining our company, and here is a summary of what they said.]]></description>
            <content:encoded><![CDATA[<h1><strong>Introduction</strong></h1>
<p>Hello, I&#39;m Tetsu, and I joined the company in March 2025! In this article, I interviewed new members who joined us in March 2025 and gathered their first impressions after joining KINTO Technologies. I hope this article will be helpful for those interested in KINTO Technologies (KTC from now on) and serve as a nice reflection for those featured here!</p>
<h1><strong>Osada Yoshihiro</strong></h1>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>My name is Osada from the Group Core Systems Division. I’m in charge of the Business Development Team and the global community website TOYOTA Community by KINTO.</li>
</ul>
</li>
<li><p><strong>How is your team structured?</strong></p>
<ul>
<li>There are about 40 members in the Group Core Systems Division. We have members from various countries who communicate in Japanese, English, and various other languages. Our division includes the Business Development Group, the Systems Development Group, and the Common Service Development Group.</li>
<li>The Business Development Group is responsible for global leasing systems.</li>
<li>The Systems Development Group handles the planning, development, and operation of the Global KINTO ID Platform and TOYOTA Community by KINTO.</li>
<li>The Common Service Development Group develops and manages the Membership Platform, the Payment Platform, and the recently launched AI Platform.</li>
</ul>
</li>
<li><p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong></p>
<ul>
<li>I noticed that the company offers a variety of club activities and after-hours events, like the optional Beer Bash. The automotive club in particular seems very active, with events like race viewings and circuit karting. Even non-members can join some of the events, which gives the impression that both work and club life at KTC are full of energy.</li>
</ul>
</li>
<li><p><strong>What’s the atmosphere like on the team?</strong></p>
<ul>
<li>We start each day with a cheerful  “Good morning!” Our team is made up of people from different countries and professional backgrounds, and we make progress through open and friendly discussions.</li>
</ul>
</li>
<li><p><strong>How did you feel about writing a blog post?</strong></p>
<ul>
<li>I thought it was a great opportunity to help others learn more about KTC and the Group Core System Division.　It also gave me a chance to reflect on my own experience.</li>
</ul>
</li>
<li><p><strong>KJ-san -&gt; Question to Yoshihiro Osada-san</strong></p>
<blockquote>
<p>I heard you’re a big fan of movies. Could you recommend a few that you think are must-sees?</p>
</blockquote>
<ul>
<li>One movie I’d really like everyone at KTC to watch is &quot;Tucker: The Man and His Dream.&quot; It’s a 1988 American film directed by Francis Ford Coppola and executive produced by George Lucas, with an outstanding team behind it. It’s set in a different era, but the passion for cars really comes through.</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/t.sugiyama/osada.gif" alt="osada"></p>
<h1><strong>Tetsu</strong></h1>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>My name is Tetsu, and I work as a platform engineer in the Platform Group!</li>
</ul>
</li>
<li><p><strong>How is your team structured?</strong></p>
<ul>
<li>I belong to the Platform Engineering Team within the Platform Group, which consists of six members at the Jimbocho office and three members at the Osaka Tech Lab.</li>
<li>Even though there’s a physical distance between Tokyo and Osaka, we stay connected using tools like Slack, Gather, etc. We also plan study sessions together with members from both locations!</li>
</ul>
</li>
<li><p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong></p>
<ul>
<li>Nothing big in particular. At first, I thought it might be hard to communicate with people from other teams since our offices are in different locations. But thanks to events like BeerBash and active internal clubs, we have plenty of chances to interact with other teams, which makes our work much smoother.</li>
</ul>
</li>
<li><p><strong>What’s the atmosphere like on the team?</strong></p>
<ul>
<li>We have serious technical discussions and enjoy casual chats during breaks in a friendly atmosphere. When I mentioned that I wanted to study Kubernetes, some members in Osaka said they were interested too. That’s why we decided to plan study sessions together, and now we hold them across different groups, including Cloud Infrastructure Team and the DBRE team.</li>
</ul>
</li>
<li><p><strong>How did you feel about writing a blog post?</strong></p>
<ul>
<li>I used to read blog posts like this when I was thinking about changing jobs. So now that I’m the one writing it, I feel a bit nervous, especially when I think that someone out there might be doing the same. (laughs).</li>
</ul>
</li>
<li><p><strong>Yoshihiro Osada-san -&gt; Question for tetsu-san</strong></p>
<blockquote>
<p>Can you recommend some good lunch spots around the Jimbocho Office?</p>
</blockquote>
<ul>
<li>I’m a big fan of noodles, so I often go to Marugame Seimen or Kasugatei for abura soba (soupless ramen). There’s also a tendon (tempura rice bowl) place called Hachimaki that I’ve been curious about, so let&#39;s check it out together next time you come to Jimbocho!</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_Me.jpeg" alt="Tetsu"></p>
<h1><strong>YY</strong></h1>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>I&#39;m a back-end engineer in the Common Service Development Group.</li>
</ul>
</li>
<li><p><strong>How is your team structured?</strong></p>
<ul>
<li>We have 13 members working on development at the Muromachi office.</li>
</ul>
</li>
<li><p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong></p>
<ul>
<li>I felt very reassured by how well the onboarding and follow-up process was set up when I joined the company. I was also impressed by the team’s strong culture of thorough documentation, which helps accumulate and share operational know-how.</li>
</ul>
</li>
<li><p><strong>What’s the atmosphere like on the team?</strong></p>
<ul>
<li>I think everyone works with mutual respect. Our team policy is to &quot;speak up without hesitation,&quot; and it&#39;s a really reassuring environment where someone is always willing to help if you&#39;re stuck.</li>
</ul>
</li>
<li><p><strong>How did you feel about writing a blog post?</strong></p>
<ul>
<li>I&#39;ve never been the type to take the initiative with things like this, so I was very happy to be given the opportunity. I also hope it lowers the barrier for me to write more in the future.</li>
</ul>
</li>
<li><p><strong>tetsu-san -&gt; Question for YY-san</strong></p>
<blockquote>
<p>I think you mentioned that you had a car when you joined the company, but do you have any particular preferences when it comes to yours?</p>
</blockquote>
<ul>
<li>I&#39;m not sure if you’d call it a “preference,” but I guess I’d say choosing the body color I like. Even if I payed additionally for it, I went with the one that catched my eye, without worrying about resale value.</li>
</ul>
</li>
</ul>
<h1><strong>Namiki Yuji</strong></h1>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>My name is Yuji Namiki, and I’m part of the Innovation Drive Team within the Corporate IT Group.</li>
<li>I mainly work on solving problems in KTC&#39;s M365 environment, verifying and implementing new technologies, and supporting the IT operations of dealers.</li>
</ul>
</li>
<li><p><strong>How is your team structured?</strong></p>
<ul>
<li>There are nine members in the team, including myself. Our members are spread across multiple locations: Muromachi, Jimbocho, and Nagoya.</li>
</ul>
</li>
<li><p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong></p>
<ul>
<li>My first impression was,  “People respond on Slack incredibly fast!” I was surprised by the overall speed of communication. It felt like the entire organization shares a strong habit of quickly checking Slack notifications and replying right away.</li>
<li>Something I didn’t expect was how well-organized the internal documentation already was, considering how new the company is. I was also impressed by how effectively tools like Confluence and SharePoint are used. If I ever have a question, I can usually solve it myself by asking our in-house generative AI or checking the internal portal. That kind of environment was a pleasant surprise.</li>
</ul>
</li>
<li><p><strong>What’s the atmosphere like on the team?</strong></p>
<ul>
<li>It&#39;s a friendly atmosphere where it’s easy to start a conversation or invite someone out for a meal. There are lots of great restaurants around the office, so lunch is always something to look forward to. Having some mazesoba (mixed noodles) after work is also a highlight.</li>
<li>We have good working relationships with team members at other locations too. Being able to meet and work with them in person every few months really helps.</li>
<li>I also appreciate how company events make it easy to connect with people from other departments. (It&#39;s only been three months since I joined, but I’ve already had the chance to talk to around 50 to 70 people from other divisions. That makes me really happy!)</li>
</ul>
</li>
<li><p><strong>How did you feel about writing a blog post?</strong></p>
<ul>
<li>I was honestly happy when the team managing the Tech Blog reached out to us directly. After joining the company, I had been wanting to write for the Tech Blog, and this also gave me a great chance to reconnect with my fellow new hires. I’d like to take this opportunity to keep writing and posting on the Tech Blog in the future. (Aiming for one post a month!)</li>
</ul>
</li>
<li><p><strong>YY-san -&gt; Question for Namiki Yuji-san</strong></p>
<blockquote>
<p>We&#39;re both in the same sauna club. Are there any sauna spots you&#39;re interested in lately?</p>
</blockquote>
<ul>
<li>I’m really curious about <a href="https://everyday-sauna.com/stores/%e8%b6%8a%e8%b0%b7%e5%ba%97/">Everyday Sauna Koshigaya</a> (in Japanese), which just opened this May! It’s the third branch, following Maebashi (the first) and Hachioji (the second). What makes the Koshigaya location unique is that it’s the first to offer an herbal bubble bath, a new feature for Everyday Sauna. I&#39;d love to go there with the sauna club members sometime!</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/t.sugiyama/namiki.png" alt="namiki"></p>
<h1><strong>KJ</strong></h1>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>I used to be really into golf. I practiced so much that I got blood blisters. I started playing in my late 30s and was half-seriously dreaming of becoming a senior professional golfer (laughs). But after the COVID-19 outbreak, I started playing less and eventually stopped practicing, so my skills declined. Then in April 2025, I was deeply moved by McIlroy completing his career Grand Slam at the Masters, and it inspired me to start over.</li>
</ul>
</li>
<li><p><strong>How is your team structured?</strong></p>
<ul>
<li>At KINTO, where I’ve been seconded, I’m part of a four-person team called the Business Project Improvement Team, which focuses on improving leasing operations. At KTC, I’m not assigned to a specific team.</li>
</ul>
</li>
<li><p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong></p>
<ul>
<li>My first impression was that KTC isn’t a bureaucratic, top-down organization. Instead, it’s full of people who look at the company as a whole and think independently about what they can and should do. The surprising part, in a good way, was that it didn’t feel like a typical Toyota-style company.</li>
</ul>
</li>
<li><p><strong>What’s the atmosphere like on the team?</strong></p>
<ul>
<li>Since this division handles the leasing operations at the core of KINTO services, the atmosphere is pretty structured and organized. At the same time, the team is made up of friendly and unique individuals with all kinds of backgrounds. Our team is always looking for ways to improve quality and cut down workload and lead time. We have a healthy culture of questioning the status quo in a constructive way.</li>
</ul>
</li>
<li><p><strong>How did you feel about writing a blog post?</strong></p>
<ul>
<li>I thought it was an interesting initiative. I&#39;m grateful for the spotlight on new employees, who often go unnoticed, and I think it also provides helpful insight for people considering applying or joining the company, since it’s coming from someone they can relate to.</li>
</ul>
</li>
<li><p><strong>Namiki Yuji-san -&gt; Question for KJ-san</strong></p>
<blockquote>
<p>How do you interact with members of other departments?</p>
</blockquote>
<ul>
<li>I’m the only person in my division who was hired in Nagoya, so I’m usually based in the Nagoya office instead of the Muromachi office. There are far fewer KTC members in Nagoya compared to Muromachi, but because of the smaller number, we feel closer to each other and often have work-related conversations and casual conversations with people from other divisions. We also go out for lunch quite often. In addition, as I’ve been seconded to KINTO&#39;s Business Division, I’m gradually getting to know more people through the leasing operations. I&#39;m hoping to make 100 friends in the next year or two (laughs).</li>
</ul>
</li>
</ul>
<h1><strong>Finally</strong></h1>
<p>Thank you to everyone for sharing your impressions after joining the company!</p>
<p>KINTO Technologies continues to welcome new members on a regular basis! We&#39;ll be posting more stories from our newcomers across divisions, so stay tuned!</p>
<p>And yes: we&#39;re still hiring! KINTO Technologies is looking for new teammates to join us across a variety of divisions and roles. For more details, click <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[FACTORY開発チームで執筆祭り始めます]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-26-FACTORY開発チームで執筆祭り始めます/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-26-FACTORY開発チームで執筆祭り始めます/</guid>
            <pubDate>Tue, 26 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[FACTORY開発チームの夏のテックブログ執筆祭りを始めます。メンバーがそれぞれの視点で執筆していくのでお楽しみに。]]></description>
            <content:encoded><![CDATA[<h1>FACTORY開発チームで執筆祭り始めます 🎉</h1>
<p>KINTO FACTORY開発グループの水野です。</p>
<p><img src="/assets/blog/authors/takafumi_mizuno/mizuno.png" alt="Takafumi kin-ei"></p>
<p>はじめましての方も、そうでない方もこんにちは。<br>KINTO FACTORY開発グループでグループのマネージャをしている水野です。<br>普段はKINTO FACTORY開発グループのグループマネージャをやっていて、その他のプロダクトマネジメントや進行管理の役割なんかもしています。
最近は菜園で野菜を育てることにハマっています。</p>
<h2>KINTO FACTORY開発グループって？ 🏭</h2>
<p>KINTO FACTORY開発グループは、KINTO FACTORYの<a href="https://factory.kinto-jp.com/">ECサイト</a>を市場へ投入・改善を目的に立ち上がったチームです。
約3年前にPoCとして開始されたTOYOTA・LEXUS・GR車両のメーカー純正品のアップグレードビジネスを、2年前に内製化・サイトリプレイスして本番化しました。
そこから商品の追加やECサイトの改善を続けて現在に至ります。</p>
<p>現在は以下のような体制で活動しています：</p>
<ul>
<li>メンバー数：12人（2025年8月時点）</li>
<li>主な役割：プロダクトマネージャ、Webディレクター、Frontendエンジニア、Backendエンジニア、QAエンジニア</li>
<li>チームの雰囲気：フラットで自由、興味を惹かれたらやる、要望を達成する</li>
</ul>
<h2>プロダクト紹介 🚀</h2>
<p>KINTO FACTORYは、クルマの“あとから進化”を叶えるサービスなのです。
自分の愛車をアップデートできる。
しかも、オンラインで申し込めて、施工証明書もPDFで発行されるという、カスタムを体験。
名前にKINTOと付くのでKINTOの車だけが対象なのかと勘違いしやすいですが、お持ちのTOYOTA・LEXUSの車が対象なんです。</p>
<p>サービスカテゴリは大きく3つに分かれていて：</p>
<ul>
<li><strong>アップグレード</strong>：安全装備や快適機能を後付けできる。たとえば「なめらかブレーキ」や「パーキングサポートブレーキ」など。</li>
<li><strong>リフォーム</strong>：内装・外装をリフレッシュ。シートやステアリングの交換で、気分も一新。</li>
<li><strong>パーソナライズ</strong>：運転データに基づいて、もっと自分に合ったクルマに。</li>
</ul>
<p>技術的にも面白くて、裏側はAWS上のマイクロサービス構成で動いていたり、施工証明書（施工実施商品に限る）がデジタル発行だったり
どんなエンジニアが作っているのか、気になる方はぜひ下のインタビューとこれから執筆していくブログをチェックしてみてください！</p>
<p><a href="https://www.wantedly.com/companies/company_7864825/post_articles/537179">Frontendエンジニアのインタビュー</a>
<a href="https://www.wantedly.com/companies/company_7864825/post_articles/537179">https://www.wantedly.com/companies/company_7864825/post_articles/537179</a></p>
<p><a href="https://www.wantedly.com/companies/company_7864825/post_articles/959387">Backendエンジニアのインタビュー</a>
<a href="https://www.wantedly.com/companies/company_7864825/post_articles/959387">https://www.wantedly.com/companies/company_7864825/post_articles/959387</a></p>
<h2>執筆祭り、始めます ✍️🔥</h2>
<p>さて、今回の本題です。短いです。<br>KINTO FACTORY開発グループでは、8月から9月に掛けて<strong>夏のテックブログ執筆祭り</strong>を始めることにしました！</p>
<ul>
<li>KINTO FACTORYってなんなのか</li>
<li>技術的な知見の共有</li>
<li>チームの雰囲気を伝える</li>
<li>いろいろな方との接点を増やす</li>
<li>目的に共感できる仲間を増やしたい</li>
</ul>
<p>などなど、まずはこのテックブログでKINTO FACTORYを作っている人たちを「知ってもらう」ことから始めます。<br>それぞれのメンバーの役割や日常などの視点で書いてくれます。</p>
<p>すでに、メンバーそれぞれがブログを投稿してくれているので、ぜひ見てみてください！！</p>
<hr>
<p>KINTO FACTORY開発グループメンバー一同、よろしくお願いします </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Rapidly Bringing iOS App Development In-House]]></title>
            <link>https://blog.kinto-technologies.com/posts/2022-12-24-insourcing-mobile-dev-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2022-12-24-insourcing-mobile-dev-en/</guid>
            <pubDate>Mon, 25 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I'll share what we did to fully internalize our iOS app development.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hi, I&#39;m Hinomori, an iOS engineer in the Mobile Development Group. With over 10 years of experience in iOS app development, I work as a senior engineer handling day-to-day development tasks. Today, I’d like to share how simply going about my usual work eventually led to full in-house development.</p>
<h1>The Situation at the Time</h1>
<p>With only two months left until release,  development was being handled mostly by an external vendor, who reported the project was already 70–80% complete. However, the management team was new to smartphone app development and seemed uncertain about how to provide clear direction. In that situation, two iOS engineers, including myself, were brought in to join the development team.</p>
<p>It was in this situation that two iOS engineers including myself, were brought in to join the development team.</p>
<h1>Pinpointing What Needed Improvement</h1>
<p>I still remember feeling slightly dizzy when I first opened the repository. What I found was an impressively tangled mess of spaghetti code.</p>
<p>Jumping straight into a major refactor right after joining and trying to coordinate that with an external vendor would have come with a huge communication cost. So instead, we let the vendor continue development as planned while we focused on understanding the specifications and working on the refactor in parallel.</p>
<p>At that point, there were three major areas for improvement in the code:</p>
<pre><code>⛔ An underutilized CI environment

⛔ No consistent architecture

⛔ Singleton classes were being called from everywhere without control
</code></pre>
<p>There were plenty of other smaller issues too, but we decided to start by tackling these three as our main improvement tasks.</p>
<h1>Large-Scale Refactor</h1>
<p>Once we had a rough plan in place, we decided to start with improvements that didn&#39;t directly touch the code. From there, we gradually worked our way deeper into the internals.</p>
<h3>✅ An underutilized CI environment</h3>
<p>The CI environment technically existed, but it was barely being used. So we set up a proper CI workflow that could regularly handle the following two tasks:</p>
<ul>
<li>Static code analysis</li>
<li>Running unit tests</li>
</ul>
<p>At the time, the unit test coverage rate was 0%.  Since increasing it immediately wasn’t realistic, we started by simply displaying the coverage rate and focused on establishing a basic review environment.</p>
<h3>✅ No consistent architecture</h3>
<p>While there were traces of an MVVM structure, in practice, the processing wasn’t properly separated from the View. In most cases, the ViewModel was just a placeholder and didn’t function as part of a real architectural pattern. To address this, we introduced Clean Architecture principles into the existing MVVM setup. We implemented a consistent pattern across all ViewControllers and ViewModels, where the ViewModel handled Publishers using clearly defined Inputs and Outputs. We also moved unnecessary logic out of the View and into the appropriate ViewModel or Model layer.</p>
<p>Koyama-san wrote an article that covers the development approach we used. If you&#39;re interested, it&#39;s a great resource for understanding the structure in more depth.</p>
<p><a href="https://blog.kinto-technologies.com/posts/2022_12_07_Combine%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6MVVM%E3%82%92%E5%AE%9F%E7%8F%BE%E3%81%97%E3%81%9F%E8%A9%B1/">https://blog.kinto-technologies.com/posts/2022_12_07_Combine%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6MVVM%E3%82%92%E5%AE%9F%E7%8F%BE%E3%81%97%E3%81%9F%E8%A9%B1/</a></p>
<h3>✅ Singleton classes being called from everywhere without control</h3>
<p>The Singleton pattern is convenient and easy to use, but it is also often misunderstood. When misused, it can quickly lead to tight coupling, increased complexity, and poor testability. Without careful handling, it can quickly become a black box. This pattern can be especially risky in a team-based development, so we began removing classes that were being used as uncontrolled singletons</p>
<p>By completing these three tasks all at once over the course of a month, the overall visibility of the codebase improved dramatically. As a result, we were finally able to trace the actual specs incorporated in the code, and feature additions became much smoother and more manageable.</p>
<h1>Moving into Team Development</h1>
<p>Once the large-scale refactor was done, it naturally became faster for us to handle development ourselves rather than relying on the vendor. At that point, we were able to fully take over the development work. That said, all we had at that stage was a solid foundation for team development. There was still a lot left to do, such as expanding the lacking unit tests, organizing data flow, refactor the UI, and more. With a solid foundation, PR rules, branching strategy, and review flow in place, the team gradually built a consistent coding philosophy. Even as the team grew, the code stayed clean and manageable and members&#39; review feedback became more focused. I feel we&#39;ve become a self-sufficient team.</p>
<p>👨‍💻👩‍💻</p>
<p>Currently, we&#39;re taking on the challenge of SwiftUI and modern architecture as a team, learning through trial and error so we can all grow with today&#39;s development methods.</p>
<h1>Finally</h1>
<p>At KINTO Technologies, we are actively looking for teammates to take on new challenges with us. We look forward to the day we can build a team together.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Trying Out Spring AI]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-11-springAI-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-11-springAI-en/</guid>
            <pubDate>Mon, 25 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Trying Out Spring AI]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello. I am Yamada, and I develop and operate in-house tools in the Platform Engineering Team of KINTO Technologies&#39; (KTC) Platform Group. As an application engineer, I primarily develop a CMDB (Configuration Management Database), working on both the backend using Java and Spring Boot, and the frontend with JavaScript and React. Over the past year or so, I’ve also been riding the wave of generative AI, working on a chatbot for our CMDB that uses technologies like RAG and Text-to-SQL.</p>
<p>This is my previous article about Text-to-SQL. If you’re interested, feel free to check it out! 
<a href="https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/">https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/</a></p>
<p>This time, I’d like to share my experience exploring Spring AI, a topic related to generative AI. Spring AI reached General Availability (GA) on May 20, 2025, and I had the opportunity to try it out firsthand.</p>
<h2>Overview of Spring AI</h2>
<p>Spring AI is a generative AI framework that reached GA on May 20, 2025, as part of the Spring ecosystem. In the generative AI space, Python-based frameworks like LlamaIndex and LangChain are widely known. But if you’re looking to add generative AI capabilities to an existing Spring application, or want to experiment with generative AI in Java, Spring AI could be a great option.</p>
<p><a href="https://spring.pleiades.io/spring-ai/reference/">https://spring.pleiades.io/spring-ai/reference/</a></p>
<h2>What I Tried</h2>
<p>This time, using Spring AI, I implemented the following two functions:</p>
<ol>
<li><strong>Chat Function (LLM interaction)</strong> A conversational function powered by AWS Bedrock using the Claude 3.7 Sonnet model.</li>
<li><strong>Embedding Function</strong> A document embedding and similarity search function using Cohere&#39;s embedding model in combination with Chroma, a vector database.</li>
</ol>
<h2>Technology Stack</h2>
<ul>
<li>Spring Boot 3.5.0</li>
<li>Java 21</li>
<li>Spring AI 1.0.0</li>
<li>Gradle</li>
<li>Chroma 1.0.0</li>
<li>AWS Bedrock<ul>
<li>LLM Model: Anthropic Claude 3.7 Sonnet</li>
<li>Embedding model: Cohere Embed Multilingual v3</li>
</ul>
</li>
</ul>
<h2>Environment Setup</h2>
<h3>Setting Up Dependencies</h3>
<p>First, add the necessary dependencies for using Spring AI in your <code>build.gradle</code> file.</p>
<pre><code class="language-gradle">plugins {
    id &#39;java&#39;
    id &#39;org.springframework.boot&#39; version &#39;3.5.0&#39;
    id &#39;io.spring.dependency-management&#39; version &#39;1.1.7&#39;
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

ext {
    set(&#39;springAiVersion&#39;, &quot;1.0.0&quot;)
}

dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-validation&#39;
    implementation &#39;org.springframework.ai:spring-ai-starter-model-bedrock-converse&#39;
    implementation &#39;org.springframework.ai:spring-ai-starter-model-bedrock&#39;
    implementation &#39;org.springframework.ai:spring-ai-bedrock&#39;
    implementation &#39;org.springframework.ai:spring-ai-starter-vector-store-chroma&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
}

dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.ai:spring-ai-bom:${springAiVersion}&quot;
    }
}
</code></pre>
<h3>Application Configuration</h3>
<p>In <code>application.yml</code>, configure your AWS Bedrock credentials, model selection, and vector store connection settings.</p>
<pre><code class="language-yaml">spring:
  application:
    name: spring-ai-sample
  
  ai:
    bedrock:
      aws:
        region: ap-northeast-1
        access-key: ${AWS_ACCESS_KEY_ID}
        secret-key: ${AWS_SECRET_ACCESS_KEY}
        session-token: ${AWS_SESSION_TOKEN}
      converse:
        chat:
          options:
            model: arn:aws:bedrock:ap-northeast-1:{account_id}:inference-profile/apac.anthropic.claude-3-7-sonnet-20250219-v1:0
      cohere:
        embedding:
          model: cohere.embed-multilingual-v3
    model:
      embedding: bedrock-cohere
    vectorstore:
      chroma:
        client:
          host: http://localhost
          port: 8000
        initialize-schema: true
</code></pre>
<p>That&#39;s all for the configuration. From here, you can easily inject the necessary classes into your application using dependency injection (DI).</p>
<h2>Implementation Example</h2>
<h3>1. Conversational Function with a Generative AI (LLM)</h3>
<p>With Spring AI, you can easily implement conversations with an LLM using the <code>ChatClient</code> interface.</p>
<h4>Service Layer Implementation</h4>
<p>Implement the conversational function with an LLM in a class called ChatService.</p>
<pre><code class="language-java">@Service
public class ChatService {
    private final ChatClient chatClient;

    public ChatService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    public String generate(String message) {
        return this.chatClient.prompt(message).call().content();
    }
}
</code></pre>
<p>By injecting the <code>ChatClient.Builder</code>, you can automatically build a client based on the configuration file.</p>
<h4>Controller Implementation</h4>
<p>Create a REST controller to provide the API endpoint.</p>
<pre><code class="language-java">@RestController
public class ChatController {
    private final ChatService chatService;
    private final EmbeddingService embeddingService;

    public ChatController(ChatService chatService, EmbeddingService embeddingService) {
        this.chatService = chatService;
        this.embeddingService = embeddingService;
    }

    @PostMapping(&quot;/generate&quot;)
    public ResponseEntity&lt;String&gt; generate(@RequestBody ChatRequest request) {
        return ResponseEntity.ok(chatService.generate(request.getMessage()));
    }

}
</code></pre>
<p>Once this is set up, when you send a POST request to the <code>/generate</code> endpoint, you’ll receive a response generated by Claude 3.7 Sonnet.</p>
<p>Here’s an example:</p>
<pre><code class="language-bash">curl -X POST http://localhost:8080/generate -H &quot;Content-Type: application/json&quot; -d &#39;{&quot;message&quot;: &quot;こんにちわわ&quot;}&#39;

こんにちは！ 何かお手伝いできることはありますか？
</code></pre>
<h3>2. Implementing the Embedding Search Function</h3>
<p>Next, implement a function that vectorizes documents and enables semantic search.</p>
<h4>Embedding Service Implementation</h4>
<p>In this service, vectorize a sample text as a <code>Document</code> object and store it in Chroma. Then, use &quot;Spring&quot; as the query to search for similar documents. Behind the scenes, the Cohere Embed Multilingual v3 model converts the text into vectors.</p>
<pre><code class="language-java">@Service
public class EmbeddingService {
    private final VectorStore vectorStore;

    public EmbeddingService(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    public List&lt;Document&gt; embed() {
        List&lt;Document&gt; documents = List.of(
            new Document(&quot;Spring AI is a framework for building AI applications with the familiar Spring ecosystem and programming model.&quot;),
            new Document(&quot;LlamaIndex is a data framework for LLM applications to ingest, structure, and access private or domain-specific data.&quot;),
            new Document(&quot;LangChain is a framework for developing applications powered by language models through composability.&quot;)
        );

        vectorStore.add(documents);
        return vectorStore.similaritySearch(SearchRequest.builder().query(&quot;Spring&quot;).topK(5).build());
    }
}
</code></pre>
<h4>Implementing the API Endpoint</h4>
<pre><code class="language-java">@GetMapping(&quot;/embedding&quot;)
public ResponseEntity&lt;List&lt;Document&gt;&gt; embedding() {
    return ResponseEntity.ok(embeddingService.embed());
}
</code></pre>
<p>This implementation defines the <code>/embedding</code> endpoint, which vectorizes a sample document, performs a similarity search, and returns the results.</p>
<p>Here’s an example: As expected, the document containing the word &quot;Spring&quot; has the highest similarity score.</p>
<pre><code class="language-bash">curl http://localhost:8080/embedding

[
  {
    &quot;id&quot;: &quot;af885f07-20c9-4db4-913b-95406f1cb0cb&quot;,
    &quot;text&quot;: &quot;Spring AI is a framework for building AI applications with the familiar Spring ecosystem and programming model.&quot;,
    &quot;media&quot;: null,
    &quot;metadata&quot;: {
      &quot;distance&quot;: 0.5593532
    },
    &quot;score&quot;: 0.44064682722091675
  },
  {
    &quot;id&quot;: &quot;5a8b8071-b8d6-491e-b542-611d33e16159&quot;,
    &quot;text&quot;: &quot;LlamaIndex is a data framework for LLM applications to ingest, structure, and access private or domain-specific data.&quot;,
    &quot;media&quot;: null,
    &quot;metadata&quot;: {
      &quot;distance&quot;: 0.6968217
    },
    &quot;score&quot;: 0.3031783103942871
  },
  {
    &quot;id&quot;: &quot;336b3e07-1a70-4546-920d-c4869e77e4bb&quot;,
    &quot;text&quot;: &quot;LangChain is a framework for developing applications powered by language models through composability.&quot;,
    &quot;media&quot;: null,
    &quot;metadata&quot;: {
      &quot;distance&quot;: 0.71094555
    },
    &quot;score&quot;: 0.2890544533729553
  }
]
</code></pre>
<h2>Conclusion</h2>
<p>This was a brief introduction to implementing generative AI functions using Spring AI. After trying it out, Spring AI seems like a good option when integrating generative AI capabilities into Java-based systems. Also, I feel that combining the chat and embedding functions introduced here makes it relatively easy to build RAG functionality.</p>
<p>At this point, Python-based frameworks like LlamaIndex and LangChain still provide more advanced features and a richer ecosystem for generative AI. However, since Spring AI has only recently been released, there&#39;s a lot of potential for growth, and I’m excited to see how it develops.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AI-SPM Initiatives for Securing LLM Applications]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-16-aispm-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-16-aispm-en/</guid>
            <pubDate>Mon, 25 Aug 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I’m Tada from the <code>Security CoE</code> Group at KINTO Technologies. I usually work in the Osaka Office. Our group is taking on various challenges related to cloud security, with the mission of &quot;implementing guardrail monitoring and kaizen activities in real time&quot; in a multi-cloud environment. You can find a summary of what activities our members’ daily activities on <a href="https://blog.kinto-technologies.com/posts/2024-12-14-CloudSecurityEngineer/">this blog</a>.</p>
<h1>Background</h1>
<p>Following the trend of developing LLM (Large Language Model) applications in the world, our product team has been actively developing numerous LLM applications, many of which have progressed to the PoC or product-ready stage. Meanwhile, as a group responsible for monitoring cloud security, it is essential that we implement appropriate security measures for these applications as well.</p>
<p>Our LLM applications are primarily developed on AWS, Google Cloud, and Azure, and are often built using generative AI services provided by cloud vendors. Our group monitors and operates Cloud Security Posture Management (CSPM). However, currently there are no CSPM controls specifically tailored for generative AI-related services. For example, in the case of AWS, neither the AWS Foundational Security Best Practices (FSBP) nor the Center for Internet Security (CIS) provide direct controls related to generative AI services.</p>
<p>Therefore, our group has established guidelines to be followed when developing LLM applications using each cloud vendor’s generative AI services. The guidelines also include recommended usage practices and configuration methods for each vendor’s generative AI services. In other words, the configuration methods described in the guidelines are the controls that should be monitored as part of CSPM. Additionally, the control is implemented in <a href="https://www.openpolicyagent.org/docs/policy-language">Rego language</a> and is operated as part of CSPM for AWS Bedrock.</p>
<p>The term <strong>AI-SPM</strong> in the title stands for AI Security Posture Management. It is defined as &quot;a solution for visualizing, managing, and mitigating security and compliance risks of AI-related assets such as AI, machine learning (ML), and generative AI models.&quot; Given this definition, I deliberately chose to name this initiative AI-SPM.</p>
<h1>Security Guidelines to be Followed for LLM Applications</h1>
<p>In creating the guidelines, we referred to the <a href="https://genai.owasp.org/resource/owasp-top-10-for-llm-applications-2025/">OWASP Top 10 for LLM Applications 2025</a>. This document is probably a go-to resource when discussing LLM application security. This OWASP document lists the top 10 most critical vulnerabilities commonly seen in LLM applications and includes sections such as &quot;Overview,&quot; &quot;Examples of Vulnerabilities,&quot; &quot;Mitigation Strategies,&quot; and &quot;Attack Scenarios.&quot; Based on this content, I reviewed the services and features used in each cloud environment and considered best practices for developing LLM applications accordingly.</p>
<p>The first risk listed in the top 10 is &quot;LLM01: Prompt Injection.&quot;</p>
<p>This refers to <code>the risk that malicious input may unintentionally manipulate the behavior of LLM, resulting in data leakage or unauthorized actions</code>. To prevent and mitigate this risk, <code>validation and filtering of input to LLM</code> is an effective measure. When implementing this prevention and mitigation measure on cloud services, AWS offers a feature in <a href="https://aws.amazon.com/jp/bedrock/guardrails/">Amazon Bedrock Guardrails</a> to filter <code>prompt attacks</code>. So, enabling this feature serves as an effective countermeasure. After that, by checking whether this feature is enabled as a CSPM control, it becomes possible to achieve both visibility and kaizen.</p>
<p>Below is a summary of the most common risks from the Top 10, along with corresponding &quot;Prevention and Mitigation Strategies&quot; and &quot;Implementation of CSPM controls&quot; for AWS services.</p>
<table>
<thead>
<tr>
<th>Top 10</th>
<th>Risk Overview</th>
<th>Prevention and Mitigation Strategies</th>
<th>Implementation on AWS</th>
<th>Implementation of CSPM controls</th>
</tr>
</thead>
<tbody><tr>
<td>LLM01: Prompt Injection</td>
<td>Risk that malicious input (prompts) may unintentionally manipulate the behavior of LLM, leading to data leakage or unauthorized actions.</td>
<td>Model behavior restrictions, input/output validation, filtering, authority control, and human approval, etc.</td>
<td>Use the &quot;Prompt attacks&quot; content filters in Amazon Bedrock Guardrails.</td>
<td>Ensure that <code>the configure prompt attacks filter is enabled</code>, the action is set to &quot;<code>Block</code>,&quot; and the threshold is set to &quot;<code>HIGH</code>.&quot;</td>
</tr>
<tr>
<td>LLM02: Sensitive Information Disclosure</td>
<td>Risk of sensitive information, such as personal information or confidential data, may be leaked through the model’s responses or behavior.</td>
<td>Output validation and filtering, training data management, and access controls.</td>
<td>Use the &quot;Sensitive information filters&quot; in Amazon Bedrock Guardrails.</td>
<td>Verify that the <code>Sensitive information filters </code>are <code>enabled</code> for <code>Output</code>.</td>
</tr>
<tr>
<td>LLM06: Excessive Agency</td>
<td>Risk of unintended actions or manipulations due to granting excessive autonomy or authority to LLMs or their agents</td>
<td>Enforcement of least authority, human approval, and authority auditing.</td>
<td>Use the &quot;Agent&quot; feature in Amazon Bedrock Builder tools.</td>
<td>When using Agents, verify that <code>Guardrail details</code> are <code>properly associated</code> with them.</td>
</tr>
<tr>
<td>LLM09: Misinformation</td>
<td>Risk that the LLM may generate outputs containing misinformation or bias, causing adverse effects on users or society.</td>
<td>Train on diverse and reliable data, fact-check, and provide source attribution.</td>
<td>Use the &quot;contextual grounding check&quot; feature in Amazon Bedrock Guardrails.</td>
<td>Verify that <code>contextual grounding check</code> is <code>enabled</code>.</td>
</tr>
<tr>
<td>LLM10: Unbounded Consumption</td>
<td>Uncontrolled resource consumption by LLMs may lead to DoS, increased costs, and service outages, resulting from unrestricted requests or computational usage.</td>
<td>Resource limits, quota settings, and usage monitoring</td>
<td>Use the &quot;Model invocation logging&quot; feature in Amazon Bedrock.</td>
<td>Verify that <code>Model Invocation Logging</code> is <code>enabled</code>.</td>
</tr>
</tbody></table>
<p>I presented the above content at a co-hosted event, <a href="https://kinto-technologies.connpass.com/event/350508/">Cloud Security Night #2</a>, held in May. Please feel free to refer to the <a href="https://speakerdeck.com/osakatechlab/llm-apurikesiyonnotamenokuraudosekiyuritei-cspm-noshi-zhuang-pointo">materials here</a> for more details.</p>
<h1>Implementing CSPM Controls with Rego</h1>
<p>Now that the CSPM controls have been defined, the next step is to develop a mechanism for checking the control configurations. In our group, we operate CSPM using Security Hub for AWS, <a href="https://sysdig.jp/">Sysdig</a> for Google Cloud, and Defender for Cloud for Azure. While using a unified tool would be ideal, we don’t heavily rely on cloud provider consoles. Instead, we monitor CSPM alerts through APIs and send notifications to Slack when necessary. As such, the lack of tool integration hasn’t posed any particular inconvenience.</p>
<p>For the CSPM controls of the LLM application, we decided to implement them using Rego, the policy language used in Sysdig’s CSPM features. The reason we chose Rego is that it’s OSS, and for defining evaluation logic for cloud infrastructure settings, such as CSPM controls, it offers a relatively low learning curve and is easy to develop with.</p>
<p>Below is the <code>LLM01:Prompt Injection</code> control implemented in Rego. What it does is set <code>risky == true</code> (risk present) as the default value. If the <code>ContentPolicy.Filters</code> setting in Bedrock Guardrails has <code>Type == PROMPT_ATTACK</code> and <code>InputStrength==HIGH</code>, it determines that the <code>Prompt attack</code> filter is enabled and the threshold is set to <code>High.</code> In this case, it sets <code>risky == false</code>, meaning no risk is present.</p>
<pre><code class="language-HCL">default risky := true

risky := false if {
	some filter in input.ContentPolicy.Filters
    lower(filter.Type) == &quot;prompt_attack&quot;
    lower(filter.InputStrength) == &quot;high&quot;
}
</code></pre>
<p>This Rego needs to be deployed to Sysdig as a Sysdig custom control. For detailed instructions on how to create and deploy custom controls, please see the official Sysdig blog <a href="https://sysdig.jp/blog/how-to-build-custom-controls-in-sysdig-secure/">here</a>. We also used this as a reference during our development process. While not covered in this blog post, we accumulated a great deal of know-how on creating custom controls.</p>
<p>Custom controls must be deployed to Sysdig as a terraform. The following is the final main.tf used to create the custom control.</p>
<pre><code class="language-terraform">terraform {
    required_providers {
        sysdig = {
            source = &quot;sysdiglabs/sysdig&quot;
            version = &quot;&gt;=0.5&quot;
        }
    }
}
variable &quot;sysdig_secure_api_token&quot; {
  description = &quot;Sysdig Secure API Token&quot;
  type        = string
}
provider &quot;sysdig&quot; {
    sysdig_secure_url=&quot;https://app.us4.sysdig.com&quot;
    sysdig_secure_api_token = var.sysdig_secure_api_token
}
resource &quot;sysdig_secure_posture_control&quot; &quot;configure_prompt_attack_strength_for_amazon_bedrock_guardrails&quot; {
  name = &quot;Configure Prompt Attack Strength for Amazon Bedrock Guardrails&quot;
  description = &quot;Ensure that prompt attack strength is set to HIGH for your Amazon Bedrock guardrails. Setting prompt attack strength to HIGH in guardrails helps protect against malicious inputs designed to bypass safety measures and generate harmful content.&quot;
  resource_kind = &quot;AWS_BEDROCK_GUARDRAIL&quot;
  severity = &quot;High&quot;
  rego = &lt;&lt;-EOF
    package sysdig
    
    import future.keywords.if
    import future.keywords.in

    default risky := true

    risky := false if {
		  some filter in input.ContentPolicy.Filters
    	  lower(filter.Type) == &quot;prompt_attack&quot;
    	  lower(filter.InputStrength) == &quot;high&quot;
      }
  EOF
  remediation_details = &lt;&lt;-EOF
    ## Remediation Impact
    This control will help you ensure that your Amazon Bedrock guardrails are configured with high prompt attack strength, which is crucial for protecting against malicious inputs designed to bypass safety measures and generate harmful content.

    ## Remediation Steps
    1. Navigate to the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/home).
    2. Select the guardrail you want to modify.
    3. In the guardrail settings, locate the &quot;Content Policy&quot; section.
    4. Ensure that the &quot;Prompt Attack&quot; filter is set to &quot;High&quot; for the &quot;Input Strength&quot;.
    5. Save the changes to the guardrail configuration.
    6. Repeat this process for any other guardrails in your AWS environment.

    ## Remediate Using Command Line
    You can use the AWS CLI to update the guardrail configuration. Run the following command to set the prompt attack strength to HIGH for a specific guardrail:

    ```bash
    aws bedrock update-guardrail --guardrail-id &lt;guardrail-id&gt; --content-policy ’{&quot;Filters&quot;: [{&quot;Type&quot;: &quot;prompt_attack&quot;, &quot;InputStrength&quot;: &quot;high&quot;}]}’
    ```
    Replace `&lt;guardrail-id&gt;` with the ID of your guardrail.
    Repeat this command for other guardrails in your AWS environment.
    
    ## Additional Information
    For more information on configuring Amazon Bedrock guardrails, refer to the [Amazon Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html).

    ## Contact Information
    Slack Channel: #security-coe-group
  EOF
}
</code></pre>
<p>Initially, Sysdig did not support Amazon Bedrock as a target resource for CSPM. However, after reaching out to Sysdig, they responded with impressive speed, which made this initiative possible. Beyond the functionality, this kind of responsiveness is incredibly reassuring as a Sysdig user.</p>
<p>Following the same approach, we implemented several of the controls listed in the <a href="#llm-%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A7%E9%81%B5%E5%AE%88%E3%81%99%E3%81%B9%E3%81%8D%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AC%E3%82%A4%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3">Security Guidelines to be Followed for LLM Applications</a> using Rego and deployed them to Sysdig.</p>
<h1>AI-SPM Operations by Sysdig</h1>
<p>Several of the custom controls that were deployed are defined as custom policies, enabling visibility into AWS Bedrock resources. Based on the results of this visibility, any identified issues are addressed through kaizen. In our company, our group submits kaizen requests to the product development group, always including both the issue and kaizen methods.</p>
<p>The screen below shows the Sysdig console, where the CSPM control for <code>LLM01：Prompt Injection</code> is being reviewed. The control name is <code>Configure Prompt Attack Strength for Amazon Bedrock Guardrails</code>. The naming was done to align with its intended role. The status indicates that there are three AWS Bedrock Guardrail resources, one is marked as <code>Failling</code>, while the remaining two are <code>Passing</code>.</p>
<p><img src="/assets/blog/authors/tadatomo/ai-spm01.png" alt="ai-spm01"></p>
<p>By drilling down into the above screen, you can see the impact of kaizen and how to kaizen. This content also reflects the contents written in main.tf.</p>
<p><img src="/assets/blog/authors/tadatomo/ai-spm02.png" alt="ai-spm02"></p>
<p>However, in actual operations, we rarely access the Sysdig console directly. Instead, we typically check alerts through the Sysdig API.</p>
<h1>Conclusion</h1>
<p>In this article, I introduced an approach to securing LLM applications by developing Rego-based configuration checks for Amazon Bedrock and operating them using Sysdig. The guidelines not only include Amazon Bedrock but also Azure AI Foundry and Google Cloud Vertex AI, and we plan to continue developing Rego policies and operating them through Sysdig.</p>
<p>In addition to traditional CSPM, AI-SPM must go beyond general cloud infrastructure security, such as AI-specific challenges and the protection of data assets, which traditional CSPM cannot fully cover. AI technology is evolving rapidly, with new concepts such as MCP and A2A emerging recently. It is also important to promote security measures that correspond to these developments.</p>
<p>We will continue to enhance the security of AI applications while staying aligned with new technologies and challenges.</p>
<h1>Lastly</h1>
<p>The Security CoE Group is looking for people to work with us. We welcome not only those with hands-on experience in cloud security but also those who may not have experience but have a keen interest in the field. Please feel free to contact us.</p>
<p>For more information, <a href="https://hrmos.co/pages/kinto-technologies/jobs/1811937538128224276">please check here</a>.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[単体テストのテストメソッドにわかりやすい名前をつけて品質向上させよう]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-25-particular-about-unit-test-case-name/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-25-particular-about-unit-test-case-name/</guid>
            <pubDate>Mon, 25 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[テストコードの品質向上のために、まずはテストメソッド名を改善してみませんか？非エンジニアにも分かる振る舞いベースの命名で、チーム全体の開発効率を向上させる方法を紹介します。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは。KINTO FACTORYでバックエンドエンジニアをしている上原(<a href="https://x.com/penpen_77777">@penpen_77777</a>)です。
普段はGoやRustを使って開発をしており、エディタはVim/NeoVimを愛用しています。</p>
<p>今回は、テストコードの品質向上について、特に<strong>テストメソッド名の付け方</strong>にフォーカスしてお話しします。</p>
<p>今回の内容について以下の書籍を参考にさせていただきました。
<a href="https://book.mynavi.jp/ec/products/detail/id=134252">https://book.mynavi.jp/ec/products/detail/id=134252</a></p>
<h1>テストコードに関わる「辛さ」</h1>
<p>皆さんは日々テストコードを書いていると思いますが、こんな経験はありませんか？</p>
<ul>
<li>テストコードはあるが中身が理解できていないため、テストの結果が信用できない、十分にテストできている自信がない</li>
<li>他の人が書いたテストコードをレビューしていて、内容がよくわからないけれどなんとなくApproveしてしまった</li>
<li>どういうテストをしているかよくわからないけどCIが落ちてしまうとPRを出せないので、テストがPASSするように雰囲気でテストコードを直した</li>
</ul>
<p>このような課題を解決するために、まずは <strong>テストメソッド名を改善してみませんか？</strong> ということを提案したいと考えています。</p>
<h1>なぜテストメソッド名を改善する必要があるのか？</h1>
<p>テストコードの品質についてなぜテストメソッド名を改善する必要があるのかについて、具体例を見ながら考えていきましょう。</p>
<h2>コード例: ECサイトシステム</h2>
<p>Go言語で記述されたECサイトシステムで、以下のような商品を購入するメソッドを実装しました。
商品の在庫があれば注文することができ、なければエラーを返して注文できません。</p>
<pre><code class="language-go">// ECサイトシステム構造体
type ECSystem struct {
    // 商品ごとの在庫数
    stock map[string]int
}

// 商品を購入するメソッド
func (c *ECSystem) Order(itemID string) error {
    // 商品がない場合はエラーを返す
    if c.stock[itemID] &lt;= 0 {
        return ErrOutOfStock
    }

    // 商品があれば在庫数を減らす
    c.stock[itemID]--

    // 正常終了
    return nil
}
</code></pre>
<h2>改善前のテストメソッド名</h2>
<p>このプロダクションコードに対して単体テストを行いたいため、 以下のようなテストコードを作成したとします。</p>
<pre><code class="language-go">func Test_正常系_在庫数が0より大きい場合(t *testing.T) { }
func Test_異常系_在庫切れエラー(t *testing.T) { }
</code></pre>
<p>これらの名前を見ると、以下のような疑問が浮かびます：</p>
<p>🤔 「在庫数が0より大きい場合、どういう結果が出てくるのが正しいのだろう？」
🤔 「在庫切れエラーとは、どういう条件で発生するのだろう？」</p>
<p>このパターンのテストメソッド名は意外と見かけることが多いと筆者は感じています。
どうテストしていくか、テストメソッド内の中身をどう書いていくか考えるのに気が取られて、テストメソッド名の検討に時間が取れなかった結果が現れていると感じています。</p>
<p>今回はサンプルコードでメソッド内の実装も薄いのでテストメソッド名からどういうテストか容易に推測できますが、
実際のプロダクトコードだとテストメソッドの実装量も数も多くなるため、どういうテストか理解できなくなってしまいます。</p>
<h2>第一段階の改善</h2>
<p>改善前のテストメソッド名がわかりづらいと感じる1つの要因は、名前から「事前条件」もしくは「想定結果」のどちらか一方が欠落しているためです。</p>
<pre><code class="language-go">func Test_正常系_在庫数が0より大きい場合(t *testing.T) { } // 「想定結果」の欠落
func Test_異常系_在庫切れエラー(t *testing.T) { }         // 「事前条件」の欠落
</code></pre>
<p>逆に言えば、テストメソッド名に「事前条件」と「想定結果」を両方書けばわかりやすくなるということなので、
<code>{ テスト対象メソッド }_{ 事前条件 }_{ 想定する結果 }</code>の形式に統一してみましょう。</p>
<pre><code class="language-go">func Test_Order_在庫数が0より大きい場合_正常終了(t *testing.T) { }
func Test_Order_在庫数が0の場合_在庫切れエラー(t *testing.T) { }
</code></pre>
<p>少し良くなりましたが、まだ改善の余地があります。</p>
<h2>問題点：How（どうテストするか）が含まれている</h2>
<p>現在のテストメソッド名にはHow（どうテストするか）が書かれているため、読みにくく感じます。
🤔 「Orderが正常終了ってことは...ユーザが商品を購入できるってことか...」
🤔 「Orderが在庫切れエラーを返すってことは...ユーザが商品を購入できないってことか...」</p>
<p>つまり、読んだときに脳内でWhat（どういう振る舞いをテストしているか）へ言い換えることで、脳に負荷がかかり、読みづらく感じます。</p>
<p>この命名規則について「単体テストの考え方/使い方」でも以下のように述べられています。</p>
<blockquote>
<p>私はこの十年近く様々な命名規則を試してきたのですが、その中でも、非常に有名で、おそら
く、もっとも役に立たないのが次のような命名規則です :
{テスト対象メソッド }<em>{事前条件 }</em>{想定する結果 }</p>
</blockquote>
<p>厳密な命名規則に従うのではなく、それぞれのテストに応じて適切な名前を考えるべきだという主張なのですが、 個人的には「もっとも役立たない」というのは言い過ぎかなと感じています。
命名に迷ったら規則に従うくらいの温度感で運用することで、チームメンバー間の命名のレベルを揃えられるメリットを考慮すれば非常に有用だと考えています。</p>
<h2>最終的な改善案：Whatを書く</h2>
<p>テストメソッド名には「How(どうテストするか)」ではなく「What(振る舞い)」を書きましょう。
「How」の部分（正常終了の確認、エラー発生の確認）はテストメソッド内に実装します。</p>
<pre><code class="language-go">func Test_商品の在庫がある場合_ユーザは商品を購入できる(t *testing.T) { 
   // ここで正常終了しているかをみる
}

func Test_商品の在庫がない場合_ユーザは商品を購入できない(t *testing.T) {
    // ここで在庫エラーが発生しているかをみる
}
</code></pre>
<p>さらには、非エンジニア（ドメイン・エキスパート）にも伝わるように端的に書くことが重要だと「単体テストの考え方/使い方」では述べられています。</p>
<h2>非エンジニアにも分かる名前にする理由</h2>
<p>「テストコードは開発者だけが見るものなので、非エンジニアに分かる名前にしなくても良いのでは？」という意見もあるでしょう。</p>
<p>単体テストの考え方/使い方では、この意見について以下のように述べられています。</p>
<blockquote>
<p>なぜなら、暗号めいた名前は開発者なのかどうかにかかわらず誰にでも認知的負荷をかけるもの
だからです。このような名前はテスト・ケースが実際に何を検証するのか、そして、このテ
スト・ケースの内容がどのようなビジネス要求と関係しているのかを把握するのに開発者に
対して余計な認知的負荷をかけることになります。</p>
</blockquote>
<p>開発者だけがわかるコードを書いても結局は</p>
<ul>
<li>テストで何を検証し、どのような要件と対応しているか把握しにくくなる</li>
<li>コードのメンテナンスが困難になる<ul>
<li>書いた本人でも数ヶ月後には読めなくなる</li>
</ul>
</li>
<li>他のエンジニアがコードレビューする際に理解しづらい</li>
</ul>
<p>となってしまい、最初に記述したテストコードに関わる「辛さ」</p>
<blockquote>
<ul>
<li>テストコードはあるが中身が理解できていないため、テストの結果が信用できない、十分にテストできている自信がない</li>
<li>他の人が書いたテストコードをレビューしていて、内容がよくわからないけれどなんとなくApproveしてしまった</li>
<li>どういうテストをしているかよくわからないけどCIが落ちてしまうとPRを出せないので、テストがPASSするように雰囲気でテストコードを直した</li>
</ul>
</blockquote>
<p>につながるわけです。</p>
<h2>わかりやすいテストメソッド名をつけるのは難しい</h2>
<p>正直に言うと、わかりやすいテストメソッド名をつけるのって、めちゃくちゃ難しいです。</p>
<p>なぜでしょうか？それは、一度キーボードから手を離して、モニターから目を上げて、こう自分に問いかける必要があるからです。</p>
<p><strong>「我々は一体、何を作ろうとしているんだ？」</strong></p>
<p>コードの海に溺れそうになりながら、ふと立ち止まって考えてみてください。目の前のメソッドは、誰のどんな悩みを解決しようとしているのでしょうか？</p>
<ul>
<li>そのオブジェクトは、システムの中でどんな役割を担っているのか？</li>
<li>今やっているタスクは、本当にユーザーを幸せにするのか？</li>
<li>この業務領域で使われているドメイン知識を、我々ははちゃんと知っているのか？</li>
</ul>
<p>テストメソッド名を真剣に考えるって、実はこういうことなんです。</p>
<p>そして気づくんです。わかりやすいテストメソッド名を追求していくと、不思議なことが起こることに。</p>
<p>テストコードが読みやすくなる。プロダクションコードも自然と整理される。チームでの会話も、なんだかスムーズになっていく。</p>
<p><strong>たかがテストメソッド名、されどテストメソッド名。</strong></p>
<p>小さな改善が、開発チーム全体の幸せにつながっていく。そんな体験を、ぜひあなたにも味わってもらいたいのです。</p>
<h2>まとめ</h2>
<p>今回はテストメソッド名の話を書かせていただきました。</p>
<ul>
<li>どのようなテストをしているか把握しやすくするためにテストメソッド名をわかりやすくしよう</li>
<li>非エンジニアでも理解できるくらいまで書いてみよう</li>
<li>テストメソッドの名前を改善するだけでテストコードの品質が向上できるのであれば、コストパフォーマンスが良い改善方法だと思いませんか？</li>
</ul>
<p>この記事でテストメソッド名の重要性を感じ取っていただければ幸いです。</p>
<p>今回参考にさせていただいた書籍「単体テストの考え方/使い方」は全400ページくらいありますが、テストメソッド名に割かれている説明は6ページくらいで決して長くはありません。
ですが、個人的にはこの書籍で一番重要で学びになる箇所ではと感じています。</p>
<p>単体テストで悩みがあれば、ぜひ「<a href="https://book.mynavi.jp/ec/products/detail/id=134252">単体テストの考え方/使い方</a>」を読んでみてください。
きっとあなたの悩みに対する答えを与えてくれるはずです！</p>
<h2>参考文献</h2>
<p><a href="https://book.mynavi.jp/ec/products/detail/id=134252">https://book.mynavi.jp/ec/products/detail/id=134252</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uehara/2025-08-25-particular-about-unit-test-case-name/thumb.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Automating Tasks with Google Sheets and MCP – Get It Done with Just One Line!]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-06_Experience_the_MCP-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-06_Experience_the_MCP-en/</guid>
            <pubDate>Fri, 22 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Tired of doing the same tasks over and over again? Let AI take over. I tried linking Google Sheets with AI to solve the hassle with a single line of natural language.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hi, my name is Jongseok and I&#39;m an Android app developer at KINTO Technologies.</p>
<p>One day, I was sorting through documents and transferring content into Google Sheets when it suddenly struck me.</p>
<p>&quot;This is way too tedious.&quot;</p>
<p>Typing the same kind of content over and over again, adjusting the cell formatting...<br>it&#39;s not difficult, but I wondered if this is really something a human being should be doing. That thought stuck with me.</p>
<p>That&#39;s when I started looking into whether I could automate this process using the AI tools I&#39;ve been seeing lately.<br>And what I came across was <strong>MCP (Model Context Protocol)</strong>.</p>
<p>After trying out a few different tools,<br>I found that I could actually control Google Sheets with just a single line of natural language. Honestly, it was kind of mind-blowing.</p>
<h2>What is MCP?</h2>
<p>MCP stands for Model Context Protocol. The name may sound a bit technical, but the concept is actually very intuitive.</p>
<p>Simply put, MCP is like an &quot;interpretation coordinators&quot; that allows AI to freely operate real-world tools and services. In this article, I&#39;ll focus on examples of integration with Google services.</p>
<p>To put it more simply, services we use every day, such as Gmail, Google Drive, and Google Sheets, are operated by people clicking on them. MCP enables AI to handle those tasks for you.</p>
<p>For example,</p>
<p>let&#39;s say you want to record something in Google Sheets. Normally, you would:</p>
<ol>
<li>Open the sheet</li>
<li>Click on a cell</li>
<li>Enter your data</li>
</ol>
<p>With MCP, the AI can handle all of that automatically.</p>
<p>Next, let&#39;s take a look at an actual demo:</p>
<blockquote>
<p><a href="https://www.kinto-technologies.com/products/">https://www.kinto-technologies.com/products/</a> Using the following web page as a reference, create a new Google Spreadsheet and organize the products and services listed.</p>
</blockquote>
<p>The video below shows what happens when MCP processes a request like above.</p>
<p>![demo](/assets/blog/authors/jongseok/mcp/demo.gif =1080x)</p>
<p>With just one command like this, the AI calls the Google Sheets API and automatically fills in the data into the sheet. Organizing data became incredibly easy.</p>
<h2>Is MCP Trending Right Now?</h2>
<p>Being technically possible doesn&#39;t always mean people are actually interested in it. So, I checked Google Trends to see how much attention the term MCP is getting.</p>
<p><img src="/assets/blog/authors/jongseok/mcp/mcp_graph.png" alt="MCP Graph"></p>
<p>Looking at the data from March to May 2025, there&#39;s a clear spike in search volume especially from mid-April.</p>
<p>This suggests that interest in MCP has moved beyond curiosity. Not only developers but also general users are actually starting to use MCP. I guess I jumped on that wave, too.</p>
<p>So, if you want to try out MCP, what are the options?</p>
<p>To make use of MCP, you need an environment where natural language commands can be received and executed.<br>There are several AI agents that can work with MCP, such as Claude (Anthropic), but the one I chose this time was Cursor, a code editor with built-in AI features.</p>
<h2>What is Cursor?</h2>
<p>Put simply, Cursor is a code editor with built-in AI features. If you write a code request in natural language, the AI will automatically offer suggestions, edits, and explanations.</p>
<p>It also makes writing MCP commands a lot smoother.</p>
<blockquote>
<p>If you&#39;re curious and want to learn more about Cursor, I recommend checking out their official website and blog. <a href="https://www.cursor.sh">https://www.cursor.sh</a></p>
</blockquote>
<h2>Now, Let&#39;s Get Started Together</h2>
<p>To automate tasks with MCP and Google Sheets, there are a few things you need to set up first.<br>But don&#39;t worry, no complicated knowledge is required. We&#39;ll go step by step.</p>
<h3>Preparation</h3>
<p>Before getting started with automation, make sure you&#39;ve got the following:</p>
<ul>
<li>Install an editor that supports MCP (we&#39;ll be using Cursor this time)</li>
<li>A Google account</li>
<li>Google services you want to automate (Drive, Sheets, etc.)</li>
<li>MCP Server(<a href="https://github.com/xing5/mcp-google-sheets">https://github.com/xing5/mcp-google-sheets</a>) ←This is what we will use in this article.</li>
<li>A simple natural language command you want to try</li>
</ul>
<h2>Google Cloud Platform (GCP) Setup</h2>
<p>First, we&#39;ll configure GCP to use the Google Sheets API.</p>
<h3>1-1. Create a project</h3>
<p>Go to the <a href="https://console.cloud.google.com">GCP</a> Console and create a new project.</p>
<p>![Create Project1](/assets/blog/authors/jongseok/mcp/mcp_01_01.png =1080x)</p>
<p>![Create Project2](/assets/blog/authors/jongseok/mcp/mcp_01_02.png =360x)</p>
<h3>1-2-1. Create a service account</h3>
<p>To let the AI access Google Sheets, create a Service Account.</p>
<p>![Service Account1](/assets/blog/authors/jongseok/mcp/mcp_02_01.png =1080x)</p>
<p>Click + Create service account under Service accounts.</p>
<p>![Service Account2](/assets/blog/authors/jongseok/mcp/mcp_02_02.png =360x)</p>
<p>Enter the Service account name and click Done.</p>
<h3>1-2-2. Add permissions</h3>
<p>![Service Account3](/assets/blog/authors/jongseok/mcp/mcp_02_03.png =1080x)</p>
<p>Go to Permissions -&gt; Manage access -&gt; Role, and set it to Editor, then click Save.</p>
<h3>1-2-3. Add Keys</h3>
<p>![Service Account4](/assets/blog/authors/jongseok/mcp/mcp_02_04.png =1080x)</p>
<p>Go to Keys -&gt; click Add key, select JSON and click Create. Then the authentication file will be downloaded.</p>
<p>![Service Account5](/assets/blog/authors/jongseok/mcp/mcp_02_05.png =1080x) The contents of the file will look something like this. That&#39;s it. You&#39;ve successfully created your service account. We will use this file <strong>client_email</strong> later.</p>
<h3>1-3. Set up Google services</h3>
<p>![Service Account5](/assets/blog/authors/jongseok/mcp/mcp_03_01.png =1080x)</p>
<p>Click Library under APIS &amp; Services.</p>
<p>![Service Account5](/assets/blog/authors/jongseok/mcp/mcp_03_02.png =1080x) You can see the services we want to use this time.</p>
<table>
<thead>
<tr>
<th align="center">Drive</th>
<th align="center">Sheets</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/jongseok/mcp/mcp_03_03.png" alt="Service Drive"></td>
<td align="center"><img src="/assets/blog/authors/jongseok/mcp/mcp_03_04.png" alt="Service Sheets"></td>
</tr>
</tbody></table>
<p>Click Enable for the two services.</p>
<h3>2-1. Set up Google Drive</h3>
<p>![Google Drive1](/assets/blog/authors/jongseok/mcp/mcp_04_01.png =1080x) Go into Google Drive and create a folder to use AI. Examples) Created &quot;Sheets For MCP&quot; -&gt; Then click &quot;Share.&quot;</p>
<p>![Google Drive2](/assets/blog/authors/jongseok/mcp/mcp_04_02.png =360x) In the top field, copy and paste the <strong>client_email</strong> from the JSON file made in <a href="#1-2-3.-keys%E8%BF%BD%E5%8A%A0">1-2. Create a service account.</a></p>
<p>![Google Drive3](/assets/blog/authors/jongseok/mcp/mcp_04_03.png =360x)</p>
<p>Check that it has been added and click Done.</p>
<h3>2-2. Get the Folder ID</h3>
<p>![Google Drive4](/assets/blog/authors/jongseok/mcp/mcp_04_04.png =1080x) Once the settings are complete, obtain the <strong>Folder ID</strong>.</p>
<p>Everything you need for the setup is now ready to go. Let&#39;s move on to the actual setup and use it!</p>
<h2>MCP Setup (Cursor &amp; Terminal)</h2>
<p>This can also be done on Windows, but the instructions here are for Mac. There&#39;s a way to use a simple cloud server for MCP setup, but this time we&#39;ll use LocalServer.</p>
<p>First, we need uv and uvx. Open your terminal and get these installed. <code>uvx</code> appears to be part of <code>uv</code>, a fast Python package installer and resolver.</p>
<h3>1. Install uv and uvx &amp; Set Environment Variables</h3>
<pre><code class="language-bash">curl -LsSf https://astral.sh/uv/install.sh | sh
</code></pre>
<p>![Terminal1](/assets/blog/authors/jongseok/mcp/mcp_05_01.png =1080x) Installation completed successfully.</p>
<p>Enter values according to your environment.</p>
<pre><code class="language-bash">export SERVICE_ACCOUNT_PATH=&quot;/path/to/your/service-account-key.json&quot;
export DRIVE_FOLDER_ID=&quot;YOUR_DRIVE_FOLDER_ID&quot;
</code></pre>
<p>Set the two prepared <strong>SERVICE_ACCOUNT_PATH and DRIVER_FOLDER_ID</strong> as environment variables.</p>
<h3>2. Project Setup</h3>
<pre><code class="language-bash">git clone https://github.com/yourusername/mcp-google-sheets.git
cd mcp-google-sheets
</code></pre>
<p>Clone the project and move to the cloned directory.</p>
<p>You can place it anywhere you like, but for easier management, I moved the <strong>service_account.json</strong> file under Project.</p>
<p>![Terminal2](/assets/blog/authors/jongseok/mcp/mcp_05_02.png =360x)</p>
<p>Let&#39;s start up the server.</p>
<pre><code class="language-bash">uv run mcp-google-sheets
</code></pre>
<p>![Terminal 3](/assets/blog/authors/jongseok/mcp/mcp_05_03.png =1080x) The local server is now ready.</p>
<p>Finally, let&#39;s configure the MCP for use.</p>
<h3>3. MCP Setup</h3>
<p>![Cursor1](/assets/blog/authors/jongseok/mcp/mcp_06_01.png =1080x) To set up MCP, click the ⚙️ icon in the top right corner. ![Cursor2](/assets/blog/authors/jongseok/mcp/mcp_06_02.png =1080x) In the MCP section, click + Add new global MCP Server. ![Cursor3](/assets/blog/authors/jongseok/mcp/mcp_06_03.png =1080x) You&#39;ll see a input screen.</p>
<pre><code class="language-bash">{
   &quot;mcpServers&quot;: {
     &quot;mcp-google-sheets-local&quot;: {
       &quot;command&quot;: &quot;uv&quot;,
       &quot;args&quot;: [
         &quot;run&quot;,
         &quot;--directory&quot;,
         &quot;/Users/jongseok.bae/MCP/GoogleServices/mcp-google-sheets&quot;,
         &quot;mcp-google-sheets&quot;
       ],
       &quot;env&quot;: {
         &quot;SERVICE_ACCOUNT_PATH&quot;: &quot;/Users/jongseok.bae/MCP/GoogleServices/mcp-google-sheets/service_account.json&quot;,
         &quot;DRIVE_FOLDER_ID&quot;: &quot;1n4HUOiglwjiTKHcw8jSATYcPtCRqNn6O&quot;
      }
    }
  }
}
</code></pre>
<ul>
<li>In <code>args</code>, enter the path to your project directory.</li>
<li>In <code>env</code>, enter SERVICE_ACCOUNT_PATH and DRIVE_FOLDER_ID. Input the above values and save.</li>
</ul>
<p>![Cursor4](/assets/blog/authors/jongseok/mcp/mcp_06_04.png =1080x) A 🟢 light means you&#39;re all set. You should now see a list of available MCP tools like list_spreadsheets, create_spreadsheet, get_sheet_data, etc.</p>
<p>Your setup is now complete. Good job!</p>
<p>Now, let&#39;s try giving natural language commands and see how it automatically updates Google Sheets.</p>
<h2>Try It Out</h2>
<p>Now that we&#39;re ready, it&#39;s time to put it into action.</p>
<p>For example, simply give a natural language command like:</p>
<blockquote>
<p><a href="https://news.google.com/home?hl=en-US&gl=US&ceid=US:en">https://news.google.com/home?hl=en-US&amp;gl=US&amp;ceid=US:en</a> Create a new Google Spreadsheet named Today&#39;s Google News.<br>Organize the top news articles listed into three columns: title, summary, and URL.</p>
</blockquote>
<p>In fact, here&#39;s what&#39;s happening behind the scenes:</p>
<ol>
<li>You enter a command in Cursor</li>
<li>The MCP Server receives the command</li>
<li>It analyses the natural language and converts it to Google Sheets API</li>
<li>A spreadsheet is automatically created, and the data is inserted</li>
</ol>
<p>![Cursor4](/assets/blog/authors/jongseok/mcp/mcp_06_05.png =720x) This is what it looks like behind the scenes when MCP runs. (Analyzes the natural language and executes the process) ![demo2](/assets/blog/authors/jongseok/mcp/demo2.gif =1080x)</p>
<p>As a result, the spreadsheet described earlier was automatically generated!</p>
<h2>Summary</h2>
<p>The integration between MCP and Google Sheets isn&#39;t just a technical introduction.</p>
<p>The experience of running tasks through a single natural language command<br>is no longer futuristic, <strong>it&#39;s fully achievable right now</strong>.</p>
<p>Of course, <strong>there are still things to consider, such as security and access control.</strong><br>However, by starting with small automations gave me a strong sense of just how much more efficient work can become.</p>
<p>If you’re thinking about automating your tasks, feel free to use this case study as a reference and give it a try.<br>You might be surprised how quickly you can start making changes.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/jongseok/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[FEエンジニアがリアルイベント参加でUX課題を改善した話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent/</guid>
            <pubDate>Fri, 22 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTO FACTORYのエンジニアがリアルイベントで発見したUX課題を改善。現場での学びと対策は、あなたのプロダクト改善にも活かせます。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、<a href="https://factory.kinto-jp.com/">KINTO FACTORY</a> （以下 FACTORY）のフロントエンド開発を担当しているきーゆのです。</p>
<p>今回は、FACTORYのリアルイベントにFEエンジニアが参加してきた件についてまとめてみようと思います。</p>
<p>ここでいうリアルイベントは技術系のカンファレンスとかではなく、<strong>車好きのオーナーが広い敷地に集う、車好きのためのミーティングにてFACTORYプロモーションブースをFEエンジニアがお手伝いしてきた</strong>というお話です。</p>
<p>ITやネットにあまり馴染みのない方々も参加するイベントなので、我々エンジニアとは異なった視点のフィードバックを得られる、とても貴重な機会でした。</p>
<h1>イベントの展示内容</h1>
<p><img src="/assets/blog/authors/keyuno/FACTORY_ReportOfRealEvent/boothImage.jpg" alt=""></p>
<p>我々のプロダクトは「愛車にメーカー純正オプションをWEB上からの申し込みで後付けできるアップグレードサービス」を提供しています。
基本はWEBや<a href="https://factory.kinto-jp.com/magazine/">マガジン記事</a>などで商品について知っていただき購入していただく流れのため、購入前に実車両を見る機会はほとんどありません。
そのため、このようなイベントではWebでは見ることのできない施工済みの車両も展示します。
今回は「メーターデザインカスタマイズ」を施工済みのカローラクロスを展示しました。</p>
<p><img src="/assets/blog/authors/keyuno/FACTORY_ReportOfRealEvent/meterDesignCustomize.jpg" alt=""></p>
<p>「メーターデザインカスタマイズ」は、車のメーターパネルに新しいデザインを追加できる商品です。
※画像はクラシックギア</p>
<p>実際に施工されたメーターパネルを見ると、想像以上にワクワクしてきますね！
<strong>免許を持っていない方が思わず教習所に入校手続きをしてしまう</strong>くらい、魅力に溢れています。</p>
<h1>FACTORYの紹介とノベルティ</h1>
<p>イベントでは来場されたオーナーの皆様にFACTORY上で車両情報を登録していただくまでを案内します。
最後までご対応いただいた方にはハズレなしのガチャガチャに挑戦していただき、ノベルティをお渡しします。</p>
<p><img src="/assets/blog/authors/keyuno/FACTORY_ReportOfRealEvent/novelties.jpg" alt=""></p>
<p>ペンライト、タンブラー、オリジナル車検証ケースなど、ノベルティは非常に豪華！
今回用意したオリジナル車検証ケースは、弊社のクリエイターがデザインした至極の一品です！
<strong>ペーパードライバーが思わずカローラシリーズを契約してしまう</strong>くらい、魅力に溢れています。</p>
<h1>実際の操作から見えてきたこと</h1>
<p>ここからエンジニアの本領発揮です。
実際にオーナーの皆様にFACTORYを触っていただくと、以下の課題が見えてきました。</p>
<ul>
<li>案内用のQRコードから会員登録を進めると、途中でFACTORYへの導線が切れてしまう</li>
<li>会員登録時に「利用規約はこちら」のリンクをタップすると、利用規約ページから会員登録に戻る導線がない</li>
<li>メールに届く認証URLをタップ後に誤ってページを閉じると、再度会員登録の導線に戻れない</li>
</ul>
<p>我々エンジニアが特に意識せずに進めていたフローでも、離脱に繋がる要素が散りばめられていることが見えてきました。
特に会員登録導線は、動作確認をする際の前提条件でアカウントを事務的に用意することが多いため、<strong>障壁を感じることなく会員登録することに慣れきってしまっていた</strong>のです。</p>
<h1>見えてきた課題に対するアクション</h1>
<p>イベントから帰ってきてすぐに、課題に対してアクションを実施しました。</p>
<p>まず、案内用のQRコードのURLは我々エンジニアが期待していたものと異なるURLが利用されていたことが判明しました。
そのため、正常に動作するURLを共有することでこの課題を解決しました。</p>
<p>利用規約ページから会員登録に戻る導線がない件と認証URLタップ後にページを閉じた際に戻る導線がない件は、担当チームに状況と修正案を連携しました。</p>
<h1>まとめ</h1>
<p>実際にイベントに参加してサービスに触れていただくと、我々が気づかない場所でもユーザーは困っているということがよくわかりました。</p>
<p>特にエンジニアはプロダクトを触る機会が非常に多く、サービスフローの正解パターンを最初からわかっています。このように正解パターンを知った状態に慣れてしまうと、今回のように灯台下暗しのような事態が起こります。
いくらユーザーのことを想定して開発していても、この状態のままでは自然とユーザーとの距離は開いてしまいます。</p>
<p>もし皆様が担当しているプロダクトでユーザーとコミュニケーションを取る機会があるのであれば、参加してみませんか？
今一度原点に立ち返って「誰のために開発しているのか」、「何のために開発しているのか」を振り返る意味でも、機会があるのならば参加するべきだと思います。</p>
<p>もしそのような機会がなくても、社内の無関係メンバーに依頼してユーザーテストをしてみたり、端末を変えてみたりすることでも普段とは異なる気づきを得られるかと思います。</p>
<p>百聞は一見にしかず、です。これぞユーザーファーストではないでしょうか。</p>
<p>以上、読んでいただきありがとうございました。</p>
<h1>おまけ</h1>
<p>帰りの新幹線で隣になった初対面の方と、ハイボール片手にお菓子交換をしました。
これも出張の醍醐味？かもしれません(笑)</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/keyuno/FACTORY_ReportOfRealEvent/thumbnail.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Agent Store v1.0 Launch: An In-House Platform to Streamline Internal Agent Development]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-30-Agent-Store-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-30-Agent-Store-en/</guid>
            <pubDate>Thu, 21 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A Quick Look at Agent Store v1.0]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>Introduction</h2>
<p>Hi, I&#39;m Alex from the AI First Group. As AI technology rapidly advances, demand for agent development is growing fast. However, getting started with efficient agent development can be challenging. Information about the latest tools is fragmented, and development resources are limited. To tackle this, we&#39;ve launched Agent Store v1.0, a platform to share internally developed AI agents and centralize our technical know-how.</p>
<h2>Purpose of Agent Store</h2>
<p>The Agent Store has two primary purposes:</p>
<ol>
<li><strong>Boost in-house agent development efficiency</strong><ul>
<li>Shorten development cycle by reusing existing agents</li>
<li>Enables rapid builds using templates</li>
</ul>
</li>
<li><strong>Build technical know-how</strong><ul>
<li>Centralize management of in-house Agent-related technologies</li>
<li>Facilitate <em>yokoten</em> (horizontal deployment) of best practices</li>
</ul>
</li>
</ol>
<h2>Usage Patterns and Target Users</h2>
<p>Agent Store is a platform where employees can freely develop, share, download, and reuse AI agents. For reusability, we envision it like an app store—users can download agents and deploy them in their own environments.</p>
<p>The Agent Store is made up of the following components:</p>
<ul>
<li>Github repository for sharing internally developed agents</li>
<li>How CI/CD works for agent development</li>
</ul>
<p><img src="/assets/blog/authors/alex.q/agent-store/repository.png" alt="Agent StoreのGithubリポジトリ"> <em>Github repository for Agent Store</em></p>
<h3><strong>Usage Format</strong></h3>
<p>Agent Store v1.0 primarily supports AWS Bedrock agents.</p>
<p>We designed the agent’s CI/CD process with an Infrastructure as Code (IaC) approach in mind. Agents shared through the Agent Store are stored as SAM templates in its GitHub repository, making them ready for deployment via CloudFormation.</p>
<p>Users looking to reuse an agent can download its SAM template and deploy it to their environment using CloudFormation. Deployment is automated using Github Actions.</p>
<p>Those who want to build new agents can also jumpstart development using the blank SAM templates available in the Agent Store.</p>
<h3><strong>Target Users (v1.0)</strong></h3>
<ul>
<li>Engineers looking to start building agents using AWS Bedrock</li>
<li>Engineers wanting to share Bedrock-built agents within their organization</li>
<li>Engineers looking to discover and utilize existing agents</li>
</ul>
<h3><strong>Expected Use Cases</strong></h3>
<p>Agent Store is designed for the following scenarios:</p>
<ol>
<li><strong>Creating new AI agents</strong><ul>
<li>Speed up development using Agent Store&#39;s CI/CD flow</li>
</ul>
</li>
<li><strong>Sharing AI agents</strong><ul>
<li>Share custom-built agents across your company</li>
</ul>
</li>
<li><strong>Reusing shared AI agents</strong><ul>
<li>Equip existing products with AI agents</li>
<li>App development to streamline internal operations</li>
<li>Avoid building from scratch and boost development efficiency</li>
</ul>
</li>
<li><strong>AI agent PoC</strong><ul>
<li>Deploy existing agents to quickly conduct PoC</li>
<li>Shortening the time required for effect verification</li>
</ul>
</li>
<li><strong>Acquire know-how for developing AI agents</strong><ul>
<li>Learn best practices by looking at similar cases</li>
<li>Lowering technical barriers</li>
</ul>
</li>
</ol>
<h2>Agent Development, Sharing, and Reuse Flow Using Agent Store</h2>
<ol>
<li><p><strong>Flow for developing a new agent</strong> The flow begins by retrieving a blank SAM template from the Agent Store repository, filling it out, and deploying it.  </p>
<ul>
<li>For more information about SAM, see <a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_Bedrock.html">here</a>.</li>
<li>If you&#39;re using an agent&#39;s Action Group, make sure to fill out and deploy the templates in this order: ①Lambda, ②Agent. <img src="/assets/blog/authors/alex.q/agent-store/cicd_1.png" alt=" Flow for developing a new agent"></li>
</ul>
</li>
<li><p><strong>Flow for sharing agents</strong> Prepare a SAM template for the agent you developed and submit a pull request to the Agent Store repo. The agent reviewer will check the content and merge it if there are no problems. <img src="/assets/blog/authors/alex.q/agent-store/cicd_2.png" alt="Agent sharing flow"></p>
</li>
<li><p><strong>Flow for reusing a shared agent</strong> The overall process is similar to developing a new agent, but it begins by retrieving the SAM template of a shared agent from the Agent Store. After making any needed changes or additions, you can move on to deployment. <img src="/assets/blog/authors/alex.q/agent-store/cicd_3.png" alt="Agent reuse flow"></p>
</li>
</ol>
<h2>Agent Architecture</h2>
<p>Here&#39;s the architecture of the agent. After deployment, the Bedrock agent can invoke and execute Lambda functions configured as Action Groups when needed. Stack management is handled through CloudFormation. <img src="/assets/blog/authors/alex.q/agent-store/architecture_1.png" alt="Agent Architecture"></p>
<p>You can also build multi-agent collaboration,where multiple agents work together. <img src="/assets/blog/authors/alex.q/agent-store/architecture_2.png" alt="Multi-agent architecture"></p>
<h2>Future Development Plans</h2>
<p>To boost adoption of the Agent Store, we&#39;re planning a variety of internal study sessions, workshops, and hackathons.</p>
<p>Currently, v1.0 is designed for engineers with AWS Bedrock experience (Type A), but we plan to expand our target users in stages. </p>
<table>
<thead>
<tr>
<th>User type</th>
<th>Description</th>
<th>Support status</th>
</tr>
</thead>
<tbody><tr>
<td>Engineer type A</td>
<td>Engineers experienced with AWS Bedrock</td>
<td>Supported in v1.0</td>
</tr>
<tr>
<td>Engineer type B</td>
<td>Engineers interested in agent platforms other than AWS Bedrock</td>
<td>In planning</td>
</tr>
<tr>
<td>Non-engineers / Beginners</td>
<td>Employees with no coding or development experience</td>
<td>In planning</td>
</tr>
</tbody></table>
<h2>Summary</h2>
<p>Agent Store v1.0 is a platform designed to streamline agent development and foster knowledge sharing. Currently, it&#39;s available to AWS Bedrock users, but we plan to expand support to a broader user base and integrate with a wider range of agent frameworks. To make the most of our in-house AI development resources and accelerate innovation, we&#39;re committed to actively expanding and evolving Agent Store.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/alex.q/agent-store/agent-store-cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Is Generative AI Leading Users to Tech Support Scam Websites?]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-30-Gen-AI-Lead-to-a-Support-Scam-Site-en /</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-30-Gen-AI-Lead-to-a-Support-Scam-Site-en /</guid>
            <pubDate>Wed, 20 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Is Generative AI Leading Users to Tech Support Scam Websites?]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello! I&#39;m <a href="https://blog.kinto-technologies.com/posts/2025-02-26-newcomers-introduction-oct-nov/#%E3%81%9F%E3%81%AA%E3%81%A1%E3%82%85%E3%83%BC">Tanachu</a> from the Security and Privacy Group.</p>
<p>In this article, I&#39;ll cover a recent security incident at our company involving a generative AI chat tool.  Although the technical issue is not particularly new, I would like to introduce it because the circumstances under which it occurred were somewhat unusual.</p>
<h1>Overview of the Case</h1>
<p>Our company has created an environment where all employees can easily use the AI ​​chat tool.  One day, while using the tool, an employee asked the AI a question. When they clicked on one of the links provided in the response, it led to a support scam website.</p>
<p><img src="/assets/blog/authors/tanachu/2025-05-30_Gen-AI-Lead-to-a-Support-Scam-Site/Gen-AI-Chat-Demo.png" alt="Gen-AI-Chat-Demo.png"> <em>Image of a generative AI chat</em></p>
<p><img src="/assets/blog/authors/tanachu/2025-05-30_Gen-AI-Lead-to-a-Support-Scam-Site/Support-Scam-Site-Demo.png" alt="Support-Scam-Site-Demo.png"> <em>Image of a support scam website</em></p>
<p>Even though we had security products in place, they couldn&#39;t block the messages, and the user was ultimately led to a support scam website. Fortunately, thanks to the employee&#39;s calm and quick judgment, we were able to avoid any serious damage.</p>
<p>This experience was a clear reminder that, just like with regular web searches, the links provided by generative AI can&#39;t always be trusted.</p>
<h1>Cause of the Incident</h1>
<p>To find out why the generative AI ended up suggesting a scam support website, we looked up the website it pointed to using the internet archive service <a href="https://web.archive.org/">WAYBACK MACHINE</a>. We found that the website had actually provided what seemed to be legitimate content in the past.</p>
<p>We also discovered several other websites that listed this website as a reference.</p>
<p>This suggests that the generative AI may have learned from the past content of the website in question, along with information about the websites it had introduced. As a result, a hallucination-like phenomenon may have occurred, which led the AI to present incorrect information and possibly suggest a link to a scam support website.</p>
<p>Here&#39;s a summary of how the content on the problematic websites has changed, based on the investigation results.</p>
<ul>
<li><p><strong>2012 to early 2018</strong> There was content history matching the domain name during this period. It is assumed that the site was considered reliable at this time.</p>
</li>
<li><p><strong>Late 2018 to early 2019</strong> A domain management service sales page was shown, suggesting that the owner might have let go of the domain.</p>
</li>
<li><p><strong>Since late 2019</strong> The domain has shown a history of unrelated content, such as medical issues, online casinos, fake warning screens, and domain parking.</p>
</li>
</ul>
<p>For information on why security products were unable to detect the virus, check out &quot;<a href="https://blog.kinto-technologies.com/posts/2025-05-30-Gen-AI-Lead-to-a-Support-Scam-Site/#%E4%BB%98%E9%8C%B2%EF%BC%9A%E8%AA%BF%E6%9F%BB%E3%83%A1%E3%83%A2">Appendix: Research Notes</a>.&quot;</p>
<h1>Measures for Similar Cases</h1>
<p>At this point, it seems extremely difficult to take fundamental steps against cases like this, for the following reasons.</p>
<h4><strong>Challenges in Learning Generative AI</strong></h4>
<p>One possible reason behind this incident is that the generative AI may have learned from older content on the website in question, as well as from other websites it featured. Even if the content of a website is later updated, it&#39;s unlikely that those changes will be reflected in the AI&#39;s responses, including any related security risks.</p>
<h4><strong>The Best Prevention is Awareness</strong></h4>
<p>What we can take away from this case is that links suggested by generative AI aren&#39;t always safe. That&#39;s why it&#39;s so important to learn from real examples and understand how to respond if you actually encounter one.</p>
<h1>Summary</h1>
<p>This case was a somewhat unusual one, showing that links presented by generative AI are not necessarily safe. Depending on the characteristics of the fraudulent site, it may be difficult for security products to detect it.  Also, if the site content is changed after the generative AI has learned, it may not be able to reflect the risk.</p>
<p>There may not be a solid solution at this point, but learning from cases like this can be a good reminder to stay alert about security when using generative AI.</p>
<h1>Appendix: Research Notes</h1>
<p>These are notes on investigating into why the issue wasn&#39;t picked up by security products. Please keep in mind this was a simple research, so it&#39;s just for reference.</p>
<h4><strong>1. Security Vendor Detection Status</strong></h4>
<p>We looked up the domain of the problematic website using our security tools and <a href="https://www.virustotal.com/gui/home/search">VirusTotal</a>. Almost all the vendors flagged it as &quot;safe.&quot;</p>
<h4><strong>2. Website Source Code</strong></h4>
<p>After checking the source code of the website in question, it seems there&#39;s a mechanism in place that sends a request to &quot;domaincntrol[.]com&quot; (note the missing o after the c) and then uses the response to dynamically decide where to direct the visitor.</p>
<p>We tried accessing the site a few times in a safe environment, and found that it redirected us to various other websites, including support scam pages and domain parking websites. This may have allowed the malware to slip past detection by security vendors.</p>
<h4><strong>3. Hosting Environment of Support Scam Website</strong></h4>
<p>The final support scam website is hosted on the &quot;web.core.windows[.]net&quot; domain, presumably utilizing the Microsoft Azure environment. Not just limited to Microsoft Azure, scam websites hosted on cloud services are generally hard to block with security products. That&#39;s because blocking cloud service domains outright isn&#39;t realistic, given how easy it is to set them up and how much it could impact legitimate business operations.</p>
<p>*As of this article&#39;s publication, we&#39;ve confirmed that this support scam website has been taken down from Microsoft Azure.</p>
<h4><strong>4. Survey Results Using PublicWWW</strong></h4>
<p>Using a tool called <a href="https://publicwww.com/">PublicWWW</a>, which lets you search website source code, we searched the distinctive string &quot;domaincntrol[.]com/?orighost=&quot; found on the problematic websites. The search revealed that this string appears in the code of over 20,000 websites. We also checked a few of those websites in more detail and found that they showed the same behavior — redirecting users to scammy support websites.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[改善DAY & 勉強会を活用したCWVスコア改善]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-20-factory-improve-cwv-score/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-20-factory-improve-cwv-score/</guid>
            <pubDate>Wed, 20 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[CWVスコアを向上したお話]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは。ご覧いただきありがとうございます！</p>
<p><a href="https://factory.kinto-jp.com/">KINTO FACTORY</a>（以下 FACTORY）という今お乗りのクルマをアップグレードできるサービスで、フロントエンド開発をしている Nakamoto です。</p>
<p>今回は、FACTORY 開発Gで取り入れた<strong>改善DAY</strong>や、社内での<strong>フロントエンド勉強会</strong>を活用して、ページパフォーマンス（CWV）を改善した事例を紹介させていただきます。</p>
<h1>改善DAYとフロントエンド勉強会</h1>
<h3>改善DAY</h3>
<p>FACTORY 開発Gでは、案件開発している最中で気付いた、技術的負債などの「すぐには対応できない（しなくてもよい）タスク」を、忘れないようにとりあえずチケット化しバックログに残しておく運用になっていました。その運用がずっと続いたところ、日々の案件開発になかなか切れ目が無く、それら改善タスクがバックログに積み上がる一方で消化されることがなく、チケット総数が100件を軽く超えるようになっていました。</p>
<p>そこで、スクラムの振り返りなどで、それらの改善タスクをずっと放置しておくのも問題だという声もあがり、週に１日午後の時間を改善タスクの消化のみにあて、そこにはMTGなどを入れない取り組みをチーム全体で始めてみました（Outlook 上で該当時間をブロックし、他部署からの MTG も入れられないように工夫しています）。</p>
<p>今回は、この<strong>改善DAY</strong>を活用し、ずっとできていなかったページパフォーマンス = CWV（Core Web Vitals）のスコア向上を目指すことにしました。</p>
<h3>フロントエンド勉強会</h3>
<p>また、社内の有志によるフロントエンド勉強会を週に１度開催しており、過去のイベント発表をみんなで見て感想を共有したり、過去〜最新の Baseline をみんなで確認したりと、参加メンバーで都度議題を決め、フロントエンド界隈の技術交流や、実際の開発プロダクトや開発の実情について共有する場というものがあります。</p>
<p>そんな中、勉強会のアウトプットの一つとして、実際のサービスで活かせることがなにかできないか考えるようになり、私の方から題材の一つとして「FACTORYパフォーマンス改善」を提案しました。
実際に参加メンバーみんなで FACTORY ページを検証する枠を作って頂き、合計２回に渡ってライブ検証することができました。</p>
<p>具体的に取り組んだのは以下になります。</p>
<ul>
<li>ブラウザの Lighthouse を使用して現状のスコアを確認（ネットワークや CPU のスロットリングを適宜設定）</li>
<li>各スコアの改善提案を確認する</li>
<li>分析タグなどページレンダリングに関係のないものをブロックしパフォーマンスの差分を確認</li>
<li>パフォーマンスタブでより詳細なブラウザの動きを確認する</li>
<li>キャプチャ画像からレイアウトシフトを確認</li>
</ul>
<p>これらにより、ブラウザに用意されている検証ツールの使い方や、どのようなところに改善ポイントがあるのか、を明確にすることができました。</p>
<p>:::message
フロントエンド勉強会について詳しくは、<a href="/posts/2025-06-30-frontend-study-group/">こちらの記事</a>もご覧頂ければと思います！
:::</p>
<h1>スコア改善</h1>
<p>ここからは、実際に勉強会で得た知見を活用し、どのようにスコア改善を進めたか紹介していきます。</p>
<p>ページパフォーマンスを図る指標として、Core Web Vitals (CWV) がありますが、それらは下記３つの指標に分類されます。</p>
<ul>
<li>Largest Contentful Paint (LCP): ページの主要なコンテンツがどれだけ速く読み込まれるかを測定</li>
<li>Interaction To Next Paint (INP): ユーザーの操作に対するページの応答性を評価</li>
<li>Cumulative Layout Shift (CLS): ページの視覚的な安定性を測定</li>
</ul>
<p>FACTORY はサービスの性質上、商品や車種画像の制約が厳しく（商品・車種の色味などが厳格のため）、静的コンテンツ変更（画像圧縮など）は難しかったため、ページ内のコンテンツ配置の変更だけで改善が見込める CLS にまずは焦点を当てました。</p>
<ul>
<li>Google Search Console を参考に、どのページで CLS スコアが悪いのか確認</li>
<li>SSG により静的ページとして作成し、クライアントでの API アクセス時のロード画面をなくす</li>
<li>画像などのコンテンツロード前後で、高さがシフトしている場所などを探す</li>
</ul>
<p>実際には、CLS スコアの悪いページにて、ブラウザの「パフォーマンス」タブで確認し、画面キャプチャを見ながらレイアウトシフトしている箇所をひとつひとつ潰していきました。</p>
<p>中でも特定に時間を要したのが、TOPページで発生していた CLS です。
以下のように解析ツールで見てみても、どこがレイアウトシフトしているのかパッと見では特定できませんでした。</p>
<p><img src="/assets/blog/authors/k.nakamoto/improve-cwv/cls.gif" alt="cls.gif"></p>
<p>ただ、該当の画像あたりを確認すると、<a href="https://developer.mozilla.org/en-US/docs/Web/CSS/contain-intrinsic-size"><code>contain-intrinsic-size</code></a> をcssで定義していることに気づきました。どうやら、こちらのプロパティは比較的新しいもので、非対応のブラウザでは正しく画像のサイズが設定されずレイアウトシフトの原因になっているようでした。</p>
<p>代わりに、画像のサイズ固定を <code>aspect-ratio</code> へ変更しアスペクト比を設定することで、レイアウトシフトを無くすことできました。</p>
<h1>結果</h1>
<p>今回は、改善DAYでの取り組みと実際の CWV スコアはどうだったのか、両面から振り返ってみたいと思います。</p>
<h3>改善DAYでのチケット消化</h3>
<p>以下の図は、改善チケットをまとめたボードのチケット解決数を表したものです。
４月ごろに「改善 DAY」の仕組みを導入しており、グラフ中の赤線が期間中（１ヶ月）に追加したチケット、緑線が完了したチケットを示しております。</p>
<table>
<thead>
<tr>
<th>4月チケット消化</th>
<th>6月チケット消化</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/k.nakamoto/improve-cwv/jira_apr.jpg" alt="jira_apr.jpg"></td>
<td><img src="/assets/blog/authors/k.nakamoto/improve-cwv/jira_jun.jpg" alt="jira_jun.jpg"></td>
</tr>
</tbody></table>
<p>上記はチーム全体の結果になりますが、着実に改善タスクの消費量が増えていることがわかるかと思います。
また、最近は全社的に AI を利用した開発も積極的に取り入れていることもあり、このような改善タスクは「シンプルなタスクな反面、取り掛かるにはコンテキストスイッチなどが発生し人間がやるには腰が重い」作業となり、比較的 AI に任せやすいタスクとも言えます。</p>
<p>そのような面も有り、日々機能追加などの案件の開発も動いている割には、上記のような改善活動にもしっかりコミットできていることが読み取れます。</p>
<h3>CWVスコア</h3>
<p>それでは、改善活動前後での今回のテーマである、CWV スコアがどのように改善されてきたか振り返ります。</p>
<p><img src="/assets/blog/authors/k.nakamoto/improve-cwv/cwv_result_pc.jpg" alt="cwv_result_pc.jpg">
<em>PCスコア</em></p>
<p><img src="/assets/blog/authors/k.nakamoto/improve-cwv/cwv_result_sp.jpg" alt="cwv_result_sp.jpg">
<em>モバイルスコア</em></p>
<p>上記のグラフは、Google Search Console で確認できる、URL単位でのパフォーマンススコアの移り変わりになります。</p>
<p>PC / モバイル両方とも <strong>良好URLの数 (緑部分) が増え 不良URL (赤部分) が減少 or ０</strong> となり、大多数のページでパフォーマンス改善することができました。
黄色で表されている <em>改善が必要なURL</em> が多少残っていますが、こちらは今回対応した CLS スコアだけの結果では無さそうですので、今後はそちらの別指標についても改善していきたいと思います。</p>
<h1>まとめ</h1>
<p>今回は、FACTORY 開発G で導入している<strong>改善DAY</strong>と、社内で開催している<strong>フロントエンド勉強会</strong>を活用し、実際に FACTORY のページスコア（CWV）の向上に取り組んだ事例を紹介させて頂きました。</p>
<p>日々、案件を進めるうえでは、納期などの都合でどうしても技術的な負債の消化を後回しにしてしまいがちですが、今後も改善DAYを活用し、そうした技術的負債が溜まらないようにしていくことや、社内勉強会からは、開発しているプロダクト・サービスは違えど、知見や改善案はそんな中からも発見できるので、それらをうまく活用しながら FACTORY のサービスを日々改善し安定運用していきたいと思います！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/k.nakamoto/improve-cwv/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[KMP Project with Tuist: KMP iOS Build System Construction]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-01-KMP-with-Tuist-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-01-KMP-with-Tuist-en/</guid>
            <pubDate>Tue, 19 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[How to construct a build system for iOS modules in a KMP app project using Tuist]]></description>
            <content:encoded><![CDATA[<h2>0. Introduction</h2>
<p>My name is <a href="https://blog.kinto-technologies.com/authors/8ff703cc-0401-5075-b94e-ff4b91ead205">Choi Garamoi</a>, and I am responsible for developing the Android app &quot;<a href="https://play.google.com/store/apps/details?id=com.kinto.one.app">KINTO Kantan Moushikomi</a>.&quot;</p>
<p>This app uses <a href="https://kotlinlang.org/docs/multiplatform.html">KMP</a> and shares some business logic with <a href="https://apps.apple.com/jp/app/kinto-%E3%81%8B%E3%82%93%E3%81%9F%E3%82%93%E7%94%B3%E3%81%97%E8%BE%BC%E3%81%BF/id6450170362">iOS app</a>.</p>
<p>This time, I&#39;ve summarized the results of using <a href="https://tuist.dev">Tuist</a> to enable smoother development of a KMP project.</p>
<h2>1. Overview</h2>
<p>In Android Studio, the KMP Application project is created using the New Project Wizard. Once it is created, the <a href="https://monorepo.tools">Monorepo</a> will look like this:</p>
<pre><code>KmpProject/
├── androidApp/          # Android専用モジュール(Kotlin/Android)
├── iosApp/              # iOS専用モジュール(Swift)
└── shared/              # 共通ビジネスロジック(Kotlin/Multiplatform)
</code></pre>
<p>Similar to <a href="https://gradle.org">Gradle</a> for Android, a build environment suitable for modularization and team development is also important in iOS. This article will show you how to build one using <a href="https://tuist.dev">Tuist</a>.</p>
<h2>2. Challenges in Xcode Project</h2>
<p>Xcode manages the information required to build an app in <code>*.xcodeproj</code>, but there are some challenges.</p>
<ol>
<li><strong>Merge conflicts occur frequently</strong>: When settings are changed, Xcode automatically updates the <a href="https://beromkoh.medium.com/what-is-project-pbxproj-in-xcode-d99e831eda99"><code>project.pbxproj</code></a> file. This file is in an unstructured text format, which often leads to Git conflicts when multiple people edit it simultaneously.</li>
<li><strong>Practically meaningless diffs are generated</strong>: Xcode&#39;s GUI operations can generate many diffs without any real changes, cluttering the history.</li>
<li><strong>Difficult to automate</strong>: Many settings are GUI dependent, making it difficult to automate builds using CI/CD or scripts.</li>
<li><strong>Difficult to review</strong>: <code>project.pbxproj</code> is hard to read, making it difficult to review changes.</li>
<li><strong>Scalability is limited</strong>: As the team size grows, managing multiple targets and build settings can become cumbersome.</li>
</ol>
<p>The <code>*.xcodeproj</code> directory is like a combination of <a href="https://gradle.org">Gradle</a> and <a href="https://www.jetbrains.com/help/idea/configure-project-settings.html"><code>.idea</code> directories</a> in an Android project, so it is not possible to separate local Xcode settings from iOS app build settings.</p>
<p><a href="https://trends.google.com/trends/explore?date=all&q=xcode%20conflict,xcode%20merge,xcode%20dev">Google Trends</a> also shows that there are more searches for &quot;xcode conflict&quot; than for &quot;xcode dev,&quot; indicating that there are more conflicts during development.</p>
<h2>3. What is <a href="https://tuist.dev">Tuist</a>?</h2>
<p>It is a tool that can generate <a href="https://developer.apple.com/documentation/xcode/projects-and-workspaces">Xcode projects and workspaces</a> in the <a href="https://www.swift.org">Swift</a> language and can also build in combination with <a href="https://developer.apple.com/xcode">Xcode</a> terminal tools.</p>
<p>Main features are as follows:</p>
<ol>
<li>Modularization support</li>
<li>Environment-independent builds (team development oriented)</li>
<li>Automation support</li>
<li><a href="https://www.swift.org/documentation/package-manager">Swift Package Manager</a> support</li>
</ol>
<p>Other Xcode build tools include <a href="https://www.swift.org/documentation/package-manager">Swift Package Manager</a>, <a href="https://github.com/yonaskolb/XcodeGen">XcodeGen</a>, and <a href="https://bazel.build">Bazel</a>.</p>
<ul>
<li><a href="https://www.swift.org/documentation/package-manager">Swift Package Manager</a> : It only provides the dependency management functionality of Gradle (the <code>dependencies</code> block).</li>
<li><a href="https://github.com/yonaskolb/XcodeGen">XcodeGen</a> : Insufficient checking of tool settings makes it prone to human error.</li>
<li><a href="https://bazel.build">Bazel</a> : It is complex to use because it is intended for large-scale projects and is overengineered for small and medium-sized projects.</li>
</ul>
<h2>4. Introducing <a href="https://tuist.dev">Tuist</a></h2>
<p>The following steps are based on <a href="https://docs.tuist.dev/en/guides/develop/projects/adoption/migrate/xcode-project">Migrate an Xcode project</a> and reflect the latest information:</p>
<h3>4-1. Install Tuist</h3>
<pre><code class="language-shell">brew update
brew upgrade tuist
brew install tuist
</code></pre>
<p>For installation methods other than <a href="https://brew.sh">Homebrew</a>, refer to the <a href="https://docs.tuist.dev/en/guides/quick-start/install-tuist">Manual</a>.</p>
<h3>4-2. Add Tuist Config files</h3>
<p>Add three files (Manifest files): <code>Tuist.swift</code>, <code>Project.swift</code>, and <code>Tuist/Package.swift</code>.</p>
<pre><code>KmpWithSwift/
├── Tuist.swift
├── Tuist/
│   └── Package.swift
├── Project.swift
├── androidApp/
│   └── ...
├── iosApp/
│   └── ...
└── shared/
    └── ...
</code></pre>
<h3>4-3. Add a Module (<code>Target</code>) to the Settings</h3>
<p><code>Project.swift</code> is the main config file, and you can use the sample from <a href="https://docs.tuist.dev/en/guides/develop/projects/adoption/migrate/xcode-project">Migrate an Xcode project</a> as is for the other files.</p>
<h4>4-3-1. <code>Tuist.swift</code></h4>
<pre><code class="language-Swift">import ProjectDescription

let tuist = Tuist(project: .tuist())
</code></pre>
<h4>4-3-2. <code>Project.swift</code></h4>
<p>Add target settings and build scripts for KMP common modules.</p>
<ul>
<li><code>infoPlist</code> : Whole screen setup.</li>
<li><code>scripts</code> : <strong>Command to build the KMP common module.</strong></li>
</ul>
<pre><code class="language-swift">import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        .target(
            name: &quot;App&quot;,
            destinations: .iOS,
            product: .app,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.App&quot;,
            infoPlist: .extendingDefault(
                with: [
                    &quot;UILaunchScreen&quot;: [
                        &quot;UIColorName&quot;: &quot;&quot;,
                        &quot;UIImageName&quot;: &quot;&quot;,
                    ],
                ]
            ),
            sources: [&quot;iosApp/iosApp/**&quot;],
            resources: [&quot;iosApp/iosApp/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        )
    ]
)
</code></pre>
<h4>4-3-3. <code>Tuist/Package.swift</code></h4>
<pre><code class="language-swift">// swift-tools-version: 6.0
import PackageDescription

#if TUIST
    import struct ProjectDescription.PackageSettings

    let packageSettings = PackageSettings(
        productTypes: [:]
    )
#endif

let package = Package(
    name: &quot;App&quot;,
    dependencies: [
    ]
)
</code></pre>
<h4>4-4. Delete the Old Xcode Project</h4>
<ol>
<li>Delete <code>./iosApp/iosApp.xcodeproj</code>.</li>
<li>Add <code>*.xcodeproj</code> to <code>./.gitignore</code>.</li>
</ol>
<h4>4-5. Check</h4>
<p>Check whether Tuist has been set up correctly.</p>
<ol>
<li>Verify that the Tuist Manifest file can be opened in Xcode.<pre><code class="language-shell"># KmpWithSwiftディレクトリで
tuist edit
</code></pre>
</li>
<li>Generate an Xcode project with Tuist.<pre><code class="language-shell"># KmpWithSwiftディレクトリで
tuist generate
</code></pre>
</li>
<li>Once Xcode is open, run the app.</li>
<li>If the app starts normally, the process is complete.</li>
</ol>
<h2>5. Separate iOS Settings from Common Features</h2>
<p>The common module <code>./shared/build.gradle.kts</code> does not properly separate the scope of responsibility for the common business logic build settings and the iOS-specific <a href="https://developer.apple.com/documentation/xcode/creating-a-static-framework">XCFramework</a> build.</p>
<pre><code class="language-kotlin">kotlin {
    // ...

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = &quot;shared&quot;
            isStatic = true
        }
    }

    // ...
}
</code></pre>
<h3>5-1. Response</h3>
<p>If you separate the iOS <a href="https://developer.apple.com/documentation/xcode/creating-a-static-framework">XCFramework</a> settings below from <code>:shared</code> and move them to <code>ios</code>, you can set them more effortlessly and enhance modularity.</p>
<pre><code class="language-kotlin">it.binaries.framework {
    baseName = &quot;shared&quot;
    isStatic = true
}
</code></pre>
<h3>5-2. Procedure</h3>
<ol>
<li>Build XCFramework from the <code>./iosApp/shared</code> module.</li>
<li>Update the scripts in the <code>App</code> target (<code>./iosApp/iosApp</code> directory).</li>
</ol>
<h4>5-2-1. Add the <code>:iosApp:shared</code> Module</h4>
<p>Add the <code>./iosApp/shared/build.gradle.kts</code> file and add the module to <code>./settings.gradle.kts</code>. <strong>No Android settings are required.</strong></p>
<pre><code class="language-kotlin">// ./iosApp/shared/build.gradle.kts
plugins {
    alias(libs.plugins.kotlin.multiplatform)
}

kotlin {
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = &quot;shared&quot;
            isStatic = true

            export(projects.shared)
        }
    }

    sourceSets {
        commonMain.dependencies {
            api(projects.shared)
        }
    }
}
</code></pre>
<pre><code class="language-kotlin">// ./settings.gradle.kts
// ... 省略 ...
rootProject.name = &quot;KmpProject&quot;
include(
    &quot;:androidApp&quot;,
    &quot;:iosApp:shared&quot;,
    &quot;:shared&quot;
)
</code></pre>
<p>Delete the XCFramework configuration from <code>./shared/build.gradle.kts</code>.</p>
<pre><code class="language-kotlin">// ./shared/build.gradle.kts
plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidLibrary)
}

kotlin {
    // ... 省略 ...

    iosX64()
    iosArm64()
    iosSimulatorArm64()

    // ... 省略 ...
}
// ... 省略 ...
</code></pre>
<h4>5-2-2. Tuist Target Update</h4>
<p>Update the Gradle command in <code>scripts</code> of <code>Project.swift</code>.</p>
<p>Change the module in <code>scripts</code> from <code>:shared</code> to the added <code>:iosApp:shared</code> (<code>./gradlew :shared:embedAndSignAppleFrameworkForXcode</code> ➡️ <code>./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode</code>).</p>
<pre><code class="language-swift">// ./Project.swift
import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        .target(
            name: &quot;App&quot;,
            destinations: .iOS,
            product: .app,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.App&quot;,
            infoPlist: .extendingDefault(
                with: [
                    &quot;UILaunchScreen&quot;: [
                        &quot;UIColorName&quot;: &quot;&quot;,
                        &quot;UIImageName&quot;: &quot;&quot;,
                    ],
                ]
            ),
            sources: [&quot;iosApp/iosApp/**&quot;],
            resources: [&quot;iosApp/iosApp/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        )
    ]
)
</code></pre>
<h2>6. Multimodularization</h2>
<p>As your app scales, modularizing features becomes essential.</p>
<pre><code class="language-mermaid">%%{
  init: {
    &#39;theme&#39;: &#39;neutral&#39;
  }
}%%

graph TB
    App ==&gt; FeatureA
    App ==&gt; FeatureB
    FeatureA --&gt; :iosApp:shared
    FeatureB --&gt; :iosApp:shared
</code></pre>
<p>But if each feature requires <code>:iosApp:shared</code>, the Tuist config becomes as follows:</p>
<pre><code class="language-swift">// ./Project.swift
import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        .target(
            name: &quot;App&quot;,
            destinations: .iOS,
            product: .app,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.App&quot;,
            infoPlist: .extendingDefault(
                with: [
                    &quot;UILaunchScreen&quot;: [
                        &quot;UIColorName&quot;: &quot;&quot;,
                        &quot;UIImageName&quot;: &quot;&quot;,
                    ],
                ]
            ),
            sources: [&quot;iosApp/iosApp/**&quot;],
            resources: [&quot;iosApp/iosApp/**&quot;],
            dependencies: [
                .target(&quot;FeatureA&quot;),
                .target(&quot;FeatureB&quot;)
            ]
        ),
        .target(
            name: &quot;FeatureA&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.FeatureA&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/FeatureA/**&quot;],
            resources: [&quot;iosApp/FeatureA/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        ),
        .target(
            name: &quot;FeatureB&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.FeatureB&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/FeatureB/**&quot;],
            resources: [&quot;iosApp/FeatureB/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        )
    ]
)
</code></pre>
<p>This configuration has the following two major issues:</p>
<ol>
<li>A common XCFramework will be built for the number of feature modules (<code>:IOSApp:shared</code>).</li>
<li>Depending on your feature module target settings and build options, there is a risk that the app may use multiple versions of <code>:iosApp:shared</code>.</li>
</ol>
<p>To solve this problem, wrap <code>:iosApp:shared</code> with a Tuist target.</p>
<h3>6-1. Add Wrapping Target</h3>
<p>Add the <code>KmpCore</code> target so that feature modules share Xcode targets instead of directly using the Gradle <code>:shared</code> module.</p>
<pre><code class="language-mermaid">%%{
  init: {
    &#39;theme&#39;: &#39;neutral&#39;
  }
}%%

graph TB
    App ==&gt; FeatureA
    App ==&gt; FeatureB
    FeatureA ==&gt; KmpCore
    FeatureB ==&gt; KmpCore
    KmpCore --&gt; :iosApp:shared
</code></pre>
<p>The source code is in <code>iosApp/shared/**</code>, which is the same as <code>:iosApp:shared</code>, but uses <code>KmpCore</code> as a wrapping target to utilize the namespace generated by KMP and to encapsulate it. As a result, the only target that holds information about the KMP common code is <code>KmpCore</code>.</p>
<pre><code class="language-swift">// ./Project.swift
import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        .target(
            name: &quot;App&quot;,
            destinations: .iOS,
            product: .app,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.App&quot;,
            infoPlist: .extendingDefault(
                with: [
                    &quot;UILaunchScreen&quot;: [
                        &quot;UIColorName&quot;: &quot;&quot;,
                        &quot;UIImageName&quot;: &quot;&quot;,
                    ],
                ]
            ),
            sources: [&quot;iosApp/iosApp/**&quot;],
            resources: [&quot;iosApp/iosApp/**&quot;],
            dependencies: [
                .target(&quot;FeatureA&quot;),
                .target(&quot;FeatureB&quot;)
            ]
        ),
        .target(
            name: &quot;FeatureA&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.FeatureA&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/FeatureA/**&quot;],
            resources: [&quot;iosApp/FeatureA/**&quot;],
            dependencies: [.target(name: &quot;KmpCore&quot;)]
        ),
        .target(
            name: &quot;FeatureB&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.FeatureB&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/FeatureB/**&quot;],
            resources: [&quot;iosApp/FeatureB/**&quot;],
            dependencies: [.target(name: &quot;KmpCore&quot;)]
        ),
        .target(
            name: &quot;KmpCore&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.KmpCore&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/shared/**&quot;],
            resources: [&quot;iosApp/shared/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        )
    ]
)
</code></pre>
<h3>6-2. Expose <code>shared</code> via <code>KmpCore</code></h3>
<p>Simply the <code>KmpCore</code> target dependency on <code>shared</code> does not allow access from <code>FeatureA</code> and <code>FeatureB</code> to <code>:shared</code> code.</p>
<p>Additional configuration is required to allow <code>FeatureA</code> and <code>FeatureB</code> to access the KMP common code (Gradle&#39;s <code>:shared</code> module) via <code>KmpCore</code>.</p>
<p>First, add the <code>settings</code> configuration to the <code>KmpCore</code> target.</p>
<pre><code class="language-swift">// ./Project.swift
import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        // ... 省略 ...
        .target(
            name: &quot;KmpCore&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.KmpCore&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/shared/**&quot;],
            resources: [&quot;iosApp/shared/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ],
            settings: .settings(base: [
                &quot;FRAMEWORK_SEARCH_PATHS&quot;: &quot;iosApp/shared/build/xcode-frameworks/**&quot;,
                &quot;OTHER_LDFLAGS&quot;: &quot;-framework shared&quot;
            ])
        )
    ]
)
</code></pre>
<p>Add the following Swift file so that the <code>KmpCore</code> target exposes the <code>shared</code> namespace in the <code>KmpCore</code> namespace.</p>
<pre><code class="language-swift">// ./iosApp/shared/KmpCore.swift
@_exported import shared
</code></pre>
<h3>6-3. Check</h3>
<p>After building the project in Xcode, <code>FeatureA</code> and <code>FeatureB</code> can access <code>:shared</code> via <code>import KmpCore</code>.</p>
<p>For example, if the <code>:shared</code> module has a <code>SomeModel</code>(<code>shared/src/commonMain/kotlin/ktc/garamoi/choi/kmp/with/tuist/SomeModel.kt</code>) class, it can be accessed from <code>FeatureA</code> as follows.</p>
<pre><code class="language-Swift">import Foundation
import KmpCore

public class SomeFeatureAClass {
    let model: SomeModel

    // ...
}
</code></pre>
<p>If a compilation error occurs, the initial build may become unstable due to build order or caching issues. In that case, you can resolve the issue by performing a clean build or building multiple times. </p>
<h2>7. Conclusion</h2>
<ol>
<li>Xcode&#39;s <code>*.xcodeproj</code> is not suitable for automation and team development.</li>
<li>I recommend using <a href="https://tuist.dev">Tuist</a> as an alternative to <code>*.xcodeproj</code> for Xcode projects.</li>
<li>By wrapping the KMP common module&#39;s XCFramework generation in an Xcode project target, feature modularization becomes easier.</li>
</ol>
<h2>8. References</h2>
<ol>
<li><a href="https://kotlinlang.org/docs/multiplatform.html">Kotlin Multiplatform</a> : The official Kotlin project that provides various tools for cross-platform development using the Kotlin language.</li>
<li><a href="https://gradle.org">Gradle</a> : A de-facto build tool for Android and Java projects.</li>
<li><a href="https://tuist.dev">Tuist</a> : Build tools for Xcode projects.</li>
<li><a href="https://www.swift.org">Swift</a> : An OOP language developed by Apple to replace Objective-C.</li>
<li><a href="https://developer.apple.com/xcode">Xcode</a> : An IDE for Apple platforms.</li>
<li><a href="https://developer.apple.com/documentation/xcode/projects-and-workspaces">Xcode / Projects and workspaces</a></li>
<li><a href="https://www.swift.org/documentation/package-manager">Swift Package Manager</a> : The official dependency management tool for the <a href="https://www.swift.org">Swift</a> language.</li>
<li><a href="https://github.com/yonaskolb/XcodeGen">XcodeGen</a> : A tool to generate <a href="https://developer.apple.com/documentation/xcode/projects-and-workspaces">Xcode Project</a> in YAML and JSON.</li>
<li><a href="https://bazel.build">Bazel</a> : A build tool developed by Google. Targets large-scale <a href="https://monorepo.tools">Monorepo</a>.</li>
<li><a href="https://monorepo.tools">Monorepo Explained</a> : A system for managing multiple software in a single repository.</li>
<li><a href="https://trends.google.com/trends/explore?date=all&q=xcode%20conflict,xcode%20merge,xcode%20dev">Google Trends : <code>xcode conflict</code>, <code>xcode merge</code>, <code>xcode dev</code></a> : The proportion of Xcode conflict searches is higher when compared to general Xcode development searches.</li>
<li><a href="https://beromkoh.medium.com/what-is-project-pbxproj-in-xcode-d99e831eda99">What is <code>project.pbxproj</code> in Xcode</a></li>
<li><a href="https://www.jetbrains.com/help/idea/configure-project-settings.html">Project configuration / Projects / Project settings</a> : Explanation of the <code>.idea</code> directory in Android Studio and IntelliJ IDEA.</li>
<li><a href="https://docs.tuist.dev/en/guides/develop/projects/adoption/migrate/xcode-project">Migrate an Xcode project</a> : Manual steps to turn an existing Xcode project into a Tuist project.</li>
<li><a href="https://brew.sh">Homebrew</a> : A system package management tool for macOS.</li>
<li><a href="https://docs.tuist.dev/en/guides/quick-start/install-tuist">Install Tuist</a></li>
<li><a href="https://developer.apple.com/documentation/xcode/creating-a-static-framework">Xcode / Bundles and frameworks / Creating a static framework</a></li>
<li><a href="https://developer.apple.com/swift/resources">Swift logo</a> : The official logos can be downloaded below.</li>
<li><a href="https://kotlinlang.org/assets/images/favicon.svg?v2">Kotlin logo</a></li>
<li><a href="https://gradle.org/icon/safari-pinned-tab.svg">Gradle logo</a></li>
<li><a href="https://docs.tuist.dev/logo.png">Tuist logo</a></li>
<li><a href="https://play.google.com/store/apps/details?id=com.kinto.one.app">KINTO Kantan Moushikomi</a> : Android App</li>
<li><a href="https://apps.apple.com/jp/app/kinto-%E3%81%8B%E3%82%93%E3%81%9F%E3%82%93%E7%94%B3%E3%81%97%E8%BE%BC%E3%81%BF/id6450170362">KINTO Kantan Moushikomi</a> : iOS App</li>
<li><a href="https://blog.kinto-technologies.com/authors/8ff703cc-0401-5075-b94e-ff4b91ead205">Choi Garamoi</a></li>
</ol>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ChoiGaramoi/KMP-with-Tuist/header.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: April 2025 Update]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-27-newcomer-202504-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-27-newcomer-202504-en/</guid>
            <pubDate>Fri, 15 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We asked five new members who joined us in April 2025 to share their impressions after joining our company, and here is a summary of what they said.]]></description>
            <content:encoded><![CDATA[<h1><strong>Introduction</strong></h1>
<p>Hello, I&#39;m hiro, and I joined the company in April 2025! In this article, I interviewed new members who joined us in April 2025 and gathered their first impressions after joining KINTO Technologies. I hope this article will be helpful for those interested in KINTO Technologies (KTC from now on) and serve as a nice reflection for those featured here!</p>
<h1><strong>Minami</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/image-20250613-072106.png" alt="Minami"></p>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>I&#39;ll be joining the newly established Data Strategy Division from July.</li>
</ul>
</li>
<li><p>**How is your team structured? **</p>
<ul>
<li>This team handles everything from analysis and proposing business strategies/tactics to implementing measures.</li>
<li>I’m looking forward to working with the extremely talented analysts, data scientists, and engineers to grow the company.</li>
</ul>
</li>
<li><p>** What was your first impression of KTC when you joined? Were there any surprises? **</p>
<ul>
<li>I was surprised by how high the degree of freedom is in how I can work and spend my time in the office.</li>
<li>I’m looking forward to what’s to come, because even though some people are calm, they are secretly full of passion.</li>
</ul>
</li>
<li><p>**What is the atmosphere like on-site? **</p>
<ul>
<li>The team is relatively young, with a friendly and cheerful atmosphere.</li>
<li>I’ll do my best to demonstrate the strengths of the team under the new structure.</li>
</ul>
</li>
<li><p>**How did you feel about writing a blog post? **</p>
<ul>
<li>I read blog posts before joining KTC, so I hope my posts will be helpful for those who are joining the company.</li>
</ul>
</li>
<li><p><strong>Question from MAo-san to Minami-san</strong></p>
<blockquote>
<p>Please let me know if you have any recommended travel destinations, either domestic or abroad!</p>
</blockquote>
<ul>
<li>I went to Kauai in Hawaii last year and it was amazing!</li>
</ul>
</li>
</ul>
<h1><strong>H.N</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/malta.jpeg" alt="H.N"></p>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>I work in the Business System Development Division and am mainly responsible for the dealer operations area.</li>
<li>Recently, my hobby is searching for tasty restaurants near the Muromachi office.</li>
</ul>
</li>
<li><p>**How is your team structured? **</p>
<ul>
<li>I belong to the Nimbus team within the Business System Development Division, and work on a daily basis with three full-time employees and a partner.</li>
</ul>
</li>
<li><p>** What was your first impression of KTC when you joined? Were there any surprises? **</p>
<ul>
<li>There are more opportunities to participate in company events and dinner parties than I had expected, and it looks like I’ll be able to interact with people from other departments, so it has been a pleasant surprise!</li>
</ul>
</li>
<li><p>**What is the atmosphere like on-site? **</p>
<ul>
<li>Even though they are busy, many of my seniors create an atmosphere where it is easy to ask about problems or questions, which is very helpful as I try to catch up on things every day!</li>
</ul>
</li>
<li><p>**How did you feel about writing a blog post? **</p>
<ul>
<li>I’m not used to writing these types of articles, including tech blogs, but I hope this will be an opportunity for me to start enjoying it!</li>
</ul>
</li>
<li><p><strong>Question from Minami to H.N</strong></p>
<blockquote>
<p>Please tell us about an interesting company event that you attended!</p>
</blockquote>
<ul>
<li>I haven&#39;t been able to attend any company events yet, but I would like to attend the following!<ul>
<li>KTCBeerBash</li>
<li>In-house study groups on generative AI</li>
<li>Events that could deepen interactions across departments and teams!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1><strong>K.S</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/haiace.jpg" alt="K.S"></p>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>I am primarily responsible for improving the UI/UX of the my route app. My hobby is going camping with my family.</li>
</ul>
</li>
<li><p>**How is your team structured? **</p>
<ul>
<li>The new structure is scheduled to begin in July. The PDM and Development Team will work together to make the apps even better.</li>
</ul>
</li>
<li><p>** What was your first impression of KTC when you joined? Were there any surprises? **</p>
<ul>
<li>When I joined the company, the Jimbocho office had just been renovated and is very beautiful. What’s more, the chairs and tables in the break area are all from outdoor manufacturers, making it very stylish!</li>
</ul>
</li>
<li><p>**What is the atmosphere like on-site? **</p>
<ul>
<li>Everyone on the my route team is kind and will support you with any problems you may have. The welcome party was also great!</li>
</ul>
</li>
<li><p>**How did you feel about writing a blog post? **</p>
<ul>
<li>I started reading blogs introducing my former seniors more frequently, which became a good opportunity to learn more about the company.</li>
</ul>
</li>
<li><p><strong>Question from H.N.-san to K.S.-san</strong></p>
<blockquote>
<p>Let me know if you have any recommended campsites or travel destinations that you’ve been to with your family!</p>
</blockquote>
<ul>
<li>These are close to Tokyo, children and pets are welcome, and they’re high quality.</li>
<li><a href="https://tacoglamp.com/">TACO GLAMP</a></li>
<li><a href="https://www.moroyama-yuzunosato.com/">Moroyama Town Yuzu no Sato Auto Campsite</a></li>
<li><a href="https://www.mikabo.co.jp/camp/">Mikabo Kogen Auto Campsite</a></li>
<li>There are also many more.</li>
<li>But honestly, as long as there’s a bonfire, anywhere is fine. lol</li>
</ul>
</li>
</ul>
<h1><strong>Chiru</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/02.jpg" alt="Chiru"></p>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>Nice to meet you!　I am Chiru.　I belong to the Corporate IT Group in the IT/IS Division.　</li>
<li>Until now, I have built my career as a web development engineer, but at KTC, I work hard every day to utilize my engineering skills in corporate IT, working on the company’s internal information systems and supporting our dealers’ IT operations, in order to strengthen the organization!</li>
</ul>
</li>
<li><p>**How is your team structured? **</p>
<ul>
<li>The team belongs to the Innovation Drive Team! There are nine members on the team including myself, and each person is active in their own area of expertise!</li>
<li>The team’s goals are to &quot;make KTC’s IT environment the best it can be,&quot; and to &quot;deliver the value created at KTC to the outside world.&quot; With these goals in mind, the team aims to be &quot;a group of engineers that maximizes KTC’s value and connects it to external value,&quot; not just within the company.</li>
</ul>
</li>
<li><p>** What was your first impression of KTC when you joined? Were there any surprises? **</p>
<ul>
<li>My first impression was that there are a lot of energetic people!　</li>
<li>As soon as I joined the company, I noticed that it’s one with a lot of interaction, with company events and regular beer bashes.</li>
<li>As for the surprises, the things that had been talked about during the casual meeting and the job interview were the same, so there weren’t any major surprises. However, because it is a group company of a large corporation, I had imagined that the work flow would be rigid and there would be strict restrictions, but that wasn’t the case at all. In fact, I was surprised at how fast-paced things are!</li>
</ul>
</li>
<li><p>**What is the atmosphere like on-site? **</p>
<ul>
<li>Since the members of my team are based in different locations, we are mostly only together online, but strangely enough, I don’t feel any distance between us.　When we run into problems or have questions, we can communicate immediately via Slack or Zoom, so we’re able to work with a very pleasant atmosphere.</li>
<li>When a problem occurs, everyone gathers around asking, &quot;What happened? What’s wrong?&quot;, and when we have team meetings, the exchange of opinions gets so lively that we sometimes run out of time. I think it’s great that we have such an active team!</li>
</ul>
</li>
<li><p>**How did you feel about writing a blog post? **</p>
<ul>
<li>I had been reading tech blogs before I joined the company and knew that I would eventually be writing one, so I thought, &quot;It’s finally time.&quot; lol</li>
</ul>
</li>
<li><p><strong>Question from K.S.-san to Chiru-san</strong></p>
<blockquote>
<p>Please tell me if you know of any delicious restaurants serving set meals near the Nagoya office!</p>
</blockquote>
<ul>
<li>I recommend the freshly fried tempura set meals from Tempura &amp; Wine Kojima, located in Yanagibashi Central Market near the office.</li>
<li>If you come to Nagoya, let’s go together!</li>
</ul>
</li>
</ul>
<h1><strong>MAo</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/image.png" alt="MAo"></p>
<ul>
<li><p><strong>Self-introduction</strong></p>
<ul>
<li>I belong to the Corporate IT Group in the IT/IS Division. I’m mainly in charge of visualization, creating BI and improving operations before and after it, working in close proximity to the field.</li>
</ul>
</li>
<li><p>**How is your team structured? **</p>
<ul>
<li>I belong to the Innovation Drive Team, which consists of about 10 people.</li>
</ul>
</li>
<li><p>** What was your first impression of KTC when you joined? Were there any surprises? **</p>
<ul>
<li>Everyone talks to me a lot!!</li>
</ul>
</li>
<li><p>**What is the atmosphere like on-site? **</p>
<ul>
<li>It’s an atmosphere where everyone can bring their own ideas and think of better solutions.</li>
</ul>
</li>
<li><p>**How did you feel about writing a blog post? **</p>
<ul>
<li>I was excited about what to write.</li>
</ul>
</li>
<li><p><strong>Question from Chiru-san to MAo-san</strong></p>
<blockquote>
<p>Tell me what you’re into lately!</p>
</blockquote>
<ul>
<li>I like sipping tea while watching the cars go by on the road! I think to myself, &quot;Everyone’s on the move.　I’ll do my best, too!&quot;</li>
</ul>
</li>
</ul>
<h1><strong>Finally</strong></h1>
<p>Thank you everyone for sharing your thoughts after joining our company!</p>
<p>There are more and more new members at KINTO Technologies every day! We will be posting more stories from our newcomers across divisions, so stay tuned!</p>
<p>And yes: we are still hiring! KINTO Technologies is looking for new teammates to join us across a variety of divisions and roles. For more details, please check <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AWS Bedrock Guardrailsの導入取り組み:前編-生成AIセキュリティの必要性]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-20-aws-bedrock-guardrails-deployment-inclusion-first-part/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-20-aws-bedrock-guardrails-deployment-inclusion-first-part/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[生成AIセキュリティの必要性とそれを実現する生成AIガードレールの概要について(Security側面)]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>0. はじめに</h2>
<p>KINTOテクノロジーズのCloud Infrastructure G(CIG)でInfrastructure Architectを担当している劉(YOU)です。</p>
<p>AIの著しい進化に伴い、世界的に生成AIを活用して様々な取り組みを実施することが日常になりました。生成AIをどれだけ使いこなせるかが重要ですが、併せて生成AIを利用することにより発生するリスク対策も重要になります。</p>
<p>弊社の今年の年間テーマとして<a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">AIファースト</a>が掲げられており、積極的なAI活用を始め社内外で多くの活動を実施しています。社内ではさまざまな部署が生成AIを活用したアプリケーションの開発をする動きが増えています。しかしそれに比例するように生成AI利用におけるセキュリティ対策の重要性も増しており、アプリケーションの開発スピードを落とすことなく実施できる対策の検討が急務となっています。</p>
<p>社内のクラウド環境に責任を持つCIGでは、社内のSCoEチームが発表している様々な<a href="https://blog.kinto-technologies.com/posts/2025-06-16-aispm/">AIセキュリティ関連の資料</a>を元に、生成AIセキュリティを実現するために何をすべきかという点で取り組みの優先順位を決めました。<a href="https://owasp.org/www-project-top-10-for-large-language-model-applications/">OWASP Top 10 for LLM Application 2025</a>をKTCのクラウド環境に適した内容になるよう整理し、それを実現する方法として生成AIガードレール、特にAWS Bedrock Guardrailsを採用することにしました。</p>
<p>本記事ではこのような背景の中で、我々がAWS Bedrock Guardrailsの導入に向けて取り組んだことを詳しく解説します。下記の事項を中心に前編と後編で分けてご紹介します。</p>
<ul>
<li>なぜ生成AI活用でセキュリティを確保する必要があるのか</li>
<li>生成AIの安全を担保するために生成AIガードレールがなぜ重要なのか</li>
<li>生成AIガードレールの中でもなぜAWS Bedrock Guardrailsを選択したのか</li>
<li>今までKTCがAWS Bedrock Guardrailsで何をやっているか</li>
</ul>
<p>前編である本記事では、<strong>生成AIセキュリティの必要性と実現するための生成AIガードレールの概要</strong>についての情報を記載します。後編でご紹介するAWS Bedrock Guardrailsの具体的な説明の前に、「生成AIセキュリティ」と「生成AIガードレール」についての基本的な考え方を整理し、それを踏まえてAWS Bedrock Guardrailsの役割を正確にご理解いただけるような情報をご提供します。なお、生成AIのセキュリティ全領域をカバーするとなると内容が長くなり過ぎるため、生成AIガードレールを中心とした内容を簡単に取り上げます。</p>
<p>後編では、<strong>生成AIガードレール適用のためのAWS Bedrock Guardrails</strong>についてまとめる予定です。理論の話がメインの前編とは違い、後編では主にAWSを使った実装ベースの解説を行います。元々は後編のみの記事を書く予定で書き始めたのですが、この話題でセキュリティの前提となる情報を割愛することは難しかったので前編・後編に分割することにしました。AWS Bedrock Guardrailsの取り組みについては後編で重点的に扱いますのでご留意ください。</p>
<p>本文書では<a href="https://llmatscale.ai">LLMs at Scale.ai</a>のJothi Periasamy氏の著書<a href="https://www.amazon.com/Guardrails-GEN-AI-Government-Services-ebook/dp/B0DT9TGRD7">Guardrails-GEN-AI-Government-Services</a>から主要な考え方を多く参考にさせていただきました。生成AIガードレールに興味があり、深掘りしたいという方は是非ご覧になって下さい。</p>
<p>:::message
本記事にまとめた情報は弊社内のセキュリティルールの内容とは直接関係は有りません。あくまでも<strong>取り組みの参考資料として活用された内容・外部公開されているソースのみを記載</strong>してます。
:::</p>
<h2>1. 生成AIセキュリティの重要性</h2>
<h3>1-1. 生成AIのセキュリティリスク</h3>
<p>ここ最近では、生成AIのアップデートに関するニュースを目にしない日はありません。日本国内を見ても、生成AIの導入が加速化し、それとともに企業や公共機関が直面するセキュリティリスクとそれに対応する方法への関心が高まっているように思います。 これから繰り返し生成AIの脅威について取り上げますが、その中でも最も<strong>代表的なセキュリティリスク</strong><a href="https://llmatscale.ai/publications.php#Guardrails-pub">^1</a>には次のようなものがあります。</p>
<p><img src="/assets/blog/authors/you/02/b.png" alt="自作イメージ">
<em>1-1-1.代表的なセキュリティリスク</em></p>
<p>このようなセキュリティリスクは国内でも深刻に受け止めてられており、日本政府からも各AI事業者に対して積極的に高いセキュリティガバナンスを保つことを要求しています。</p>
<p>海外との交流では2023年G7議長国として、基盤モデル及び生成AIを含む高度なAIシステムを議論する広島AIプロセス<a href="https://www.soumu.go.jp/hiroshimaaiprocess/documents.html">^2</a>を主導し、国際行動規範を設けました。</p>
<p>国内では、総務省と経済産業省が2024年4月にAI事業者ガイドライン<a href="https://www.meti.go.jp/press/2024/04/20240419004/20240419004.html">^3</a>を発表しました。 このガイドラインは広島AIプロセスの内容を踏襲しており、人間中心、安全性、プライバシー保護など10項目を含めています。</p>
<p><img src="/assets/blog/authors/you/02/c.png" alt="自作イメージ">
<em>1-1-2. 国内外の生成AIセキュリティに対する動き</em></p>
<p>しかし、政府が積極的にAIセキュリティの推進を行ってはいるものの、<strong>現在はまだガバナンス未整備</strong>の企業が大多数です。その理由としては色々有りますが、客観的な情報を提示しながら考察してみます。</p>
<h3>1-2. 統計で見る生成AIセキュリティの現状</h3>
<p>日本企業を対象にした生成AIとセキュリティに関する意識調査<a href="https://resources.trendmicro.com/jp-docdownload-form-m747-web-awareness-survey-generative-ai-security.html">^4</a>では次のような結果がでています。</p>
<p><img src="/assets/blog/authors/you/02/d.png" alt="自作イメージ">
<em>1-2-1.生成AIとセキュリティに関する意識調査</em></p>
<p>この時、回答者が心配したリスク要素として「法的権利の侵害(64%)」、「情報漏洩(61%)」、「ハルシネーション(56%)」、「不正・不具合の混入(34%)」が上位にランクインしており、<strong>前述したセキュリティリスクと実際の企業が感じてる危険要素</strong><a href="https://llmatscale.ai/publications.php#Guardrails-pub">^1</a>とも結果が合致しています。</p>
<p>特に法的に問題視される内容が1・2位に揃ってランクしており、各企業が個人情報保護法の順守を重要視していることが見て取れます。2024年改正でのウェブスキミング対策<a href="https://blog.jpac-privacy.jp/revisionofguidelines_202404/">^5</a>を見ても、国として流出等発生時の報告·通知義務と安全管理措置の対象が拡大していることがわかります。 生成AIの運用時にも、これらの規定を遵守するための取り組みが欠かせません。</p>
<p>こういった流れを見ると、改めて<strong>セキュリティリスクに対する懸念が生成AIの利用に影響を与えている</strong>と強く感じます。</p>
<p>総務省の情報通信白書<a href="https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r06/html/nd151120.html">^6</a>によると、業務における生成AIの活用状況の実体は国別に次のような結果が出ています。</p>
<p><img src="/assets/blog/authors/you/02/02.png" alt="総務省の資料">
<em>1-2-2.業務における生成AIの活用状況<a href="https://www.soumu.go.jp/johotsusintokei/whitepaper/ja/r06/html/nd151120.html">^6</a></em></p>
<blockquote>
<p>日本で46.8％（“業務で使用中”と回答した割合）であり、他国と比較するとその割合は低い。“トライアル中”までを含めると、米国、ドイツ、中国の企業は90％程度が使用しており、日本企業は社内向け業務から慎重な導入が進められていることがわかった</p>
</blockquote>
<p>日本企業の生成AI使用率は世界的に比べて低い水準ですが、これは主に<strong>データ、資産、個人情報などのリスク管理方案およびガバナンス体系の不在</strong><a href="https://www.jri.co.jp/page.jsp?id=108778">^7</a>だと述べています。これを見ても、AIシステム開発の一連のガバナンス政策を計画する段階から商用化後まで、全段階にわたって構築することが重要であることが分かります。
<em>詳細のデータは右のリンクを参照「<a href="https://www.ipa.go.jp/digital/chousa/dx-trend/dx-trend-2025.html">IPA DX白書2025</a>」</em></p>
<p>前述の調査<a href="https://resources.trendmicro.com/jp-docdownload-form-m747-web-awareness-survey-generative-ai-security.html">^4</a>では、回答者の77%が生成AIを使用していますが、その内の27.1%が<strong>生成AIに関するセキュリティ教育</strong>がないと回答し、全回答者の93.6%が「関連ガイドラインを整備中」と答えました。つまり、回答者が属している<strong>企業の大多数が安全なガイドラインを必要</strong>と感じているということです。</p>
<h3>1-3.　安全なガイドライン</h3>
<p>これらのデータを踏まえ、「<strong>安全なガイドラインを作成</strong>」 するために一番重要なことはなんでしょうか？この問いの答えの一つになるのが<strong>構造化された生成AIガードレールの採用</strong>です。ガードレールにはプライバシーリスクや入力値・出力値を検査し、セキュリティプロトコルを強制する統制を可能にし、インジェクション/ハルシネーション、毒性（Toxicity、生成AIが有害なコンテンツを生成するリスク）などのリスクを緩和する仕組みが取り入れられています。</p>
<p>一般的なガードレールは、主に行動を許容するかブロックするかに焦点を当てています。しかし、生成AIにおけるガードレールには、倫理的な運用目標も含まれます。「有害であったり、テーマから外れた出力を検知し、それを予防するメカニズムを導入すること」が、この分野におけるAI主導の運用を実現する鍵となります。これは単に脅威を阻止するだけでなく、AIの動作に直接関与することを意味します。特に、生成AIガードレールを適切に適用する戦略によって、高い透明性、責任性、安全性を確保した状態で生成AIを利用することが可能になります。</p>
<h2>2. 生成AIガードレールについて</h2>
<h3>2-1.　概要</h3>
<p><strong>生成AIガードレール</strong>とは、生成AIシステムが安全かつ倫理的に動作し、組織の目標や社会の価値観に適合することを保障するための<strong>構造化されたフレームワーク、指針</strong>です。ガードレールは、AIシステムの入力、処理、および出力を制御する役割を果たします。このような制御は、迅速なインジェクション、個人情報の流出、ハルシネーション、偏見といった危険を軽減することで、出力結果が正確で安全かつ関連性のあるものであることを確認するのに役立ちます。特に、組織が法的基準を順守し、倫理的原則を守り、機密情報を保護できるようサポートします。</p>
<p>一般的には、企業が提供するGPT, Claude, Geminiのような基盤モデルの場合、ある程度のセキュリティが自主的に保証されていますが、それでも完璧ではありません。最近の研究<a href="https://www.aisi.gov.uk/work/advanced-ai-evaluations-may-update">^8</a>によると、テストされたすべてのLLM（大規模言語モデル）は基本的な脱獄攻撃に対して依然として非常に脆弱であり、一部のモデルは特別な迂回の試みがなくても有害な結果を提供することが確認されています。そのため、このようなガードレールの重要性はさらに高まっています。</p>
<p>生成AIソリューションでのガードレールは次の3つの段階で実現します。</p>
<ol>
<li><p><strong>入力段階 (プロンプト/ユーザー質問)</strong>: ユーザー入力の有効性検証と悪意のあるプロンプトブロック</p>
</li>
<li><p><strong>検索段階（拡張コンテンツ）</strong>:RAGシステムで検索された情報の品質と関連性検証</p>
</li>
<li><p><strong>出力段階(応答)</strong>: 生成された応答の安全性、正確性、適切性の検証</p>
</li>
</ol>
<p>様々なタイプのガードレール制御がありますが、一般的にガードレール制御はこうなります。</p>
<p><img src="/assets/blog/authors/you/02/e.png" alt="自作イメージ">
<em>2-1-1.一般的なガードレール制御</em></p>
<p> さらに、これらすべてのコントロールは、ビジネスとセキュリティの要件に応じて高度にカスタマイズできます。 すべてのガードレール アーキテクチャ オプションは、ビジネスの要件とセキュリティ ポリシーによって決定されます。</p>
<h3>2-2.　アーキテクチャ</h3>
<p>ガードレールの実装には様々な方法がありますが、まずは方法論的な部分を整理してみましょう。</p>
<p><strong>単純なRAGアーキテクチャ</strong><a href="https://github.com/AnandJVishwakarma/Gaurdrails-for-RAG-Pipeline">^9</a>は、外部システムと相互作用せず、独自のランタイム環境内でのみ動作し、エージェントが関与しない実装方式です。 </p>
<p><img src="/assets/blog/authors/you/02/f.png" alt="perplexity生成イメージ">
<em>2-2-1.単純なRAGアーキテクチャ</em></p>
<p>ナレッジベース、意味検索、順位、ベクトルデータベースのようなLLMおよびそのサポートプロセスと直接相互作用を重視し、カスタマイズされたガードレールコンポーネントのようなガードレールコントロールを使用して、ユーザープロンプトとLLM応答およびナレッジベース検索を制御します。 <strong>簡単に設計·実装できる</strong>ため、オープンソースソリューションと組み合わせて使用する場合は、この方法を採用することをお勧めします。</p>
<p><strong>エージェントベースのアーキテクチャ</strong><a href="https://langchain-ai.github.io/langgraph/concepts/agentic_concepts/">^10</a>は、外部システムおよびアプリケーション、メモリ、コード インタープリタ、外部システムの呼び出しなど、さまざまなタイプのツールが存在します。 ここでは、エージェント アーキテクチャ タイプによってガードレールの制御が決まります。</p>
<p><img src="/assets/blog/authors/you/02/g.png" alt="perplexity生成イメージ">
<em>2-2-2.エージェントベースのアーキテクチャ</em></p>
<p>例えば、スーパーバイザーエージェントがいるエージェントアーキテクチャでは、スーパーバイザーエージェントと協業エージェント間の共通ガードレール制御を通じて、より複雑なセキュリティシステムを実現することができます。 しかし、エージェントを活用するアーキテクチャはまだベストプラクティスが成立できてないことで、<strong>複雑度も高いし、実装も非常に難しい</strong>状態になっています。そこで、技術変化を対応しているエンタープライズソリューションを活用したら、検証されたエージェントベースのアーキテクチャをすぐに使えます。</p>
<h3>2-3.　実装するための選択肢の比較</h3>
<p><img src="/assets/blog/authors/you/02/h.png" alt="自作イメージ">
<em>2-3-1.ソリューションの種類</em></p>
<p>次に、企業でガードレールを適用する際に
・オープンソースソリューションを採用して独自にガードレールを構成する
・エンタープライズソリューションを採用してガードレールを構成する
について長所と短所を比較してみましょう。</p>
<table>
<thead>
<tr>
<th>評価項目</th>
<th>Open Source Soulution</th>
<th>Enterprise Soulution</th>
</tr>
</thead>
<tbody><tr>
<td><strong>初期コスト</strong></td>
<td>◎</td>
<td>△</td>
</tr>
<tr>
<td><strong>人的コスト</strong></td>
<td>x</td>
<td>○</td>
</tr>
<tr>
<td><strong>ライセンスコスト</strong></td>
<td>◎</td>
<td>x</td>
</tr>
<tr>
<td><strong>カスタマイズ性</strong></td>
<td>◎</td>
<td>△</td>
</tr>
<tr>
<td><strong>ベンダー依存性</strong></td>
<td>◎</td>
<td>△</td>
</tr>
<tr>
<td><strong>定期的アップデート</strong></td>
<td>△</td>
<td>◎</td>
</tr>
<tr>
<td><strong>技術的専門性要求</strong></td>
<td>x</td>
<td>○</td>
</tr>
<tr>
<td><strong>専門サポートサービス</strong></td>
<td>△</td>
<td>◎</td>
</tr>
<tr>
<td><strong>セキュリティ関連対応力</strong></td>
<td>△</td>
<td>◎</td>
</tr>
</tbody></table>
<p><em>2-3-2.ソリューションの長所と短所</em>
<em>非常に良好：◎、良好：○、不良：△、非常に不良：x</em></p>
<p>オープンソースソリューションを利用して実装すると、自社独自の要件とドメインに合わせてカスタマイズできます。 そして、ビジネス要件に合うように内部データとサービスロジックに特化した細かい制御が可能だという点は非常に大きなメリットです。 さらに、他のオープンソースを活用するのと同様に、初期コストが節約されるだけでなく、特定のベンダーロックインも防止できます。</p>
<p> しかし、オープンソースソリューションを利用して実装するということは、<strong>AIに対する高い技術的専門性と開発ができる専門部隊が必要</strong>になります。 そして、該当ガードレールに対して持続的なアップデートとメンテナンスが発生し、専任のAI関連部署が存在しなかった場合、一般的な企業では<strong>人的コストが大幅にかかって</strong>しまいます。 そして結局、カスタマイズをした瞬間から自主的にガードレールの評価を進めなければならないため、検証されていない<strong>セキュリティホール</strong><a href="https://secure-navi.jp/blog/000037">^11</a>が存在する可能性があります。 何よりも安全なガイドラインがない状況であれば、要件を定義することからが問題になります。</p>
<p>その為、上記を運用できない場合はエンタープライズソリューションの利用が有効です。 すぐに使用可能な検証済みのセキュリティ機能を持っており、随時発生するセキュリティイシューに対して持続的なアップデートと専門的なサポートサービスを提供します。 そして生成AIセキュリティに要求される多様な規定を自動的に支援してくれます。 つまり、<strong>「安全なガイドラインの作成」をすぐに享受できる</strong>のがエンタープライズソリューションだということです。 </p>
<p>ただし、エンタープライズソリューションはライセンスコストや使用量によるコスト増が発生します。 そして、特定ベンダーのガードレールオプションを使用することになれば、特定ベンダーへの依存性が増加することも避けられません。 また、自分たちのビジネス要件に合ったガードレールを実装したい場合、カスタマイズオプションの制限が存在する可能性があります。これは<strong>独自のセキュリティ規定が存在する場合、その規定を準拠できない</strong>かも知れません。</p>
<p>もちろん、エンタープライズ ソリューションの欠点を補完した事例も存在します。 私が今まで調べたところ、かなり理想的な形はブルームバーグの<strong>ハイブリッドガードレール戦略</strong>があります。 下記はブルンバーグのAIグループ首席エンジニアであるラーマン氏の話<a href="https://www.cio.com/article/2503234/how-guardrails-allow-enterprises-to-deploy-safe-effective-ai.html">^12</a>です。</p>
<blockquote>
<p>これらすべてを実現するために、ブルームバーグはオープンソースモデルと商用モデルの両方を使用し、内部で訓練されたモデルも使用しています。 「私たちは特定の技術と強結合されていません」とラーマン氏は言います。 つまり、1つのプラットフォームのガードレールを使用するだけでは十分ではないということです。 ブルームバーグが1つのスタックで成功したとしても、同社は既製のガードレールツールが提供するものを超えることを望んでいるだろうと、彼は言います。</p>
</blockquote>
<p><img src="/assets/blog/authors/you/02/j.png" alt="https://en.wikipedia.org/wiki/Swiss_cheese_model">
<em>2-3-3.スイスチーズモデル</em></p>
<p>様々なガードレールを重畳させるスイスチーズ戦略を活用して、特定のガードレールにセキュリティ欠点が発生しても、幾層にも重なったガードレールで問題発生を防止できるようにしました。本当に理想的で優れた方法だとは思いますが、このような方法をする状況は非常に<strong>厳格なセキュリティ規定とリソースが存在する時に行うもの</strong>だと思います。 その為、大多数の企業は合理的なガードレールを選定するために、多くの考慮をする必要があります。 もう少し本質的な内容に近付けるためには、ガードレールの設計についての領域に触れることになります。</p>
<h3>2-4.　アプローチ・設計</h3>
<p>「<strong>Building Guardrails for Large Language Models</strong>」<a href="https://arxiv.org/abs/2402.01822">^13</a>という論文ではAIガードレール構築について深層的に扱っています。ここではAIガードレール設計は技術的複雑性と多様な利害関係者の要求事項をバランスよく調和させなければならない複合的な課題だと定めています。 </p>
<p>KTCが主に考慮した部分は<strong>Conflicting Requirements</strong>(相反する要求事項)、<strong>Multidisciplinary Approach</strong>(多学際的アプローチ)の二つの側面について扱っております。独自にガードレールの開発はしないため、開発論について扱う残りの二つは除外しました。</p>
<p>相反する要求事項はAIの安全性と性能の間の相反する要求事項を解決することを意味します。 </p>
<p><img src="/assets/blog/authors/you/02/k.png" alt="perplexity生成イメージ(ピクトグラム)＋自作">
<em>2-4-1.相反する要求事項</em></p>
<p>全てのガードレールを最初から適用してセキュリティを保障しようとしても、<strong>求める答えを出さないAIなら、AIを使用する目的自体が失われる</strong>ことになります。 そのため、最も重要なガードレールから始めて、必要に応じて徐々に範囲拡張することをお勧めします。 </p>
<p>ガードレールを徐々に拡張するためには多くの<strong>学際的アプローチ</strong>も必要です。 これは、<strong>技術的専門性、倫理的フレームワーク、ガバナンス構造、利害関係者の関与を組み合わせる</strong>ことを意味します。 例えば、 弊社ではSCoE(Security Center of Excellence)チーム、AI First部、プラットフォームG、クラウドインフラGなど多様な利害関係者がキックオフから参加しており、AIシステム構築初期から各分野の専門家集団がKTCに必要なガードレールの検討/適用を実施しています。そのガードレールを適用したAIシステムの一つがAgent-store<a href="https://blog.kinto-technologies.com/posts/2025-05-30-Agent-Store/">^14</a>です。</p>
<h3>2-5.　統合(Integration)</h3>
<p>最後に、<strong>ガードレールは完全なセキュリティを保証するサービスではない</strong>ことを注意する必要があります。 単一のガードレールではすべてのセキュリティリスクを完璧に防御することはできないし、ガードレール以外でも様々な側面からセキュリティリスクを考慮する必要があります。 例としては次のとおりです。</p>
<p><img src="/assets/blog/authors/you/02/l.png" alt="perplexity生成イメージ + https://www.veryicon.comのアイコン + 自作"> 
<em>2-5-1.生成AIガードレールの統合</em></p>
<p>その他にも説明できなかった要素が存在しますが、ガードレールだけで生成AIセキュリティを担保することはできず、<strong>様々な観点での対応が必須</strong>ということを理解していただければと思います。 </p>
<h2>3. まとめ</h2>
<p>生成AIの活用が本格化する中で、個人情報流出やプロンプトインジェクション、ハルシネーションなど、従来にはなかった多様なセキュリティリスクが顕在化しています。日本国内でもAIセキュリティに対する意識は高まりつつあり、政府・民間ともにガイドラインやガバナンスの整備が進められていますが、実際の現場では依然として不安や課題が残っています。</p>
<p>こうした状況において、生成AIガードレールは、AIシステムの安全性・倫理性を担保し、組織の目標や法規制に適合したAI活用を実現するための重要な仕組みとなります。ガードレールは入力・出力段階での検証やフィルタリングを通じて、さまざまなリスクを低減し、AIの有用性と安全性のバランスを実現します。</p>
<p>生成AIガードレールの実装にはオープンソースソリューションやエンタープライズソリューションなど複数の選択肢があり、それぞれにメリット・デメリットがあります。各社のリソースや要件に応じて最適な構成を選択することが重要です。また、生成AIガードレールだけに依存するのではなく、プロンプトエンジニアリングやPCS(People Centric Security)、継続的なモニタリングなど、様々なセキュリティ対策の組み合わせが必要となります。</p>
<p>AIセキュリティの実現には、技術面だけでなく、法務・倫理・ガバナンスなど多様な観点からのアプローチと、いくつもの専門部署の連携が欠かせません。まずは最も重要なリスクから段階的に生成AIガードレールを導入し、状況に応じて拡張していくことが、実践的で現実的な戦略となります。</p>
<p>KTCでも生成AIのセキュリティをどのように保障するかを検討し、多々実施している施策の1つとして生成AIガードレールを導入しています。</p>
<p>次回（後編）の記事では、今回の記事の内容を踏まえ、どういった観点から AWS Bedrock Guardrails の採用に至ったのか、AWS Bedrock Guardrails はどのように機能するのか、そしてAWS Bedrock Guardrailsを通じてどのような試みをしたのかについて取り上げる予定です。ぜひお楽しみに！</p>
<hr>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/you/02/00.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[【SRE NEXT】あなたの"NEXT"は？312件のアンケート結果！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-31-sre-next-thanks/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-31-sre-next-thanks/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[あなたの"NEXT"は？312件のアンケート結果！]]></description>
            <content:encoded><![CDATA[<p>こんにちは！SREチームのkasaiです。</p>
<p>KINTOテクノロジーズ株式会社は、「SRE NEXT 2025」にて、プラチナスポンサーとして協賛いたしました！</p>
<p>ブースにお越しいただいた方ありがとうございました！
参加者の皆様とお話しすることができ、いい刺激になりましたし、さまざまな学びを得ることができました。</p>
<p>SRE NEXTに参加した弊社社員による座談会の様子は、以下の記事で読むことができますので、ぜひご覧ください！
<a href="https://blog.kinto-technologies.com/posts/2025-07-18-sre_next_look_back/">https://blog.kinto-technologies.com/posts/2025-07-18-sre_next_look_back/</a></p>
<p>ブースでは「あなたのNEXTは？」というお題でアンケートをとらせていただきました。
ご協力いただいた方ありがとうございます！</p>
<p>この記事ではアンケート結果を共有したいと思います。</p>
<p><img src="/assets/blog/authors/kasai/20250731/board.png" alt="">
<em>ボードに貼られた付箋たち（左1日目,右2日目）</em>
![](/assets/blog/authors/kasai/20250731/20250724_162209.jpeg =400x)
<em>2日分の付箋の束</em></p>
<h1>アンケート結果</h1>
<p>アンケートへの回答数は2日間で312件でした！
回答内容をいくつかのカテゴリに分類して共有したいと思います。
※回答のカテゴリ分類はGeminiを使って分類しています。</p>
<ol>
<li><p>SRE・組織文化 (60件)
SREの実践や文化の醸成、採用、チームビルディングに関する回答です。</p>
<ul>
<li>SREを推進できるようになる</li>
<li>SRE文化の浸透</li>
<li>エンジニア採用を成功させる</li>
<li>ワクワクできるエンジニア組織にする！</li>
<li>Embedded SRE共通基盤</li>
</ul>
<p>SREとしてどう振る舞うか、SRE的な考えをどう浸透させるかといった話や、採用が難しくもっと人が欲しいといった声が多くあったようです。
私もどうすれば他のチームの人にSRE的な考えをメリットとして感じてもらえるか、上手く伝えられるかを悩んでいるので、とても共感することができました。</p>
</li>
<li><p>AI活用 (58件)
AIやLLMを活用した業務効率化や、新しい価値創出に関する回答です。</p>
<ul>
<li>AI活用をインフラ・SREに</li>
<li>Agentic DevOpsの実現</li>
<li>インシデント対応をAIだけでできるようにする</li>
<li>AI活用でトイル削減</li>
<li>AI利用で週休7日！！</li>
</ul>
<p>もうすでに使っていて活用を広げたいと思っている人もいれば、これから使いたいと思っている人もいました。
私も生成AIは使っていますが、コードを書く時の補助程度にしか使っておらず、例えば、インシデント対応をすべてAIでやってもらえるようにするといったことはできていないので、そういうコードを書く時以外にも使えるように挑戦していきたいと思いました。</p>
</li>
<li><p>技術・サービス改善 (58件)
SLI/SLO導入、パフォーマンス、技術的負債の解消など、サービス品質向上に関する回答です。</p>
<ul>
<li>SLI/SLOの導入</li>
<li>パフォーマンスを爆速にしてユーザ体験よくしたい</li>
<li>技術負債の解消</li>
<li>運用を全て自動化したい！</li>
<li>eBPFやっていき</li>
</ul>
<p>SLI/SLOの導入やオブザーバビリティの拡充が多くありました。
私もSLI/SLOについてまだまだ勉強することが多くありますし、導入実績も少ないので、どんどん実践していき知識・知見をためていきたいと思います。</p>
</li>
<li><p>事業・キャリア (41件)
事業への貢献、プロダクト成長、IPO、転職や昇進などキャリアに関する回答です。</p>
<ul>
<li>ビジネスサイドを理解して仕事する</li>
<li>事業Growth！</li>
<li>IPO</li>
<li>CTOになる！</li>
<li>転職</li>
</ul>
<p>技術的な話だけではなく、事業に関する話もありました。
私は信頼性に関することを開発側だけではなく、ビジネス側とも会話する必要があると感じていますが、他にも同じような考えの人がいたり、ビジネスをどうグロースさせていくかを考えている人もいたようです。
私自身はまだまだビジネス側と距離がある状態になってしまっているので、この距離を縮めて、SRE的に事業へ貢献できたらと思いました。</p>
</li>
<li><p>登壇・アウトプット (30件)
カンファレンス登壇やブログ執筆など、社外への情報発信に関する回答です。</p>
<ul>
<li>キートーク頑張る</li>
<li>毎月アウトプット</li>
<li>PHPカンファレンス2026北海道やります！！</li>
<li>CFPたくさん出す！</li>
<li>Speakerになる</li>
</ul>
<p>カンファレンスという場だったからかもしれませんが、登壇に関するものも多かった印象です。
SRE NEXTに登壇すると書いていた人もいたので、登壇したくなるようなイベントを開催できていることがとても素晴らしいなと思いました。
私もブログや登壇等で、弊社のことについて広め、多くの人にどういう会社なのかということを知っていただけるようにしていきたいと思いました。</p>
</li>
<li><p>その他 (65件)
上記のカテゴリーに当てはまらない、プライベートな目標やユニークな回答です。</p>
<ul>
<li>サウナ行く！</li>
<li>南極でペンギン見る</li>
<li>幸せになる</li>
<li>夜ご飯を食べる</li>
<li>旅行に行く</li>
</ul>
<p>仕事やSREに関係ないことも書いてもらっていました。
健康の維持や気分転換も仕事する上で必要だなと再認識しました。</p>
</li>
</ol>
<p>せっかくなのでこのままGeminiに総括もお願いしました！</p>
<blockquote>
<p>全312件の回答、誠にありがとうございました。今回のアンケート結果からは、現代のエンジニアが持つ、技術、組織、事業、そして個人としての多岐にわたる健全な関心事が鮮明に浮かび上がってきました。</p>
<p>最も多くの回答が集まったのは「SRE・組織文化」であり、これはSREを単なる技術的な役割としてだけでなく、チームや組織全体の文化として捉え、その醸成や発展に強い意欲を持っていることの表れです。僅差で「AI活用」と「技術・サービス改善」が続き、最先端技術への探求心と、サービスの信頼性向上というSREの核となる責務への高い意識が両立していることがわかります。これら3つのカテゴリーがほぼ同数で上位を占めている点は、非常にバランスの取れたエンジニアリング観を象徴していると言えるでしょう。</p>
<p>また、「事業・キャリア」や「登壇・アウトプット」に関する回答も多く、日々の業務を越えて、事業への貢献やコミュニティへの価値還元といった、より広い視野で自らの役割を捉えている方が多いことも印象的です。</p>
<p>そして、「その他」のカテゴリーは、仕事だけでなく、健康やプライベート、個人の夢といったウェルビーイングを大切にする価値観を明確に示しています。</p>
<p>総じて、技術的な卓越性を追求しつつも、組織や人、事業への貢献を忘れず、個人の人生の充実も図るという、現代的で成熟したエンジニア像が浮かび上がる、非常に示唆に富んだアンケート結果でした。</p>
</blockquote>
<p>以上がアンケート結果およびそのまとめでした。
SRE NEXT 2025のテーマは「Talk NEXT」です。そのためそれにちなんだお題、またブースに来てくれた人と対話できるようにという思いでこのアンケートをすることに決めました。
抽象度の高いお題にしたことによって、SREsだけではなく、他ロールのエンジニア、ひいてはエンジニア以外の人とも対話することができて、とても良いお題だったと感じています。</p>
<p>回答に関して、昨今のトレンドである生成AI周りの話が多く、SRE領域でAIをどう使っていくかを考えている人が多かった印象です。
また、私自身、システムのメトリクス取得はできていますが、そのデータを使ってどう意思決定をするか、ビジネス側に影響を与え、事業に貢献できるかといったことが、まだまだできていないので、そこができるようになるとSREとしても面白くなっていくと思っていますし、挑戦していきたいなと思いました。
そのほかの回答も共感できるものが多く、当日ブースでは楽しく会話させていただきました。ありがとうございます！</p>
<h1>最後に</h1>
<p>最後に改めまして、ブースにお越しいただきありがとうございました！
たいへん多くの方にお越しいただきました。</p>
<p>SRE NEXTの運営の方々も素晴らしいイベントありがとうございました。
スタンプラリーなどの企画のおかげでこれだけ多くの人がブースに訪れてくれたと思います。
来年のSRE NEXTも何かしらの形で関われたらと思います。</p>
<p>また来年のSRE NEXTでお会いしましょう！</p>
<h1>We Are Hiring！</h1>
<p>KINTOテクノロジーズでは一緒に働く仲間を探しています！
まずは気軽にカジュアル面談からの対応も可能です。少しでも興味のある方は以下のリンクからご応募ください！
<a href="https://hrmos.co/pages/kinto-technologies">https://hrmos.co/pages/kinto-technologies</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/kasai/20250731/cover_image.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Hardening Designers Conference 2025 参加レポート]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-01-HardeningConference2025-Report/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-01-HardeningConference2025-Report/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[セキュリティ堅牢化における『分断』と自社での取り組み]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは！KINTO テクノロジーズ クラウドセキュリティGの大高です。
普段は、クラウド環境のガードレールの整備とCSPMや脅威検知を利用した監視と改善活動に取り組んでいます。</p>
<p>現在のセキュリティを取り巻く状況について最新の情報にキャッチアップすべく、<a href="https://wasforum.jp/2025/06/hardening-designers-conference-2025/">Hardening Designers Conference 2025</a>に参加しましたので、レポートをお届けします。</p>
<h1>Hardening Project とは</h1>
<p>Hardening Projectは、実践的なサイバーセキュリティスキルの向上を目的とした競技型のイベントです。
プログラムの参加者は脆弱性を抱えたシステムを運用しながら、外部からの攻撃に対して防御・復旧・改善を行い、現場さながらの対応力を養います。
単なる技術力だけでなく、チームワークやドキュメント整備、運用体制の構築など、総合的なインシデント対応力が評価される点が特徴です。</p>
<p>今回参加した、『Hardening Designers Conference 2025』は、10月の競技型イベントに向けて、『Invisible Divide』というテーマをもとにした、ハンズオンとカンファレンスという位置付けのイベントです。</p>
<h1>Day1 Hands-on Program</h1>
<p>ハンズオンでは、『Living off the Land』呼ばれる攻撃手法について体験しました。
Living off the Landとは、攻撃者がシステムに元々備わっている正規ツールや機能を悪用して侵入・操作を行う手法です。例えば、Windows 環境ではPowerShellやWMIなどを使うことで、攻撃を行います。</p>
<p>ポイントは、攻撃者が使用するツールは標準搭載のツールや機能であり、外部から持ち込んだファイルなどを使用しないため、通常のオペレーションとの判別が難しく、セキュリティツールによる検知が困難になるという点です。</p>
<p>攻撃に使われるコマンド例には、かつて私がシステム管理をしていた際にお世話になったコマンドもいくつか含まれていました。
通常の運用の中で使われないツールやコマンドであれば無効化することで対策できるかもしれませんが、頻繁に使用し簡単に無効化できないものであれば、ログを取得して監視するなどしかないのかもしれません。</p>
<p>サーバー攻撃が高度化し、攻撃操作と正規のオペレーションの境界が非常にわかりにくくなっていることを実感したワークショップでした。</p>
<h1>Day2・3 Conference Program</h1>
<p>2・3日目は、様々なLTやセッションを通して、サイバーセキュリティの文脈における『分断』について、最新技術の共有や取り組みの紹介、トークセッションが行われました。</p>
<p>セキュリティの現場では、さまざまなステークホルダー間に「分断」が存在し、それがHardening（セキュリティ堅牢化）を阻む障害となることがあります。例えば、実際の現場では以下のような分断が発生しています。</p>
<p><img src="/assets/blog/authors/k.otaka/Techblog-Hardening-1.png" alt="セキュリティ分断の概要図"></p>
<h3>開発・運用・セキュリティ担当者間の分断</h3>
<p>機能の実装や運用の効率性を重視するあまり、セキュリティが後回しにされることがあります。例えば、パスワード管理やアカウント制御が適切に行われず、脆弱性が生まれることがあります。これを防ぐには、セキュリティを「制約」ではなく「品質の一部」として捉え、開発初期からセキュリティ要件を組み込むセキュリティバイデザインやシフトレフトの取り組みが重要になってきます。</p>
<h3>システム利用者とシステム開発者・運用者の分断</h3>
<p>システムの利用者は使いやすさを求める一方で、セキュリティの重要性を理解していないことがあり、技術者はシステムへの機能要求とセキュリティの実装の間で板挟みになることがあります。このギャップを埋めるために、ユーザーへの教育とシステムの構築・運用の際に利用者との丁寧なコミュニケーションを図り理解を求める姿勢が必要になります。</p>
<h3>ルール策定者と実行者の分断</h3>
<p>ルール策定者（セキュリティ担当者）は公的機関や専門機関が公表している様々なガイドラインを参考に理想的なベースラインやルールを定めますが、現場（システム開発・運用担当者）ではシステム上の制約や運用負荷によりベースラインに忠実な実装が困難な場合があります。実際に適用するには、制約や運用負荷を考慮した上で、適切にセキュリティを実装できるように柔軟な対応を行うことが重要です。</p>
<h3>攻撃者と防衛者の分断</h3>
<p>攻撃者が技術革新と連携によって高度な攻撃を仕掛ける一方、防衛者はコストや関係者の理解不足によりセキュリティ対策が後手に回ることがあります。サイバーセキュリティ被害にあった企業は情報開示をためらい、同様の被害を防ぐための知見が共有されないこともしばしばです。防衛側も情報共有や連携を強化したいところですが、なかなか思うように進まない状況があります。</p>
<h3>AIと人間の分断</h3>
<p>AIを使用したプログラムコードの生成や生成AIを用いたSOC業務の高度化など、ITの現場では生成AI活用の取り組みが広がっていますが、生成AIは明確な指示がなければセキュリティを考慮することができない場合が多いというのが現状のようです。生成AIの発展は目覚ましいですが、まだ人間とAIの間には、能力に差があるようです。AIを適切に活用するには、プロンプト設計やガードレールの導入など、人間側の工夫がまだまだ不可欠です。</p>
<p>改めて考えると、実に多くの『分断』が存在していることがわかります。
セキュリティについて、こういった切り口から考えたことはなかったため、非常に参考になりました。</p>
<h1>『分断』を乗り越えるKINTOテクノロジーズの取り組み</h1>
<p>私たちクラウドセキュリティGでは、「ビジネスのためのセキュリティ」を基本方針とし、セキュリティがビジネスの足かせになるのではなく、ビジネスを加速させる存在であるべきだと考えています。  </p>
<p>セキュリティ対策は以下の2つの側面から実施しています </p>
<ul>
<li>予防的ガードレール：アカウントのセキュリティプリセット環境（開発チームに提供する前に最低限必要なセキュリティ設定を事前に実装したアカウント）を提供し、初期段階から安全な設計を支援。</li>
<li>発見的ガードレール：Sysdig、AWS Security Hub、Amazon GuardDutyなどを活用したSOC監視により、リアルタイムでの脅威検知と対応および定期的なPosture管理による、問題のある設定のカイゼン活動を実施。</li>
</ul>
<p>これらのセキュリティ対策・運用を通して、自社のセキュリティガイドラインを遵守しつつ、開発者が必要なセキュリティを担保された環境かつ自由に安心して開発に集中できる環境を整備する取り組みを進めています。これは、ルール策定者と実行者の分断及び開発・運用・セキュリティ担当者間の分断を乗り越えるための取り組みといえます。</p>
<p>また、AIセキュリティについても少しずつ取り組みを開始しています。（具体的な取り組みについては<a href="https://blog.kinto-technologies.com/posts/2025-06-16-aispm/">こちら</a>）しかし、技術の進化やトレンドの変化が非常に速く、現状では少々後手に回っている印象です。社内においても生成AIの業務利用やプロダクトへの実装は活発に進んでおり、どのようにコントロールを実装し、AIとの分断を乗り越えていくのかが今後のチャレンジとなります。  </p>
<p>さらに、IPAが公開している「家づくりで理解する要求明確化の勘どころ」を参考に、KINTOテクノロジーズのシステム開発プロジェクトにおける心構えを見直す取り組みを進めています。これは、システム構築に限らず、セキュリティの観点からも、システム利用者と技術者の間にある分断を意識し、より良い関係性と成果を生み出すための取り組みといえます。IPAの家づくりについては、<a href="https://www.ipa.go.jp/archive/files/000065172.pdf">こちら</a>を参照ください。</p>
<h1>まとめ</h1>
<p>Hardening Designers Conference 2025を通して、普段自分自身が意識したことのない『分断』という観点から、さまざまなセキュリティの動向を学ぶことができ、有意義な時間となりました。また、自分の組織のセキュリティを『分断』という観点から確認してみることで、今の取り組みについて再確認することができました。</p>
<p>これからも、より良いセキュリティを実現すべく『分断』を乗り越えていく取り組みを継続・改善していきたいと思いました。</p>
<h1>さいごに</h1>
<p>私の所属するクラウドセキュリティ グループでは、一緒に働いてくれる仲間を募集しています。クラウドセキュリティの実務経験がある方も、経験はないけれど興味がある方も大歓迎です。お気軽にお問い合わせください。</p>
<p>詳しくは、<a href="https://hrmos.co/pages/kinto-technologies/jobs?category=2037393016523087875">こちらをご確認ください。</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: May 2025 Update]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-01-newcomer-202505-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-01-newcomer-202505-en/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We asked six new members who joined us in May 2025 to share their impressions after joining our company, and here is a summary of what they said.]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1>** Introduction**</h1>
<p>Hello, I&#39;m satoshi, and I joined the company in May 2025! In this article, I interviewed new members who joined us in May 2025 and gathered their first impressions after joining KINTO Technologies. I hope this article will be helpful for those interested in KINTO Technologies (KTC) and serve as a nice reflection for those featured here!</p>
<h1><strong>Y</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/78e8f991-3db3-4f95-bb68-842204e6efac.png" alt="Y"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>This is Y of the Human Resources Group. I previously worked as an engineer.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The Organizational Human Resources Team I belong to consists of three members, but we often collaborate with the Talent Acquisition Team and the Labor and General Affairs Team within the same group, as well as with the KINTO administration department.</li>
</ul>
</li>
<li>**What was your first impression of KTC when you joined? Were there any surprises? **<ul>
<li>Many people gave me a calm and mature impression, but communication is very open and friendly.</li>
</ul>
</li>
<li>**What is the atmosphere like on-site? **<ul>
<li>I can talk to anyone about anything, which is a big help.</li>
</ul>
</li>
<li>**How did you feel about writing a blog post? **<ul>
<li>This entry doesn&#39;t have any tech elements, but I hope that’s okay.</li>
</ul>
</li>
<li><strong>Question from Satoshi to Y</strong><blockquote>
<p>Please share a cute story about your cat.</p>
</blockquote>
<ul>
<li>He seems to like the scent of detergent, so when I fold laundry, he often comes over to sniff, lick, and rub against me.</li>
<li>Even freshly washed clothes and towels quickly end up covered in cat hair.</li>
</ul>
</li>
</ul>
<h1><strong>D.K</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/7bb2b777-2d64-4cb1-b90e-54e0008dc87c.png" alt="D.K"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I’m in charge of the screening and credit management business area in the Business Systems Development Division.</li>
<li>I’m involved in financial systems, and my previous job was in sales finance.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>It&#39;s actually a one-person team. While continuing to define and develop the product requirements, our next challenge will be to gather members to operate and maintain the product after its release in June next year and build a team.</li>
</ul>
</li>
<li>**What was your first impression of KTC when you joined? Were there any surprises? **<ul>
<li>I was impressed by the fact that there were more events and in-house demonstration tests related to the latest technologies, such as AI and IoT, than I had expected from the Tech Blog, which was a pleasant surprise.</li>
<li>KTC is engaged in a wide range of technology research and product development that is not limited to leasing, and I felt that it was the perfect company for someone who loves new things.</li>
</ul>
</li>
<li>**What is the atmosphere like on-site? **<ul>
<li>It&#39;s an environment where you can feel free to ask questions, seek advice, or chat with people beyond your own team. I often see members engaging in serious discussions, regardless of whether it is work-related or not.</li>
<li>We also have a close relationship with KINTO, the profit center, and I feel that the style of product development, where the technical side takes the lead with a sense of ownership, is very characteristic of KTC.</li>
</ul>
</li>
<li>**How did you feel about writing a blog post? **<ul>
<li>I had been referring to the Tech Blog, so I felt like it was finally my turn.</li>
<li>I tried to share my honest opinions as much as possible. I hope this will help people outside the company to get a feel for our atmosphere.</li>
</ul>
</li>
<li><strong>Question from Y to D.K</strong><blockquote>
<p>Please share your recommended lunch spots around the Muromachi office.</p>
</blockquote>
<ul>
<li>It&#39;s a rather unassuming place, but Sobayoshi Nihombashi flagship store.</li>
<li>This udon and soba restaurant is directly managed by a bonito flakes wholesaler, so the interior is filled with the aroma of dashi.</li>
<li>Be sure to order rice as well. It comes with bonito flakes, and the aroma when you eat the bonito rice with a little soy sauce is irresistible!</li>
</ul>
</li>
</ul>
<h1><strong>N</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/564ef475-a658-459a-909a-fb652e7f1774.png" alt="N"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I&#39;m in charge of quality assurance (QA) for apps in the Quality Assurance Group. My previous job was also in app QA.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The Quality Assurance Group has 11 members in total, and the team I belong to has four members, each responsible for a specific app.</li>
</ul>
</li>
<li>**What was your first impression of KTC when you joined? Were there any surprises? **<ul>
<li>My impression of KTC was that it was a place with many mature, calm people, but I also got the impression that there are many passionate people who actively share and learn technical information.</li>
</ul>
</li>
<li>**What is the atmosphere like on-site? **<ul>
<li>The atmosphere is friendly, and you can feel free to ask questions or seek advice.</li>
</ul>
</li>
<li>**How did you feel about writing a blog post? **<ul>
<li>It&#39;s been a while since I last wrote a Tech Blog, but I&#39;m hoping to get used to it as I&#39;ll have more opportunities to write in the future.</li>
</ul>
</li>
<li><strong>Question from D.K. to N</strong><blockquote>
<p>I heard you like comedy, so please tell me who your favorite comedians are these days!</p>
</blockquote>
<ul>
<li>It&#39;s very specific, but the skit by the comedy duo Tensai Pianist about inspecting a doorknob is the best!</li>
<li>Personally, I like all the comedians who appeared in M-1 from around 2016 to 2019. Please be sure to check them out.</li>
</ul>
</li>
</ul>
<h1><strong>M.K</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/mk_icon.jpg" alt="M.K"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I manage data in the DataOps Group. I work at Osaka Tech Lab.</li>
<li>Previously, I used to work in the fields of online advertising and online media.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>There are about 10 members in the group, but in my regular work I team up with another member from Osaka.</li>
<li>The group&#39;s manager is also in Osaka, which makes communication easy.</li>
</ul>
</li>
<li>**What was your first impression of KTC when you joined? Were there any surprises? **<ul>
<li>The working style and tools used were similar to those of the companies I had worked for before, so I was able to start working without any discomfort.</li>
</ul>
</li>
<li>**What is the atmosphere like on-site? **<ul>
<li>There is an atmosphere where it&#39;s easy to ask any questions, and I found it easy to catch up.</li>
<li>Also, perhaps unique to Osaka Tech Lab, people from other groups would proactively talk to me in the office, which made it easy to fit in.</li>
</ul>
</li>
<li>**How did you feel about writing a blog post? **<ul>
<li>I also used the Tech Blog when I was looking for a job, so I hope it will be helpful to someone else in the future.</li>
</ul>
</li>
<li><strong>Question from N to M.K</strong><blockquote>
<p>Since you drive, please tell us some recommended places to go for a drive.</p>
</blockquote>
<ul>
<li>I personally use car sharing a lot, and since I don&#39;t drive regularly, I try to use highways with two or more lanes in each direction that are safe and comfortable whenever possible.</li>
<li>In the Kanto region, I like Chiba and Ibaraki, and in the Osaka area, the Chugoku Expressway is easy to drive on.</li>
</ul>
</li>
</ul>
<h1><strong>m</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/514c16b6-e439-4a6f-ae53-4ab492a4a61a.jpeg" alt="m"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I’m based at Osaka Tech Lab and handle app QA in the Quality Assurance Group. Before joining KTC, I worked in development.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The Quality Assurance Group has 11 members in total, and the team I belong to has four members, each responsible for a specific app (I’m on the same team with N).</li>
</ul>
</li>
<li>**What was your first impression of KTC when you joined? Were there any surprises? **<ul>
<li>I got the impression that KTC is very proactive about process improvements using generative AI. I also felt that many members are highly passionate about technology.</li>
</ul>
</li>
<li>**What is the atmosphere like on-site? **<ul>
<li>Many members are friendly, which makes it easy to fit in. It&#39;s also an environment where I can feel free to ask questions.</li>
</ul>
</li>
<li>**How did you feel about writing a blog post? **<ul>
<li>I had been reading Tech Blog before joining KTC, so when it was finally my turn, I thought, “At last...!”</li>
</ul>
</li>
<li><strong>Question from M.K. to m</strong><blockquote>
<p>The Osaka office has been renovated. How does it compare to the previous one?</p>
</blockquote>
<ul>
<li>The view is fantastic, and the scenery is really beautiful from morning to night.</li>
<li>It also feels more open, and for me personally, it has become a very comfortable environment to concentrate in.</li>
</ul>
</li>
</ul>
<h1><strong>satoshi</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/43420416-a4ef-43df-bf04-27d587693e62.png" alt="satoshi"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I’m part of the my route Development Division, working on backend development.</li>
<li>In my previous job, I was also engaged in backend development in the web field, and before that, I worked as a network engineer at a company in the networking industry.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The Backend Team has four members, plus more than 10 members from partner companies.</li>
<li>Since July, the Frontend Team has also joined us in the same office, making it a livelier environment!</li>
</ul>
</li>
<li>**What was your first impression of KTC when you joined? Were there any surprises? **<ul>
<li>I thought it might be a formal company, but it&#39;s not like that at all. We can develop while actively communicating.</li>
<li>As for AI, the whole company is very enthusiastic about it, and being able to use AI in all aspects of our work has been a huge help.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>We develop products in a friendly way. It&#39;s also a very open environment where it’s easy to consult with others.</li>
<li>Since my route is a B2C product, we get many inquiries from customers, and every day is pleasantly busy.</li>
</ul>
</li>
<li>**How did you feel about writing a blog post? **<ul>
<li>I&#39;ve read tech blogs from various companies before, so I felt a bit sentimental thinking, “It’s finally my turn.”</li>
</ul>
</li>
<li><strong>Question from m to satoshi</strong><blockquote>
<p>Please tell us something about your hometown.</p>
</blockquote>
<ul>
<li>I&#39;m from Kyoto Prefecture!　I grew up in a tough environment with hot, humid summers and bitterly cold winters.</li>
<li>I still go back occasionally and enjoy touring Kyoto alongside visitors from overseas.</li>
</ul>
</li>
</ul>
<h1><strong>In Closing</strong></h1>
<p>Thank you everyone for sharing your thoughts after joining our company!</p>
<p>There are more and more new members at KINTO Technologies every day! We will be posting more stories from our newcomers across divisions, so stay tuned!</p>
<p>And yes: we are still hiring! KINTO Technologies is looking for new teammates to join us across a variety of divisions and roles. For more details, please check <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年5月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-01-newcomer-202505/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-01-newcomer-202505/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年5月に入社した6人の皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1><strong>はじめに</strong></h1>
<p>こんにちは、2025年5月入社の satoshi です！
本記事では、2025年5月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。
KINTO テクノロジーズ(以下、KTC)に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1><strong>Y</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/78e8f991-3db3-4f95-bb68-842204e6efac.png" alt="Y"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>人事グループのYです。前職ではエンジニアをしていたこともあります</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>私の所属している組織人事チームは3名ですが、同じグループ内の採用チーム・労務総務チームや、KINTO管理部の皆さんとも連携する機会が多くあります</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>落ち着いた大人な印象の方が多いですが、コミュニケーションはとてもフランクです</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>何でも相談できるので、とても助かっています！</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>techな要素は無いエントリですが、大丈夫ですかね笑</li>
</ul>
</li>
<li><strong>satoshi ⇒ Yさんへの質問</strong><blockquote>
<p>飼ってる猫ちゃんのカワイイエピソードを教えてください！</p>
</blockquote>
<ul>
<li>洗剤の匂いが好きなのか、洗濯物をたたんでいるとよくクンクン・ペロペロ・スリスリしに寄ってきます</li>
<li>せっかく洗った服やタオルも、あっという間に猫毛まみれになります笑</li>
</ul>
</li>
</ul>
<h1><strong>D.K</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/7bb2b777-2d64-4cb1-b90e-54e0008dc87c.png" alt="D.K"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>業務システム開発部で、審査・与信管理業務領域を担当しています。</li>
<li>金融システムに関与しており、前職も販売金融でした。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>実は1人チームです。プロダクトの要件定義と開発を進めながら、来年６月の本リリース後から運用・保守するメンバーを集め、チームビルディングするのが今後の課題です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>テックブログから感じていた以上にAIやIoTなど最新技術情報に関するイベントや社内実証試験が多いことが印象的で、良い意味でのギャップでした。</li>
<li>KTCはリース業務に縛られない幅広い技術の研究やプロダクト開発が行われており、新しいモノ好きに最適な会社だと感じました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>チームを超えて気軽に質問・相談・雑談できる雰囲気です。業務内外隔てなく真剣に議論する姿をよく見かけます。</li>
<li>また、業務部門であるKINTOとの距離感が近く、当事者意識を持って技術側がリードしてプロダクトを開発していくスタイルがKTCらしいなと感じています。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>TechBlogを参照していたので、とうとう周ってきたなと。</li>
<li>できるだけ率直な意見を記載しました。社外の方が弊社の雰囲気を感じ取る参考になれば幸いです。</li>
</ul>
</li>
<li><strong>Yさん ⇒ D.Kさんへの質問</strong><blockquote>
<p>室町オフィス周辺のおすすめランチを教えてください！</p>
</blockquote>
<ul>
<li>地味ですが、「そばよし　日本橋本店」です。</li>
<li>鰹節問屋直営のうどん・蕎麦屋なので、店内がだしの香りでいっぱいです。</li>
<li>是非ご飯も注文してみてください。鰹節の削粉がついてくるので、少しだけ醤油をかけて食べるおかかご飯が鰹節の香りがたまりません！</li>
</ul>
</li>
</ul>
<h1><strong>N</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/564ef475-a658-459a-909a-fb652e7f1774.png" alt="N"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>QAグループでアプリの方のQAを担当しています。前職もアプリのQAでした。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>QAグループ全体は11名で、私が所属しているチームは４名で各アプリを担当しています</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>KTCへの印象は大人な方が多く穏やかな印象を持っていましたが、熱量が高く技術的な情報を発信されている/学ばれている方が多い印象も加わりました！</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>和気藹々としていて気軽に質問したり、相談も出来る雰囲気です！</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>TechBlogを書くのは久しぶりですが、これから何かと書く機会が増えるので慣れていこうと思っています</li>
</ul>
</li>
<li><strong>D.Kさん ⇒ Nさんへの質問</strong><blockquote>
<p>お笑いが好きだと伺っていたので、最近の推し芸人を教えてください！</p>
</blockquote>
<ul>
<li>すごくピンポイントなのですが、天才ピアニストさんのドアノブ点検のコント最高です！</li>
<li>個人的にM-1 2016~2019あたりの漫才師さんどの方も好きです！ぜひ観てみてください</li>
</ul>
</li>
</ul>
<h1><strong>M.K</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/mk_icon.jpg" alt="M.K"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>DataOpsグループでデータの管理をしています。Osaka Tech Labで働いています。</li>
<li>以前はインターネット広告やインターネットメディアの領域にいました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>グループは全部で10人ほどですが、普段の仕事ではもう一人の大阪のメンバーとチームを組んでいます。</li>
<li>グループのマネージャーも大阪にいるので、コミュニケーションは取りやすい環境にあります。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>今までに働いた会社と働き方や使っているツールが近かったので、違和感なく働き始めることができました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>なんでも質問しやすい雰囲気があり、キャッチアップしやすいと感じました。</li>
<li>また、Osaka Tech Lab特有かもしれませんが、違うグループの方にもオフィスで積極的に話しかけてもらえるので馴染みやすかったです。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>私も仕事をさがしていたときTechBlogにお世話になったので、今度は誰かの参考になれば嬉しいです。</li>
</ul>
</li>
<li><strong>Nさん ⇒ M.Kさんへの質問</strong><blockquote>
<p>車を運転されるということで、ドライブでおすすめの場所教えてください！</p>
</blockquote>
<ul>
<li>私自身はカーシェアをよく使っていて、普段から乗るわけではないので、なるべく安全で快適な片側2車線以上の高速道路を使うようにしています。</li>
<li>関東なら千葉や茨城方面、大阪近辺だと中国自動車道が走りやすくて好きです。</li>
</ul>
</li>
</ul>
<h1><strong>m</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/514c16b6-e439-4a6f-ae53-4ab492a4a61a.jpeg" alt="m"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>OsakaTechLabに所属しており、QAグループにてアプリのQAを担当しています。前職までは開発をしていました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>QAグループ全体は11名で、私が所属しているチームは４名で各アプリを担当しています。(nさんと一緒のチームです)</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>生成AIを利用したプロセス改善等に関してとても意欲的である印象を持ちました。また技術に対しても熱量の高い方が多い印象です。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>気さくな方が多く馴染みやすい感じです。質問も気軽にできる環境です。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>KTC入社前にTechBlogを見ていたので自分もついに、、、！と思っていました笑</li>
</ul>
</li>
<li><strong>M.Kさん ⇒ mさんへの質問</strong><blockquote>
<p>大阪オフィスが新しくなりました。前のオフィスと比べてどうですか？</p>
</blockquote>
<ul>
<li>見晴らしがとても良いです。（朝から夜までずっと景色が本当に綺麗）</li>
<li>開放感があって個人的にはとても集中しやすい環境になりました。</li>
</ul>
</li>
</ul>
<h1><strong>satoshi</strong></h1>
<p><img src="/assets/blog/authors/f-satoshi/202505-newcomer/43420416-a4ef-43df-bf04-27d587693e62.png" alt="satoshi"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>my route開発部でバックエンド開発を行なっています</li>
<li>前職はWeb系で同じくバックエンド開発、前々職はネットワーク系の企業でネットワークエンジニアをやっていました</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>バックエンドチームは4人、その他に協力会社の方々が10人以上います</li>
<li>7月からフロントエンドチームも同じオフィスに合流し、賑やかになりました！</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>堅い会社なのかなと思いきやそんなことはなく、活発にコミュニケーションを取りながら開発出来ています</li>
<li>また、AIに関しては全社的に熱量高く取り組んでいて、業務全般でAIを活用することができ凄く助かってます</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>仲良く開発しています。相談もとてもしやすい雰囲気です</li>
<li>my routeはBtoCのプロダクトのためお客様からの問い合わせも多く、日々わちゃわちゃしています</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>これまで様々な会社のテックブログを読んでいたので、ついに自分もという感慨深い？気持ちです</li>
</ul>
</li>
<li><strong>mさん ⇒ satoshiへの質問</strong><blockquote>
<p>出身地について何か語ってください、、！</p>
</blockquote>
<ul>
<li>出身は京都府です！　夏は高温多湿、冬は極寒の過酷な環境で育ちました</li>
<li>今でもたまに帰り、インバウンドの人々に混じって京都巡りを楽しんでいます</li>
</ul>
</li>
</ul>
<h1><strong>さいごに</strong></h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Improving CWV Scores Through Kaizen Day and Study Sessions]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-20-factory-improve-cwv-score-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-20-factory-improve-cwv-score-en/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A Story About Improving CWV Scores]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello. Thank you for reading this article!</p>
<p>My name is Nakamoto, and I work on developing the frontend for<a href="https://factory.kinto-jp.com/">KINTO FACTORY</a> (hereafter FACTORY), a service that enables users to upgrade their current vehicles.</p>
<p>This time, I would like to introduce a case where we improved page performance (CWV) by utilizing <strong><em>Kaizen</em> DAY</strong> that was implemented in the FACTORY Development Group and <strong>the internal frontend study sessions</strong> </p>
<h1><em>Kaizen</em> DAY and Frontend Study Sessions</h1>
<h3><em>Kaizen</em> DAY</h3>
<p>In the FACTORY Development Group, we made it a practice to ticket technical debts and non-urgent tasks noticed during project development, keeping them in the backlog so they wouldn’t be forgotten. As this practice went on, daily project development rarely paused, causing the improvement tasks to accumulate in the backlog, eventually pushing the total number of tickets to over 100.</p>
<p>As a result, during Scrum retrospectives, concerns were raised about leaving those improvement tasks unattended. So, as a team, we launched an initiative to dedicate one afternoon each week solely to addressing them, blocking that time in Outlook to prevent meetings from other departments.</p>
<p>This time, we decided to use <em>Kaizen</em> Day to focus on improving page performance that we hadn’t been able to tackle until now, which is measured using CWV (Core Web Vitals) scores.</p>
<h3>Frontend Study Sessions</h3>
<p>Additionally, we hold a weekly volunteer-led frontend study session where participants choose the topics. These include watching past event presentations together to share insights, reviewing baselines from past to present, exchanging frontend techniques, and sharing practical knowledge about actual development products and processes.</p>
<p>I began considering how the outcomes of our study sessions could be applied to actual services, and proposed &#39;FACTORY performance improvement&#39; as a potential topic. All members coordinated their schedules to review the FACTORY page together, conducting two live verification sessions.</p>
<p>We worked on the following.</p>
<ul>
<li>Checked the current scores using the browser&#39;s Lighthouse (setting network and CPU throttling as appropriate)</li>
<li>Reviewed improvement proposals for each score</li>
<li>Blocked analysis tags and other elements unrelated to page rendering to check performance differences</li>
<li>Checked more detailed browser behavior with a performance tab.</li>
<li>Checked layout shifts from captured images</li>
</ul>
<p>Through these actions, we were able to clarify how to use the verification tools provided by the browser and identify where the improvement points are.</p>
<p>:::message For more details about the frontend study session, please also check <a href="/posts/2025-06-30-frontend-study-group/">this article</a>! :::</p>
<h1>Score Improvement</h1>
<p>From here, I will introduce how we progressed with score improvement by utilizing the insights gained from the study sessions.</p>
<p>Core Web Vitals (CWV) are indicators for measuring page performance, which can be classified into the following three metrics.</p>
<ul>
<li>Largest Contentful Paint (LCP): Measures how quickly the main content of the page loads</li>
<li>Interaction To Next Paint (INP): Evaluates the responsiveness of the page to user interactions</li>
<li>Cumulative Layout Shift (CLS): Measures the visual stability of the page</li>
</ul>
<p>Due to the nature of FACTORY&#39;s services, there are strict constraints on product and vehicle images (due to strict color specifications, etc.), making it difficult to change static content (such as image compression), so we first focused on CLS, which can be improved just by changing the content layout on the page.</p>
<ul>
<li>Referencing Google Search Console, checked which pages had poor CLS scores</li>
<li>Created static pages using SSG to eliminate loading screens during API access on the client side</li>
<li>Looked for areas where height shifts before and after loading content such as images</li>
</ul>
<p>In practice, we examined pages with poor CLS scores using the browser’s Performance tab, and systematically addressed each area causing layout shifts while reviewing the screen captures.</p>
<p>Among them, the CLS occurring on the TOP page took a particularly long time to identify. Even when viewed with an analysis tool as shown below, it was not immediately clear where the layout shifts were occurring.</p>
<p><img src="/assets/blog/authors/k.nakamoto/improve-cwv/cls.gif" alt="cls.gif"></p>
<p>However, upon checking the relevant images, I noticed that <a href="https://developer.mozilla.org/en-US/docs/Web/CSS/contain-intrinsic-size"><code>contain-intrinsic-size</code></a> was defined in CSS. It seems that this property is relatively new, and in unsupported browsers, the image size is not set correctly, causing layout shifts.</p>
<p>Instead, by changing the fixed image size to <code>aspect-ratio</code> and setting the aspect ratio, we were able to eliminate layout shifts.</p>
<h1>Results</h1>
<p>This time, I would like to reflect on both the efforts made on <em>Kaizen</em> DAY and the actual CWV scores.</p>
<h3>Ticket Resolution on <em>Kaizen</em> DAY</h3>
<p>The following charts show the number of resolved tickets on the board summarizing the improvement tickets. Around April, we introduced the &#39;<em>Kaizen</em> DAY&#39; system, with the red line in the graph indicating tickets added during the period (one month) and the green line indicating completed tickets.</p>
<table>
<thead>
<tr>
<th>April ticket resolution</th>
<th>June ticket resolution</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/k.nakamoto/improve-cwv/jira_apr.jpg" alt="jira_apr.jpg"></td>
<td><img src="/assets/blog/authors/k.nakamoto/improve-cwv/jira_jun.jpg" alt="jira_jun.jpg"></td>
</tr>
</tbody></table>
<p>The above results are for the entire team, and it can be seen that the completion of improvement tasks is steadily increasing. Recently, the company has been actively incorporating AI into development. Many of these improvement tasks are straightforward but involve frequent context switching, making them cumbersome for humans to handle and therefore well-suited for delegation to AI.</p>
<p>Given this aspect, it can be understood that despite ongoing development of feature additions and other projects, there is a solid commitment to the improvement activities as mentioned above.</p>
<h3>CWV Scores</h3>
<p>Now, let&#39;s reflect on how the CWV scores, which are the theme of this discussion, have improved before and after the improvement activities.</p>
<p><img src="/assets/blog/authors/k.nakamoto/improve-cwv/cwv_result_pc.jpg" alt="cwv_result_pc.jpg"> <em>PCスコア</em></p>
<p><img src="/assets/blog/authors/k.nakamoto/improve-cwv/cwv_result_sp.jpg" alt="cwv_result_sp.jpg"> <em>モバイルスコア</em></p>
<p>The graphs above show the changes in performance scores for individual URLs, which can be checked in Google Search Console.</p>
<p>For both PC and mobile, <strong>the number of good URLs (green) has increased, and the number of bad URLs (red) has decreased or dropped to zero</strong>, leading to performance improvements on the majority of pages. There are still some <em>URLs that need improvement</em>(yellow), but these do not seem to be solely the result of the CLS scores addressed this time. We plan to work on improving those other metrics in the future.</p>
<h1>Summary</h1>
<p>This time, I presented a case in which we improved the page scores (CWV) of FACTORY by using <strong><em>Kaizen</em> DAY</strong> implemented in the FACTORY development group and <strong>internal frontend study sessions</strong>.</p>
<p>In our day-to-day project work, deadlines and other constraints often lead us to postpone addressing technical debt. However, I intend to continue making use of <em>Kaizen</em> DAY to prevent such debt from accumulating. Through internal study sessions, I can gain valuable insights and improvement ideas from other products and services under development. By applying these ideas effectively, I hope to continuously improve and ensure the stable operation of FACTORY’s services.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/k.nakamoto/improve-cwv/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[How Frontend Engineers Improved UX Issues by Attending an In-person Event]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-22-FACTORY_ReportOfRealEvent-en/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Engineers from KINTO FACTORY improved UX issues discovered at an in-person event. On-site Learnings and measures can also be applied to improve your products.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I am Ki-yuno and working on frontend development for <a href="https://factory.kinto-jp.com/">KINTO FACTORY</a> .</p>
<p>In this post, I’d like to share my experience of attending FACTORY’s in-person event as a frontend engineer.</p>
<p>The in-person event I’m talking about wasn’t a tech conference, <strong>but a large meet-up for car enthusiasts. The frontend engineers joined to help run the FACTORY promotion booth at the venue</strong>.</p>
<p>Since the audience included people less familiar with IT or the internet, it became a great opportunity to hear feedback from viewpoints quite different from our own as engineers.</p>
<h1>Event Exhibition Content</h1>
<p><img src="/assets/blog/authors/keyuno/FACTORY_ReportOfRealEvent/boothImage.jpg" alt=""></p>
<p>Our product provides an upgrade service that allows customers to add genuine manufacturer options to their vehicles through online applications, even after purchase. Most customers discover our products through websites or <a href="https://factory.kinto-jp.com/magazine/">magazine articles</a> and often make their purchases without many chances to see the actual vehicles in advance. At an event like this, we also display vehicles that have been upgraded and cannot be seen on the web. This time, we showcased a Corolla Cross that had undergone meter design customization.</p>
<p><img src="/assets/blog/authors/keyuno/FACTORY_ReportOfRealEvent/meterDesignCustomize.jpg" alt=""></p>
<p>“Meter design customization” is a product that allows you to add a new design to the vehicle’s instrument panel. *The image shows Classic Gear.</p>
<p>Seeing the actual customized instrument panel is even more exciting than expected! It looks so good that even<strong>someone without a driver&#39;s license might feel tempted to enroll in driving school on the spot</strong>.</p>
<h1>Introduction to FACTORY and Novelties</h1>
<p>At the event, we ask the event participants to register their vehicle information on FACTORY. Everyone who registers will get a chance to play a no-lose gacha and take home a novelty gift</p>
<p><img src="/assets/blog/authors/keyuno/FACTORY_ReportOfRealEvent/novelties.jpg" alt=""></p>
<p>The novelties are quite impressive, including penlights, tumblers, and original cases for vehicle inspection certificates. The original cases for vehicle inspection certificates we prepared are beautifully designed by our team of creators! They look so good that even <strong>someone who doesn&#39;t drive at all might tempted to sign a contract for Corolla</strong>.</p>
<h1>Insights from Observing Vehicle Owners Using FACTORY</h1>
<p>This is where the engineers truly shine. When the vehicle owners actually tried FACTORY, the following issues became apparent.</p>
<ul>
<li>When registering via the QR code provided for guidance, the navigation to FACTORY gets interrupted midway.</li>
<li>Tapping the “Terms of conditions” link during registration leads to a page with no way to return to the registration flow.</li>
<li>If the user accidentally closes the page after tapping the authentication URL sent by email, there’s no clear path back to resume registration.</li>
</ul>
<p>In the flows we engineers had designed without much deliberate consideration, we identified elements that could cause users to drop off. Especially with the registration process,<strong>we had become so accustomed to setting up accounts as a routine part of testing that we overlooked the potential barriers real users might face</strong>.</p>
<h1>Actions Against the Identified Issues</h1>
<p>As soon as we got back from the event, we took action to address the issues.</p>
<p>First, we discovered that the QR code URL used for guidance was different from the one we engineers had expected. Therefore, we resolved this issue by sharing another URL that works correctly.</p>
<p>Regarding the issues where users can&#39;t return to the registration page from the terms and conditions page, and where there&#39;s no way to go back after closing the page opened by tapping the authentication URL, we&#39;ve communicated the situation and proposed fixes to the responsible team.</p>
<h1>Conclusion</h1>
<p>By actually participating in the event and observing users engaging with the service, we realized that there are issues affecting users in ways we hadn’t been aware of.</p>
<p>Especially engineers have many opportunities to interact with the product and know the correct patterns of the service flow from the beginning. When you get used to knowing the correct patterns, it&#39;s easy to overlook things that are right under your nose, just like in this situation. No matter how user-focused development may be, if nothing evolves, the gap between you and the user will inevitably widen.</p>
<p>If you have an opportunity to communicate with users about the product you are developing, I highly recommend that you participate.  I believe it&#39;s important to return to the basics and reflect on who you&#39;re developing for and why. If there&#39;s an opportunity, I encourage you to take part.</p>
<p>Even if such opportunities aren&#39;t available, you can still gain fresh insights by asking unrelated team members within the company to help with user testing or by trying different devices.</p>
<p>Hands-on experience makes all the difference. Isn&#39;t this what we mean by user-first?</p>
<p>Thank you very much for reading!</p>
<h1>P.S.</h1>
<p>I exchanged snacks with a stranger sitting next to me on the Shinkansen on my way back, while enjoying highballs. Is this also one of the pleasures of a business trip? It might be. (laughs)</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/keyuno/FACTORY_ReportOfRealEvent/thumbnail.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Boost Quality with Clear Unit Test Method Names]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-25-particular-about-unit-test-case-name-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-25-particular-about-unit-test-case-name-en/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[To improve the quality of your test code, why not improve the test method name first? We'll show you how to improve the development efficiency of our entire team with behavior-based naming that non-engineers can clearly understand.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello. Hi there. I&#39;m Uehara (@penpen_77777), a backend engineer in the KINTO FACTORY Development Group. I usually use Go and Rust for development, and my preferred editors are Vim and NeoVim.</p>
<p>This time, I&#39;ll talk about the improvement of test code quality, particularly focusing on <strong>how to name test methods</strong>.</p>
<p>I referred to the following book for this topic.
<a href="https://book.mynavi.jp/ec/products/detail/id=134252">https://book.mynavi.jp/ec/products/detail/id=134252</a></p>
<h1>Challenges related to test code</h1>
<p>If you write test code on a daily basis, have you ever had the following experiences?</p>
<ul>
<li>You have test code, but you don’t fully understand its purpose, which creates doubts about the reliability of the test results and reduces confidence in the adequacy of the testing.</li>
<li>In reviewing test code someone else wrote, you have vaguely approved it even though you don&#39;t fully understand it.</li>
<li>You have tweaked test code to pass the testing phase on a whim, concerning that you are not able to submit a pull request (PR) if the continuous integration (CI) failed as you don&#39;t really know what kind of test you&#39;re doing.</li>
</ul>
<p>To solve these problems, <strong>why not start by improving your test method names?</strong> That&#39;s what I would like to propose.</p>
<h1>Why Do We Need to Improve Test Method Names?</h1>
<p>Let&#39;s look at specific examples of why test method names need to be improved to ensure test code quality.</p>
<h2>Code Examples E-commerce site system</h2>
<p>In the e-commerce site system written in Go, we have implemented the following method of purchasing products. If the product is in stock, the order can be placed; otherwise, it returns an error and the order fails.</p>
<pre><code class="language-go">// ECサイトシステム構造体
type ECSystem struct {
    // 商品ごとの在庫数
    stock map[string]int
}

// 商品を購入するメソッド
func (c *ECSystem) Order(itemID string) error {
    // 商品がない場合はエラーを返す
    if c.stock[itemID] &lt;= 0 {
        return ErrOutOfStock
    }

    // 商品があれば在庫数を減らす
    c.stock[itemID]--

    // 正常終了
    return nil
}
</code></pre>
<h2>Test Method Name Before Improvement</h2>
<p>Suppose that you created the following test code to conduct unit testing for this production code:</p>
<pre><code class="language-go">func Test_正常系_在庫数が0より大きい場合(t *testing.T) { }
func Test_異常系_在庫切れエラー(t *testing.T) { }
</code></pre>
<p>When you look at these names, the following questions come to mind:</p>
<p>🤔 &quot;If the inventory quantity is greater than 0, what is the correct result?&quot;
🤔 &quot;Under what conditions does the out-of-stock error occur?&quot;</p>
<p>I personally feel that I see this pattern of test method names surprisingly often. I think this is what happens when we get so caught up in how to test and what to write inside the test method that there&#39;s no time left to think about the method name itself.</p>
<p>In this case, the sample code and the method implementation are simple, so you can easily guess what the test is about from its name. Meanwhile, the actual product code contains a large number of lines of code for a test method involves a large number of the methods, making us difficult to understand what kind of test it is.</p>
<h2>Improvement of the First Stage</h2>
<p>One reason why the name of the test method before improvement is difficult to understand is that the name does not contain either the preconditions or expected results.</p>
<pre><code class="language-go">func Test_正常系_在庫数が0より大きい場合(t *testing.T) { } // 「想定結果」の欠落
func Test_異常系_在庫切れエラー(t *testing.T) { }         // 「事前条件」の欠落
</code></pre>
<p>To put this the other way around, it is easier to understand if you put both preconditions and expected results in the test method name. Let&#39;s unify it into the following format:<code>{ Method under test }_{ Preconditions }_{ Expected results }</code></p>
<pre><code class="language-go">func Test_Order_在庫数が0より大きい場合_正常終了(t *testing.T) { }
func Test_Order_在庫数が0の場合_在庫切れエラー(t *testing.T) { }
</code></pre>
<p>It&#39;s a little better, but there&#39;s still room for improvement.</p>
<h2>Problem: Method Name Includes How the Method Is Tested</h2>
<p>The current test method names focus on how the tests are executed, which makes them harder to read and interpret.
🤔 &quot;The name includes the content of a successful end of the order process... Does that mean the user can buy the product?&quot;
🤔 &quot;The name describes that the order returns an out-of-stock error... Does that mean the user can&#39;t buy the product?&quot;</p>
<p>In other words, when you read it, your brain has to translate it into the &quot;what&quot;(or what kind of behavior is tested), which adds cognitive load and makes it hard to read.</p>
<p>Regarding this naming convention, the book &quot;Unit Testing: Principles, Practices, and Patterns&quot; states the following.</p>
<blockquote>
<p>I&#39;ve tried various naming conventions over the past decade, and among them, the most famous and probably the most useless one is: { Method under test }<em>{ Preconditions }</em>{ Expected results }</p>
</blockquote>
<p>The argument is that we should think of an appropriate name for each test rather than following a strict naming convention, but personally, I feel that calling it &quot;the most useless&quot; is a bit of an exaggeration. I believe it&#39;s very useful if you consider the benefit of standardizing naming convention among team members, by operating with the mindset of following rules if you are unsure how to name a method.</p>
<h2>Final Improvement: Write the &quot;What&quot;</h2>
<p>In your test method names, write the &quot;what&quot; (the method behavior), not the &quot;how&quot; (how you test the method). The &quot;how&quot; part (to check a successful end and error occurrence) is implemented in a test method.</p>
<pre><code class="language-go">func Test_商品の在庫がある場合_ユーザは商品を購入できる(t *testing.T) { 
   // ここで正常終了しているかをみる
}

func Test_商品の在庫がない場合_ユーザは商品を購入できない(t *testing.T) {
    // ここで在庫エラーが発生しているかをみる
}
</code></pre>
<p>Furthermore, &quot;Unit Testing: Principles, Practices, and Patterns&quot; states the importance of naming methods concisely to help even non-engineers (domain experts) understand them.</p>
<h2>Why Should We Use Names That Non-Engineers Can Understand?</h2>
<p>&quot;Since test code is only seen by developers, why don&#39;t we have to make it a name that non-engineers can understand?&quot; You might hear such opinion.</p>
<p>&quot;Unit Testing: Principles, Practices, and Patterns&quot; addresses this opinion as follows:</p>
<blockquote>
<p>This is because cryptic names impose a cognitive burden on everyone, regardless of whether they are a developer or not. These names place an extra cognitive load on developers to figure out what the test case actually verifies and how its content relates to which business requirement.</p>
</blockquote>
<p>Even if you write code that only developers can understand, you&#39;ll end up where:</p>
<ul>
<li>It becomes difficult to grasp what the test is verifying and which requirements it corresponds to.</li>
<li>Code maintenance becomes difficult.<ul>
<li>Even the person who wrote it won&#39;t be able to read it a few months later.</li>
</ul>
</li>
<li>It&#39;s hard for other engineers to understand during code reviews.</li>
</ul>
<p>This results in the following challenges related to test code I mentioned at the beginning:</p>
<blockquote>
<ul>
<li>You have test code, but you don’t fully understand its purpose, which creates doubts about the reliability of the test results and reduces confidence in the adequacy of the testing.</li>
<li>In reviewing test code someone else wrote, you have vaguely approved it even though you don&#39;t fully understand it.</li>
<li>You have tweaked test code to pass the testing phase on a whim, concerning that you are not able to submit a pull request (PR) if the continuous integration (CI) failed as you don&#39;t really know what kind of test you&#39;re doing.</li>
</ul>
</blockquote>
<p>That&#39;s how we get there.</p>
<h2>Naming Test Methods Clearly Is Hard</h2>
<p>To be honest, coming up with clear, easy-to-understand test method names is really hard.</p>
<p>Why is that? That&#39;s because it requires you to take your hands off the keyboard, look away from the monitor, and ask yourself as follows:</p>
<p><strong>&quot;What are we actually trying to create?&quot;</strong></p>
<p>When you feel like you&#39;re drowning in a sea of code, just stop for a moment and consider what problems the method right in front of you is designed to solve and for whom.</p>
<ul>
<li>What role does this object play in the system?</li>
<li>Is the task you are currently working on really going to make your users happy?</li>
<li>Do we really have a good grasp of the domain knowledge used in this business area?</li>
</ul>
<p>Thinking seriously about test method names is actually about all of this.</p>
<p>And then you&#39;ll notice that something magical starts to happen when you explore clearer test method names.</p>
<p>The test code becomes easier to read. The production code naturally gets more organized. Conversations in the team are becoming smoother.</p>
<p><strong>It&#39;s just a test method name, but it&#39;s so much more.</strong></p>
<p>Small improvements lead to happiness for the entire development team. That&#39;s the kind of experience I really want you to have.</p>
<h2>Conclusion</h2>
<p>This time, I wrote about the test method name.</p>
<ul>
<li>Make the test method name easy to understand to help grasp what kind of test is conducted.</li>
<li>Provide the names so clearly that even non-engineers can understand them.</li>
<li>If simply improving test method names can enhance the quality of your test code, don&#39;t you think it&#39;s a cost-effective improvement?</li>
</ul>
<p>I hope this article has given you a sense of the importance of test method names.</p>
<p>The book &quot;Unit Testing: Principles, Practices, and Patterns,&quot; which I referred to this time, has about 400 pages; meanwhile, the explanation about the test method names is about 6 pages, which is not long at all. However, personally, I feel this is the most important and instructive part of the book.</p>
<p>If you&#39;re struggling with unit testing, I highly recommend reading &quot; <a href="https://book.mynavi.jp/ec/products/detail/id=134252">&quot;Unit Testing: Principles, Practices, and Patterns</a>.&quot; I&#39;m sure it will give you the answer to your problems!</p>
<h2>References</h2>
<p><a href="https://book.mynavi.jp/ec/products/detail/id=134252">https://book.mynavi.jp/ec/products/detail/id=134252</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uehara/2025-08-25-particular-about-unit-test-case-name/thumb.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[FACTORY development team is kicking off a writing festival]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-26-FACTORY-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-26-FACTORY-en/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[The FACTORY development team's summer tech blog writing festival is about to begin. Please look forward to each member writing from their own perspective.]]></description>
            <content:encoded><![CDATA[<h1>FACTORY development team is kicking off a writing festival 🎉</h1>
<p>I&#39;m Mizuno from the KINTO FACTORY development group.</p>
<p><img src="/assets/blog/authors/takafumi_mizuno/mizuno.png" alt="Takafumi kin-ei"></p>
<p>Hello to those meeting me for the first time—and to those who already know me.<br>My name is Mizuno and I am the group manager of the KINTO FACTORY development group.<br>I usually work as the group manager for the KINTO FACTORY development group, and also take on other product management and progress management roles. Lately, I&#39;ve been into growing vegetables in my garden.</p>
<h2>What is the KINTO FACTORY Development Group?</h2>
<p>The KINTO FACTORY Development Group is a team formed to launch and continuously improve the KINTO FACTORY <a href="https://factory.kinto-jp.com/">e-commerce site</a> . The upgrade business for genuine manufacturer parts for Toyota, Lexus, and GR vehicles, which started as a proof of concept about three years ago, was moved into full production two years ago through in-house development and a complete site renewal. Since then, we have continued to add products and improve our e-commerce site.</p>
<p>Currently, we operate as follows:</p>
<ul>
<li>Number of members: 12 (as of August 2025)</li>
<li>Main roles: Product manager, web director, frontend engineer, backend engineer, QA engineer</li>
<li>Team atmosphere: Open and flexible. If something sparks our interest, we dive in. We work together to fulfill requests.</li>
</ul>
<h2>Product Introduction</h2>
<p>KINTO FACTORY is a service that allows you to &quot;evolve&quot; your vehicles after purchase.  You can update your vehicles.  What&#39;s more, you can enjoy a customization experience by applying online and receiving a PDF certificate that verifies your vehicle upgrade. Since the service name includes KINTO, it&#39;s easy to mistakenly think that this only applies to KINTO vehicles, but it actually applies to any TOYOTA or LEXUS vehicle you own.</p>
<p>There are three main service categories:</p>
<ul>
<li><strong>Upgrades</strong> : Add safety features and comfort functions after purchase.  For example, &quot;smooth braking&quot; and &quot;parking support brake.&quot;</li>
<li><strong>Modification</strong> : Refresh the interior and exterior. Replacing the seats and steering wheel will give you a new feel.</li>
<li><strong>Personalization</strong> : Use driving data to tailor your car to better suit your individual style and needs.</li>
</ul>
<p>From a technical point of view, it should be quite interesting as it runs on a microservice architecture on AWS behind the scenes, and the upgrade certificates (limited to products for which upgrades have been carried out) are issued digitally. If you&#39;re curious to know what kind of engineers are behind the system, be sure to check out the interviews below and our upcoming blog posts!</p>
<p><a href="https://www.wantedly.com/companies/company_7864825/post_articles/537179">Frontend Engineer Interview</a></p>
<p><a href="https://www.wantedly.com/companies/company_7864825/post_articles/959387">Backend Engineer Interview</a></p>
<h2>The writing festival begins ✍️🔥</h2>
<p>Now, we&#39;ve come to the main topic of this post. It&#39;s short.<br>The KINTO FACTORY development group has decided to hold <strong>the summer tech blog writing festival</strong> from August through September!</p>
<ul>
<li>Explain what KINTO FACTORY is.</li>
<li>Share technical knowledge.</li>
<li>Communicate the team atmosphere.</li>
<li>Increase contact with various people.</li>
<li>Increase the number of team members who share the same goals.</li>
</ul>
<p>We will start by letting people know about the team members behind KINTO FACTORY through this tech blog.<br>Each member will write about their role and daily experiences. </p>
<p>Some members have already posted on the blog, so be sure to check them out!</p>
<hr>
<p>All of us in the KINTO FACTORY Development Group hope you enjoy our articles.  </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[What Does a PdM at KTC Do? — A Day in the Life of a PdM]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-27-factory-pdm-life-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-27-factory-pdm-life-en/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[ KTCにおけるPdMの仕事って実際どんな感じであるかをまとめ]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello. I am K. Ichinose, serving as a Product Manager (PdM) at KINTO Technologies Co., Ltd. (hereafter referred to as KTC).</p>
<p>&quot;Isn&#39;t PdM&#39;s job just coming up with ideas and planning?&quot;<br/>
Many people might think so.  In reality, our daily tasks are diverse, and we communicate with a wide range of people.<br/></p>
<p>In this article, I will share a typical day in the life of a PdM at KTC, specifically focusing on my role as the PdM for the KINTO FACTORY.<br/>
I hope you will find the PdM role fascinating and get a clearer picture of what it’s like to work at KTC.<br/></p>
<h1>What is the Role of PdM at KTC?</h1>
<p>Generally, PdM is responsible for determining the direction of the product and leading from planning to development, release, and improvement.<br/>
At KTC, we handle <strong>Mobility Services</strong>related products. <br/></p>
<p>In this role, I am responsible for the service known as<a href="https://factory.kinto-jp.com/">KINTO FACTORY</a> (hereafter FACTORY).<br/>
FACTORY is an upgrade service that allows you to retrofit TOYOTA and LEXUS genuine options at authorized dealerships.<br/>
This allows you to add the latest features and parts even after purchase, increasing the value and usability of the vehicle.<br/></p>
<p>I am mainly work on the developer-oriented area of the &quot;product management triangle&quot; and act as a bridge between business and developers.<br/>
I have also recently started data analysis.<br/>
<img src="/assets/blog/authors/K.Ichinose/FACTORY_PdMTriangle.jpg" alt="FACTORY_PdMTriangle.jpg"> <em>The figure illustrates the role of PdM in three areas: &quot;Business&quot;, &quot;Customers&quot;, and &quot;Developers&quot;.</em></p>
<p>Specifically, we gather ideas and requests, organize them into requirements, create a development schedule, and drive the project forward through to release.<br/>
Furthermore, in development projects that span multiple products across KTC, internal coordination and collaboration with other teams are also important tasks.</p>
<h1>A Day in the Life of a PdM</h1>
<p>Here, I will introduce an example of my daily schedule.<br/>
(KTC has a flexible work schedule, but I start work at roughly the same time every day. I&#39;m the type of person who can concentrate better with a regular routine.)</p>
<table>
<thead>
<tr>
<th>Time</th>
<th>Daily activities</th>
</tr>
</thead>
<tbody><tr>
<td>09:30</td>
<td><strong>Start of workday, check emails</strong><br/>I check Slack and emails and organize my tasks for the day.<br/>I decide whom to contact and what decisions need to be made each day.</td>
</tr>
<tr>
<td>10:00</td>
<td><strong>Morning meeting (Daily Scrum)</strong><br/>I share progress and issues with the development team.<br/>By sharing even small issues early on, we can prevent larger delays later.</td>
</tr>
<tr>
<td>10:30</td>
<td><strong>In-team review</strong><br/>We review PRD (Product Requirements Document), DesignDoc, and other specification documents.</td>
</tr>
<tr>
<td>11:00</td>
<td><strong>Regular meeting with the business team (Toyota or KINTO)</strong><br/>We confirm new feature requirements and specifications related to FACTORY.<br/>I also communicate proposals and requests from KTC.</td>
</tr>
<tr>
<td>12:00</td>
<td><strong>Lunch break</strong><br/>Lunch near the office (the area around the company&#39;s location in Muromachi is a bit expensive...).</td>
</tr>
<tr>
<td>13:00</td>
<td><strong>Requirement organization and proposal document creation</strong><br/>I translate the requirements received during the planning stage into concrete requirements and compile them into a format that can be shared with engineers and designers.</td>
</tr>
<tr>
<td>15:00</td>
<td><strong>Feasibility and workload estimation for development projects</strong><br/>We coordinate with the lead engineers to confirm the implementation method, issues, and required man-hours.<br/>We establish a realistic schedule here.</td>
</tr>
<tr>
<td>16:00</td>
<td><strong>Regular meetings for all projects within the company</strong><br/>We discuss the progress, issues, and schedule adjustments for projects that span multiple products.</td>
</tr>
<tr>
<td>17:00</td>
<td><strong>Data analysis:</strong><br/>I analyze usage data to understand user trends. I Look for hints for improvement.</td>
</tr>
<tr>
<td>As needed</td>
<td><strong>Responding to inquiries</strong><br/>I handle questions about specifications and design within KTC, operational improvements, test improvements, release adjustments, and more.<br/>I also handle new features from KINTO/Toyota and operational consultations, among other things.</td>
</tr>
</tbody></table>
<h1>Why It&#39;s Great to Work as a PdM at KTC</h1>
<ul>
<li>You can contribute to the future of the mobility industry<ul>
<li>You can be directly involved in this new challenge to <a href="https://global.toyota/jp/newsroom/corporate/39422385.html">enhance the value of your car</a>.</li>
</ul>
</li>
<li>You have the chance to communicate with various stakeholders<ul>
<li>It is inspiring to interact with different collaborators in both inside and outside the company, Toyota, KINTO and to hear opinions from different perspectives.</li>
</ul>
</li>
<li>Team Atmosphere<ul>
<li>It is easy to express opinions regardless of one&#39;s position or age, and discussions are positive.</li>
</ul>
</li>
</ul>
<h1>Why it is so rewarding</h1>
<p>FACTORY is a relatively new service that has not yet gained widespread recognition.<br/>
As a result, there’s significant room for growth, and being involved in shaping the direction of the service allows you to experience its progress firsthand.<br/>
There is a great sense of achievement when the feature you&#39;ve worked on gets released and users actually start signing up. </p>
<h1>For Those Wanting to Join KTC as a PdM</h1>
<p>In my opinion, there are three essential qualities required to succeed as a PdM at KTC:</p>
<ul>
<li>Communication skills<ul>
<li>The ability to build trust with stakeholders and communicate information smoothly.</li>
</ul>
</li>
<li>Flexibility<ul>
<li>The ability to positively embrace change and turn it into improvements.</li>
</ul>
</li>
<li>Passion for the product<ul>
<li>A deep understanding of the product you are responsible for and the ability to be dedicated to it.</li>
</ul>
</li>
</ul>
<p>The third point, in particular, is the source of attention to detail, and improvement ideas.<br/>
Having passion can also positively impact the motivation of the entire team.</p>
<h1>Conclusion</h1>
<p>Being a PdM at KTC is a rewarding job in the rapidly changing mobility sector, where you can help develop new services into better forms.<br/>
I hope this article inspires you to explore the role of a PdM!  <br/>
If you&#39;re interested, I hope you will join us to work together at KTC to create the mobility of the future!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/K.Ichinose/PdMimage.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: June 2025 Update]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-28-newcomer-202506-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-28-newcomer-202506-en/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We asked seven new members who joined in June 2025 to share their impressions after joining our company, and here is a summary of what they said.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hi there. This is emim who joined the company in June 2025!</p>
<p>In this article, I interviewed the new members who joined in June 2025 and collected their first impressions after starting work at KINTO Technologies. I hope this article will be helpful for those interested in KINTO Technologies (KTC) and serve as a reflection for the members featured here!</p>
<h1>Jun</h1>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I work on a business system for used vehicles as a member of the Business System Group in the Business System Development Division.</li>
<li>Two jobs ago, I worked as a PM on contracted development projects at a system integrator. Most recently, I was involved in developing B2B SaaS products at a startup.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>Our team handles system development and operations for KINTO&#39;s used vehicles.</li>
<li>Our team consists of five KTC members including myself.  Most of the development (programming) is outsourced to partners, and KTC members mainly handle product management, project management, requirements definition, system design, and operational maintenance.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li><p>We&#39;re constantly reviewing operations and systems as the business expands, and actively adopting new technologies. It&#39;s a dynamic environment where there&#39;s never a dull moment.</p>
</li>
<li><p>In the system I am currently working on, we use Java for the backend and Vue3 for the frontend. We haven&#39;t been able to utilize AI much yet, but we are trying to use GitHub Copilot and Devin in development.</p>
</li>
</ul>
</li>
<li><strong>What made you decide to join KTC and were there any surprises?</strong><ul>
<li>I joined KTC because I wanted to develop systems in a company where IT is directly connected to the business. We’d like to dedicate more time to developing solutions that make operations and system management easier, but we haven’t been able to get to that yet.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>There is a small break area with a tea machine, where I can enjoy the sunlight. I always eat lunch there. It’s not especially luxurious, but it offers a refreshing change of pace.</li>
</ul>
</li>
<li><strong>Question from S to Jun</strong><blockquote>
<p>You mentioned wanting to drive various cars. Please tell me which car you are most interested in right now and why!</p>
</blockquote>
<ul>
<li>It&#39;s the GR Yaris. I want to try driving a genuine sports car.</li>
</ul>
</li>
</ul>
<h1>Nakano</h1>
<p>![なかのさんのプロフィール画像](/assets/blog/authors/emim/2025-08-27-newcomer/nakano.jpeg =300x)</p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I work in the Digital Transformation Solutions Group, a team that provides digital transformation support for dealerships. I am involved in PdM work and future planning tasks.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The team consists of eight members, including PdM and designers, working on the design and direction of the dealer digital transformation products.<br>  We are advancing the project in collaboration with the sales and development teams in the same division.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>Everyone is calm, kind, and capable!<br>  I was impressed that everyone has not only system development skills but also a deep understanding of and interest in the complex dealership operations.</li>
</ul>
</li>
<li><strong>What made you decide to join KTC and were there any surprises?</strong><ul>
<li>At my previous company, an advertising agency, IT was just one part of the business.  I wanted to work for a company where IT is the core focus and knowledgeable professionals gather. My impression hasn&#39;t changed since joining. This is a place where experts gather!</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>I work in Osaka Tech Lab. I really like the the panoramic view of Osaka from my desk. (It can be seen from any seat.)<br>  One of the perks of this office is the mountain view. It helps me unwind between tasks.</li>
</ul>
</li>
<li><strong>Question from Jun to Nakano</strong><blockquote>
<p>Please share one thing you found amazing  after joining KTC🙇</p>
</blockquote>
<ul>
<li>I was amazed by how many proactive and positive people there are!<br>  Since I joined, things are constantly moving forward. Everyone&#39;s so proactive about their work, technical growth, expanding our sites, and more. (The team spirit at Osaka Tech Lab is seriously incredible!)</li>
</ul>
</li>
</ul>
<h1><a href="/authors/01985418-e96d-7bc7-b05d-90a3a0215597/">Kazuki Otaka</a></h1>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I am Otaka, who joined the cloud security group in June! I work on cloud security, leveraging CSPM and threat detection mechanisms to monitor compliance with guardrails and drive continuous improvement activities across our multi-cloud environment. I have also written the KTC tech blog articles in the past. Please feel free to read them. (For past articles, <a href="/authors/01985418-e96d-7bc7-b05d-90a3a0215597/">click here</a>)</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>There are three of us, two in Osaka Tech Lab and one in Jimbocho office.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>The atmosphere is calm, and the people are very warm and welcoming. Internal study sessions, lightening talks, and Beer Bashes are frequently held. There are many highly skilled and professional people. I want to do my best not to lag behind!</li>
</ul>
</li>
<li><strong>What made you decide to join KTC and were there any surprises?</strong><ul>
<li>In my previous job, I had many opportunities to work on building systems in on-premise environments. I decided to join KINTO Technologies, which builds systems entirely in the cloud, as I wanted to acquire skills in building and operating systems using cloud environments. I feel that we are in an environment at the forefront of cloud infrastructure operations more than I had imagined, with IaC (Infrastructure as Code) being advanced mainly on AWS and the verification and utilization of generative AI being pursued.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>Usually, it&#39;s quiet and calm. But when equipment was suddenly installed in the office and a PoC began, I could really feel the spirit of people trying new things, and I quite liked that.</li>
</ul>
</li>
<li><strong>Question from Nakano to Kazuki Otaka</strong><blockquote>
<p>Please tell me about the interesting and difficult aspects of security work!</p>
</blockquote>
<ul>
<li>Security risks can severely affect the business when issues arise, but on the other hand, overly strict security can obstruct business operations and development. As cyber attacks evolve, finding the balance between implementing and operating necessary security without hindering business growth is challenging, but that&#39;s also what makes it interesting.</li>
</ul>
</li>
</ul>
<h1>Yuna</h1>
<p>![Yuna&#39;s profile image](/assets/blog/authors/emim/2025-08-27-newcomer/yuna.jpeg =300x)</p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I am Yuna, part of the Business System Division&#39;s Business System Group, which develops business systems used by dealers. Currently, I work on a renewal project for the system used by dealers during business negotiations.</li>
<li>Recently, a car I subscribed through KINTO was delivered, and going for drives every weekend has become my new hobby.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>It&#39;s a team of three full-time employees including myself.  I am working on requirements definition and maintenance operations while receiving daily support from the leader and another team member. I am working steadily to contribute while absorbing new knowledge and skills.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>I have been warmly welcomed both in and outside my team. It is a very nice atmosphere.</li>
<li>Although KINTO&#39;s products and operations are complex with much to learn, I&#39;m catching up smoothly with excellent guidance from my team.</li>
</ul>
</li>
<li><strong>What made you decide to join KTC and were there any surprises?</strong><ul>
<li>I joined as a product manager, continuing from my previous job.<br>  To advance my career, I searched for a company where I could pursue what I wanted to do as a product manager. That&#39;s how I found KTC.</li>
<li>During the interview, I talked with the current manager and leaders about the job details, and I really liked the team&#39;s vibe. Those were the deciding factors.</li>
<li>I knew before joining that I would be working in a more tech-oriented area than in my previous job. However, once I started, I realized the role required more advanced skills, and I&#39;ve been learning continuously ever since.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>I work on the 16th floor of the Muromachi office. It is directly connected to Mitsukoshimae Station and Shin-Nihonbashi Station, making it comfortable even on rainy days.</li>
<li>There is a Starbucks within the building, and there are many restaurants and shops nearby, making it a very convenient environment. Taking a short walk during lunch break is a good way to refresh myself, and it helps me work better in the afternoon.</li>
</ul>
</li>
<li><strong>Question from Kazuki Otaka to Yuna</strong><blockquote>
<p>Do you have a favorite among your hobbies of making sweets and baking bread? Once I was also into bread, and I used to go to a bakery near Tokyo Gakugei University!</p>
</blockquote>
<ul>
<li>Recently, I&#39;ve been enjoying making &quot;Mayonnaise Corn Bread&quot; topped with plenty of seasonal summer corn. The sweet corn and savory mayonnaise infuse the fluffy bread dough with flavor. Even the wait while it bakes is a joy.</li>
</ul>
</li>
</ul>
<h1>emim</h1>
<p>![emimのプロフィール画像](/assets/blog/authors/emim/emimIcon.png =300x)</p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>In the Engineering Office, a cross-organizational team, we conduct analysis, create systems, and engage with various divisions and teams from a designer&#39;s perspective to enhance the overall development capabilities of the organization.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>There are only three members in the team, including myself. One is a manager who focuses on the entire organization while being a front-end developer, and another is focused on improving software processes and development productivity. The other is me. I undertake various initiatives to enhance designers&#39; presence within the development organization.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>The Engineering Office is highly distinctive. Everyone works on different things and we&#39;re not in one location, yet we collaborate and share information remarkably well. I think this is because the team has members with some unique skills.</li>
</ul>
</li>
<li><strong>What made you decide to join KTC and were there any surprises?</strong><ul>
<li>I&#39;ve always liked cars but the mobility industry was something unfamiliar to me.  Then, I thought that I could apply my skills acquired so far, and I submitted my application.<br>  Before joining the company, I was given quite detailed information by my team members. So there wasn&#39;t much of a gap.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>Working in Nihonbashi Muromachi makes my commute enjoyable every day.</li>
</ul>
</li>
<li><strong>Questions from Yuna to emim</strong><blockquote>
<p>I heard you like movies and TV dramas. Are there any films or shows you&#39;ve seen that have influenced your work in design?</p>
</blockquote>
<ul>
<li>Thank you for remembering my hobbies! I really love sci-fi, and I&#39;m always excited when I come across UIs and devices I&#39;ve never seen before. My decision to pursue design stemmed from my desire to design such unknown/novel devices. I tend to study and approach design with a focus on information architecture, creating systems that can properly deliver information across any type of digital device.</li>
</ul>
</li>
</ul>
<h1>Taka</h1>
<p>![Takaさんのプロフィール画像](/assets/blog/authors/emim/2025-08-27-newcomer/taka.jpeg =300x)</p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I believe my work is about creating strategies in the space between n=1 (individual cases) and statistical generalizations. I love quantitative data analysis and in-depth interviews for achieving goals, and I could do it forever. The three important impulses that have shaped my life are &quot;Observe,&quot; &quot;Weave a story,&quot; and &quot;Curate.&quot;</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>We have members with experience at operating companies (from large enterprises to startups), talented data analysts, and skilled engineers.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>Culturally, we&#39;re a team that values a goal-driven mindset. We value a proactive approach to problem-solving that isn&#39;t constrained by convention.</li>
</ul>
</li>
<li><strong>What made you decide to join KTC and were there any surprises?</strong><ul>
<li>I joined because I have a hypothesis that individual-focused marketing is crucial during this period of global transformation in the automotive industry and I wanted to put this into practice and drive change.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>The bright sunlight coming in through the large windows on the 16th floor of the Muromachi office. It&#39;s nice to work with a view of the open sky and plenty of natural light, since we&#39;re not surrounded by tall buildings.</li>
</ul>
</li>
<li><strong>Questions from emim to Taka</strong><blockquote>
<p>I heard you&#39;ve worked in various roles throughout your career. Is there anything you feel is unfinished or that you haven&#39;t accomplished yet? Also, what are your ambitions at KTC, and how do you plan to use your best skills here?</p>
</blockquote>
<ul>
<li>Well, I&#39;ve worked in different roles, but I actually feel that my core hasn&#39;t changed, and I believe what I&#39;ve been doing is &quot;design to connect people.&quot; The one thing I haven&#39;t accomplished yet is starting a business. My ambition is as I wrote why I joined the company!</li>
</ul>
</li>
</ul>
<h1>S</h1>
<p>![Sさんのプロフィール画像](/assets/blog/authors/emim/2025-08-27-newcomer/s.png =300x)</p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I work in a team that develops the front end of KINTO ONE (a new vehicle subscription service).</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The team consists of eight engineers (six in Tokyo, one each in Osaka and Fukuoka).</li>
<li>We adopt a Scrum development framework with one-week sprints.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>I&#39;m the only one based in Osaka, so I primarily communicate with other team members online. The team has a welcoming atmosphere where it&#39;s easy to ask questions.</li>
<li>When I ask questions in chat, I get quick responses, so I don&#39;t have any issues at work.</li>
</ul>
</li>
<li><strong>What made you decide to join KTC and were there any surprises?</strong><ul>
<li>I joined because I believe the mobility sector will continue to evolve, and I wanted to contribute to the product development that supports that growth.</li>
<li>I was able to learn thoroughly about the team before joining, so there was mostly no gap between my expectations and reality.</li>
</ul>
</li>
<li><strong>What do you like about the office?</strong><ul>
<li>I like that the office is directly connected to Osaka Station, making commuting easy. I also appreciate how spacious and clean it is, and I find it interesting to see areas designed 
to look like a garage and roads inside.</li>
</ul>
</li>
<li><strong>Question from Taka to S</strong><blockquote>
<p>Please tell me if you have any experiences where you talked about something you really like or find interesting, but the other person was put off. (Your true impulses might be hidden there.)</p>
</blockquote>
<ul>
<li>I find complete nutrition foods convenient and easy to prepare for my daily meals, so I&#39;m trying products from various manufacturers. My refrigerator is filled with those. When I tell people about it, they often get taken aback.</li>
</ul>
</li>
</ul>
<h1>In Closing</h1>
<p>Thank you everyone for sharing your thoughts after joining our company!</p>
<p>There are more and more new members at KINTO Technologies every day! We will be posting more stories from our newcomers across divisions, so stay tuned!</p>
<p>And yes, we are still hiring! KINTO Technologies is looking for new teammates to join us across a variety of divisions and roles. For more details, please check <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[The Power of In-House Design: Fueling Our New Office With a Unifying Story ]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-08-29-osaka-tech-lab-story-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-08-29-osaka-tech-lab-story-en/</guid>
            <pubDate>Fri, 01 Aug 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article introduces an initiative where, on the occasion of the relocation of the Osaka office "Osaka Tech Lab," KTC members created a concept and slogan based on their passion, and then established a new space with a story unique to a design organization.]]></description>
            <content:encoded><![CDATA[<p>Our Osaka office &quot;Osaka Tech Lab&quot; moved in July. (<a href="https://www.kinto-technologies.com/company/osakatechlab/">Check out the new office here</a>) The goal for this office was to accelerate innovation, new things, and experimentation and create products from the Osaka Tech Lab. A grassroots movement started to gather people who resonated with this goal—kind of a &quot;follow me!&quot; vibe—and get them involved. That&#39;s when the request came to me in the Creative Office at our Tokyo office.</p>
<p>I wanted to realize the ambitions of the Osaka office members from a creative perspective! I wanted to be part of making this happen!  So I decided to join in and make every effort to contribute to them although my power may be insufficient. (Doing what you want to do—that flexibility is pretty appealing, isn&#39;t it?)</p>
<p>First, to visualize what the new office should be like as it comes to life, we decided to create a concept and develop a story that would connect to the office&#39;s interior design and recruitment promotions. The project title is &quot;Osaka Tech Lab 2.0 STORY.&quot;</p>
<h1>Osaka Tech Lab 2.0 STORY: the Journey towards Our Goal</h1>
<p>I interviewed the CEO and vice president about a story of what they aim to achieve in the future, starting with the reasons behind the company&#39;s decision to establish an office in Osaka. Following the interview, I thought the key part of this new office project&#39;s concept would be what we should do to achieve our ideal vision and what kind of place we create.  <img src="/assets/blog/authors/aya_sugimoto/250829/story_01.png" alt="story_01"> </p>
<p>Based on this core idea, we created a slogan (concept words) in simple terms that anyone in the company could understand, to get all members on the same page. By the time this part came up, we were already communicating weekly with the Osaka members and the Tokyo-based production team. This fostered the following mindset: &quot;Let&#39;s have a breakthrough!&quot;  It was a fun and lively meeting where we could freely discuss what are in our minds, and sometimes a casually spoken word would be adopted as is. (Thanks to the project manager who encouraged us to have fun discussions on this.)</p>
<p><img src="/assets/blog/authors/aya_sugimoto/250829/story_02.png" alt="story_02"> <img src="/assets/blog/authors/aya_sugimoto/250829/story_03.png" alt="story_03"> </p>
<h1>And Here Is the Concept Words That We Created:</h1>
<p><img src="/assets/blog/authors/aya_sugimoto/250829/concept.png" alt="concept"> *At the concept planning stage, it was written as &quot;Co-LAB&quot;, but we decided to spell &quot;CO-LAB&quot; using all capitals as &quot;CO&quot; contains a broader meaning, rather than an abbreviation for Company. *We played with the words in our slogan. Shown in the above image, the &quot;GO&quot; in &quot;Shu-GO&quot; (to gather) is a pun on &quot;Let&#39;s go!&quot; to add a sense of action. Similarly, &quot;SHIN&quot; in &quot;Has-SHIN&quot; (to transmit) is meant to evoke several positive Japanese words that share the sound. The Osaka office members were so fired up that we decided, to use this as our slogan, unlike stuffy traditional concept.  The slogan implies the spirit of sharing opinions and ideas and brings them to life, regardless of the members&#39; roles, including designer, engineer, and manager.</p>
<h1>The Slogan Is Set!  Next Up: Wall Design for the Space Where People Gather</h1>
<p>The Osaka office has a space called Junction, just like the Muromachi office in Tokyo. It&#39;s designed to be a place where people both inside and outside the company gather to spark innovation. For this place, we plan to depict a meaningful graphic measuring about 8,400mm wide by 2,500mm high on a wall. <img src="/assets/blog/authors/aya_sugimoto/250829/graphic.png" alt="graphic">This is the graphic we painted on the wall. As it&#39;s something that will remain in the office in a long term, it was a very rewarding project for us as creators. The designers at Osaka Tech Lab collaborated with those in Tokyo, brainstorming ideas to put the finishing touches on the design with generative AI. It&#39;s a design packed with our slogan, &quot;Shu-GO! Has-SHIN! CO-LAB,&quot; and the spirit of &quot;Let&#39;s have a breakthrough!&quot;   </p>
<h1>I&#39;ll share some interesting design tricks with you next time!</h1>
<p>Next time, I would like to share another story about the wall design based on the concept of &quot;Shu-GO!   Has-SHIN!  CO-LAB&quot; with details about which designers are particular, along with the behind-the-scenes of <a href="https://www.kinto-technologies.com/company/osakatechlab/">LP</a> creation.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aya_sugimoto/250829/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[AWS Bedrock AgentCoreをマネジメントコンソール経由でデプロイしてみた話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-28-aws-agentcore/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-28-aws-agentcore/</guid>
            <pubDate>Wed, 30 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[記事の説明を入力]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>初めに</h2>
<p>こんにちは！<br>KINTO テクノロジーズ、AI ファーストグループの nicepear と AlexQ です！
AI ファーストグループは、生成 AI の活用推進、ユースケース開発、技術調査、PoC、教育研修など幅広く活動している横串組織です。</p>
<p>今回我々は、AWS Bedrock AgentCore のエージェントを AWS のマネジメントコンソール経由でデプロイしてみたので、その経験を共有できればと思います。</p>
<h2>AWS Bedrock AgentCore とは</h2>
<p>AWS Bedrock AgentCore は、企業が AI エージェントを本格的に開発し、安全かつ大規模に運用するために AWS が提供する新しい基盤サービスです。</p>
<p><a href="https://aws.amazon.com/jp/blogs/news/introducing-amazon-bedrock-agentcore-securely-deploy-and-operate-ai-agents-at-any-scale/">公式</a>によりますと主な要素は以下です：</p>
<blockquote>
<p><strong>AgentCore Runtime</strong> – セッション分離を備え、サンドボックス化された低レイテンシーのサーバーレス環境を提供し、人気のオープンソースフレームワーク、ツール、モデルを含むあらゆるエージェントフレームワークをサポートし、マルチモーダルワークロードと長時間実行エージェントを処理します。</p>
<p><strong>AgentCore Memory</strong> – セッションと長期メモリを管理し、エージェントが過去のインタラクションから学習するのをサポートしつつ、モデルに関連コンテキストを提供します。</p>
<p><strong>AgentCore Observability</strong> – メタデータのタグ付け、カスタムスコアリング、軌跡の検査、トラブルシューティング/デバッグフィルターを使用して、エージェント実行のステップバイステップのビジュアライゼーションを提供します。</p>
<p><strong>AgentCore Identity</strong> – AI エージェントが、ユーザーに代わって、または事前に認可されたユーザーの同意を得てエージェント自身によって、AWS サービス、および GitHub、Salesforce、Slack などのサードパーティーツールやサービスに安全にアクセスできるようにします。</p>
<p><strong>AgentCore Gateway</strong> – 既存の API と AWS Lambda 関数をエージェント対応ツールに変換し、MCP などのプロトコルやランタイム検出にわたる統合アクセスを提供します。</p>
<p><strong>AgentCore Browser</strong> – エージェントのウェブオートメーションワークフローをスケールするためのマネージドウェブブラウザインスタンスを提供します。</p>
<p><strong>AgentCore Code Interpreter</strong> – エージェントが生成したコードを実行するための独立した環境を提供します。</p>
</blockquote>
<h2>AWS AgentCore Runtime にエージェントをデプロイする方法</h2>
<p>AWS AgentCore でのAIエージェントのデプロイメントについて、公式では以下の <strong>bedrock-agentcore-starter-toolkit</strong> というライブラリを使用するアプローチが紹介されています：</p>
<p><a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-getting-started-toolkit.html">https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-getting-started-toolkit.html</a></p>
<p>また、<strong>bedrock-agentcore-starter-toolkit</strong> を使わない方法として、boto3 API を使う方法も公式で取り上げられています。</p>
<p><a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-custom.html">https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-custom.html</a></p>
<p>今回は、公式ドキュメントに記載されていない<strong>マネジメントコンソールでの手動デプロイメント</strong>を試してみたので、その経験と注意点をご紹介したいと思います。</p>
<h2>マネジメントコンソールでのデプロイ手順</h2>
<p>大きな手順は以下です：</p>
<ol>
<li>エージェントのソースコードを用意する</li>
<li>Docker ファイルを作成する</li>
<li>マネジメントコンソールで ECR リポジトリを作成する</li>
<li>Docker で ARM64 image を作成し ECR リポジトリにデプロイする</li>
<li>AgentCore のマネジメントコンソールでエージェント新規作成する</li>
</ol>
<h2>エージェントのソースコードを用意する</h2>
<p>今回は公式の Strand Agents を使用してエージェントを構築しました。
また、デプロイ手順をテストするのが目的なため、比較的シンプルな構成にしております。</p>
<p><strong>agentcore.py</strong></p>
<pre><code class="language-python">import os
from strands import Agent, tool
from strands_tools import calculator
from strands.models.litellm import LiteLLMModel
from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()
os.environ[&quot;OPENAI_API_KEY&quot;] = &quot;YOUR_API_KEY&quot;

@tool
def get_tenki():
   &quot;&quot;&quot; 天気を調べるためのツール &quot;&quot;&quot;
   return &quot;晴れです。&quot;

model = &quot;gpt-4.1&quot;
model = LiteLLMModel(
   model_id=model
)

agent = Agent(
   model=model,
   tools=[calculator, get_tenki],
   system_prompt=&quot;あなたは賢いエージェントです。ユーザーの指示に応じて、数字の計算を行い、あるいは天気を調べてあげてください。&quot;
)

@app.entrypoint
def revoke_agent(payload):
    user_input = payload.get(&quot;prompt&quot;)
    response = agent(user_input)
    print(response.message[&#39;content&#39;][0][&#39;text&#39;])
    return response.message[&#39;content&#39;][0][&#39;text&#39;]

if __name__ == &quot;__main__&quot;:
   app.run()
</code></pre>
<p>なお、ディレクトリ構造は以下のようにしております。</p>
<pre><code>AWS-AGENTCORE/
├── agentcore.py                
├── Dockerfile 
├── requirements.txt      
</code></pre>
<h2>Docker ファイルを作成する</h2>
<p>以下のように Docker ファイルを用意しました。</p>
<pre><code class="language-docker">FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt /app

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy agent file
COPY agentcore.py /app

ENTRYPOINT [&quot;python&quot;, &quot;/app/agentcore.py&quot;]
</code></pre>
<h2>マネジメントコンソールで ECR リポジトリを作成する</h2>
<p>リポジトリ作成は通常の手順と特に変わりません。</p>
<p>まずはAWS マネジメントコンソールにログインし、ECR にアクセスします。
そして右上の「リポジトリ作成」をクリックします。
![](/assets/blog/authors/alex.q/agentcore/ECRにログイン.png =800x)</p>
<p>リポジトリ名を入力し右下の「作成」をクリックします。
![](/assets/blog/authors/alex.q/agentcore/ECRリポジトリ作成.png =800x)</p>
<p>作成が完了したら、リポジトリ一覧で確認できます。
![](/assets/blog/authors/alex.q/agentcore/作成結果.png =800x)</p>
<h2>Docker で ARM64 image を作成し ECR リポジトリにデプロイする</h2>
<p>基本 ECR リポジトリで表示されるプッシュコマンド順で進めていきます。
プッシュコマンドはここで見つかります：</p>
<p>![](/assets/blog/authors/alex.q/agentcore/プッシュコマンド表示.png =800x)</p>
<p>![](/assets/blog/authors/alex.q/agentcore/プッシュコマンド.png =800x)</p>
<p>まずは AWS CLI で、ECR にログインします：</p>
<pre><code>aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin account-id.dkr.ecr.us-west-2.amazonaws.com
</code></pre>
<p>次に Docker イメージを構築します。ここで、プッシュコマンドの書いた通りに構築するとあとでAgentCoreエージェントを作成する際にエラーになりました。
どうやら原因は、ARM64のDockerコンテナを用意する必要があるところみたいですね。</p>
<pre><code># エラーを起こしたコマンド
docker build -t agentcore/techblog .
</code></pre>
<p><a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-custom.html">公式</a>でもARM64を使うと書いてありましたね。</p>
<blockquote>
<p>Build the image locally for testing:</p>
<pre><code>docker buildx build --platform linux/arm64 -t my-agent:arm64 --load .
</code></pre>
</blockquote>
<p>なので、改めて以下のコマンドでイメージを構築しました。</p>
<pre><code>docker build --platform linux/arm64 -t agentcore/techblog .
</code></pre>
<p>構築が完了したら、イメージにタグを付けます。</p>
<pre><code>docker tag agentcore/techblog:latest account-id.dkr.ecr.us-east-1.amazonaws.com/agentcore/techblog:latest
</code></pre>
<p>最後に、以下のコマンドを実行して、先ほど作成した ECR リポジトリにこのイメージをプッシュします。</p>
<pre><code>docker push account-id.dkr.ecr.us-east-1.amazonaws.com/agentcore/techblog:latest
</code></pre>
<h2>AgentCore のマネジメントコンソールでエージェント新規作成する</h2>
<p>最後に、Bedrock AgentCore のマネジメントコンソールにアクセスします。
![](/assets/blog/authors/alex.q/agentcore/agentcoreメイン画面.png =800x)</p>
<p>Agent Runtime に入っていきます。
![](/assets/blog/authors/alex.q/agentcore/agent_runtime画面.png =800x)</p>
<p>右上の「Host Agent」をクリックします。
![](/assets/blog/authors/alex.q/agentcore/Host_Agentをクリック.png =800x)</p>
<p>必要な情報を記入していきます。</p>
<ul>
<li>Name は「agentcore_techblog」にしました。（※ -（ハイフン）も使えると書いてありますが実際使うとエラーになりました。ここにバグがあるかと思います。）</li>
<li>Docker image は先ほどプッシュしたやつを選びます。</li>
<li>IAM ロールは、既存のものがあればそれを使えます。なければここで新規作成できるので、今回は新規作成でいきます。
![](/assets/blog/authors/alex.q/agentcore/agent_runtime作成.png =800x)</li>
</ul>
<p>ここでまたエラーになりました。
![](/assets/blog/authors/alex.q/agentcore/ロールエラー.png =800x)</p>
<p>どうやら、ここで新規作成した IAM ロールの権限が正しく設定されていなかったようです。
IAM で確認しましょう。</p>
<p>先ほど作成した ECR リポジトリが「../agentcore/techblog」に対して、自動で作成された IAM ロールのアクセスできるリソースは「../techblog」になっていますね...
これもバグではないかと。
![](/assets/blog/authors/alex.q/agentcore/リポジトリエラー.png =800x)</p>
<p>手動で「../agentcore/techblog」に直しましょう。
![](/assets/blog/authors/alex.q/agentcore/リポジトリエラー修正.png =800x)</p>
<p>これで、AgentCore Runtime を無事作成できました。
![](/assets/blog/authors/alex.q/agentcore/agent作成成功.png =800x)</p>
<h2>AgentCore Runtime のエージェントと会話してみる</h2>
<p>デプロイしたエージェントと会話してみましょう。</p>
<p>公式でもサンプルコードを提供しているので、早速それを使ってみます。
質問内容に関して、今回構築したエージェントはツールを使って天気を調べることができるとなっているので今日の天気について聞いてみましょう。</p>
<pre><code>input_text = &quot;今日の天気は？&quot;

response = client.invoke_agent_runtime(
    agentRuntimeArn=&quot;YOUR_AGENT_RUNTIME_ARN&quot;,
    qualifier=&quot;&lt;Endpoint Name&gt;&quot;,
    payload=input_text
)
</code></pre>
<p>うまく返事が返ってきました。
![](/assets/blog/authors/alex.q/agentcore/agent返答.png =800x)</p>
<h2>おわりに</h2>
<p>今回は、AWS Bedrock AgentCore を使って AI エージェントをマネジメントコンソール経由でデプロイする手順をご紹介しました。</p>
<p>公式ドキュメントでは主に bedrock-agentcore-starter-toolkit や boto3 API を使用したデプロイ方法が紹介されていますが、マネジメントコンソールを使った手動デプロイも可能であることを確認できました。ただし、いくつかの注意点がありました：</p>
<ul>
<li>Docker イメージは必ず ARM64 アーキテクチャで構築する必要がある</li>
<li>エージェント名にハイフン（-）を使用するとエラーが発生する可能性がある</li>
<li>IAM ロールの自動作成時に ECR リポジトリのリソースパスが正しく設定されない場合がある</li>
</ul>
<p>これらの点に注意すれば、マネジメントコンソールを使ったデプロイも十分可能です。特に、GUI での操作に慣れている方や、小規模な PoC での利用には便利な選択肢となるでしょう。</p>
<p>AWS Bedrock AgentCore は、企業での AI エージェント運用を考える上で非常に待ちかねたサービスです。今後も様々な活用パターンを検証し、知見を共有していきたいと思います。</p>
<p>最後まで読んでいただき、ありがとうございました！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA["Appium Meetup Tokyo #2" to Be Held! The Forefront of Mobile Test Automation]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-15-Appium-Meetup-Tokyo-2-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-15-Appium-Meetup-Tokyo-2-en/</guid>
            <pubDate>Tue, 29 Jul 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>📱 Appium Meetup Tokyo #2 is On the Way! Our Popular Mobile Test Automation Event is Back, and It&#39;s Better Than Ever!</h1>
<p>This is Nakanishi from the Developer Relations Group. The second Appium Meetup Tokyo, following the excitement of the first event, will be held at KINTO Technologies&#39; Muromachi Office on Wednesday, May 28, 2025. The previous session featured practical Appium use cases and dramatic performance improvements, which were very well received by many attendees.</p>
<h2>🎯 Highlights of This Event</h2>
<p>In this event, engineers working on the front lines will share practical know-how and the latest techniques to take mobile app E2E testing and automation to the next level.</p>
<ul>
<li><p><strong>Tips and practices for cause investigating Appium test failures</strong> (Masayuki Wakizaka, MagicPod Inc.)</p>
<ul>
<li>Learn concrete debugging techniques to quickly pinpoint and fix failures in mobile E2E testing! This session will walk through practical examples using Appium Inspector and cover troubleshooting with WebDriverAgent.</li>
</ul>
</li>
<li><p><strong>Initiatives for building a VRT environment for mobile apps</strong> (Hidenori Takahashi, Casio Computer Co., Ltd.)</p>
<ul>
<li>Introducing an example of automating visual regression testing (VRT) using Appium in the Flutter app CASIO WATCHES! We&#39;ll dive into hands-on approaches for solving issues and boosting efficiency in a Scrum development environment.</li>
</ul>
</li>
<li><p><strong>Goodbye, manual link checks! Fully Automating URL Checks in Native Apps with Appium</strong> (Fumiya Oka, Pann Nu Wai, KINTO Technologies)</p>
<ul>
<li>While automating link checks is easy on the web, it&#39;s long been considered difficult to achieve in native apps due to technical constraints. We&#39;ll share detailed know-how and clever tricks to fully automate link verification in native apps using Appium, overcoming technical roadblocks.</li>
</ul>
</li>
<li><p><strong>A little trick for smooth, stable scrolling with Appium</strong> (Taiju Muto, Autify)</p>
<ul>
<li>Get bite-sized techniques that can be used immediately in practice! This session will showcase a few small tweaks that brings big improvements.</li>
</ul>
</li>
</ul>
<h2>🕒 Time Schedule</h2>
<table>
<thead>
<tr>
<th>Time</th>
<th>Session Contents</th>
</tr>
</thead>
<tbody><tr>
<td>18:30</td>
<td>Doors open</td>
</tr>
<tr>
<td>19:00</td>
<td>Event starts</td>
</tr>
<tr>
<td>19:05</td>
<td>Tips and practices for investigating appium test failures</td>
</tr>
<tr>
<td>19:45</td>
<td>Initiatives for building a VRT environment for mobile apps</td>
</tr>
<tr>
<td>20:05</td>
<td>Break</td>
</tr>
<tr>
<td>20:15</td>
<td>Goodbye manual link checks! Fully automating URL checks in native apps with appium</td>
</tr>
<tr>
<td>20:35</td>
<td>A little trick for smooth, stable scrolling with Appium</td>
</tr>
<tr>
<td>20:55</td>
<td>Social gathering (on-site only)</td>
</tr>
<tr>
<td>21:30</td>
<td>Wrap-up &amp; venue close</td>
</tr>
</tbody></table>
<h2>🏢 Event Outline</h2>
<ul>
<li><strong>Date and time</strong>: May 28, 2025 (Wed), 19:00-21:30 (Doors open at 18:30)</li>
<li><strong>Venue</strong>: KINTO Technologies Muromachi Office (16th floor, Muromachi Furukawa Mitsui Building, 2-3-1 Nihonbashi Muromachi, Chuo-ku, Tokyo)</li>
<li><strong>Participation fee</strong>: Free</li>
<li>Capacity: 20 on-site participants, 50 online participants (Zoom webinar)</li>
</ul>
<h2>🌟 How to Apply</h2>
<p>If you&#39;d like to join, please sign up early via the <a href="https://appiummeetuptokyo.connpass.com/event/353600/">event page</a>. Register soon!</p>
<h2>💬Recommended Especially for...</h2>
<ul>
<li>Those who want to take mobile app test automation to the next level.</li>
<li>Anyone interested in adopting or using Appium and learning from real success stories.</li>
<li>People looking to learn the latest automation tech and seriously lighten the burden of QA workload.</li>
</ul>
<h2>🚀 Let&#39;s Create the Future of Mobile Testing Together!</h2>
<p>In our last post-event survey, participants showed strong interest in hands-on Appium tips and techniques, and the community interaction was buzzing. This time too, we&#39;re bringing even more solid content along with fresh opportunities to connect. We&#39;re looking forward to meeting you all!</p>
<h2>For Those Considering Participating</h2>
<ul>
<li><strong>Those who want to fully implement automated testing for mobile apps</strong>  </li>
<li><strong>Those who are interested in Appium and seeking specific examples or know-how</strong>  </li>
<li><strong>Engineers and QA personnel interested in CI/CD-based operations</strong>  </li>
<li><strong>Those who want to improve their testing culture by learning from other companies&#39; experiences</strong></li>
</ul>
<p>If any of the above applies to you, we&#39;d love to have you join us at Appium Meetup Tokyo to exchange the latest insights. Future announcements and detailed information will be provided by <a href="https://x.com/AutifyJapan">@AutifyJapan</a> and <a href="https://x.com/KintoTech_Dev">@KintoTech_Dev</a>. If you have any questions or suggestions, feel free to reach out.</p>
<p>I&#39;m looking forward to seeing you at Appium Meetup Tokyo!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-05-15-appium-meetup-tokyo/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: January 2025 Update]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-13-newcomers-introduction-25jan-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-13-newcomers-introduction-25jan-en/</guid>
            <pubDate>Mon, 28 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Introducing the new members who joined KINTO Technologies in January 2025.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hi, I&#39;m Kossy. I joined the company in January 2025! In this article, I asked a few of our January 2025 new joiners to share their first impressions right after joining. I&#39;ve put their thoughts together here. I hope this content will be useful for those who are interested in KINTO Technologies (KTC), and serve as a reflection for the members who participated in the interview!</p>
<h2>S.H</h2>
<p><strong>Self-introduction</strong>
Hi, my name is S.H., and I joined the company in January 2025. I work as a project manager (PjM) in the Digital Transformation Solution Group, part of the Mobility Product Development Division. Before joining KTC, I worked at several IT and production companies, taking on roles in marketing, product management (PdM), direction, system design and development, and PjM.</p>
<p><strong>How is your team structured?</strong>
We are a team of about a dozen members, working on multiple products and projects.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
I was impressed by the high level of technical skill here. There are lots of study sessions held both inside and outside the company, and they&#39;re always well-attended. I could tell they had a strong desire to improve themselves. As for surprises, honestly, I didn&#39;t expect people to be this dedicated to learning. Some online study sessions have over 100 participants, which is truly incredible.</p>
<p><strong>What is the atmosphere like on-site?</strong>
It&#39;s really easy to ask questions or get advice. Not only do mentors support you, but lots of people will jump in to help if you&#39;re stuck. It&#39;s a very supportive and comfortable environment to work in.</p>
<p><strong>How did you feel about writing a blog post?</strong>
Totally unexpected, so I was surprised when I was asked.</p>
<p><strong>Question from you to S.H</strong></p>
<blockquote>
<p>What are your thoughts on driving digital transformation at KTC?</p>
</blockquote>
<p>Looking across the group companies, I feel like there&#39;s still a ton of potential when it comes to promoting digital transformation. I want to help create better systems and work environments for both the teams on the ground and the customers they serve.</p>
<h2>Wenjia Lu</h2>
<p><strong>Self-introduction</strong>
I handle QA tasks for the Platform Development Division.</p>
<p><strong>How is your team structured?</strong>
We have 9 QA staff working alongside 4 other team members.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
It felt like a warm and welcoming team. And the work itself is rewarding.</p>
<p><strong>What is the atmosphere like on-site?</strong>
Everyone&#39;s always communicating and getting things done together.</p>
<p><strong>How did you feel about writing a blog post?</strong>
I wasn&#39;t sure what to write.</p>
<p><strong>Question from S.H. to Lu</strong></p>
<blockquote>
<p>Has anything about KTC&#39;s company culture or events struck you as unusual??</p>
</blockquote>
<p>Not really, nothing&#39;s felt especially different to me.</p>
<h2>I</h2>
<p><strong>Self-introduction</strong>
I&#39;m I. I joined the company in January 2025. I work in direction, in the Digital Transformation Solution Group of the Mobility Product Development Division. Until now, I&#39;ve held roles in marketing, product management, and project direction.</p>
<p><strong>How is your team structured?</strong>
We&#39;ve got directors and designers on the team, and we handle multiple products.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
I think it&#39;s a great place to learn, with plenty of opportunities to stay on top of new technologies.</p>
<p><strong>What is the atmosphere like on-site?</strong>
It&#39;s a comfortable environment. There&#39;s solid support, not just for the work itself, but also for anything you might want to talk about on a personal level.</p>
<p><strong>How did you feel about writing a blog post?</strong>
I was surprised to be asked. This is my first time writing a post about joining a company.</p>
<p><strong>Question from Lu to I</strong></p>
<blockquote>
<p>What&#39;s the one thing you most want to do at KTC?</p>
</blockquote>
<p>I want to create products that really solve users&#39; issues and also contribute to strong business performance for the company.</p>
<h2>KS</h2>
<p><strong>Self-introduction</strong>
I&#39;m KS from the KINTO ONE Development Division, Content Development Group. I am mainly in charge of the frontend development for static content on KINTO ONE.</p>
<p><strong>How is your team structured?</strong>
We&#39;re a 10-person team with 1 assistant manager, 1 team lead, and 8 members. We work on development, maintenance, and operations for multiple websites, including KINTO ONE and our corporate site.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
Everything was explained clearly during the interview, so there were no surprises. I was impressed by how many study sessions are held within the company. I got the impression that the entire company is motivated to grow technically.</p>
<p><strong>What is the atmosphere like on-site?</strong>
It&#39;s a fun and collaborative environment for development. Communication flows smoothly, not just within the team but also with directors and designers from other divisions.</p>
<p><strong>How did you feel about writing a blog post?</strong>
I had read a few posts before, but I never imagined I&#39;d be writing one.</p>
<p><strong>Question from I to KDS</strong></p>
<blockquote>
<p>What&#39;s the hottest project in your team right now?</p>
</blockquote>
<p>That would be the migration project for the KINTO ONE website, from Vue.js to Next.js. I&#39;m responsible for designing and developing the design system and components.</p>
<h2>Joonki Lee</h2>
<p><strong>Self-introduction</strong>
My name is Joonki Lee.It´s pronounced &#39;Ee,&#39; not &#39;Lee,&#39; so please don&#39;t get it wrong! I&#39;m part of the Platform Group in the Platform Development Division, where I work on building environments and developing tools for developers.</p>
<p><strong>How is your team structured?</strong>
Officially, we&#39;re divided into the MSP team and the SRE team, but I refer to them as the ‘managed platform team&#39; and the ‘scratch team.&#39; The ‘scratch team&#39; mainly develops tools from scratch, whereas the ‘managed platform team&#39; builds environments using SaaS and managed services.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
I felt like the managers were really approachable. There wasn&#39;t that many surprises.</p>
<p><strong>What is the atmosphere like on-site?</strong>
It&#39;s usually pretty quiet. Everyone&#39;s focused on their own tasks. But once a discussion kicks off, it gets lively and passionate.</p>
<p><strong>How did you feel about writing a blog post?</strong>
It made me think it&#39;d be nice to share a bit about my daily work in Tech Blog.</p>
<p><strong>Question from KS to Lee</strong></p>
<blockquote>
<p>I haven&#39;t been to the Jimbocho Office yet, what&#39;s it like?</p>
</blockquote>
<p>The recent renovations have made the space feel more open and added a touch of style.</p>
<h2>Kossy</h2>
<p>![Profile image Kossy](/assets/blog/authors/kossy/profile-img.png =250x)</p>
<p><strong>Self-introduction</strong>
Hi, I&#39;m Koshiro 🙌 I work in the Platform Development Division, where I handle the building and day-to-day maintenance of the cloud infrastructure that powers KINTO and other services!</p>
<p><strong>How is your team structured?</strong>
Under the department, there are four groups: Platform, DBRE, Cloud Infrastructure, and QA. These groups are engaged in various initiatives as an organization responsible for the non-functional aspects of the service.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
Even though the company had only been around for four years, I felt that the onboarding process was well organized. Also, there were way more services in operation than I had expected, and the team was tackling technical challenges way more actively than I imagined. so I felt that these aspects created a gap in a good way.</p>
<p><strong>What is the atmosphere like on-site?</strong>
We get along really well. Every time I go into the office, we grab meals together. There&#39;s a lot of casual chatting too, and the open environment where we can talk about anything have been a great support.</p>
<p><strong>How did you feel about writing a blog post?</strong>
I&#39;d actually been wanting to start writing Tech Blog for a while, so I thought this would be the perfect chance to finally jump in!</p>
<p><strong>Question from Lee to Kossy</strong></p>
<blockquote>
<p>What&#39;s one tech topic you&#39;re really into right now?</p>
</blockquote>
<p>That would be Platform Design Patterns! I&#39;m interested in platform design patterns within the context of Platform Engineering, especially those that take into account the characteristics of platforms tailored to different users, such as variations in the responsibilities delegated to development teams!</p>
<h2>TY</h2>
<p><strong>Self-introduction</strong>
I&#39;m Yamada from the Corporate IT Group in the IT &amp; Information Security Division. I mainly work on building the employee master data and developing systems that help improve and streamline business operations.</p>
<p><strong>How is your team structured?</strong>
It&#39;s called the Innovation Drive Team and has nine members. One thing that sets us apart is that we support not just our own company, but also other group companies.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
Not really. I even had the chance to tour the office during the interview process, so I had a pretty accurate idea of the workplace. A big plus was that the managers from Nagoya took the time to travel and meet me in person during the office visit. That made a strong impression and helped me decide to join. I really appreciated that.</p>
<p><strong>What is the atmosphere like on-site?</strong>
Everyone&#39;s open to constructive discussions, and people are quick to lend a hand if you&#39;re stuck. Super easy to work here.</p>
<p><strong>How did you feel about writing a blog post?</strong>
To be honest, I&#39;m writing this right up against the deadline and kind of kicking myself for it...But I&#39;d be happy if someone reading this gets curious about our company and considers joining us!</p>
<p><strong>Question from Kossy to TY</strong></p>
<blockquote>
<p>Is there any tech or project you&#39;d like to try at KTC?</p>
</blockquote>
<p>From a tech perspective, Data Space is a technology I&#39;d like to try out. Right now, most of my work involves tackling small-scale issues that don&#39;t quite become full-blown projects, but I&#39;m passionate about improving convenience for our users (employees), and I want to keep pushing that forward.</p>
<h2>Naoki Takigawa</h2>
<p><strong>Self-introduction</strong> 
My name is Takigawa. I&#39;m on the scratch development team in the Platform Development Division, where I test and develop tools used by engineers. I am mainly working on frontend development!</p>
<p><strong>How is your team structured?</strong>
Kossy already gave a great summary, so I&#39;ll refer you to that.
Under the department, there are four groups: Platform, DBRE, Cloud Infrastructure, and QA. These groups are engaged in various initiatives as an organization responsible for the non-functional aspects of the service.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
During the interview, I was told the team was newly formed and the training structure might still be in progress. But when I joined, I found onboarding materials and OJT training were already in place. So I was like, &quot;Wait, this is actually well prepared.&quot;</p>
<p><strong>What is the atmosphere like on-site?</strong>
There&#39;s a welcoming atmosphere that makes it easy to ask questions. A lot of our team members in the Platform Development Division are based in Tokyo, and we usually travel back and forth between each other&#39;s bases about once a month.</p>
<p><strong>How did you feel about writing a blog post?</strong>
My first thought was, &quot;What should I even write?&quot;</p>
<p><strong>Question from TY to Takigawa</strong></p>
<blockquote>
<p>What technology area are you currently most interested in or really into?</p>
</blockquote>
<p>Probably AI field...? I&#39;m interested in using AI to boost development efficiency. Right now, I&#39;m experimenting with a bunch of tools, such as Devin, OpenHands, and GitHub Copilot✖️MCP. I&#39;m running them in parallel while building an AI dev team to explore what works best. Reviewing will be intense... lol</p>
<h2>Yu Qiao</h2>
<p><strong>Self-introduction</strong>
I&#39;m Yu a.k.a Alex! I work as a generative AI engineer in the AI First Group in the IT &amp; Information Security Division. I mainly support Generative AI initiatives, including business promotion and Proof of Concept (PoC) projects!</p>
<p><strong>How is your team structured?</strong>
There are six of us on the team, each with different specialized skills and active in their own specialized areas.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
The vibe I got during the interviews matched what I experienced after joining, so there weren&#39;t any big surprises. As Takigawa-san mentioned, I had heard the training setup might be a bit underdeveloped. But it turned out to be more solid than expected in a good way. That was a nice surprise.</p>
<p><strong>What is the atmosphere like on-site?</strong>
It&#39;s a place where you&#39;re free to take on any challenges you want. And if you ever hit a wall, you can always ask for help.</p>
<p><strong>How did you feel about writing a blog post?</strong>
I feel like writing it again!</p>
<p><strong>Question from Takigawa to Alex</strong></p>
<blockquote>
<p>What kind of headphones do you always wear during meetings? If you have any favorite features, let us know!</p>
</blockquote>
<p>I actually got them at my previous job. When COVID hit and we all shifted to full remote work, our team needed good noise-canceling headphones. So my boss and I went to Yodobashi Camera together and picked them out.</p>
<h2>you</h2>
<p>![Profile image you](/assets/blog/authors/you/tanuki.jpeg =250x)</p>
<p><strong>Self-introduction</strong>
I&#39;m you from the Cloud Infrastructure Group, part of the Platform Development Division! I work on a variety of tasks centered around AWS, including building, operating, and continuously improving our internal infrastructure.</p>
<p><strong>How is your team structured?</strong>
The Cloud Infrastructure Group currently has nine members. It&#39;s a flat environment where we can easily talk and collaborate with both our manager and team leaders. Everyone in the group is really driven, so it keeps me motivated every day.</p>
<p><strong>What was your first impression of KTC when you joined? Were there any surprises?</strong>
My first thought was, &quot;this company is evolving fast!&quot; I was impressed by how well-structured the internal systems and workflows were. It was much more organized than I had expected. If there was any gap, it was actually a positive one: communication was smooth and technical challenges were being tackled across the whole company.</p>
<p><strong>What is the atmosphere like on-site?</strong>
It&#39;s a fantastic workplace where people aren&#39;t afraid to take risks, and we can openly share ideas and opinions about our work!</p>
<p><strong>How did you feel about writing a blog post?</strong>
I&#39;m already thinking about the topic for my next Tech Blog!</p>
<p><strong>Question from Alex to you</strong></p>
<blockquote>
<p>What challenges would you like to take on at KTC in the future?</p>
</blockquote>
<p>I want to keep driving the evolution of both technology and culture at KTC. One person alone can&#39;t achieve much, but I&#39;m committed to doing everything I can to make a meaningful impact. Right now, I&#39;m especially focused on learning new technologies and sharing them with others.</p>
<h2>Finally</h2>
<p>Thank you everyone for sharing your thoughts after joining our company!</p>
<p>Our team at KINTO Technologies is steadily expanding with new members coming on board! We&#39;ll be posting more new-joiner stories from across divisions, so stay tuned!</p>
<p>And yes: we&#39;re still hiring! KINTO Technologies is looking for new teammates to join us across a variety of divisions and roles. For more details, check it out <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Deploying AWS Bedrock AgentCore via the Management Console]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-28-aws-agentcore-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-28-aws-agentcore-en/</guid>
            <pubDate>Mon, 28 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[記事の説明を入力]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>Introduction</h2>
<p>Hello!<br>We&#39;re nicepear and AlexQ from KINTO Technologies, AI First Group! The AI First Group is a cross-functional group promoting generative AI use, developing use cases, researching technologies, running PoCs, and providing educational training.</p>
<p>This time, we deployed the AWS Bedrock AgentCore agent via the AWS Management Console. We would like to share our experience.</p>
<h2>What is AWS Bedrock AgentCore?</h2>
<p>AWS Bedrock AgentCore is a new foundational service offered by AWS to enable companies to develop professional-level AI agents and operate them securely and on a large scale.</p>
<p>According to <a href="https://aws.amazon.com/jp/blogs/news/introducing-amazon-bedrock-agentcore-securely-deploy-and-operate-ai-agents-at-any-scale/">official documentation</a>, the main elements are:</p>
<blockquote>
<p><strong>AgentCore Runtime</strong> – Provides a sandboxed, low-latency, serverless environment with session isolation, supports any agent framework, including popular open-source frameworks, tools, and models, and processes multimodal workloads and long-running agents.</p>
<p><strong>AgentCore Memory</strong> – Manages session and long-term memory, helping the agent learn from past interactions while providing relevant context to the model.</p>
<p><strong>AgentCore Observability</strong> – Provides step-by-step visualization of agent execution using metadata tagging, custom scoring, trajectory inspection, and troubleshooting/debug filters.</p>
<p><strong>AgentCore Identity</strong> – Enables AI agents to securely access AWS services and third-party tools and services such as GitHub, Salesforce, and Slack, on behalf of a user or by AI agents themselves with pre-authorized user consent.</p>
<p><strong>AgentCore Gateway</strong> – Transforms your existing APIs and AWS Lambda functions into agent-enabled tools, providing unified access across protocols like MCP and runtime detection.</p>
<p><strong>AgentCore Browser</strong> – Provides a managed web browser instance for scaling agent web automation workflows.</p>
<p><strong>AgentCore Code Interpreter</strong> – Provides an isolated environment for executing agent-generated code.</p>
</blockquote>
<h2>How to Deploy an Agent to the AWS AgentCore Runtime</h2>
<p>Regarding the deployment of AI agents at AWS AgentCore, the official approach is to use the following library called <strong>bedrock-agentcore-starter-toolkit</strong>:</p>
<p><a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-getting-started-toolkit.html">https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-getting-started-toolkit.html</a></p>
<p>Additionally, the official method of using the boto3 API is also introduced as an alternative to using <strong>bedrock-agentcore-starter-toolkit</strong>.</p>
<p><a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-custom.html">https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-custom.html</a></p>
<p>This time, we tried <strong>Manual deployment via the management console</strong>, which is not described in the official documentation, and would like to share my experience and some points to note.</p>
<h2>Deployment Steps in the Management Console</h2>
<p>The major steps are:</p>
<ol>
<li>Prepare agent source code.</li>
<li>Create a Dockerfile.</li>
<li>Create an ECR repository in the Management Console.</li>
<li>Create an ARM64 image with Docker and deploy it to an ECR repository.</li>
<li>Create a new agent in the AgentCore Management Console.</li>
</ol>
<h2>Prepare agent source code.</h2>
<p>This time we built the agent using the official Strand Agents. Also, since the purpose is to test the deployment steps, the configuration is relatively simple.</p>
<p><strong>agentcore.py</strong></p>
<pre><code class="language-python">import os
from strands import Agent, tool
from strands_tools import calculator
from strands.models.litellm import LiteLLMModel
from bedrock_agentcore.runtime import BedrockAgentCoreApp

app = BedrockAgentCoreApp()
os.environ[&quot;OPENAI_API_KEY&quot;] = &quot;YOUR_API_KEY&quot;

@tool
def get_tenki():
   &quot;&quot;&quot; 天気を調べるためのツール &quot;&quot;&quot;
   return &quot;晴れです。&quot;

model = &quot;gpt-4.1&quot;
model = LiteLLMModel(
   model_id=model
)

agent = Agent(
   model=model,
   tools=[calculator, get_tenki],
   system_prompt=&quot;あなたは賢いエージェントです。 ユーザーの指示に応じて、数字の計算を行い、あるいは天気を調べてあげてください。&quot;
)

@app.entrypoint
def revoke_agent(payload):
    user_input = payload.get(&quot;prompt&quot;)
    response = agent(user_input)
    print(response.message[&#39;content&#39;][0][&#39;text&#39;])
    return response.message[&#39;content&#39;][0][&#39;text&#39;]

if __name__ == &quot;__main__&quot;:
   app.run()
</code></pre>
<p>The directory structure is as follows:</p>
<pre><code>AWS-AGENTCORE/
├── agentcore.py                
├── Dockerfile 
├── requirements.txt      
</code></pre>
<h2>Create a Dockerfile.</h2>
<p>We have prepared a Dockerfile as follows:</p>
<pre><code class="language-docker">FROM python:3.12-slim

WORKDIR /app

COPY requirements.txt /app

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy agent file
COPY agentcore.py /app

ENTRYPOINT [&quot;python&quot;, &quot;/app/agentcore.py&quot;]
</code></pre>
<h2>Create an ECR repository in the Management Console.</h2>
<p>Repository creation is no different from the usual procedure.</p>
<p>First, log in to the AWS Management Console and access ECR. Then click &quot;Create Repository&quot; in the top right corner. ![](/assets/blog/authors/alex.q/agentcore/ECRにログイン.png =800x)</p>
<p>Enter a repository name and click &quot;Create&quot; in the bottom right. ![](/assets/blog/authors/alex.q/agentcore/ECRリポジトリ作成.png =800x)</p>
<p>Once created, you can see it in the repository list. ![](/assets/blog/authors/alex.q/agentcore/作成結果.png =800x)</p>
<h2>Create an ARM64 image with Docker and deploy it to an ECR repository.</h2>
<p>Basically, proceed in the order of push commands displayed in the ECR repository. The push commands can be found here:</p>
<p>![](/assets/blog/authors/alex.q/agentcore/プッシュコマンド表示.png =800x)</p>
<p>![](/assets/blog/authors/alex.q/agentcore/プッシュコマンド.png =800x)</p>
<p>First, log in to ECR using the AWS CLI:</p>
<pre><code>aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin account-id.dkr.ecr.us-west-2.amazonaws.com
</code></pre>
<p>Next, build the Docker image. When we built it exactly as described in the push command, I got an error when creating the AgentCore agent later. The error appears to be caused by the requirement to build an ARM64 Docker container.</p>
<pre><code># エラーを起こしたコマンド
docker build -t agentcore/techblog .
</code></pre>
<p><a href="https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/getting-started-custom.html">The official documentation</a> also states that ARM64 should be used.</p>
<blockquote>
<p>Build the image locally for testing:</p>
<pre><code>docker buildx build --platform linux/arm64 -t my-agent:arm64 --load .
</code></pre>
</blockquote>
<p>So we built the image using the following command:</p>
<pre><code>docker build --platform linux/arm64 -t agentcore/techblog .
</code></pre>
<p>Once the build is complete, tag the image.</p>
<pre><code>docker tag agentcore/techblog:latest account-id.dkr.ecr.us-east-1.amazonaws.com/agentcore/techblog:latest
</code></pre>
<p>Finally, run the following command to push this image to the ECR repository created previously.</p>
<pre><code>docker push account-id.dkr.ecr.us-east-1.amazonaws.com/agentcore/techblog:latest
</code></pre>
<h2>Create a new agent in the AgentCore Management Console.</h2>
<p>Finally, access the Bedrock AgentCore management console. ![](/assets/blog/authors/alex.q/agentcore/agentcoreメイン画面.png =800x)</p>
<p>Go into the Agent Runtime. ![](/assets/blog/authors/alex.q/agentcore/agent_runtime画面.png =800x)</p>
<p>Click &quot;Host Agent&quot; in the top right. ![](/assets/blog/authors/alex.q/agentcore/Host_Agentをクリック.png =800x)</p>
<p>Fill in the required information.</p>
<ul>
<li>I set the name to &quot;agentcore_techblog.&quot; (* Although it is written that a hyphen (-) can be used, actually using it caused an error. A bug seems to be present here.)</li>
<li>Select the Docker image that was pushed earlier.</li>
<li>If there is an existing IAM role, we can use it. If not, we can create a new one here, so we will create a new role this time. ![](/assets/blog/authors/alex.q/agentcore/agent_runtime作成.png =800x)</li>
</ul>
<p>Here, an error occurred again. ![](/assets/blog/authors/alex.q/agentcore/ロールエラー.png =800x)</p>
<p>It seems that the permissions for the newly created IAM role were not set correctly. Let&#39;s check in IAM.</p>
<p>The ECR repository we previously created is &quot;../agentcore/techblog&quot;, but the resource the automatically created IAM role can access is &quot;../techblog&quot;... I think this is also a bug. ![](/assets/blog/authors/alex.q/agentcore/リポジトリエラー.png =800x)</p>
<p>Let&#39;s manually correct it to &quot;../agentcore/techblog.&quot; ![](/assets/blog/authors/alex.q/agentcore/リポジトリエラー修正.png =800x)</p>
<p>Now we have successfully created the AgentCore Runtime. ![](/assets/blog/authors/alex.q/agentcore/agent作成成功.png =800x)</p>
<h2>Talking to an AgentCore Runtime Agent</h2>
<p>Let’s try talking to the deployed agent.</p>
<p>Since the official documentation also provides sample code, we’ll try using it right away. Regarding the question, since the agent we built this time is supposed to be able to use tools to check the weather, let’s ask about today’s weather.</p>
<pre><code>input_text = &quot;今日の天気は？&quot;

response = client.invoke_agent_runtime(
    agentRuntimeArn=&quot;YOUR_AGENT_RUNTIME_ARN&quot;,
    qualifier=&quot;&lt;Endpoint Name&gt;&quot;,
    payload=input_text
)
</code></pre>
<p>We received a good response. ![](/assets/blog/authors/alex.q/agentcore/agent返答.png =800x)</p>
<h2>Conclusion</h2>
<p>This time, we introduced the steps to deploy an AI agent using AWS Bedrock AgentCore via the Management Console.</p>
<p>The official documentation mainly introduces deployment methods using the bedrock-agentcore-starter-toolkit and the boto3 API, but we were able to confirm that manual deployment using the Management Console is also possible. However, there were a few points to note:</p>
<ul>
<li>Docker images must be built for the ARM64 architecture.</li>
<li>Using hyphens (-) in agent names can cause errors.</li>
<li>When automatically creating an IAM role, ECR repository resource paths may not be set correctly.</li>
</ul>
<p>If you pay attention to these points, deployment using the Management Console is perfectly possible. This is a particularly convenient option for those who are accustomed to GUI operations and for use in small-scale PoCs.</p>
<p>AWS Bedrock AgentCore is a highly anticipated service for those considering running AI agents in their companies. We would like to continue to explore various usage patterns and share our findings.</p>
<p>Thank you for reading to the end!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Leave It All Up to ChatGPT! A Video Featuring a Virtual Character Created with AI (Midjourney/Runway)]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making-en/</guid>
            <pubDate>Fri, 25 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article summarizes the process of using AI such as ChatGPT, Midjourney, and Runway to create a video of a virtual character almost entirely through conversation.]]></description>
            <content:encoded><![CDATA[<p>&quot;I’d love to create a video with this kind of worldview…&quot;<br>What do you do when you feel that way?<br>Without any hesitation, I decided to leave it all up to ChatGPT.</p>
<p>Hello. My name is Momoi (<a href="https://x.com/momoitter">@momoitter</a>), and I’m a designer in the Creative Office at KINTO Technologies.</p>
<p>This article summarizes my process of using AI tools such as ChatGPT, Midjourney, and Runway to create the visuals of a pink-haired virtual character named &quot;KTC AI WEB,&quot; almost entirely through conversation.</p>
<p>Even if you don’t have any specialized skills or much time, all you need is an idea of the kind of video you’d like to create. This article is written for people who’d like to experience the process of gradually turning an image into a tangible form together with AI.</p>
<p>First, please take a look at the completed video.<br><a href="https://www.youtube.com/watch?v=GH9CdNqTyHQ">https://www.youtube.com/watch?v=GH9CdNqTyHQ</a></p>
<h2>It All Started with the &quot;Renewal&quot; of a Character</h2>
<p>This character, KTC AI WEB, was originally created for the company’s internal event, &quot;CHO All-Hands Meeting,&quot; in November 2024. !<img src="/assets/blog/authors/momoi/250606/01_old_character.jpg" alt="old_character"></p>
<p>Please take a look at this article for the creative process.
<a href="https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/">https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/</a></p>
<p>This character used cutting-edge AI technology at that time, and attracted a lot of attention within the company.</p>
<p>…But only four months have passed since then. At that time, I thought image generative AI and video generative AI were amazing and had already progressed so much. Though looking at her now, she feels a little outdated.</p>
<p>So, I thought to myself, &quot;I will take this as an opportunity to use the latest technology to upgrade this character’s world,&quot; and decided to start rebuilding it together with ChatGPT.</p>
<h2>Step 1: Share the Worldview and Generate an Image</h2>
<p>The first thing I did was share the character and worldview.
I uploaded the image of KTC AI WEB that I had originally generated to ChatGPT and told it the following:</p>
<blockquote>
<p>This character was created using slightly outdated image generative AI technology, so I would like to update her appearance.<br>She has a setting called &quot;Virtual Agent.&quot;<br>Please expand on the worldview based on that setting, and propose scene variations and prompts that can be expressed in Midjourney v7.</p>
</blockquote>
<p>The reason I chose Midjourney was because I felt that the accuracy and texture of the character depictions had significantly improved since the update to v7. I thought it would be perfect for a situation like this one, where I wanted to upgrade the look of an existing character.</p>
<p><img src="/assets/blog/authors/momoi/250606/02_chatgpt_input.jpg" alt="chatgpt_input"></p>
<p>Right after that, I received a series of specific situation ideas and corresponding prompts such as, &quot;With that worldview, how about a scene like this?&quot;<br>It felt like I was brainstorming with a film director.</p>
<p><img src="/assets/blog/authors/momoi/250606/03_chatgpt_prompt_midjourney.jpg" alt="chatgpt_prompt_midjourney"></p>
<p>When I typed the prompts into Midjourney, the visuals that were generated one after another went far beyond my imagination, and I was amazed at how expressive they were.  </p>
<p><img src="/assets/blog/authors/momoi/250606/04_midjourney01.jpg" alt="midjourney01"> <img src="/assets/blog/authors/momoi/250606/04_midjourney02.jpg" alt="midjourney02"> <img src="/assets/blog/authors/momoi/250606/04_midjourney03.jpg" alt="midjourney03"> <img src="/assets/blog/authors/momoi/250606/04_midjourney04.jpg" alt="midjourney04"> <img src="/assets/blog/authors/momoi/250606/04_midjourney05.jpg" alt="midjourney05"></p>
<p>When I first started making this video, Midjourney v7 did not have features like &quot;Omni-Reference&quot; to maintain character consistency yet. So, I made an effort to make the prompts look consistent by consciously including the easily recognizable characteristic of &quot;short pink hair&quot; in them.</p>
<p>If something different from what you imagined comes up, just tell ChatGPT things like &quot;Get a littler closer to her face,&quot; or &quot;Make the background brighter and cleaner,&quot; and it will instantly output a readjusted prompt.  </p>
<h2>Step 2: Generate a Video from an Image</h2>
<p>Once you have generated an image that you like, you can attach it to ChatGPT and make a request as seen below.</p>
<blockquote>
<p>This is an image that was generated in Midjourney based on the prompt for the proposed scene.<br>I’d like to set this image as the first frame of Runway’s Gen-4 keyframe feature and generate a video.<br>Please generate a prompt that adds some movement to make this scene more appealing.</p>
</blockquote>
<p><img src="/assets/blog/authors/momoi/250606/05_chatgpt_input_scene.jpg" alt="chatgpt_input_scene"></p>
<p>ChatGPT reads the content of the image and creates a Runway prompt that maximizes its appeal.<br><img src="/assets/blog/authors/momoi/250606/06_chatgpt_prompt_runway.jpg" alt="chatgpt_prompt_runway"></p>
<p>The reason I used Runway was because, with the advancement to Gen-4, I felt that it could animate the image without compromising Midjourney’s high-definition appeal.  </p>
<p>I uploaded the image generated by Midjourney to Runway Gen-4’s image to video.<br>By pasting the prompt output by ChatGPT, a high-quality video was generated that brought out the image’s worldview to the fullest. <img src="/assets/blog/authors/momoi/250606/07_runway.jpg" alt="runway"></p>
<p>If the image of the character or camera movement is different from what you imagined, simply tell ChatGPT, &quot;The generated video was like this, so I’d like to change this part like this,&quot; and it will re-suggest prompts.  </p>
<h2>Step 3: Select Background Music with ChatGPT</h2>
<p>ChatGPT is also extremely useful when searching for background music for videos.  </p>
<blockquote>
<p>What keywords should I use to search for background music in Adobe Stock that fits this worldview?</p>
</blockquote>
<p>When asked, it suggested several words that fit the atmosphere, such as &quot;futuristic,&quot; &quot;sci-fi,&quot; and &quot;cyberpunk.&quot;</p>
<p><img src="/assets/blog/authors/momoi/250606/08_adobestock.jpg" alt="adobestock"></p>
<h2>Step 4: Edit and Finish</h2>
<p>Stitch the generated video and background music together in Premiere Pro, and adjust the structure, length, and tempo.<br>Adding fade-ins and fade-outs when switching scenes and varying the speed of the sounds can greatly improve the overall quality of your video.</p>
<p>By combining the still images created in Midjourney with the smooth movements created in Runway, I was able to add a sense of &quot;breath&quot; and &quot;atmosphere&quot; that couldn’t be fully conveyed with still images alone, creating an image video that makes KTC AI WEB’s worldview feel even more real.
<a href="https://www.youtube.com/watch?v=GH9CdNqTyHQ">https://www.youtube.com/watch?v=GH9CdNqTyHQ</a></p>
<h2>Giving Shape to Imagination with AI</h2>
<p>What I felt most during this process was that ChatGPT helped me to gradually &quot;verbalize and materialize&quot; the vague images in my head.  </p>
<p>Whether it was Midjourney or Runway, I felt that just by saying, &quot;It’s a little different,&quot; or &quot;It’s more like this,&quot; I was able to get closer to my ideal expression.</p>
<p>By working together with AI, we’ll be able to greatly expand our creative horizons. Please give it a try.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/momoi/250606/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: February 2025 Update]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-09-newcomers-introduction-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-09-newcomers-introduction-en/</guid>
            <pubDate>Thu, 24 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Introducing the new members who joined KINTO Technologies in February, 2025.]]></description>
            <content:encoded><![CDATA[<h1>Hello</h1>
<p>Hello, I&#39;m hayashi-d1125, I joined the company in February 2025! In this article, I asked our new joiners from February 2025 to share their initial impressions after joining. I&#39;ve compiled their thoughts here. I hope this content proves helpful for anyone interested in KINTO Technologies and offers a moment of reflection for the members who took part in the interview!</p>
<h1>Yasuharu Satake</h1>
<p><img src="/assets/blog/authors/hayashi-d1125/newcomers/member_s.jpeg" alt="Satake"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I&#39;m Satake from the Project Promotion Group of the New Service Initiatives Division. I work as both a product manager (PdM) and project manager (PjM), handling new products and projects planned internally within the company.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The Project Promotion Group has a total of 15 members, six of whom make up the product management team that I’m part of.</li>
</ul>
</li>
<li><strong>What was your first impression when you joined KTC? Were there any surprises?</strong><ul>
<li>As an in-house development company within the Toyota Group, I was surprised to find how highly organized and well-developed the internal structure was.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>Our team has a strong mutual support system—whenever someone has a question, it’s easy to get information or advice from other members. When we&#39;re in the office, we often go out for lunch together, and even outside of work, there are regular social events across divisions, creating a lively and friendly environment.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I used to read this Tech Blog before joining the company, but I didn&#39;t expect to be contributing so soon after joining. I&#39;m very honored, and I enjoy sharing ideas and information, so I&#39;d love to keep writing when the opportunity arises in the future.</li>
</ul>
</li>
<li><strong>Question from Hiraku Kudo to Yasuharu Satake</strong><ul>
<li><strong>How do you interact with members of other divisions?</strong><ul>
<li>I actively participate in cross-division events like Bear Bash and club activities within KINTO Technologies, which help me build connections across the company. In particular, at Bear Bash, I performed as a DJ for the event&#39;s background music, which gave me the chance to interact with many colleagues!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Yurie Wakisaka</h1>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I work in the Corporate Planning Group of the Development Support Division. I mainly handle financial back-office tasks such as billing and budgeting at KINTO Technologies.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>Our team is made up of six members, and we share the workload by dividing tasks among ourselves.</li>
</ul>
</li>
<li><strong>What was your first impression when you joined KTC? Were there any surprises?</strong><ul>
<li>I was surprised at how quickly decisions are made and turned into action!</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>I find the on-site atmosphere to be very collaborative. Since our team is distributed across various locations, most of our communication happens remotely. However, we hold regular meetings to maintain clear communication and keep projects on track.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I&#39;m not very good at writing, but I saw this as a great opportunity and decided to take it on with a positive attitude.</li>
</ul>
</li>
<li><strong>Question from Yasuharu Satake to Yurie Wakisaka</strong><ul>
<li><strong>What differences have you noticed between KINTO Technologies and your previous workplaces?</strong><ul>
<li>I feel the company invests generously in learning, such as study groups and seminars.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Xiaolong Yang</h1>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I&#39;m Yang from the Salesforce Development Group in the Business System Development Division. I work on maintenance and development for KINTO FACTORY.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>Our group consists of six members, including myself.</li>
</ul>
</li>
<li><strong>What was your first impression when you joined KTC? Were there any surprises?</strong><ul>
<li>I felt &quot;freedom.&quot; From dress code to flexible working hours.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>Everyone on the team is kind and approachable, making it easy to ask questions whenever I&#39;m unsure about something.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I&#39;m not so confident when it comes to writing about personal thoughts, so this was challenging for me.</li>
</ul>
</li>
<li><strong>Question from Yurie Wakisaka to Xiaolong Yang</strong><ul>
<li><strong>What has been the biggest challenge you&#39;ve faced since joining the company?</strong><ul>
<li>Sometimes in meetings or chats, I encounter words or terms that I don’t understand. I&#39;m still working hard on improving my Japanese!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Yohei Hayashida</h1>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I am Hayashida from the Platform Engineering Team within the Platform Group. I&#39;m involved in developing, providing, and maintaining various tools for our development teams at KINTO Technologies. I&#39;m based at the Osaka Tech Lab.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>We have three members at the Osaka Tech Lab, and six at the Jinbocho Office. Since we work across different locations, we rely on communication tools like Slack and Teams.</li>
</ul>
</li>
<li><strong>What was your first impression when you joined KTC? Were there any surprises?</strong><ul>
<li>Given how well-developed the systems and workflows were, it was hard to believe the company had only been around for four years. On the other hand, there are still many areas within my own team that are yet to be developed, and I&#39;m excited about the opportunities to take part in building it.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>Osaka Tech Lab, where I work, started with just one person, and by the time I joined, the team had finally grown to three members. Since we&#39;re not based at the main Jinbocho Office, I sometimes feel a bit out of the loop with what&#39;s trending there. I think there&#39;s still room to improve communication across different locations.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I used to write blog posts at my previous job, so I didn&#39;t feel particularly uncomfortable or hesitant about it. But finding good topics is always a challenge regardless of the company (laughs), so I try to regularly explore new technologies to keep fresh ideas coming.</li>
</ul>
</li>
<li><strong>Question from Xiaolong Yang to Yohei Hayashida</strong><ul>
<li><strong>How do you spend your days off?</strong><ul>
<li>I have a family of five, my wife and children, so I spend most of my time off with them. Last week, we all drove to Chubu Centrair International Airport in Aichi Prefecture for a family outing.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Sakura Kodama</h1>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I&#39;m involved in data analysis in the Analysis Production Group in the Data Analytics Division.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>Our team consists of my boss, four senior colleagues, and myself.</li>
</ul>
</li>
<li><strong>What was your first impression when you joined KTC? Were there any surprises?</strong><ul>
<li>I was surprised at how thorough the orientation was. Aside from that, just as I had heard beforehand —appropriately flexible, so nothing came as a major surprise.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>Everyone is calm and kind, but highly professional. An unexpected contrast that really struck me! (I had this stereotype that professionals are scary.)</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>When I first heard about it, I honestly thought, &quot;This sounds like a pain.&quot; But once I started writing, it turned out to be a great way to reflect on where I am now, and I&#39;m glad I gave it a try. It reminded me how important it is to take on new challenges.</li>
</ul>
</li>
<li><strong>Question from Yohei Hayashida to Sakura Kodama</strong><ul>
<li><strong>What made you decide to work in data analysis?</strong><ul>
<li>While working in the outsourcing industry, I was unexpectedly assigned to an access analytics team to fill a sudden vacancy at a client&#39;s office. I had no experience and didn&#39;t even know this kind of work existed, but once I got into it, I found myself wanting to dig deeper—and here I am now.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Shuya Ogawa</h1>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I&#39;m Ogawa from the Salesforce Development Group in the Business System Development Division. I&#39;m responsible for maintaining the Factory BO system.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>Our team consists of one manager and five members. We handle Salesforce operations and maintenance, data integration, and projects related to Salesforce.</li>
</ul>
</li>
<li><strong>What was your first impression when you joined KTC? Were there any surprises?</strong><ul>
<li>I thought that working in a team of engineers would mean it might be hard to ask questions without a certain level of technical knowledge. But the atmosphere was very open, and I found it easy to ask for help. They responded quickly and were genuinely supportive.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>As I mentioned above, the atmosphere is really open and approachable.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I have never written a blog post before, so I&#39;m grateful for the opportunity.</li>
</ul>
</li>
<li><strong>Question from Sakura Kodama to Shuya Ogawa</strong><ul>
<li>**How do you refresh during work breaks? **<ul>
<li>When I work from home, I go for a 30-minute run during my lunch break. When you&#39;re running, you don&#39;t have time to think, so you&#39;re forced to clear your mind. When I work in the office, I&#39;m still figuring out the best way.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Saki Yasuda</h1>
<p><img src="/assets/blog/authors/hayashi-d1125/newcomers/member_y.jpeg" alt="Yasuda"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>My name is Yassan and I&#39;m now working at the Cloud Infrastructure Group in the Platform Development Division. As the department name suggests, I work on the cloud infrastructure that supports our service platforms.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The Cloud Infrastructure Group has nine members, but is further divided into smaller teams.</li>
</ul>
</li>
<li><strong>What was your first impression when you joined KTC? Were there any surprises?</strong><ul>
<li>I came in expecting a rigid environment with lots of strict rules, but the reality was quite the opposite. The atmosphere was casual with open communication across all levels. Even in chat, people casually use stickers, which was a surprise for me.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>At my previous job, it was hard to bring up work-related question because of the &quot;quiet&quot; atmosphere. Now, I can discuss things with people right away, and we get along well as a team, so we always go out to eat lunch together.♪</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I used to read this blog before joining the company, so it feels really special to be writing for it now!</li>
</ul>
</li>
<li><strong>Question from Shuya Ogawa to Saki Yasuda</strong><ul>
<li><strong>Do you have any favorite lunch spots around the Jinbocho Office?</strong><ul>
<li>I highly recommend a restaurant I recently visited called Mori no Butchers. The lunch menu included hearty beef and pork steaks—they were absolutely delicious! I went around 11:30 and still had to wait 30 minutes, but it was totally worth it!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Hikaru Kudo</h1>
<p><img src="/assets/blog/authors/hayashi-d1125/newcomers/member_k.png" alt="Kudo"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I&#39;m Kudo and I&#39;ve joined the Engagement Group in the Mobility Product Development Division. My role is to support the digital transformation (DX) of operations within dealerships.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>Our team is made up of three members, including the manager. We collaborate closely with other development teams in the division and the KINTO Sales department, working directly with dealerships to understand their needs for digital transformation.</li>
</ul>
</li>
<li><strong>What was your first impression when you joined KTC? Were there any surprises?</strong><ul>
<li>I got the impression that there were a lot of engineers around. Since I hadn&#39;t worked so closely with engineers before, seeing everyone&#39;s monitors filled with code was a fresh experience for me.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong><ul>
<li>I’m frequently out visiting dealerships, but I’m always inspired by how everyone prioritizes the dealers’ needs when crafting proposals.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I have never been involved in a company blog before, so knowing this will be published makes me a bit nervous.</li>
</ul>
</li>
<li><strong>Question from Saki Yasuda to Hiraku Kudo</strong><ul>
<li><strong>How do you think generative AI could be used to boost engagement?</strong><ul>
<li>We already have products that use generative AI to suggest alternative vehicle options to customers. I see great potential in utilizing it to internal tasks like streamlining inquiry handling. There are many ways generative AI can enhance operational DX at dealerships.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Conclusion</h1>
<p>Thank you everyone for sharing your thoughts on our company after joining it!</p>
<p>There are more and more new members at KINTO Technologies every day! We&#39;ll be posting more new-joiner stories from across divisions, so stay tuned!</p>
<p>And yes — we&#39;re still hiring! KINTO Technologies is looking for new teammates to join us across a variety of divisions and roles. For more details, check it out <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Develop APIs Quickly and Operate Them at Low Cost Using Lambda, TypeScript, and Express.js]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-15-serverless-api-lambda-express-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-15-serverless-api-lambda-express-en/</guid>
            <pubDate>Wed, 23 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I will explain in detail how to use Lambda, TypeScript, and Express.js to quickly develop APIs while minimizing operational costs, especially for small-scale products.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello!<br>My name is Kameyama and I work as a web engineer at KINTO Technologies.<br>I currently work in the Marketing Product Group.  </p>
<p>I this article I will talk about how I built a serverless architecture.</p>
<p>With container-based applications like those running on ECS, you&#39;re charged for CPU and memory usage based on uptime, even when there are no incoming requests. This means you can <strong>end up paying for resources you’re not actually using</strong>, especially in PoC development or in products with very low traffic.<br>For these types of use cases, it is possible to significantly reduce running costs by using a pay-as-you-go serverless architecture, in which the server runs only when in use and automatically stops if no processing is performed for a certain period of time.</p>
<p>To achieve this, we built a Lambda-based application with the following key points:</p>
<ul>
<li>Serverless development using AWS API Gateway + Lambda</li>
<li>Simple and versatile API design with TypeScript + Express</li>
</ul>
<h2>About Serverless</h2>
<p>We decided to adopt Lambda, which is widely used as part of AWS&#39;s serverless configuration. As mentioned earlier, Lambda automatically handles server startup, shutdown, and scaling, and its pay-as-you-go pricing means you are charged only for what you use, minimizing costs.</p>
<p>On the other hand, a disadvantage of such serverless APIs is that <strong>response delays due to cold starts</strong> occur. This is especially true in environments with a small number of requests or when there has been no access for a certain period of time, Lambda goes into sleep mode, and when a request comes again, it takes time for the container to start up (actual measured response time is about 1 second).</p>
<p>In summary, this infrastructure configuration is especially recommended for those who want to <strong>quickly build a prototype or develop a tool for users who can tolerate response delays (such as internal members)</strong>.</p>
<h2>How Much Cheaper with Lambda?</h2>
<p>Let&#39;s compare the costs of Fargate, an always-running container, and Lambda, the serverless type we will use this time.</p>
<h3>Fargate</h3>
<p><a href="https://aws.amazon.com/jp/fargate/pricing/">AWS Fargate costs</a></p>
<p>Assuming 0.5 vCPU and 2GB of memory, the estimated operating cost per task per hour is as follows:</p>
<ul>
<li>vCPU cost: 0.5 vCPU x $0.04048 per vCPU-hour = $0.02024/hour</li>
<li>Memory cost: 2GB x $0.004445 per GB-hour = $0.00889/hour</li>
</ul>
<p>Based on these calculations, the total cost per hour is $0.02024 + $0.00889 = $0.02913. If the task runs continuously for a full month (720 hours), the monthly cost per task would be <strong>$20.9736</strong>. (However, you can save the cost by shutting down at night or lowering the vCPU specs.)</p>
<p>This is the cost per environment, so if you need multiple environments, such as a production and development, the total cost will scale accordingly.</p>
<h3>Lambda</h3>
<p><a href="https://docs.aws.amazon.com/ja_jp/whitepapers/latest/how-aws-pricing-works/aws-lambda.html">AWS Lambda cost</a> On the other hand, Lambda costs are calculated based on the number of requests and the compute time of the container temporarily activated in response to those requests.</p>
<ul>
<li>0.00001667 USD per GB-second</li>
<li>$0.20 per 1,000,000 requests</li>
</ul>
<p>Assuming 2GB like Fargate, a compute time of 0.5 seconds per request, and 100,000 requests per month, the total monthly cost for Lambda is $0.02 (request cost) + $1.6667 (compute cost) = approximately <strong>$1.69</strong> per month.<br>Even better, even if you increase the number of environments or the number of Lambdas per environment, the total cost remains the same as long as the total number of requests is unchanged.</p>
<p>These cost simulations demonstrate the cost advantages of Lambda.</p>
<p>This kind of cost reduction is especially beneficial for low-traffic internal tools that don&#39;t generate revenue, or for PoC products, as it helps lower financial barriers.</p>
<h2>About Express</h2>
<p>We adopted Express as the server-side JavaScript framework.</p>
<p>Express is designed to allow the intuitive understanding of the concepts of routing and middleware. Its configuration is easy to handle even for developers doing server-side development with Node.js for the first time. Express allows smooth scaling from small APIs to medium and large applications. The routing description is also concise.</p>
<pre><code class="language-TypeScript">app.get(&#39;/users/:id&#39;, (req, res) =&gt; {
  res.send(`User: ${req.params.id}`);
});
</code></pre>
<p>You can easily incorporate a wide range of middleware libraries depending on your needs, such as morgan for log output, passport for authentication, express-validator for input validation, etc.  This makes it easier to add features to and maintain your application.</p>
<p>It is possible to build an endpoint using the Lambda library officially distributed by AWS, but if you build it using the general-purpose library Express, it will be easier to reuse the code after the endpoint when switching to ECS or App Runner as the scale of your application expands, rather than using a Lambda-specific library.</p>
<h1>Development Policy</h1>
<p>In this article, I adopted <strong>a configuration in which multiple API endpoints are consolidated into a single Lambda function</strong>.</p>
<p>This is to make the most of Lambda&#39;s &quot;hot start&quot; feature.</p>
<p>Once a Lambda function is started, it remains in memory for a certain period of time, which is called a &quot;hot start&quot; state. Therefore, after one API is requested and Lambda is launched, requests to other APIs within the same function can also be processed speedily.</p>
<p>By taking advantage of this property, you can expect improved performance during operation.</p>
<p>However, Lambda has a limit on the deployable package size (50MB or less when zipped and 250MB or less after unzipped), so if you pack all the APIs in your application into a single function, you will eventually reach this limit, making it unrealistic.</p>
<p>For this reason, I will assume a structure in which <strong>related APIs are grouped into the same Lambda function by screen or functional unit</strong>. Ultimately, I will proceed on the assumption of a monorepo structure in which multiple Lambda functions are managed within a single repository.</p>
<p>In this article, the goal is to enable local execution using SAM, and I will omit the configuration of the AWS console or what happens after deployment.</p>
<h1>Environment Building (Preparation Before Coding)</h1>
<p>In this article, I will explain how to build an environment that combines pnpm, which makes it easy to manage multiple Lambda functions and shared code, with AWS SAM.</p>
<p>The entire project is managed as a pnpm workspace, and each Lambda function and common library is treated as an independent workspace. The deployment tool used is AWS SAM (Serverless Application Model).</p>
<p>Mainly, the following tools are required.</p>
<ul>
<li>Node.js</li>
<li>pnpm</li>
<li>AWS CLI</li>
<li>AWS SAM CLI</li>
<li>Git (version control)</li>
</ul>
<p>Git installation is omitted.</p>
<h2>Installing Required Tools</h2>
<h3>Node.js</h3>
<p>Node.js is required as before. You can install the LTS version from the official website.</p>
<p><a href="https://nodejs.org/ja">Node.js official website</a></p>
<p>After installation, check that the version is displayed with the following command.</p>
<pre><code class="language-Shell">node -v
npm -v # pnpmをインストールするために使用する
</code></pre>
<h3>pnpm</h3>
<p>Use pnpm to manage dependent libraries. pnpm is particularly good at resolving dependencies and the efficient use of disk space in a monorepo configuration where multiple modules (Lambda functions) are managed in a single repository.</p>
<p>Install pnpm using the following method:</p>
<pre><code class="language-Shell">npm install -g pnpm
</code></pre>
<p>For methods using curl or others, please refer to the official pnpm website. pnpm installation guide</p>
<p>After installation, check the version with the following command:</p>
<pre><code class="language-Shell">pnpm -v
</code></pre>
<h3>AWS CLI</h3>
<p>As before, the AWS CLI is required for linkage with AWS. Install it and set up your credentials using aws configure.</p>
<p><a href="https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html">AWS CLI Installation Guide</a></p>
<h3>AWS SAM CLI</h3>
<p>This time I will use AWS SAM (Serverless Application Model) as the deployment tool. AWS SAM is an infrastructure as code (IaC) framework for serverless applications, and the SAM CLI supports local build, testing, and deployment.</p>
<p>Refer to the official website below and install AWS SAM CLI according to your operating system.</p>
<p><a href="https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/install-sam-cli.html">AWS SAM CLI Installation Guide</a></p>
<p>After installation, check the version with the following command:</p>
<pre><code class="language-Shell">sam --version
</code></pre>
<h2>Project Structure and Workspace Setup</h2>
<p>In the root directory of the project, place <code>package.json</code>, which defines the config files for the entire monorepo and the dependencies of tools commonly used during development (e.g., esbuild).  Each Lambda function and common library is created as an independent subdirectory, for example, inside the <code>functions</code> directory, and these are defined as pnpm workspaces.</p>
<p>Using the provided structure as a reference, I will explain the basic structure and configuration files.</p>
<pre><code class="language-Shell">sample-app/             # (ルートディレクトリ)
├── functions/
│   ├── common/         # 共通コード用ワークスペース
│   │   ├── package.json
│   │   ├── src/
│   │   └── tsconfig.json
│   ├── function-1/     # Lambda関数1用ワークスペース
│   │   ├── package.json
│   │   ├── src/        # Expressアプリやハンドラコード
│   │   └── tsconfig.json
│   └── function-2/     # Lambda関数2用ワークスペース
│       ├── package.json
│       ├── src/
│       └── tsconfig.json
├── node_modules/       # pnpmによって管理される依存ライブラリ
├── package.json        # ルートのpackage.json
├── pnpm-lock.yaml      # ルートのロックファイル
├── pnpm-workspace.yaml # ワークスペース定義ファイル
├── samconfig.toml      # SAM デプロイ設定ファイル (初回デプロイで生成)
└── template.yaml       # AWS SAM テンプレートファイル
</code></pre>
<h3>Root package.json</h3>
<p>This defines scripts and development tools (such as esbuild) shared across the entire repository.</p>
<p><code>package.json</code></p>
<pre><code class="language-JSON">{
  &quot;name&quot;: &quot;sample-lambda-app-root&quot;, // プロジェクト全体を表す名前
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;Serverless Express Monorepo with SAM and pnpm&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;private&quot;: true, // ルートパッケージは公開しない設定
  &quot;workspaces&quot;: [
    &quot;functions/*&quot; // ワークスペースとなるディレクトリを指定
  ],
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;pnpm -r build&quot;, // 全ワークスペースの build スクリプトを実行
    &quot;sam:build&quot;: &quot;sam build&quot;, // SAMでのビルド (後述)
    &quot;sam:local&quot;: &quot;sam local start-api&quot;, // SAMでのローカル実行 (後述)
    &quot;sam:deploy&quot;: &quot;sam deploy&quot; // SAMでのデプロイ (後述)
  },
  &quot;devDependencies&quot;: {
    &quot;esbuild&quot;: &quot;^0.25.3&quot; // 各ワークスペースのビルドで使う esbuild をルートで管理
    // 他、monorepo全体で使う開発ツールがあればここに追加
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;
}
</code></pre>
<h3>pnpm-workspace.yaml</h3>
<p>This defines which directories should be handled as workspaces.</p>
<p><code>pnpm-workspace.yaml</code></p>
<pre><code class="language-Yaml">packages:
  - &#39;functions/*&#39; # `functions` ディレクトリ内の全てのサブディレクトリをワークスペースとする
  # - &#39;packages/*&#39; # 別のワークスペースグループがあれば追加
</code></pre>
<h2>Dependency Management (pnpm workspaces)</h2>
<p>Describe the dependent libraries required for each Lambda function or common library in the package.json inside each workspace.</p>
<p>Example: <code>functions/function-1/package.json</code></p>
<pre><code class="language-JSON">{
  &quot;name&quot;: &quot;function-1&quot;, // ワークスペースの名前
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;Lambda Function 1 with Express&quot;,
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;esbuild src/app.ts --bundle --minify --sourcemap --platform=node --outfile=dist/app.js&quot;, // esbuildでビルド
    &quot;start:dev&quot;: &quot;nodemon --watch src -e ts --exec \&quot;node dist/app.js\&quot;&quot;, // ローカルテスト用のスクリプト (SAM Localとは別に用意しても良い)
    &quot;tsc&quot;: &quot;tsc&quot; // 型チェック用
  },
  &quot;dependencies&quot;: {
    &quot;@codegenie/serverless-express&quot;: &quot;^4.16.0&quot;, // Lambdaアダプター
    &quot;express&quot;: &quot;^5.1.0&quot;,
    &quot;@sample-lambda-app/common&quot;: &quot;workspace:*&quot; // 共通ライブラリへの依存
  },
  &quot;devDependencies&quot;: {
    &quot;@types/aws-lambda&quot;: &quot;^8.10.138&quot;, // Lambdaの型定義
    &quot;@types/express&quot;: &quot;^4.17.21&quot;,
    &quot;nodemon&quot;: &quot;^3.1.0&quot;,
    &quot;typescript&quot;: &quot;^5.4.5&quot;
    // esbuild はルートの devDependencies にあるのでここでは不要
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;
}
</code></pre>
<ul>
<li><p><code>@sample-lambda-app/common</code>: This refers to the <code>functions/common</code> workspace. By designating <code>&quot;workspace:*&quot;</code>, the local <code>common</code> workspace will be referred to. It needs to be defined as <code>&quot;name&quot;: &quot;@sample-lambda-app/common&quot;</code> in <code>package.json</code> on the <code>common</code> workspace side. </p>
</li>
<li><p><code>scripts.build</code>: This is an example of using <code>esbuild</code> to bundle TypeScript code and dependent libraries together into a single JavaScript file (dist/app.js). This is an important step to reduce the package size deployed to Lambda.</p>
</li>
</ul>
<p>To install dependent libraries, run <code>pnpm install</code> <strong>only once</strong> in the root directory of the project. pnpm looks at <code>pnpm-workspace.yaml</code> and resolves the dependencies described in <code>package.json</code> for each workspace, efficiently configuring <code>node_modules</code>.</p>
<pre><code class="language-Shell">pnpm install
</code></pre>
<p>To add a library to a specific workspace, run the following command from the root directory:</p>
<pre><code class="language-Shell">pnpm add &lt;package-name&gt; -w &lt;workspace-name&gt; # 例: pnpm add axios -w functions/function-1
pnpm add -D &lt;dev-package-name&gt; -w &lt;workspace-name&gt; # 開発依存の場合
</code></pre>
<h1>Let&#39;s Actually Write Some Sample Code</h1>
<p>The directory configuration explained earlier includes two function modules, <code>function-1</code> and <code>function-2</code>, to create a multi-function configuration, as well as a module called <code>common</code> so that these functions can use it as a shared component.</p>
<p>Now let’s write some actual code.</p>
<h2>Common Code</h2>
<p>First, let&#39;s write a sample middleware function in common, which is a common component.</p>
<p><code>functions/common/src/middlewares/hello.ts</code></p>
<pre><code class="language-TypeScript">import { Request, Response, NextFunction } from &#39;express&#39;;
/**
 * サンプル共通ミドルウェア
 * リクエストログを出力し、カスタムヘッダーを追加します。
 */
export const helloMiddleware = (req: Request, res: Response, next: NextFunction) =&gt; {
  console.log(`[Common Middleware] Received request: ${req.method} ${req.path}`);
  // レスポンスにカスタムヘッダーを追加
  res.setHeader(&#39;X-Sample-Common-Middleware&#39;, &#39;Applied&#39;);
  // 次のミドルウェアまたはルートハンドラに進む
  next();
};
続いて、middlewares/内のエクスポートを追加します。

functions/common/src/middlewares/index.ts


export * from &#39;./hello&#39;;
// middlewares内に他のミドルウェアがあればここに追加していく
さらにワークスペースのトップレベルのsrc/でもエクスポートしてあげる必要があります。

functions/common/src/index.ts


export * from &#39;./middlewares&#39;;

// middlewaresのような共通処理が他にあればここに追加していく（utilsとか）
</code></pre>
<h2>Code for function-1</h2>
<p>Next, I will write the code for function-1.</p>
<p><code>functions/function-1/src/app.ts</code></p>
<pre><code class="language-TypeScript">import express from &#39;express&#39;;
import serverlessExpress from &#39;@codegenie/serverless-express&#39;;
import { helloMiddleware, errorHandler } from &#39;@sample-lambda-app/common&#39;; // 共通ミドルウェア、エラーハンドラをインポート
// apiRouter のインポートは不要になりました
// import apiRouter from &#39;./routes/api&#39;;
// import cookieParser from &#39;cookie-parser&#39;; // 必要に応じてインストール・インポート
const app = express();
// express標準ミドルウェアの適用
app.use(express.json()); // JSONボディのパースを有効化
// app.use(cookieParser()); // クッキーパースが必要な場合このように追加する
// 共通ミドルウェアの適用
app.use(helloMiddleware);
app.get(&#39;/hello&#39;, (req, res) =&gt; {
  console.log(&#39;[Function 1 App] Handling GET /hello&#39;);
  res.json({ message: &#39;Hello from Function 1 /hello (Simplified)!&#39; });
});
app.post(&#39;/users&#39;, (req, res) =&gt; {
  console.log(&#39;[Function 1 App] Handling POST /users&#39;);
  console.log(&#39;Request Body:&#39;, req.body); // JSONボディをログ出力
  res.status(201).json({ received: req.body, status: &#39;User created (sample)&#39; });
});
// common等にエラーハンドラミドルウェアを作成し、使用する場合は全てのミドルウェアとルート定義の後に配置する。
// app.use(errorHandler); // 本記事では作成していない
// ハンドラのエクスポート
export const handler = serverlessExpress({ app });
</code></pre>
<p>Note: In the API Gateway configuration in template.yaml that will be done later, the path without /function1 will be passed, so the route defined here will be a relative path from the API Gateway base path. For example, if a request to API Gateway is /function1/hello, it will match the /hello defined here.</p>
<h2>Code for Function-2</h2>
<p><code>functions/function-2/src/app.ts</code></p>
<pre><code class="language-TypeScript">import express from &#39;express&#39;;
import serverlessExpress from &#39;@codegenie/serverless-express&#39;; // ★アダプターをインポート★
import { helloMiddleware, errorHandler } from &#39;@sample-lambda-app/common&#39;; // 共通ミドルウェア、エラーハンドラをインポート
// ルーターファイルは使用しないためインポート不要
// import apiRouter from &#39;./routes/api&#39;;
// import cookieParser from &#39;cookie-parser&#39;; // 必要に応じてインストール・インポート
const app = express();
// express標準ミドルウェアの適用
app.use(express.json()); // JSONボディのパースを有効化
// app.use(cookieParser()); // クッキーパースが必要な場合このように追加する
// 共通ミドルウェアの適用
app.use(helloMiddleware);
// ルートをごとに処理を定義
app.get(&#39;/bye&#39;, (req, res) =&gt; {
  console.log(&#39;[Function 2 App] Handling GET /bye&#39;);
  res.json({ message: &#39;Goodbye from Function 2 /bye!&#39; });
});
app.post(&#39;/items&#39;, (req, res) =&gt; {
  console.log(&#39;[Function 2 App] Handling POST /items&#39;);
  console.log(&#39;Request Body:&#39;, req.body); // JSONボディをログ出力
  res.status(201).json({ received: req.body, status: &#39;Item created (sample)&#39; });
});
app.get(&#39;/status&#39;, (req, res) =&gt; {
    console.log(&#39;[Function 2 App] Handling GET /status&#39;);
    res.json({ status: &#39;OK&#39;, function: &#39;Function 2 is running (Simplified)&#39; });
});
// common等にエラーハンドラミドルウェアを作成し、使用する場合は全てのミドルウェアとルート定義の後に配置する。
// app.use(errorHandler); // 本記事では作成していない
// ハンドラのエクスポート
export const handler = serverlessExpress({ app });
</code></pre>
<p>Since this is just a sample, all the processing within the route is written using arrow functions, but in actual development, if the processing becomes complicated it may be better to consolidate the processing into a separate ts file.</p>
<p>Also, during development, there may be times when you want to use different middleware for each route. In such a case, you can create an API router more flexibly by using the express Router library, so please look into it and give it a try. (Reference: <a href="https://expressjs.com/en/guide/routing.html">https://expressjs.com/en/guide/routing.html</a> <a href="https://expressjs.com/ja/guide/routing.html">https://expressjs.com/ja/guide/routing.html</a> ）</p>
<h1>Preparing to Locally Run SAM</h1>
<h2>AWS SAM template (template.yaml)</h2>
<p>Create a <code>template.yaml</code> file in the project route to define the AWS resources to be deployed. Describe Lambda functions, API Gateway, necessary IAM roles, and others.</p>
<p><code>template.yaml</code></p>
<pre><code class="language-Yaml">AWSTemplateFormatVersion: &#39;2010-09-09&#39;
Transform: AWS::Serverless-2016-10-31
Description: Sample Serverless Application
Globals:
  # Functions 全体に適用する共通設定 (メモリサイズやタイムアウトなど)
  Function:
    Timeout: 30
    MemorySize: 256 # 適宜調整する
    Runtime: nodejs20.x
    Architectures:
      - x86_64
    Environment:
      Variables:
        NODE_ENV: production
Resources:
  # function-1 ワークスペースに対応するLambda関数リソース定義
  Function1:
    Type: AWS::Serverless::Function # AWS SAMで定義するサーバーレス関数
    Properties:
      FunctionName: sample-express-function-1 # AWSコンソールに表示されるLambda関数名 (任意)
      Description: Express App for Function 1 (Simplified)
      # CodeUri は SAM がコードをパッケージングする際のソースディレクトリを指す。
      # ここには、sam build 前のソースコードがあるディレクトリを指定。
      CodeUri: functions/function-1/
      # Handler は、sam build によって生成された成果物の中でのエントリーポイントを指す。
      # esbuild が src/app.ts を dist/handler.js にバンドルし、
      # その中で &#39;export const handler = ...&#39; を CommonJS の &#39;exports.handler = ...&#39; に変換するため、
      # &#39;ファイル名(拡張子なし).エクスポート名&#39; と記述する。
      Handler: handler.handler
      Events:
        # API Gateway からのトリガー設定
        Function1Api:
          Type: Api # API Gateway REST APIをトリガーとする
          Properties:
            Path: /function1/{proxy+}
            # 許可するHTTPメソッド (ANYは全てのメソッドを許可)
            Method: ANY
  # function-2 ワークスペースに対応するLambda関数リソース定義
  Function2:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: sample-express-function-2 # AWSコンソールに表示されるLambda関数名 (任意)
      Description: Express App for Function 2 (Simplified)
      # CodeUri は function-2 ワークスペースのソースディレクトリを指す
      CodeUri: functions/function-2/
      # Handler は function-2 のビルド成果物の中でのエントリーポイントを指す
      Handler: handler.handler
      Events:
        # API Gateway からのトリガー設定 (function-2用)
        Function2Api:
          Type: Api
          Properties:
            # Function 2 が処理するAPI Gatewayパス
            Path: /function2/{proxy+}
            Method: ANY
</code></pre>
<ul>
<li><code>Transform: AWS::Serverless-2016-10-31</code>: This indicates a SAM template.</li>
<li><code>Resources</code>: This defines the AWS resources to be deployed.<ul>
<li><code>Type:AWS::Serverless::Function</code>: This is a Lambda function resource.</li>
<li><code>CodeUri</code>: This specifies the directory where the code to be deployed as a Lambda function is located. This specifies the location of the build artifact for each workspace, such as <code>functions/function-1/dist</code>.</li>
<li><code>Handler</code>: This specifies the function name in the code that is called first when the Lambda function is executed. This becomes the function name exported in the bundled file (<code>dist/app.js</code>).</li>
<li><code>Events</code>: This sets the events that trigger the Lambda function. <code>Type: Api</code> is a setting that triggers an HTTP request from API Gateway. This setting links to a specific endpoint using <code>Path</code> and <code>Method</code>. <code>/{proxy+}</code> is a notation that catches all requests under the path.</li>
</ul>
</li>
</ul>
<h2>Local Development and Testing (AWS SAM CLI)</h2>
<p>The AWS SAM CLI allows you to emulate and test Lambda functions and API Gateway in your local environment.</p>
<ol>
<li><p><strong>Build of each workspace</strong>: First, build the source code for each workspace into JavaScript. You can use the scripts defined in the root directory.</p>
<pre><code class="language-Shell">pnpm run build # functions/* 以下のそれぞれの build スクリプトが実行される
</code></pre>
<p> This generates build artifacts such as <code>functions/function-1/dist/app.js</code>.</p>
</li>
<li><p><strong>SAM build</strong>: Next, AWS SAM runs a build to create a package for deployment.</p>
<pre><code class="language-Shell">sam build
</code></pre>
<p> This command reads <code>template.yaml</code>, copies the build artifacts from the location specified by <code>CodeUri:</code> to a location under the <code>.aws-sam/build</code> directory, and organizes them into the format required by Lambda.</p>
</li>
<li><p><strong>Local API startup</strong>: The Local API feature provided by SAM CLI allows you to emulate API Gateway and run Lambda code locally.</p>
<pre><code class="language-Shell">sam local start-api
</code></pre>
<p> After the command is executed, a local server will start at a URL such as <code>http://127.0.0.1:3000</code>. By accessing the path defined in <code>template.yaml</code> (e.g., <code>/function1/hello</code>) via a browser, Postman, or curl, the Lambda function will be executed locally.</p>
</li>
</ol>
<p>After changing the source code during local development, you can either re-run pnpm run build → sam build → sam local start-api or use the sam local start-api --watch option to monitor code changes. (The --watch option automatically restarts the build and emulation, but depending on the actual environment configuration, some adjustments may be required.)</p>
<h1>Conclusion</h1>
<p>This time, I presented how to locally run a serverless TypeScript using Lambda and Express. To actually release the product, it is necessary to build up AWS infrastructure and make appropriate settings.</p>
<p>Since this was my first attempt with Express and a monorepo configuration, I ran into some difficulties. I have provided detailed explanations as a reminder, so this article may have ended up being a bit long.<br>I hope this will be of some help to others who are facing similar challenges.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/soju.kameyama/20250515/wordcloud_coverimage_serverless-api-lambda-express.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Googleで10x Innovation Culture Pitch練習会を受けた話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-23-google-10x/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-23-google-10x/</guid>
            <pubDate>Wed, 23 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Googleで10x Innovation Culture Pitch練習会を受けた話]]></description>
            <content:encoded><![CDATA[<p>こんにちは、Hoka winterです。</p>
<p>KINTOテクノロジーズ（以下、KTC）では、約1年にわたり<a href="https://x.com/googlejapan/status/1698591724984275216">グーグル・クラウド・ジャパン合同会社が2023年9月に公開した</a>イノベーションを生み出す組織環境づくりのためのリーダーシップ・プログラム：10X innovation culture programを実施してきました。</p>
<p>今回はいつも実施している10Xとは別に、10x Innovation Culture Pitch練習会を受けた話をします。</p>
<p><strong>10x Innovation Culture Pitch練習会とは</strong>
この研修の目的は、社内で「10X Innovation Culture Program」を実施するために必要なファシリテーション力を養うことです。そのためには、「10X Innovation Culture Program」に対する深い理解が必要です。この研修は、その理解を深めるためのものです。</p>
<p>KTCがこの研修を受けるのは2回目です。前回はマネージャーを中心に研修を受けてもらい、その後、10Xの進行が格段に良くなったので、今回はチームリーダーを中心に有志のメンバーに参加いただきました。</p>
<p>10x Innovation Culture Pitch練習会は、大きく分けて2つの構成です。１つは「イノベーションを生み出すための6つの要素をインプット」し、もう1つは「自分の言葉でアウトプットする」という研修です。</p>
<p>![](/assets/blog/authors/hoka/20250714/image6.png =600x)</p>
<p><strong>研修準備</strong></p>
<p>これまでGoogleの皆さんに10Xを教えていただきながら学んだことは、少しずつKTC側の難易度が上がっていくということ。</p>
<p>1回目の研修はKTC全員が「参加するだけ」でしたが、2回目の今回は、「KTC社員がカルチャーセッションのプレゼンター」を担当することになりました。つまり、参加者に対し「イノベーションを生み出すための6つの要素をインプット」する重要な役割です。</p>
<p>![](/assets/blog/authors/hoka/20250714/image3.png =600x)</p>
<p>有難いことにプレゼンテーションスライドはGoogleの皆さんが用意してくれたので、私たちKTCは6つの要素を読み上げるだけ。</p>
<p>それだけのことなのに、すごく難しかったのです！！！</p>
<p>6つの要素には、イノベーションを起こす組織であるためのGoogleの考え方や事例がたくさん記載されています。しかし、これを読むだけでは参加者の心に届きません。KTCのエピソードや、自分の体験談を交えながら、自分の言葉で話せるようになるまで、何度も練習しました。</p>
<p>特に、1回目の研修でプレゼンターを担当したGoogleの方々を思い出し、堂々とした話し方、聞きやすいスピードを意識しました。</p>
<h3><strong>研修当日</strong></h3>
<p>さて、いよいよ迎えた本番。渋谷のGoogleオフィスに27名が集まりました。今回も大阪、名古屋、東京から参加しています。</p>
<p>Googleのkotaさんによるオープニングトークでスタート。いつもありがとうございます。</p>
<p>続いて、一番10Xをリードしてくれている部長のきっしーは、名古屋オフィスからオンラインで激励メッセージをくれました。</p>
<p>参加者は「え？今から何が始まるの？この研修、何？」という空気の中、私たちプレゼンターが1テーマずつ発表をしていきました。参加者の方に10Xをインプットしてもらえるでしょうか。</p>
<p>オリジナルストーリーでプレゼンしたあわっち、緊張しすぎる私、オンラインで登壇するゆきき、先生のように落ち着いているなべやん、本番に最高演技できるみずき、笑わせる余裕のあるおたけ。みんな今日が一番うまくできました（自画自賛）。</p>
<p>参加者アンケートにおいても「カルチャーセッションが良かった」を選んでくれた人が10人もいました。また直接「前回のGoogleプレゼンターに劣らないくらい素晴らしかったよ。」「スライドを見てプレゼンを聞くだけで、スーッと話が入って来た。」と声をかけてもらえたのも嬉しかったです。</p>
<p><img src="/assets/blog/authors/hoka/20250714/image5.png" alt=""></p>
<p>続いて、アウトプットの時間です。</p>
<p>1チームあたり6人＋Googler１人で各部屋に移動し、先ほどのプレゼンターと同じように一人ずつプレゼンテーションをしていきます。20分×6人、計120分の集中アウトプットタイムです。</p>
<p><img src="/assets/blog/authors/hoka/20250714/image2.png" alt=""></p>
<p>参加者は、先ほどプレゼンターが使用したスライドと同じものを使って、一人10分ずつプレゼンテーションをしました。プレゼンテーションをする前に読み込む時間は5分。</p>
<p>プレゼンテーションを聞いている間、他のメンバーはフィードバックシートに良かったところと改善点を記入していき、プレゼンテーション後にフィードバックしていきます。</p>
<p>![](/assets/blog/authors/hoka/20250714/image1.png =600x)</p>
<p>私はDチームに参加していたのですが、「みんな家で練習してきたのかな？」と疑ってしまうくらい上手でした。フィードバックタイムは自然とプレゼンテーションの良かったところを話し合い、ディスカッションが盛り上がりました。例えば、以下のようなコメントが上がりました。</p>
<ul>
<li>スライドや台本にとらわれず、要約しながら話す</li>
<li>自分の言葉で話す</li>
<li>失敗エピソードで共感を生む</li>
<li>聞いている人に寄り添って、正論を押し付け過ぎない</li>
<li>「やる気スイッチ」などキャッチコピー作るのが上手い、分かりやすい</li>
</ul>
<p>![](/assets/blog/authors/hoka/20250714/image7.png =600x)</p>
<p>事後アンケートではプログラムの満足度は平均4.7点と非常に高かったです。</p>
<p>また、「プログラムの内容で良かった点」として以下の項目を選択していました。（n=22、複数回答）</p>
<ul>
<li>他の参加者のプレゼンを聴けて良かった: 20人  </li>
<li>自分が練習する機会があってよかった: 17人  </li>
<li>他者からのフィードバックをもらえるのがよかった: 21人</li>
</ul>
<h3><strong>クロージング</strong></h3>
<p>プレゼンテーションが終わった後、最初のセミナールームで総括を行いました。他のグループはどんな感じだったのかな？と思っていたら、先ほどのフィードバックシートをGoogleの生成AI 「Gemini」を使って総括してくださいました。</p>
<p>![](/assets/blog/authors/hoka/20250714/image4.png =600x)</p>
<p>後日、他のグループのフィードバックシートを見ようと思っていたのですが、その場でGeminiを介してテキスト化し全員に共有され、まさに「Feedback is a gift!」なシーンでした。</p>
<p>研修内容だけでなく、短時間でインストールする方法、フィードバックシートの活用方法や、他グループの情報を共有する方法など、どうやって学ぶとより効率が良いかもたくさん教えていただきました。</p>
<p>Googleの皆さん、本当にありがとうございました。</p>
<h3><strong>今後について</strong></h3>
<p>今回の研修を通して、「管理職以外のメンバーにも難易度の高い10x Innovation Culture Pitch練習会は効果的である」ということが分かったので、2025年度は10x Innovation Culture Pitch練習会をKTCでも実施していきたいと思います。</p>
<p>KTCのイノベーションを生み出すための挑戦はまだまだ続きます。</p>
<p>![](/assets/blog/authors/hoka/20250714/image8.png =600x)</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Contributing to OSS from Scratch: 11 Steps to Add a New Resource to Terraform AWS Provider]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-30-terraform-aws-provider-contribution-dynamodb-zeroetl-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-30-terraform-aws-provider-contribution-dynamodb-zeroetl-en/</guid>
            <pubDate>Tue, 22 Jul 2025 11:00:00 GMT</pubDate>
            <description><![CDATA[This article is for anyone looking to make their first contribution by adding a new resource to Terraform AWS Provider, and aims to share insights to help you work efficiently from the beginning.]]></description>
            <content:encoded><![CDATA[<p>Hello. I’m <a href="https://x.com/_p2sk_">@p2sk</a> from the DBRE team.</p>
<p>The DBRE (Database Reliability Engineering) team is a cross-functional organization focused on resolving database issues and developing platforms.</p>
<p>Recently, I had the opportunity to contribute to the OSS repository <a href="https://github.com/hashicorp/terraform-provider-aws">terraform-provider-aws</a>. Specifically, I implemented a new resource called <a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/redshift_integration">aws_redshift_integration</a> that enables users to manage managed data integrations between DynamoDB or S3 and Redshift, officially made available by AWS in October 2024, using Terraform. <a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105">The PR has already been merged</a> and <a href="https://github.com/hashicorp/terraform-provider-aws/releases/tag/v5.95.0">released in v5.95.0</a>, and the feature is now available.</p>
<p>This was my first OSS contribution, so I was a little worried about whether I could complete it, but with the help of generative AI, I was able to see it through to creating the PR.</p>
<p>It can sometimes take months after a new AWS feature becomes GA before it’s supported in Terraform (implemented as a new resource in terraform-provider-aws). In such cases, I felt that it was a huge advantage to have the option to implement it myself instead of waiting for official support. That’s why this article is for anyone looking to make their first contribution like me by adding a new resource to Terraform’s AWS Provider, and aims to share insights to help you work efficiently from the beginning.</p>
<p>Maybe in the future we’ll be able to just hand an issue over to a coding agent and have it generate the entire PR, but at the moment it seems quite difficult. I hope this article will be helpful to anyone in a similar situation.</p>
<h1>About the Resource I Added</h1>
<p>The resource I implemented enables management of two AWS features. Each is briefly described below:</p>
<ul>
<li>Zero-ETL integration from DynamoDB to Redshift</li>
<li>Event integration from S3 to Redshift</li>
</ul>
<p>Zero-ETL integration is a managed data integration feature that eliminates the need to build an ETL pipeline. The &quot;zero-ETL integration&quot; feature was initially launched as a data integration between Aurora MySQL and Redshift, and has since expanded support to multiple other sources and targets. Here’s the architecture diagram:</p>
<p>![Architecture diagram for DynamoDB to Redshift zero-ETL integration](/assets/blog/authors/m.hirose/2025-04-23-11-13-17.png =700x) <em>Source: AWS - <a href="https://aws.amazon.com/jp/blogs/news/get-started-with-amazon-dynamodb-zero-etl-integration-with-amazon-redshift/">Getting started with Amazon DynamoDB zero-ETL integration with Amazon Redshift</a></em></p>
<p>Similarly, event integration from S3 to Redshift allows files added to an S3 bucket to be automatically and quickly integrated into Redshift.</p>
<p>Although these two features are technically separate, they share the <a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html">same API for creating an integration</a>. Since resources in terraform-provider-aws are mapped directly to APIs, supporting this API in Terraform makes it possible to implement both features at the same time. So in the end, I only needed to add one resource.</p>
<h1>Criteria for Adding Resources</h1>
<p>The official documentation states the following :</p>
<blockquote>
<p>New resources are required when AWS adds a new service, or adds new features within an existing service which would require a new resource to manage in Terraform. Typically anything with a new set of CRUD API endpoints is a great candidate for a new resource.</p>
</blockquote>
<p>Japanese translation:</p>
<blockquote>
<p>AWS が新しいサービスを追加したり、既存のサービスに新しい機能を追加したりする場合、Terraform で管理するための新しいリソースが必要になります。 一般的に、新しい CRUD API エンドポイントセットを持つものは、新しいリソースの候補として最適です。</p>
</blockquote>
<p>So, <code>having a new set of CRUD API endpoints</code> is a major factor in deciding whether a new resource should be added. In this case, the criteria were met, so I went ahead and implemented a new resource.</p>
<h1>Contribution Flow</h1>
<p>The process is very well explained in the <a href="https://hashicorp.github.io/terraform-provider-aws/">official documentation</a>.</p>
<ol>
<li>Configure Development Environment</li>
<li>Debug Code (Skipped this time because it was a new resource)</li>
<li>Change Code</li>
<li>Write Tests</li>
<li>Continuous Integration</li>
<li>Update the Changelog</li>
<li>Create a Pull Request</li>
</ol>
<p>Based on the above items, the steps recommended in this article are summarized below. In addition, the effort levels marked using ★ based on my own experience. You’ll need to check the official documentation for detailed instructions, but I hope these notes from actually doing it will help you get a smoother start.</p>
<ol>
<li>Investigate or create related issues ★</li>
<li>Preliminary research on the relevant AWS API and SDK ★</li>
<li>Configure development environment ★</li>
<li>Validate the target resource and code dependent resources ★★★</li>
<li>Generate boilerplate code using the scaffolding tool ★</li>
<li>Modify the code and check if it works ★★★★★</li>
<li>Write a test ★★★</li>
<li>Run a continuous integration test locally ★★</li>
<li>Update the documentation ★</li>
<li>Create a pull request ★</li>
<li>Create and push a changelog ★</li>
</ol>
<p>Before diving into the details of each step, I want to first highlight a few things that are good to know before starting development.</p>
<h1>Points to Note</h1>
<h3>Mixed coding styles due to multiple SDKs</h3>
<p>In terraform-provider-aws, the repository contains two different patterns using different SDKs.</p>
<ul>
<li><a href="https://developer.hashicorp.com/terraform/plugin/framework">Terraform plugin framework</a><ul>
<li>The new SDK recommended for use at this time</li>
</ul>
</li>
<li><a href="https://developer.hashicorp.com/terraform/plugin/sdkv2">Terraform Plugin SDKv2</a><ul>
<li>No longer recommended for new development, However, it’s still used for maintaining and fixing existing resources.</li>
<li>There may still be code for the unsupported v1 version, so in reality, there are three possible patterns.</li>
</ul>
</li>
</ul>
<p>Therefore, if you’re using generative AI to assist with research or coding, it’s a good idea to include the Terraform Plugin Framework as a target in your prompt.</p>
<p>If you’re interested in the historical background of this, check out <a href="https://chatgpt.com/c/67ff1569-e854-800f-befd-b3684340141f">ChatGPT Deep Research’ results</a>, though keep in mind there’s always a chance of hallucination.</p>
<h3>Licensing</h3>
<p>Terraform itself <a href="https://www.hashicorp.com/en/blog/hashicorp-adopts-business-source-license">changed its license to BSL</a> in 2023, which means it is no longer defined as OSS, but terraform-provider-aws will still remain OSS under the MPL 2.0.</p>
<p>Various providers appear to be used in <a href="https://github.com/opentofu/opentofu">opentofu</a>, which was forked from Terraform. <a href="https://github.com/opentofu/terraform-provider-aws">The AWS Provider for opentofu</a> is also forked from terraform-provider-aws, so by contributing to the provider, you’ll indirectly contribute to both Terraform and opentofu.</p>
<p>If you’re interested in the details behind this, check out <a href="https://chatgpt.com/share/68006d80-01b8-800f-a854-a08f02fbcb53">ChatGPT Deep Research’s results</a>. (Take the same precautions regarding hallucination.)</p>
<p>The following section explains the actual steps. Note that the test execution times mentioned in this article are approximate values based on the following environment.</p>
<ul>
<li>Machine: MacBook Pro</li>
<li>Chip: Apple M2 Pro</li>
<li>Memory: 32 GB</li>
</ul>
<h1>1. Investigate or Create Related Issues</h1>
<p>When creating a PR, include a link to the related issue (e.g. &quot;Relations&quot; in the image below). So, first search for a related issue and if you don’t find one, create one.</p>
<p>![Related issue description](/assets/blog/authors/m.hirose/2025-04-17-12-45-46.png =700x)</p>
<p>If an issue has already been created, someone else might be working on it. Be sure to skim through the comments to check whether it looks like work has already started.</p>
<p>In this case, an issue already existed, so I simply linked to it when I created the PR.</p>
<h1>2. Preliminary Research on the Relevant AWS API and SDK</h1>
<p>To implement a new resource, the Go SDK (aws-sdk-go-v2) must support the relevant CRUD operations for the resource. I assume that the SDK will generally be provided at the same time as it becomes GA, but there may be some lag. The go.mod in terraform-provider-aws also needs to be updated to a version that corresponds to the relevant resource, but <a href="https://github.com/hashicorp/terraform-provider-aws/commits/main/go.mod">it seems to be updated frequently by the maintainers</a>, so in many cases you won’t need to do it yourself, it’s likely already up-to-date.</p>
<p>This time, I found it convenient to bookmark the following references so I could refer to them whenever I wanted during development. They’re also useful if you want to feed them into a generative AI for reference.</p>
<p>API Reference</p>
<ul>
<li><a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html">https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html</a></li>
<li><a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_ModifyIntegration.html">https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_ModifyIntegration.html</a></li>
<li><a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DeleteIntegration.html">https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DeleteIntegration.html</a></li>
<li><a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DescribeIntegrations.html">https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DescribeIntegrations.html</a></li>
</ul>
<p>SDK Reference</p>
<ul>
<li><a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.CreateIntegration">https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.CreateIntegration</a></li>
<li><a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.ModifyIntegration">https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.ModifyIntegration</a></li>
<li><a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DeleteIntegration">https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DeleteIntegration</a></li>
<li><a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DescribeIntegrations">https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DescribeIntegrations</a></li>
</ul>
<p>Initially, my motivation was to make DynamoDB zero-ETL integration compatible with Terraform, but when I looked through the references, I found that the API’s SourceARN parameter pattern also supported S3, as shown in the figure below. That’s when I realized I’d need to validate the S3 integration as well. Since the validation scope can end up being broader than expected, it’s a good idea to review all input and output in the reference before jumping in.</p>
<p>![CreateIntegration()のSourceARN](/assets/blog/authors/m.hirose/2025-04-17-15-43-29.png =700x) 
<em>Source: AWS - <a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html">Redshift CreateIntegration API Reference</a></em></p>
<p>Also, depending on the type of resource, there may be no Delete or Modify available. In those cases, you only need to implement what’s provided. For example, with the zero-ETL integration between Aurora MySQL and Redshift, only <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/rds#Client.CreateIntegration">Create</a> / <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/rds#Client.DeleteIntegration">Delete</a> / <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/rds#Client.DescribeIntegrations">Describe</a> were available at the time of GA, with <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/rds#Client.ModifyIntegration">Modify</a> added later.</p>
<p>Redshift has two SDK directories: redshift and redshiftserverless. I wasn’t sure whether I need to implement both, but since the relevant API didn’t exist under <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshiftserverless">redshiftserverless</a>, and the functions under redshift could also create integrations for serverless, I concluded that implementing it under redshift alone would be sufficient.</p>
<h1>3. Configure Development Environment</h1>
<p>Just follow the steps in the <a href="https://hashicorp.github.io/terraform-provider-aws/development-environment/">official documentation</a> and you should be good to go. However, running <code>make testacc</code>, which creates the actual resource and checks if it works, is unnecessary at this point. You may not need to run <code>make test</code> either, but it took around 30 to 40 minutes in my environment.</p>
<p>By following the steps in the <a href="https://hashicorp.github.io/terraform-provider-aws/development-environment/#using-the-provider">Using the Provider</a> section, you’ll be able to run Terraform commands using the locally built provider. You can consider it working correctly if a warning like the one below appears during execution.</p>
<p><img src="/assets/blog/authors/m.hirose/2025-04-17-16-01-59.png" alt="Warning when using the built provider"></p>
<p>This confirms that your locally built provider is being used when running Terraform. Although you can check if it works via the &quot;acceptance test&quot; described later, I found that using the local build directly with Terraform commands is a much faster way to iterate between building and testing. Personally, checking it this way felt more intuitive since it aligned with how I normally use Terraform. <a href="https://qiita.com/minamijoyo/items/3a7467f70d145ac03324#%E3%83%97%E3%83%AD%E3%83%90%E3%82%A4%E3%83%80%E3%81%AE%E3%83%87%E3%83%90%E3%83%83%E3%82%B0">If you want to debug in more detail, you might find delve useful.</a>.</p>
<h1>4. Validate the Target Resource and Code Dependent Resources</h1>
<p>Before starting to code, it’s a good idea to check if the new AWS resource you’re planning to add works as expected. This helps build a deeper understanding of how it works. In this case, you will most likely need to create dependent resources before creating a new AWS resource. For example, in my case, the integration depended on the following AWS resources. (To be precise, the source can be either a provisioned Redshift, Redshift Serverless, or S3.)</p>
<ul>
<li>aws_vpc</li>
<li>aws_subnet</li>
<li>aws_security_group</li>
<li>aws_dynamodb_table</li>
<li>aws_dynamodb_resource_policy</li>
<li>aws_redshift_subnet_group</li>
<li>aws_redshift_parameter_group</li>
<li>aws_redshift_cluster</li>
<li>aws_redshift_resource_policy</li>
<li>aws_kms_key</li>
<li>aws_kms_alias</li>
<li>aws_kms_key_policy</li>
<li>aws_redshiftserverless_namespace</li>
<li>aws_redshiftserverless_workgroup</li>
<li>aws_s3_bucket</li>
<li>aws_s3_bucket_public_access_block</li>
<li>aws_s3_bucket_policy</li>
<li>aws_iam_role</li>
<li>aws_iam_role_policy</li>
<li>aws_redshift_cluster_iam_roles</li>
</ul>
<p>I highly recommend coding the dependent resources as .tf files at this point. The reasons are as follows.</p>
<ul>
<li>If your validation and development cannot be completed in one day, it will be costly, so you’ll want to apply and destroy each time.</li>
<li>You’ll need a similar configuration for &quot;acceptance test&quot; described later, so having it ready upfront will save time.<ul>
<li>Formatting with <code>terraform fmt</code> now will also make local CI testing smoother later on.</li>
</ul>
</li>
</ul>
<p>I think you can speed up the HCL coding significantly by leveraging generative AI. After coding the dependent resources, you can use the AWS Console or CLI to manually create the target resource and validate its behavior.</p>
<h1>5. Generate Boilerplate Code Using the Scaffolding Tool</h1>
<p><a href="https://hashicorp.github.io/terraform-provider-aws/add-a-new-resource/">When adding new resources</a>, it’s recommended to use a scaffolding tool called <a href="https://hashicorp.github.io/terraform-provider-aws/skaff/">Skaff</a> to generate the base code.</p>
<p>The resource type name follows a specific <a href="https://hashicorp.github.io/terraform-provider-aws/naming/#resources-and-data-sources">naming rule</a>: <code>aws_${service name}_${AWS resource name}</code>. The AWS resource name should match the function name used in the SDK. For example, in this case, the &quot;CreateIntegration&quot; function is provided, so the AWS resource name is &quot;Integration.&quot; It seems best to use the value of the <a href="https://github.com/hashicorp/terraform-provider-aws/tree/main/internal/service">service directory</a> in the repository as the service name.</p>
<p>Therefore, the resource type name in this case is <code>aws_redshift_integration</code>. I also used this as the name of my feature branch, <code>f-aws_redshift_integration</code>. With Skaff, you just need to specify the AWS resource name, so after changing to the directory for the relevant service, I executed the following command.</p>
<pre><code>$ pwd
/Users/masaki.hirose/workspace/terraform-provider-aws/internal/service/redshift
$ skaff resource --name Integration
</code></pre>
<p>Running Skaff generates three files: the resource code, test code, and documentation. You can view the generated file <a href="https://github.com/hashicorp/terraform-provider-aws/commit/bc67695fd2362702d5fb79e328747682ecdb0cac">here</a>, and it is a user-friendly file with extensive comments. Comparing these initial files to the <a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105/files">final merged code</a> also gives a clear picture of what needs to be modified.</p>
<h1>6. Modify the Code and Check If It works</h1>
<p>Based on the generated code, I began modifying it so that it actually worked. <a href="https://hashicorp.github.io/terraform-provider-aws/add-a-new-resource/">As described in the documentation</a>, the first step is implementing the resource schema, followed by the CRUD handlers.</p>
<p>In the Terraform Plugin Framework, the CRUD handlers are named intuitively: &quot;Create,&quot; &quot;Read,&quot; &quot;Update,&quot; and &quot;Delete.&quot; For example, the first time you run <code>terraform apply</code> to create a new resource, the Create() function implemented here will be called. Within that, the corresponding function in the Go SDK (in this case <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.CreateIntegration">CreateIntegration</a>) is executed, and internally the corresponding AWS API (in this case <a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html">CreateIntegration</a>) is executed to create the resource.</p>
<p>If <code>terraform apply</code> is used to perform modifications without replacing, the Update() function is executed, and if <code>terraform destroy</code> is used to delete the resource, the Delete() function is executed. Whenever resource information needs to be read, The Read() function gets called.</p>
<h3>Resource schema implementation</h3>
<p>In the Schema() function, you define the arguments that Terraform accepts and the attributes that it outputs as schema information. Define each field in the Attributes map, as shown in the code below. Each attribute is a struct whose key is a name in Terraform (snake case) and whose value implements the schema.Attribute interface, using an appropriate one from schema.MapAttribute, or schema.StringAttribute.</p>
<pre><code>// 修正後の Schema() 関数の一部を抜粋
func (r *integrationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			&quot;additional_encryption_context&quot;: schema.MapAttribute{
				CustomType:  fwtypes.MapOfStringType,
				ElementType: types.StringType,
				Optional:    true,
				PlanModifiers: []planmodifier.Map{
					mapplanmodifier.RequiresReplace(),
				},
			},
			names.AttrARN: framework.ARNAttributeComputedOnly(),
			names.AttrDescription: schema.StringAttribute{
				Optional: true,
			},
			&quot;integration_name&quot;: schema.StringAttribute{
				Required: true,
			},
</code></pre>
<p>As shown above, the parameters marked as required in the SDK reference should be set with <code>Required: true</code>, and if a change requires replacing the resource, add a <code>RequiresReplace()</code> modifier. Personally, I found it challenging to choose the appropriate modifier. Modifiers can be implemented on your own, so I decided to implement one manually, but later found that a maintainer had replaced mine with an existing built-in modifier after creating the PR. If you’re unsure, it is a good idea to first understand the functions provided by the modifier that corresponds to the target type, such as <a href="https://github.com/hashicorp/terraform-plugin-framework/tree/main/resource/schema/stringplanmodifier">stringplanmodifier</a>, and check whether you can use them. Through the maintainer’s feedback after submitting the PR, I learned that most cases can actually be handled using existing modifiers.</p>
<p>Along with that, I also defined the ResourceModel struct.</p>
<pre><code>type integrationResourceModel struct {
	AdditionalEncryptionContext fwtypes.MapValueOf[types.String] `tfsdk:&quot;additional_encryption_context&quot;`
	Description                 types.String                     `tfsdk:&quot;description&quot;`
	IntegrationARN              types.String                     `tfsdk:&quot;arn&quot;`
	IntegrationName             types.String                     `tfsdk:&quot;integration_name&quot;`
	KMSKeyID                    types.String                     `tfsdk:&quot;kms_key_id&quot;`
	SourceARN                   fwtypes.ARN                      `tfsdk:&quot;source_arn&quot;`
	Tags                        tftags.Map                       `tfsdk:&quot;tags&quot;`
	TagsAll                     tftags.Map                       `tfsdk:&quot;tags_all&quot;`
	TargetARN                   fwtypes.ARN                      `tfsdk:&quot;target_arn&quot;`
	Timeouts                    timeouts.Value                   `tfsdk:&quot;timeouts&quot;`
}
</code></pre>
<h3>Implementing CRUD handlers and related logic</h3>
<p>All CRUD handlers are implemented by creating an input struct for the SDK and calling SDK functions. You’ll also implement the functions used in the CRUD handler. This includes the following:</p>
<ul>
<li>A finder function to retrieve the resource information</li>
<li>A waiter function to wait for create, update, or delete to complete</li>
<li>A status function to check the state of the resource </li>
<li>A sweeper function to remove all resources (mainly for testing; not always required)</li>
</ul>
<p>Please note that some services have their own Go files such as wait.go or find.go. In that case, you need to add your logic there. If not, it seems fine to include all the logic in the file you’re working on. The Redshift service I used already had wait.go, so I added the relevant logic there.</p>
<h3>Registering resources</h3>
<p>Once the implementation is complete, you need to register the resource so that the Terraform Provider can recognize it. The following annotations are required, but since it’s already included in the code generated by Skaff, you don’t need to write it yourself. Just be careful not to delete it by mistake.</p>
<pre><code>// @FrameworkResource(&quot;aws_redshift_integration&quot;, name=&quot;Integration&quot;)
func newIntegrationResource(context.Context) 
</code></pre>
<p>After writing the above annotations, run <code>make gen</code> in the project root directory. This will regenerate service_package_gen.go in each service package, and your newly implemented resource will be registered with the provider. Once you have reached this stage, you can run <code>make build</code>, and if it passes, you will be able to check that it works using commands like <code>terraform apply</code>.</p>
<h3>Verifying operation</h3>
<p>Write the newly implemented resources in HCL and run <code>terraform apply</code> to check that it works. In step 4. Validate the target resource and code dependent resources, the dependent resources have already been coded, so here you can define only the newly implemented resource in a separate file under a different directory, and manage it with a separate state. This way, you can apply and destroy only the resource you’re checking to see if it works, which helps speed things up. Alternatively, if everything is written in a single file, you can still apply just the new resource by specifying a target, like this:</p>
<pre><code>terraform plan -target=new_resource_type.hoge -out=myplan
terraform apply myplan
</code></pre>
<h1>7. Write a Test</h1>
<p>In terraform-provider-aws, there are three tests:</p>
<ul>
<li>Acceptance Tests<ul>
<li>These verify that Terraform can successfully create, update, and delete AWS resources. Since resources are actually operated, monetary costs are incurred. That’s why the documentation states that running them is optional.</li>
</ul>
</li>
<li>Unit Tests<ul>
<li>Function level tests. In this case, I judged that they weren’t necessary and skipped them.</li>
</ul>
</li>
<li>CI Tests<ul>
<li>Comprehensive testing including linting, formatting, and other checks after PR is created.</li>
</ul>
</li>
</ul>
<p>Since CI tests only run what is already prepared, acceptance tests and unit tests are the tests that should be written by the contributor. Unit tests are recommended when implementing complex logic, but since that wasn’t the case this time, I judged they weren’t necessary and wrote only the acceptance test. For acceptance tests, the AWS resources needed for testing must be written in HCL, as shown in the code below:</p>
<pre><code>func testAccIntegrationConfig_base(rName string) string {
	return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 3), fmt.Sprintf(`
data &quot;aws_caller_identity&quot; &quot;current&quot; {}
data &quot;aws_partition&quot; &quot;current&quot; {}
resource &quot;aws_security_group&quot; &quot;test&quot; {
  name   = %[1]q
  vpc_id = aws_vpc.test.id
  ingress {
    protocol  = -1
    self      = true
    from_port = 0
    to_port   = 0
  }
  ...
</code></pre>
<p>Since the dependent resources were already written in code in step 4. Validate the target resource and code dependent resources, this step was very easy with simple copy-and-paste. When running tests, you can execute them at the function level by specifying the function name, like this:</p>
<pre><code>make testacc TESTS=TestAccRedshiftIntegration_basic PKG=redshift
</code></pre>
<p>To run all tests for a specific resource at once, delete the part after the underscore and run it like this:</p>
<pre><code>make testacc TESTS=TestAccRedshiftIntegration PKG=redshift
</code></pre>
<h1>8. Run a Continuous Integration Test Locally</h1>
<p>The terraform-provider-aws repository has a strict CI pipeline to ensure code quality. These checks run automatically after creating a PR, but it&#39;s a good idea to run them locally first and make sure everything passes before submitting.</p>
<p>A complete check can be run with <code>make ci</code>, but in my case, it took several hours to complete. So, I recommend first fixing any issues detected with <code>make ci-quick</code> and then running <code>make ci</code> to minimize the wait time.</p>
<p>For me, after a few rounds of fixes, I was able to pass all checks with <code>make ci-quick</code> locally. But when running <code>make ci</code>, I encountered one issue that required modifying the GNUmakefile. Since this may be a problem specific to my environment, I didn’t include it in the PR and instead worked around it with a local fix.</p>
<p>As described in the <a href="https://hashicorp.github.io/terraform-provider-aws/continuous-integration/">Documentation</a>, running <code>make testacc-lint-fix</code> first can automatically fix issues only related to <code>terrafmt</code>, so that’s a good step to begin with.</p>
<h1>9. Update the Documentation</h1>
<p>Update the documentation generated by Skaff. What you write here will be reflected as <a href="https://registry.terraform.io/providers/-/aws/5.95.0/docs/resources/redshift_integration">Frequently viewed documentation</a>. There shouldn’t be any issues if you refer to existing documentation and follow their format.</p>
<h1>10. Create a Pull Request</h1>
<p>This step should be pretty straightforward and not cause any confusion.</p>
<h1>11. Create and Push a Changelog</h1>
<p>I think you can create it without any problems by following the <a href="https://hashicorp.github.io/terraform-provider-aws/#6-update-the-changelog">official documentation</a>. The PR number is required according to the file naming rule, so you need to submit a PR first, then create a changelog and push it afterward.</p>
<p>That covers the steps up to creating a PR. In the next section, I’ll share the insights I gained through this initiative.</p>
<h1>Changes Made by the Maintainer</h1>
<p>The PR was successfully merged and released in <a href="https://github.com/hashicorp/terraform-provider-aws/releases/tag/v5.95.0">v5.95.0</a> recently, and the feature is now available for use. Before the merge, the maintainer made some revisions to the code. Here’s an overview of what those changes were:</p>
<h3>Removal of the ID from schema.attribute</h3>
<p>Although the following comment was already included in the code generated by Skaff, I overlooked it and left the ID attribute, so it was removed as unnecessary. It’s a good idea to refer to the AWS API reference to decide whether to keep it or not.</p>
<pre><code>// Only include an &quot;id&quot; attribute if the AWS API has an &quot;Id&quot; field, such as &quot;IntegrationId&quot;
names.AttrID: framework.IDAttribute(),
</code></pre>
<h3>Changes to variable names, etc.</h3>
<p>This was the majority of the changes, and I realized my naming needed more attention. On the other hand, the struct name &quot;resourceIntegrationModel&quot; was automatically generated by Skaff, but it was modified to &quot;integrationResourceModel.&quot; This might indicate that Skaff’s naming logic isn’t entirely consistent.</p>
<h3>Replacing my custom modifier with an existing one</h3>
<p>To address a specific issue, I implemented my own plan modifier, but <a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105/commits/97781b020e741c9a1179cc970aaba803ed0b4779">it was modified to an existing one.</a></p>
<p>Since I wasn’t fully confident about this part, I <a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105#discussion_r2028088558">left a detailed comment in the PR</a>. In response, I received the following feedback, which made me realize I should have looked more closely into the existing modifiers beforehand. However, by clearly explaining why I implemented it the way I did, the maintainer was able to make an informed correction.</p>
<blockquote>
<p>This can be accomplished with the RequiresReplaceIfConfigured plan modifier.</p>
</blockquote>
<p>To see whether this fix could have been guided by an LLM, I <a href="https://chatgpt.com/share/6806fea9-4ee8-800f-b68a-5acf8467d053">modified the prompt I was using during implementation and sent it to LLM</a>, and this time, the LLM suggested a fix using the existing modifier. During development, I had assumed that I had no choice but to create my own modifier, and gave the LLM overly specific instructions, which may have limited its ability to suggest a better solution. This experience taught me that there’s room to improve how I use the LLM.</p>
<h3>Addition of Check Items in Acceptance Tests</h3>
<p>As noted in <a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105/commits/b0d61d3c8497b27a3f0ace2554cb08d836e4f599#diff-e58168bb583bffa00f127c71c11074222f40f3c5875bc76f4d348c5ee063af99R56-R60">this commit</a>, I learned that acceptance tests can be written to specify whether a test scenario is expected to &quot;create or update a resource.&quot; This helps detect unintended resource recreation, which can be very useful.</p>
<h1>Cost of Creating AWS Resources</h1>
<p>Since I ran the acceptance tests myself and also ran individual checks to see if it works, some monetary cost was incurred from creating AWS resources. I used Terraform to manage the infrastructure as code (IaC), and destroyed resources frequently when they weren’t needed. Still, the total came to about $50. Most of this was the cost of Redshift, which will significantly vary depending on the resources you’re implementing.</p>
<h1>Other Thoughts</h1>
<h3>Lesson learned: a huge effort goes into standardization</h3>
<p>In repositories like those related to Terraform, which involve thousands of contributors, it’s essential to have a solid &quot;track&quot; that allows everyone to reach the same goal. If standardization is weak, maintainers (reviewers) have to put in a lot more effort, and that slows down feature releases. Given this background, I really felt a strong push toward code standardization by providing various resources and tools like:</p>
<ul>
<li>Extensive documentation<ul>
<li>Detailed guides for each type of contribution (bug fixes, adding new resources, etc.)</li>
<li>Description of rules such as naming</li>
</ul>
</li>
<li>Scaffolding using the dedicated tool &quot;Skaff&quot;<ul>
<li>Automatic generation of base code that can be easily fixed</li>
</ul>
</li>
<li>Locally run CI tests<ul>
<li>Thorough checks can be performed from various perspectives, including lint, formatting, and testing.</li>
<li>By getting everything to pass locally first, there’s a high chance that your code will pass all the CI checks after you open the PR, reducing the burden on maintainers.</li>
</ul>
</li>
</ul>
<p>In particular, you can really see the effort put into enabling local execution of CI-equivalent tests in the <a href="https://hashicorp.github.io/terraform-provider-aws/continuous-integration/">documentation</a> below.</p>
<blockquote>
<p>NOTE: We’ve made a great effort to ensure that tests running on GitHub have a close-as-possible equivalent in the Makefile.</p>
</blockquote>
<p>Japanese translation</p>
<blockquote>
<p>注: GitHub で実行されるテストについては、Makefile に可能な限り同等のコードが含まれるよう最大限の努力を払っています。</p>
</blockquote>
<p>This helps minimize inconsistencies in code style, even down to the smallest details. For example, as shown below, if a value is hardcoded instead of using a <a href="https://github.com/hashicorp/terraform-provider-aws/blob/main/names/attr_consts_gen.go">const-defined constant</a>, the system prompts you to use the appropriate constant.</p>
<p><img src="/assets/blog/authors/m.hirose/2025-04-21-11-11-18.png" alt="Items detected during CI test execution"></p>
<p>As you can see, the test items are very detailed and cover a wide range, but on the flip side, once both the acceptance tests and local CI tests pass, I was able to create my very first PR with confidence.</p>
<p>In the DBRE team I belong to, <a href="https://blog.kinto-technologies.com/posts/2023-05-29-serverless-with-monorepo-nx-en/">DevOps specialists</a> had already structured the entire development flow from scaffolding to formatting, linting, and testing as described above. Thanks to that, I was able to follow the process smoothly.</p>
<h3>Reflection: there is room for improvement in the use of generated AI</h3>
<p>Looking back, I realize there was room for improvement in how I used generative AI. To speed up my understanding of an unfamiliar repository, I could have indexed it with GitHub Copilot. That said, in cases like this one where the repository contains a mixture of code from different SDKs, I realized it&#39;s important to be more deliberate, such as clearly specifying the currently recommended SDK when asking questions. In fact, I looked into the Plan Modifier area through deep research and tried a solution I found in an issue online. However, it didn’t work because the solution was based on the old SDK. Instead, I fed the LLM with a set of relevant sources, and it returned code that resolved the issue with almost no modification. I hope to leverage LLMs more effectively to stay up to date and accelerate development.</p>
<h3>Challenges: mixed code from different SDKs</h3>
<p>As mentioned above, the repository contained a mix of code with different SDKs, so &quot;not all existing code could be used for reference.&quot; It took me a while to realize this. For example, the implementation of the sweeper function differs between the current SDK (Terraform Plugin Framework) and the previous one. In this case, the target service was Redshift, but the<a href="https://github.com/hashicorp/terraform-provider-aws/tree/main/internal/service/redshift/sweep.go">file for implementing the Redshift sweeper function</a> hadn’t yet been updated to use the current SDK. I based my initial implementation on the old SDK, which resulted in non-working code. I solved the problem by finding functions implemented with the current SDK in another service and using them as a reference. That said, it’s best to be mindful of whether the existing code you’re referencing follows the current recommended SDK conventions.</p>
<h1>Dividing Tasks Between AI and Humans</h1>
<p>Lastly, I’ve summarized my current perspective on which steps are better handled by AI or humans in the table below. After completing this development, I also had the <a href="https://devin.ai/">AI engineer Devin</a> try the same task for validation purposes, but as written in the official documentation it seemed necessary to break down the task into smaller steps when delegating to AI. Of course, this is just my current view, and is likely to change as AI evolves.</p>
<table>
<thead>
<tr>
<th>Step</th>
<th>AI / Human</th>
<th>Notes</th>
</tr>
</thead>
<tbody><tr>
<td>1. Investigate or Create Related Issues</td>
<td>Human</td>
<td>Fastest to search manually via web or <a href="https://github.com/hashicorp/terraform-provider-aws/issues">GitHub Issues</a></td>
</tr>
<tr>
<td>2. Preliminary Research on the Relevant AWS API and SDK</td>
<td>Human</td>
<td>Quicker to look it up manually</td>
</tr>
<tr>
<td>3. Configure Development Environment</td>
<td>Human</td>
<td>Quicker to set it up manually</td>
</tr>
<tr>
<td>4. Validate the Target Resource and Code Dependent Resources</td>
<td>AI + Human</td>
<td>Using LLMs is effective for coding dependencies</td>
</tr>
<tr>
<td>5. Generate boilerplate code using the scaffolding tool</td>
<td>Human</td>
<td>Quicker to run manually</td>
</tr>
<tr>
<td>6. Modify the Code and Check If It works</td>
<td>AI + Human</td>
<td>Let the LLM draft the base, then finish the details manually</td>
</tr>
<tr>
<td>7. Write a Test</td>
<td>AI + Human</td>
<td>Let the LLM draft the base, then finish the details manually</td>
</tr>
<tr>
<td>8. Run CI tests locally</td>
<td>AI or Human</td>
<td>LLM may adjust code to pass tests, but long test run times may consume more credits depending on the product</td>
</tr>
<tr>
<td>9. Update the Documentation</td>
<td>AI + Human</td>
<td>Feed merged document to LLM to generate a draft</td>
</tr>
<tr>
<td>10. Create a Pull Request</td>
<td>Human</td>
<td>Likely faster to handle manually</td>
</tr>
<tr>
<td>11. Create and Push a Changelog</td>
<td>Human</td>
<td>Likely faster to handle manually</td>
</tr>
</tbody></table>
<h1>Conclusion</h1>
<p>Contributing to the Terraform Provider seemed like a high hurdle to overcome, but I found that once you get used to it, the process goes smoothly—thanks to well-maintained guides, scaffolding tools, and a solid testing framework. Since this was my first time, I spent a lot of time reading through the documentation, but I believe I&#39;ll be able to develop faster next time. If you&#39;re excited to Terraform new AWS features as soon as they are released, I definitely encourage you to give it a try. I hope this article can be a helpful reference when you do.</p>
<p>KINTO Technologies&#39; DBRE team is actively looking for new members to join us! Casual interviews are also welcome, so if you&#39;re even slightly interested, feel free to contact us via <a href="https://twitter.com/_p2sk_">DM on X</a>. Don&#39;t forget to follow us on <a href="https://twitter.com/KintoTech_HR">our recruitment X</a> too!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/m.hirose/conver_image_hirose_terraform.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[SRE NEXT 2025振り返り - KINTOテクノロジーズの運営メンバーによる座談会]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-18-sre_next_look_back/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-18-sre_next_look_back/</guid>
            <pubDate>Fri, 18 Jul 2025 17:00:00 GMT</pubDate>
            <description><![CDATA[SRE NEXTに初スポンサーを出したので振り返り会座談会をしてみました]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>1. イベント概要</h2>
<p>2025年7月11日、12日に5回目の開催となるSRE NEXTが開催されました。弊社はプラチナスポンサーとして、企業ブースの出展とスポンサーセッションへの登壇をしました。
数多くの素晴らしいセッションに加え、スポンサーブースや書籍コーナーにて多くの方々と交流させていただくことができ、非常に貴重な2日間を過ごすことができました。
本記事では、今回が初出展となったKINTOテクノロジーズのメンバーとイベントを振り返る座談会をした結果についてお伝えします。</p>
<h2>2. KINTOテクノロジーズとSRE</h2>
<h3>2-1. どんな組織</h3>
<p>KINTOテクノロジーズはトヨタグループ初の内製開発組織としてクルマのサブスクKINTOを始め、コンシューマー向けのモビリティ関連サービスのシステム開発や保守運用をしています。2025年7月現在で約400名のエンジニア、デザイナー、プロダクトマネージャーなどが在籍しており、社内外に提供するサービスを開発しています。
このような組織の中でSREチームはプラットフォームを担当する部署の1つのチームとして、プロダクトチームと連携して信頼性の維持向上や開発者への支援を行っています。</p>
<h3>2-2. SREの現状</h3>
<p>当日のスポンサーセッションにて長内が発表しましたが、KINTOテクノロジーズでは横断組織が充実しており、クラウドインフラエンジニア、DBRE、プラットフォームエンジニアリング、セキュリティ専門部隊、CCoEおよびファイナンス連携する部隊など、多くの企業でプラットフォーム系SREsが担っているであろう責務の多くを複数のチームで分担しています。</p>
<p>当日の登壇資料はこちら👉 <a href="https://speakerdeck.com/tgidgd/rorugaxi-fen-hua-saretazu-zhi-desrehahe-wosuruka">ロールが細分化された組織でSREは何をするか？ - Speaker Deck</a></p>
<p>SREingの実践を推進する2名のエンジニアはプロダクト開発チームと連携してプラクティスの実践を試みていますが、サービスレベルをビジネス指標や開発プロセスと結びつける難しさや、チームトポロジーにおけるプラットフォーム・パターンでのアプローチの難しさを感じながらも、自分たちができる価値提供のありかたを試行錯誤し続けています。</p>
<h3>2-3. 出展のモチベーション</h3>
<p>KINTOテクノロジーズでは2022年にテックブログチームを立ち上げ、2023年にはテックブログ&quot;チーム&quot;から技術広報&quot;グループ&quot;へとステップアップし情報発信に力を入れました。
2024年にはカンファレンスのスポンサー活動を開始し、最近でも開発生産性カンファレンスに代表の小寺が登壇したり、さまざまなジャンルのカンファレンスに協賛したりと、エンジニアコミュニティを支援しています。
エンジニアたちが直接コミュニケーションを取れるカンファレンスという機会はこの界隈の魅力だなと感じており、この機会に携われていることを嬉しく思っております。</p>
<p>KTCのSREの領域はメンバーが少なく、これからの成長を目指すフェーズなので、まずはKTCのSREの存在を知ってもらうこと、
そしてロールが細分化されているといったKTCならではの環境下において、我々ならではの苦悩や取り組みを共有することで、同じような課題に取り組む方々への参考となればというモチベーションでスポンサーセッションを行うことにしました。</p>
<h2>3. 当日の動き</h2>
<h3>3-1. ブース運営</h3>
<p>弊社は来訪者のみなさんに「あなたの”NEXT”は？」というテーマで付箋を貼ってもらい、ご協力いただいた方にはガチャガチャを回してノベルティをプレゼントをしていました。KINTOのマスコットキャラクターである「くもびぃ」のぬいぐるみ(大/小)や、トヨタ車のトミカをノベルティの1つとして提供していましたが、みなさんにとても好評でした。</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/booth_1.jpg" alt="">
<em>スポンサーブースで提供したノベルティ</em></p>
<p>ブース運営1日だけでボードが埋まるほどの”NEXT”を皆さんに記載いただき、参加者の方々と今年のテーマでもある「Talk Next」を一緒に体験することができました。</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/booth_2.jpg" alt="">
<em>訪問いただいた方々に多くの”NEXT”を記載いただきました</em></p>
<h3>3-2. 登壇</h3>
<p>弊社からはスポンサーセッションとして、SREチームの長内が「ロールが細分化された組織でSREは何をするか？」というタイトルで発表しました。初めての外部登壇ということで非常に緊張する様子が伺えましたが、日々悩みながらも地道に取り組んだ成果ということもあり、本人も納得のいく発表ができたようです。</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/presentation.png" alt="">
<em>初めての外部登壇で緊張している長内</em></p>
<p>登壇後はどのような反応をいただけるか非常に不安でしたが、幸運にも数多くの方にAsk the speakerの場に訪問いただき、20分の発表時間には入れられなかった裏話なども含めて楽しくお話しさせていただきました！</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/ask_the_speaker.png" alt="">
<em>Ask the speaker の様子</em></p>
<h3>3-3. 新しい学び</h3>
<p>弊社は若手エンジニアも多く、外部イベントへの参加に慣れていないメンバーも数多くいます。今回のイベントはそういったエンジニアの刺激になる体験も多く、「詳解 システム・パフォーマンス」の著者であるBrendan Gregg氏を始め、著名なエンジニアの方々と交流できたのは非常に貴重な機会となりました。</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/photo_with_brendan_gregg.jpg" alt="">
<em>Brendan Gregg氏とのツーショットに興奮する若手エンジニア</em></p>
<p>また、クラウドエンジニアとしてキャリアをスタートした若手エンジニアは、物理ネットワークを支える技術には疎いという課題があったのですが、会場でディスプレイされていたネットワークルーターやスイッチなどの役割について非常に分かりやすく解説いただくような機会もあり、技術力向上に直接的に役立つような経験もすることができました。</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/ask_noc.png" alt="">
<em>物理ネットワークを知らないクラウドエンジニアがルーターやスイッチについて教えてもらっている風景</em></p>
<h3>3-4. 参加者との交流</h3>
<p>今回はスポンサーとして、KINTOやKINTOテクノロジーズを多くの方に知っていただくことを目的に参加しましたが、実際にはそれ以上に、参加者の皆さんとの交流から得られた刺激や学びが何よりの収穫になりました。</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/photo_with_participants.jpg" alt="">
<em>訪問者と歓談する運営メンバー</em></p>
<h2>4. 参加メンバーによる座談会</h2>
<p>前述のようなとても楽しい二日間を過ごした運営メンバーにて、振り返りの座談会をしてみました。</p>
<p>SRE: <a href="https://x.com/tgidgd">長内</a>、kasai / クラウドインフラ: <a href="https://x.com/kodai1_jp">こっしー</a>、<a href="https://x.com/sri_uz0102">白井</a> / 技術広報: <a href="https://x.com/ukcpo">ゆかち</a></p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/look_back_roundtable.jpg" alt="">
<em>オフィスで座談会をする運営メンバーたち</em></p>
<h3>4-1. 何が一番印象に残ってる？</h3>
<p>kasai「ずっとブースにいたのでセッション見れてないですが、ブースで来てくれた人と話してて、SREの仕事をする中でどう生成AIを使っていくかということに悩まれてる方が何人かいたのが印象的でした。」</p>
<p>長内「自分の発表に興味を持って聞きに来てくれた人がいたのが、すごく嬉しかったです。その後のAsk the speakerでも直接話しに来てくれる人がいて、本当にありがたいなって思いました。」</p>
<p>白井「参加者全員のイベントを絶対成功させようという熱量が1番でした。Talk Nextということで、みなさんがノウハウを共有しあい、互いにリスペクトを持って話している姿が良いなーと感じました。運営の方にはSRE NEXTを作り上げてくださったことに感謝しつつ、チャンスがあれば運営側として参加させていただきたいなと思いました。」</p>
<p>こっしー「私はコミュニティの熱量の高さが一番印象に残ってます。真剣にセッションを聞く方もいれば、色んな場所で楽しそうに交流する方々もいて、同じテーマで日々悩む方々が経験を共有する場としてとても良い場所だなって思いました。」</p>
<p>ゆかち「今回運営協力してくれたみんなのコミュ力の高さですかね！それぞれの人柄が出ており、楽しそうにブース対応しているのを見ていて嬉しかったです。せっかくなのでXにポストしたハイライトもみて欲しいです(笑)」</p>
<p>![](/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/yukachi_x.jpg =500x500)</p>
<h3>4-2. 初の外部登壇どうだった？ To 長内さん</h3>
<p>長内「知らない人の前で何かを発表したのって、もしかすると小学生の時のピアノの発表会以来かもしれないです…(笑)」</p>
<p>こっしー「今回の登壇にはどんなモチベーションがあったんですか？伝えたいことがあるとか、ここは皆とシェアしたいとかそういうものがあったんですか？」</p>
<p>長内「最初のモチベーションとしてはまずKTCのSREという存在を認知してもらおうっていうのがメインでした。じゃあそのために何話そうって考えてたんですけど、スポンサーが決まった時点ではこれだ！って思えるものがなくて…
でも登壇することが決まった以上は聞いてくれる人に何かしら刺さるネタを話したいよねってなって、その中で今回の発表にもあった改善ツールの案とかも出てきて、そこからアウトラインが徐々に決まっていきましたね。そこが決まってからは話したい内容で情報量が不足している部分を追加で集めつつ、今までやってきたことも繋げていく感じで。登壇をきっかけに、自分たちの今後やっていくこともある程度見てきたこともあって、登壇駆動でかなり成長できた気がします。」</p>
<p>こっしー「ブースでもKTCさんの発表良かったですって言って頂く方も多かったんですが、Ask the speakerではどういった質問がありましたか？」</p>
<p>長内「発表の中にあったNew Relic Analyzerがどのような仕組みで動いているのかだったり、Devinの提案の精度を上げるためにどのような取り組みをしていきたいかなど、発表のことだけでなく足を運んで頂いた方の課題感なども交えて色々なことをお話しできました。それと、以前一緒に働いていた方も足を運んでくれて、当時の話もしつつ互いの近況を伝え合う良い時間になりました。」</p>
<p>こっしー「同じような領域で悩みを抱えている企業さんとか、やろうとしているけどやれないような障壁に対してどうやってアプローチする？みたいな質問があったりしたんですね。」</p>
<p>長内「そうですね。やっぱり皆さん似たような悩みを抱えているんだなというのを実感しました。」</p>
<p>ゆかち「そういえば登壇を私の隣で聞いていた人が1日目にブースに来てくれていた方だったので、登壇後に声かけてみたら福岡に住んでる方で、7月に福岡拠点できたんです〜！というお話から福岡で開催するイベントに招待できたんですよ！ 長内さんの登壇を聞いた上で弊社に興味持ってもらえたようなので、すごい嬉しかったです！」</p>
<p>長内「SRE NEXTの2日後に面接する方も来てくれてて、発表も聞いてもらったことでよりKTCのことを理解してもらえたんじゃないかなって思いました。」</p>
<p>こっしー「初の外部登壇、緊張したけど、想定してなかったこととか、イメージしきれてなかったことも特になかった？」</p>
<p>長内「本当は3日前くらいから何も食えなくなるくらい緊張してる想定だったんですけど、意外と緊張しないなと思って。結構ご飯食べれるじゃんってなってました。」</p>
<p>ゆかち「初めてだし、とちゃんと念入りに準備してたからなんだろうね。」</p>
<p>長内「そうなのかもしれない。意外と前立っても、みんなが見える位置にバーって座ってくれてたのもあるし、発表中もこっしーさんのカチューシャに付いてるくもびぃと目が合ったりして、自分としてはリラックスして喋ったつもりでした。ただ、写真を見たらめちゃめちゃ険しい顔してて、こんな顔してたんだ俺…って思いました(笑)」</p>
<p>こっしー「直前めっちゃ目が血走ってたよね。僕は長内さんは全然喋れるかなと思ってたけど、みんなが煽るし緊張してる感じになってるから、始まる直前こっちがドキドキしてきて(笑)
でも、意外と安定してたし話の内容も隣のチームとしても勉強になるものとか、そのアプローチすげぇみたいなものがいっぱいありました。」</p>
<p>ゆかち「あの日ちょっと後悔したのが、発表前にみんなで前行って背中叩きにいけば良かったなって(笑)
こっしーさんや白井くんが登壇するってなっても心配はないんですけど、長内さんって今まで外部登壇経験もないし、顔がこわばっているのもあってすごい心配でした(笑)
でも話し出したら安定していて、なんか感動しちゃいました(笑)」</p>
<p>こっしー「結果、やって良かったと！」</p>
<p>長内「次回以降の課題は表情管理ですね(笑)」</p>
<h3>4-3. Talk Next<del>次に何やる？</del></h3>
<p>kasai「今改善ツールを作ってるんですけど、それはやり切りたいと思ってますし、それをやる過程で喋れることがさらに増えると思うので、それをまた外部に発信していけたらいいなと思っています。」</p>
<p>長内「自分としても改善ツールの品質や提案の精度を上げるというものもありますが、やっぱりこういったツールは使ってもらう人に興味を持ってもらわないことには始まらないので、開発を続けつつ、色んなプロダクトの人たちへの普及活動ということもやっていきたいです。あとはサービスレベルの部分もエンジニア内で決めようとするとうまくいかなかったという結論にしましたが、事業側の人たちと会話して、どれくらいの品質が必要かといったことも話せるようになっていきたいですね。」</p>
<p>白井「今回のイベントを通じてカンファレンスの運営などに携わって色んな人とのネットワークを広げたいと思ったのと、もっと開発者目線で使いやすいプラットフォームを作っていくぞというモチベーションに繋がりました。」</p>
<p>ゆかち「白井くん今回初めてブースに立ってもらったけど、そうやって言ってもらえるといいきっかけになったな〜と思えて嬉しい！今回は、粟田さんやこっしーさんがSRE界隈で知り合いが多くて、KTCを知ってくれている人が多かった気がしていて、みんながそうやってネットワークを広げていってくれることでKTCの知名度も上がっていくし、何より知り合いが増えれば増えるほどカンファレンス参加が楽しくなるので、もっとみんなにも前のめりに参加していって欲しいな～と思いました！」</p>
<p>こっしー「もっと社外の皆さんとコミュニティを盛り上げていけるようにしたいし、そのために社内での文化作りとかプラクティスを実践していきたいですね。」</p>
<h2>5. まとめ</h2>
<h3>5-1. 学んだこと</h3>
<p>今回のSRE NEXTでは各社の発表や参加者の方々との交流を通じ次のようなことを学びました。</p>
<ul>
<li>同じような課題感を持っていることも多いが、会社の数だけアプローチがあり、似たアプローチでもその結果は様々である</li>
<li>エンジニアリングだけでなく、ビジネスや組織といった観点からもSREのアプローチを考えることが大切である</li>
<li>プロダクトチームとの信頼関係作りがSREの活動に大きな影響を与えるという話が多く、日々のコミュニケーションの重要性を再認識した</li>
</ul>
<h3>5-2. KTCのSREの「NEXT」</h3>
<p>これらを踏まえ、KINTOテクノロジーズのSREは次のようなことに挑戦したい（目指したい）と考えています。</p>
<ul>
<li>改善ツールの更なる発展と普及活動</li>
<li>事業側に越境した妥当性のあるサービスレベルの策定</li>
<li>得られた知見を社内外に発信し、コミュニティの活性化に貢献する</li>
</ul>
<h2>6. さいごに</h2>
<p>SRE NEXTの運営の方々をはじめ、ブースに来ていただいた方、セッションを聞いて頂いた方、弊社メンバーと交流して頂いた方、大変ありがとうございました。
初めてのSRE NEXTスポンサー、すごく良い経験になりました。今後もSREingの実践と試行錯誤に励み、新しい学びの共有をできる機会を楽しみにしております！</p>
<p><img src="/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/ktc_members.jpg" alt="">
<em>KINTOテクノロジーズから参加した運営メンバー</em></p>
<h2>仲間募集中</h2>
<p>KINTOテクノロジーズでは、モビリティプラットフォームを一緒に作る仲間を募集しています。ぜひ採用サイトもご訪問ください！
👉 <a href="https://hrmos.co/pages/kinto-technologies">KINTOテクノロジーズ株式会社 採用情報</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/n.osanai/2025-07-18-sre_next_look_back/cover_image.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Getting Agile People on Board and Getting Things Rolling: Prologue]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-07-21-getting-agile-people-involved-prologue-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-07-21-getting-agile-people-involved-prologue-en/</guid>
            <pubDate>Fri, 18 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Getting Agile People on Board and Getting Things Rolling: Prologue]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello! I&#39;m <a href="https://twitter.com/quindim">Kin-chan</a> from the Development Support Division at KINTO Technologies.</p>
<p>I usually work as a corporate engineer, maintaining and managing IT systems used throughout the company.</p>
<p>In this article, I&#39;d like to share an initiative I&#39;ve been working on to promote Agile practices across teams and departments within the company. If you&#39;re someone who&#39;s passionate about starting something new from the ground up and driving it forward, I hope this will be helpful and encouraging.</p>
<pre><code>*This article is part of a series on the theme of &quot;Agile.&quot;
In order for us to become &quot;agile as an organization,&quot;
we have tackled all sorts of challenges and difficulties.
Although there have been failures at times, we have continued to grow steadily.
In this series of articles, I would like to introduce some of our actual efforts.
</code></pre>
<h1>Background</h1>
<p>I joined KINTO Technologies in January 2023.</p>
<p>Having been involved in various Agile-related activities both inside and outside the company in the past, I joined KINTO Technologies with a strong desire from the start to connect with the in-house Agile experts across different teams.</p>
<p>In my experience so far:</p>
<ul>
<li>Involved in software development teams as a Scrum Master and Scrum Coach in the company</li>
<li>Promoted business improvement initiatives centered on Agile in the administrative department</li>
<li>Helped build a community of practice by regularly sharing ideas and activities with other in-house practitioners</li>
<li>Regularly participated in external Agile communities and conferences</li>
</ul>
<p>...and so on.</p>
<p>Though I had that desire in mind, once I actually started working in the Corporate IT Group, my first impression was that the product development team was farther away than I had thought.</p>
<p>That feeling came not just from the &quot;organizational distance&quot; between the product development side and the corporate side, but also from the physical distance. I&#39;m based in the Nagoya Office, but most of the engineers working on product development are in Tokyo. That physical separation played a big part.</p>
<p>That sense of distance proved to be quite a hurdle, and since I&#39;m naturally a bit socially awkward, I couldn&#39;t actively interact with people in other departments for a while after joining the company. So, I spent my days as a serious corporate engineer quietly holding on to an agile mindset.</p>
<h1>How it started</h1>
<p>In the Development Support Division where I work, we have regular one-on-one meetings with our managers.</p>
<p>About two months after I joined, I brought up with my then-manager (who&#39;s now the department head) that I wanted to connect with some of the Agile experts in the company.</p>
<p>During that conversation, my manager mentioned several names, but the one who matched best was Kinoshita-san, who had taken Scrum Master training not too long ago. Kinoshita san is an engineer, a member of the company&#39;s Tech Blog team, and also the writer of a post on <a href="https://blog.kinto-technologies.com/posts/2022_08_10_lsm_training/">becoming a Licensed Scrum Master (LSM)</a>&quot; I had actually read Kinoshita-san&#39;s articles before joining KINTO Technologies, so I told my manager I&#39;d really love the chance to connect with him. Thanks to that, I was given the opportunity to interact with the Tech Blog team.</p>
<h1>Meeting The Tech Blog Team</h1>
<p>When I first met team members of the Tech Blog Team, my honest impression was, &quot;They seem like a fun and unique group of people.&quot; Even though each member has a different main job, they all actively contribute to growing this shared product called the &quot;Tech Blog.&quot; Through that, they&#39;ve been able to connect with people across the organization and build a kind of cross-functional momentum. To me, this was &quot;one form of an ideal internal community.&quot;</p>
<p>The Tech Blog team actually started from one passionate person named <a href="https://www.wantedly.com/companies/company_7864825/post_articles/510568">Nakanishi san</a> taking action. After interacting with the team a few times, I found myself thinking, &quot;I want to help spread a positive culture within the company together with everyone.&quot;</p>
<p>Then came a turning point.</p>
<p>Some Tech Blog team members mentioned, &quot;Kinoshita-san&#39;s articles have been consistently getting solid page views, so Agile-related posts could really take off too&quot; That comment, paired with the fact that I&#39;ve always had a strong passion for Agile, sparked the idea to launch a Tech Blog series focused on Agile. </p>
<h1>So, Where to Begin?</h1>
<p>The idea of an &quot;Agile Series&quot; sounded great at first, but once I actually sat down to think about it, I realized I didn&#39;t really know anything about what Agile activities at KINTO Technologies even looked like. That meant, I had pretty much zero content to start with.</p>
<p>Therefore, the first step for me was to be referred to some experts.  With the help of the Tech Blog team&#39;s network, I was able to connect with:</p>
<ul>
<li>Someone who previously obtained their Licensed Scrum Master certification within the company</li>
<li>Someone at KINTO Technologies who&#39;s about to dive into Scrum, drawing on their experience from their previous job</li>
<li>Someone who&#39;s planning to take the Licensed Scrum Master training course soon</li>
</ul>
<p>The team helped set up some great opportunities to connect, and from there, a natural flow of conversations and interviews about &quot;Agile at KINTO Technologies&quot; started to take shape.</p>
<p>At first, I couldn&#39;t help but feel a kind of emotional distance, because of the physical distance between Nagoya and Tokyo. But by this point, that had faded and distance no longer really mattered. I started to see everyone as friends who just happen to be a little farther away.</p>
<p>Taking the very first step always feels tough. But I came to realize that once you find even the tiniest push to move forward, your body naturally follows.</p>
<h1>Next Episode</h1>
<p>That&#39;s it for this time.</p>
<p>In the next article, I&#39;ll talk about what happened as the story continued to unfold.</p>
<ul>
<li>Being able to interact with internal experts and directly feel their thoughts on Agile</li>
<li>Getting the chance to join an actual Scrum event and see the energy of the scene up close</li>
<li>Being able to talk about that all-too-familiar &quot;Agile reality&quot; where things don&#39;t always go smoothly</li>
<li>How the interview led to starting an internal meetup for Agile experts to connect</li>
</ul>
<p>I plan to share those experiences.</p>
<p>This Agile Series will mainly spotlight Agile at KINTO Technologies. Along the way, I&#39;ll be introducing various things step by step, like experts actively working within the company, their team members, and even Agile practices I&#39;ve come across outside of software development.</p>
<p>I Hope you&#39;re excited for what&#39;s coming next!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Strands Agentsを試してみた話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-18-try-strands-agent/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-18-try-strands-agent/</guid>
            <pubDate>Fri, 18 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[ConverseAPIの利用者が、Strands Agentsを試してみたあれやこれや]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは！   </p>
<p>KINTOテクノロジーズのデータ戦略部DataOpsG所属の上平です。<br>普段は社内のデータ分析基盤と「cirro」というAIを活用した社内アプリの開発・保守・運用を担当しています。   </p>
<p>「cirro」では、AIにAmazon Bedrockを利用しており、Bedrockの呼び出しにはAWSのConverse APIを使用しています。<br>本記事では、「cirro」にツールや子エージェントの機能を実装するために、ローカル環境でStrands Agentsを検証した事例をご紹介します。</p>
<h2>本記事の対象者</h2>
<p>本記事は、Amazon BedrockをConverse APIやInvoke Model経由で利用した経験のある方を対象としています。</p>
<h2>Strands Agentsとは</h2>
<p>2025年5月16日にAWS Open Source Blogで公開されたオープンソースのAIエージェントSDKです。<br>以下は、AWSのAmazon Web Services ブログで公開されている図です。</p>
<p><img src="/assets/blog/authors/kamihira/202508-try-strands-agents/AWS-agentic-loop.png" alt="AWSブログからの引用図"></p>
<p>図のように、ツールを備えたAIを実装するには、Agentic Loopと呼ばれるループ処理が必要です。<br>この処理では、AIの応答がユーザーへの回答なのか、ツールを使ってさらに処理を進めるべきかを判断します。   </p>
<p>Strands Agentsを使えば、このループ処理を開発者が自前で実装することなく、AIエージェントを構築できます。</p>
<p><a href="https://aws.amazon.com/jp/blogs/news/introducing-strands-agents-an-open-source-ai-agents-sdk/">参考、図の出典：Strands Agents – オープンソース AI エージェント SDK の紹介</a> </p>
<h2>ローカル環境でStrands Agentsを動かす！</h2>
<p>※本セクションは、過去にConverse APIなどを用いてBedrockを利用した経験がある方を前提としています。<br>そのため、モデルのアクセス許可設定などの基本的な手順については説明を省略しています。
また、サンプルのため例外処理も省略しています。</p>
<h3>準備</h3>
<ul>
<li>ライブラリ
以下のコマンドで、ライブラリをインストールします。</li>
</ul>
<p><code>pip install strands-agents strands-agents-tools</code></p>
<h3>実行①</h3>
<p>（運のいい方は・・・）最短下記のコードで動きます。   </p>
<pre><code>from strands import Agent
agent = Agent()
agent(&quot;こんにちは！&quot;) 
</code></pre>
<p>多くのブログなどではこのコードが紹介されていますが、私の環境ではうまく動きませんでした😂<br>それはそうですよね・・・モデルもBedrockを呼び出すリージョンも指定していないので・・・</p>
<h3>実行②</h3>
<p>モデルを正しく呼び出すためには、以下のようにモデルとリージョンを指定する必要があります。<br>ここでは、弊社のようにSSOでログインし、スイッチロールによって権限を取得する環境を前提としています。   </p>
<h4>【ポイント】</h4>
<ul>
<li>呼び出すモデルとリージョンをロールが呼び出せるものに設定する。<br>例：anthropic.claude-3-sonnet-20240229-v1:0（モデル）、us-east-1（リージョン）※リージョンはセッション作成時のプロファイル内で指定しています。</li>
</ul>
<pre><code>import boto3
from strands import Agent
from strands.models import BedrockModel

if __name__ == &quot;__main__&quot;:
   
    # セッション作成
    session = boto3.Session(profile_name=&#39;&lt;スイッチ先のロール&gt;&#39;)

    # モデル設定
    bedrock_model = BedrockModel(
        boto_session=session,
        model_id=&quot;us.amazon.nova-pro-v1:0&quot;,
        temperature=0.0,
        max_tokens=1024,
        top_p=0.1,
        top_k=1,
        # Trueにするとストリーミングで出力される。
        # ストリーミングでツール利用がサポートされないモデルがあるため、OFF
        streaming=False 
    )

    # エージェントのインスタンスを作成
    agent = Agent(model=bedrock_model)

    # 質問を投げる
    query = &quot;こんにちは！&quot;
    response = agent(query)
    print(response)
</code></pre>
<p>ここまでで、Converse APIと同様に temperature などのパラメータを指定してBedrockを呼び出すことができるようになりました🙌<br>でも、Strands Agentsを使うなら…やっぱり<strong>ツールを呼び出したい</strong>ですよね！   </p>
<h3>実行③</h3>
<p>下記のようにツールを定義すれば、質問に応じてツールを使用し、Agentic Loopを実行した後の回答を出力してくれます。</p>
<h4>【ポイント】</h4>
<ul>
<li>ツールとしたい関数を「@tool」でデコレートしてます。</li>
<li>ツールは<code>Agent(model=bedrock_model, tools=[get_time])</code>で、関数の配列として渡しています。</li>
</ul>
<pre><code>import boto3
from strands import Agent
from strands.models import BedrockModel

#------ツール用に読み込んだライブラリ------------
from strands import tool
from datetime import datetime


# ツールの定義
@tool(name=&quot;get_time&quot;, description=&quot;時刻を回答します。&quot;)
def get_time() -&gt; str:
    &quot;&quot;&quot;
    現在の時刻を返すツール。
    &quot;&quot;&quot;
    current_time = datetime.now().strftime(&quot;%Y-%m-%d %H:%M:%S&quot;)
    return f&quot;現在の時刻は {current_time} です。&quot;

if __name__ == &quot;__main__&quot;:

    # セッション作成
    session = boto3.Session(profile_name=&#39;&lt;スイッチ先のロール&gt;&#39;)

    # モデル設定
    bedrock_model = BedrockModel(
        boto_session=session,
        model_id=&quot;us.amazon.nova-pro-v1:0&quot;,
        temperature=0.0,
        max_tokens=1024,
        top_p=0.1,
        top_k=1,
        # Trueにするとストリーミングで出力される。
        # ストリーミングでツール利用がサポートされないモデルがあるため、OFF
        streaming=False 
    )

    # ツールを使用するエージェントのインスタンスを作成
    agent = Agent(model=bedrock_model, tools=[get_time])

    # 質問を投げる。ツールを使用しないとAIは時刻が判別できない。
    query = &quot;こんにちは！今何時？&quot;
    response = agent(query)
    print(response)
</code></pre>
<p>私の環境では下記回答を得ることができました！</p>
<pre><code>&lt;thinking&gt; 現在の時刻を調べる必要があります。そのためには、`get_time`ツールを使用します。 &lt;/thinking&gt;

Tool #1: get_time
こんにちは！現在の時刻は 2025-07-09 20:11:51 です。こんにちは！現在の時刻は 2025-07-09 20:11:51 です。
</code></pre>
<h2>応用</h2>
<p>ツールについて、今回ロジックベースの処理を返すだけのツールでしたが、<br>例えばツール内でAgentを作成し、回答をチェックさせるなどの処理を組み込めば、<br>AIがAIを呼び出す<strong>マルチエージェント</strong>な仕組みが簡単に作れます。   </p>
<p>時刻に加え、子エージェントがトリビアも返すように、ツールを修正したコードは以下です。   </p>
<h4>【ポイント】</h4>
<ul>
<li><code>if __name__ == &quot;__main__&quot;:</code>で宣言したグローバルスコープの<code>session</code>を使いまわしています。<br>これをしない場合、私の環境ではモデル設定に1分程度オーバーヘッドが発生しました。<br>おそらくは何らかの資源確保で時間がかかってしまうのでは…と思います。</li>
</ul>
<pre><code>@tool(name=&quot;get_time&quot;, description=&quot;現在日時と、日時にちなんだトリビアを回答します。&quot;)
def get_time() -&gt; str:
    &quot;&quot;&quot;
    現在の時刻を返すツール。

    注意：この関数では boto3.Session を使った BedrockModel の初期化に
    グローバルスコープで定義された `session` 変数が必要です。
    `session` は `if __name__ == &quot;__main__&quot;:` ブロックなどで事前に定義しておく必要があります。
    &quot;&quot;&quot;

    current_time = datetime.now().strftime(&quot;%Y-%m-%d %H:%M:%S&quot;)
    
    # モデル設定
    bedrock_model = BedrockModel(
        boto_session=session,
        model_id=&quot;us.anthropic.claude-sonnet-4-20250514-v1:0&quot;,
        temperature=0.0,
        max_tokens=1024,
        top_p=0.1,
        top_k=1,
        streaming=False 
    )
    agent = Agent(model=bedrock_model)

    # ここが子エージェントから回答を得る部分！
    response = agent(f&quot;現在の時刻は {current_time} です。日時と日付にちなんだトリビアを1つ教えてください。&quot;)

    return f&quot;現在の時刻は {current_time} です。{response}&quot;
</code></pre>
<p>最終的なAIの回答は以下にりました。</p>
<pre><code>こんにちは！現在の時刻は 2025-07-10 18:51:23 です。今日は「納豆の日」です！   

これは「なっ（7）とう（10）」の語呂合わせから制定されました。1992年に関西納豆工業協同組合が関西での納豆消費拡大を目的として始めたのがきっかけです。   

面白いことに、納豆は関東では古くから親しまれていましたが、関西では苦手な人が多く、この記念日も「関西で納豆をもっと食べてもらおう」という願いから生まれたんです。現在では全国的に「納豆の日」として認知されており、この日にはスーパーなどで納豆の特売が行われることも多いですよ。

夕食の時間帯ですし、今日は納豆を食べてみるのはいかがでしょうか？
</code></pre>
<h3>備考</h3>
<p>マルチエージェントは比較的簡単に実装できますが、<br>実際に試してみたところ、AIを複数呼び出す分だけトークン数と応答時間が増加するため、使いどころに悩むところです。   </p>
<p>以下は、親エージェントと子エージェントを用いた際の処理コストの内訳です。    </p>
<table>
<thead>
<tr>
<th>区分</th>
<th>親エージェント</th>
<th>子エージェント</th>
<th>全体</th>
</tr>
</thead>
<tbody><tr>
<td>入力トークン</td>
<td>1086</td>
<td>54</td>
<td>1140</td>
</tr>
<tr>
<td>出力トークン</td>
<td>256</td>
<td>219</td>
<td>475</td>
</tr>
<tr>
<td>処理時間</td>
<td>7.2秒</td>
<td>7.3秒</td>
<td>14.5秒</td>
</tr>
</tbody></table>
<p>このように、<strong>子エージェントの応答が加わることで全体の処理時間が倍増</strong>していることがわかります。<br>そのため、マルチエージェントの活用は、<strong>出力の多様性が求められたり、ロジックベースでは対応が難しい複雑なタスク</strong>に限定するのが現実的かもしれません。   </p>
<h2>おわりに</h2>
<p>今回は、データ戦略部で展開しているAI活用システム「cirro」を拡張するために、<br>Strands Agentsを検証した際の“動かすためのポイント”をご紹介しました。<br>意外とハマりどころが多く、実際に動かす際の参考になれば幸いです。   </p>
<p>Strands Agentsを使うことで、ツールや子エージェントによる機能拡張が容易になります。<br>一方で、処理時間やトークン数の増加、システム組み込み時の権限管理など、課題も見えてきました。   </p>
<p>なお、記事内で触れた「cirro」は、Pythonで開発された完全サーバレスなシステムで、<br>ユーザー自身がタスクや参照データを柔軟に拡張できることが特徴です。<br>現在は、ダッシュボードの案内やアンケート分析などに活用しています。<br>こちらについて、AWSの紹介記事はありますが、いずれ詳しくご紹介できればと思っています！<br><a href="https://aws.amazon.com/jp/solutions/case-studies/kinto-technologies/">AWSのcirroの紹介記事</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Kickstart Your Cloud Journey with These 12 AWS Certifications]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-11-AWS-All-Certifiacition-to-start-cloud-life-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-11-AWS-All-Certifiacition-to-start-cloud-life-en/</guid>
            <pubDate>Thu, 17 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Reflections on the Journey to earning all 12 AWS certifications]]></description>
            <content:encoded><![CDATA[<h2>1. Starting Point: Overview</h2>
<p>Nice to meet you! I&#39;m YOU, an infrastructure architect in the Cloud Infrastructure Group at KINTO Technologies.</p>
<p>I joined the company this January, and this is my first post on the Tech Blog. I’m excited to share more in the future! I started my AWS certifications with SAA in October 2023 and completed MLA in February 2025, achieving all 12 AWS certifications in 1 year and 4 months. I&#39;d like to take this opportunity to share my personal thoughts and information I picked up while working toward the 12 certifications.</p>
<p><img src="/assets/blog/authors/you/01-01.jpeg" alt="All Certi"></p>
<p>First off, by &quot;12 AWS certifications,&quot; I mean every certification that AWS currently offers. The criteria are revised annually</p>
<p><a href="https://aws.amazon.com/jp/blogs/psa/2024-japan-aws-all-certifications-engineers/">https://aws.amazon.com/jp/blogs/psa/2024-japan-aws-all-certifications-engineers/</a> </p>
<p>and announced in advance on the <a href="https://aws.amazon.com/jp/blogs/psa/">AWS Japan APN Blog</a>, where selected individuals are also recognized. In 2024, only 1,222 individuals were officially recognized as &quot;AWS All Certifications Engineers.&quot; According to the official article,</p>
<blockquote>
<p>&quot;earning and maintaining all AWS certifications&quot; demonstrates a solid understanding of AWS technologies and the ability to offer customers reliable and up-to-date technical guidance.</p>
</blockquote>
<p>While there are many companies offering cloud services—like Azure and GCP—AWS stands out as the industry standard. That&#39;s thanks to its sheer volume and quality of services, unmatched pace of updates, and the flexibility that comes from its leading market share.</p>
<p>With the growing spotlight on AI, the importance of cloud technology is also rising. Some people might think, &quot;Cloud or AI? That has nothing to do with me.&quot; But just like how using a computer has become second nature in most jobs, it won&#39;t be long before using AI in everyday life becomes just as common.</p>
<p>The cloud provides easy access to both AI models and the computing power required to run them, making cloud technology essential to staying current in today&#39;s landscape. So, why is getting certified important when learning AWS and the cloud?</p>
<p>That&#39;s exactly what I&#39;ll explain next.</p>
<h2>2. Current Status: Where I Stand</h2>
<p>Unfortunately, having a certification doesn&#39;t necessarily make a big difference in how well you can use the cloud. To give an example, let&#39;s treat &quot;cloud&quot; like learning English. Say you studied hard for the TOEIC and got a high score in hopes of using English more effectively. But do you think that alone means you&#39;ve really improved your English?</p>
<p>No matter how good you are at test strategies, or how many words and grammar rules you memorize, it doesn&#39;t mean much if you can&#39;t actually use English when it counts. That said, it&#39;s definitely wrong to say TOEIC isn&#39;t helpful for improving your English skills. If it had no value, there&#39;s no way so many universities and companies would use TOEIC scores as a benchmark. TOEIC is a test that quantifies business English skills, which is why the score is recognized as a reflection of ability, not just a number.</p>
<p>In the same way, having all 12 AWS certifications sets a clear benchmark in the cloud field. It turns abstract knowledge into something visible and concrete in the form of a qualification. Here&#39;s a breakdown of the benefits that come from this kind of visualization:</p>
<blockquote>
<ul>
<li>Clear goal setting: since the certifications follow a roadmap provided by AWS, you can plan your learning step by step.</li>
<li>Motivation: setting an exam date gives you a clear deadline, which helps create an environment where you can stay focused and motivated.</li>
<li>Knowledge assurance: you&#39;ll gain and confirm the minimum level of knowledge needed to pass the exam.</li>
<li>Review: even for those already familiar with the cloud, it&#39;s a good opportunity to review and check what&#39;s required for certification.</li>
<li>Discovery: because the exams evolve with updates, they give you chances to learn about areas you might not normally encounter.</li>
</ul>
</blockquote>
<p>Even if you switch the wording to another language, doesn&#39;t the content still come across naturally and make sense? In the end, it&#39;s not just about getting certified to boost your cloud skills, or getting certified because you want to work with the cloud. What really matters is the value in the learning process itself.</p>
<h3>The future of AWS certification</h3>
<p>Next, I&#39;d like to dig into something I felt over the past year or so while preparing for AWS certification: &quot;Where is AWS certification headed from here?&quot; </p>
<p>:::message
Just to be clear, **this is purely my own personal speculation without any official backing; nothing from AWS itself. ** Please keep that in mind if you quote this.
:::</p>
<p>When I first started studying for AWS certification back in 2022, ChatGPT was taking off, and interest in AI was growing rapidly. In response, AWS began rolling out more and more AI-focused services, and from 2024, they made some big changes to their certification structure.</p>
<p>In April 2024, three existing Specialty certifications were discontinued:</p>
<blockquote>
<p>AWS Certified Data Analytics – Specialty (DAS) 
AWS Certified Database – Specialty (DBS) 
AWS Certified: SAP on AWS – Specialty (PAS)</p>
</blockquote>
<p>To replace DAS and DBS, a new certification was introduced in March 2024:</p>
<blockquote>
<p>AWS Certified Data Engineer – Associate (DEA)</p>
</blockquote>
<p>Later, in October 2024, AWS introduced two more certifications to reflect the roadmap for new AI services like Amazon Q and Amazon Bedrock, along with enhancements to existing services like Amazon Sagemaker:</p>
<blockquote>
<p>AWS Certified AI Practitioner (AIF) 
AWS Certified Machine Learning Engineer – Associate (MLA)</p>
</blockquote>
<p>This was a major shake-up, and honestly, it caused some headaches even for individual learners like me. The content I had been studying was significantly updated, so I had to completely rethink my exam schedule. It&#39;s certain that AWS certifications will continue to evolve, especially with AI leading the way as a major tech trend.</p>
<p>While this is purely speculation, the certification that seems most likely to change is:</p>
<blockquote>
<p>AWS Certified Machine Learning Engineer – Specialty (MLS)</p>
</blockquote>
<p>The MLS was last updated in July 2022, so its content is already outdated compared to the AIF and MLA. It may simply be updated as a Specialty-level certification, but there&#39;s a strong chance it will be restructured into a new Professional qualification. Why? Because the current certification path is organized into three tiers: Practitioner, Associate, and Professional.</p>
<p><img src="/assets/blog/authors/you/01-02.jpeg" alt="path"><a href="https://aws.amazon.com/jp/blogs/big-data/upgrade-your-resume-with-the-aws-certified-big-data-specialty-certification/">^1</a></p>
<p>In the same way, after AIF and MLA qualifications, a Professional-level certification is likely to follow. Whether a specialty certification will be upgraded to a professional level is ultimately up to AWS. But if that does happen, we&#39;ll likely need to anticipate a higher-level DEA certification as well.</p>
<blockquote>
<p>(Tentative) AWS Certified Machine Learning Engineer – Professional (MLP) 
(Tentative) AWS Certified Data Engineer – Professional (DEP)</p>
</blockquote>
<p>This is a logical prediction, but it comes with its own problems. AWS seems to uphold a symbolic 12-certification crown structure, so adding two more would break that and push the total beyond 13.</p>
<p>One way to avoid this is to reduce the number of existing specialties—especially those that have become unclear—as new certifications are added.</p>
<blockquote>
<p><del>(For example) AWS Certified Security – Specialty (SCS)</del> 
AWS Certified Advanced Networking – Specialty (ANS)</p>
</blockquote>
<p>Unlike some of the other specialty certifications that have already been retired, SCS and ANS are built around deeper, professional-level knowledge. Over 60% of the content overlaps with the Professional-level certifications. SCS focuses on organization-wide security, while ANS emphasizes networking with on-premises environments. That said, there are some current shortcomings that can&#39;t be ignored.</p>
<p>SCS hasn&#39;t been updated to reflect developments in AI, so it doesn&#39;t cover AI-related security topics. With AI evolving so quickly, security and compliance around AI are becoming increasingly critical. So the question now is whether to add AI content into SCS, or to spread it across each professional-level certification. I think the second option is more likely, since many specialty certifications have already been merged or discontinued to align with the AI trend. In the case of ANS, it&#39;s in a similar position to SCS. Even though networking can support AI, within AWS itself, there&#39;s not a big difference in capability. It is true that Azure is required for OpenAI, GCP for Gemini, and a multi-cloud setup is necessary to use AI services provided by other cloud vendors. However, since AWS tends to be less proactive in supporting non-AWS products, there haven&#39;t been any updates to multi-cloud-related certifications so far. On the other hand, due to the growing anti-cloud sentiment, hybrid cloud is gaining attention, so the ANS certification system is likely to remain.</p>
<p>In any case, reducing the number of certifications helps maintain the 12-certification status, so that&#39;s one possible approach. Another is consolidating roles, such as DevOps Engineer, instead of introducing new professional-level certifications.</p>
<blockquote>
<p>(Tentative) AWS Certified MLOps Engineer – Professional (MOP)</p>
</blockquote>
<p>AWS describes <a href="https://aws.amazon.com/jp/what-is/mlops/">MLOps</a> as &quot;an ML culture and practice that unifies ML application development (Dev) with ML system deployment and operations (Ops).&quot;</p>
<p><img src="/assets/blog/authors/you/01-03.jpeg" alt="MLOps"><a href="https://aws.amazon.com/what-is/mlops/">^2</a></p>
<p>This refers to the entire process involved in machine learning. By going through the data engineering and data analysis handled in DEA, you can make use of the entire machine learning flow used in AIF, MLA, and MLS. So if you were to choose just one area to develop as a new professional skill, I believe this would be a practical and effective path.</p>
<h3>Question Types of AWS Certifications</h3>
<p>It&#39;s not just the types of certifications that are changing. There are also updates to the exam formats.</p>
<p>Since the SOA lab exam was discontinued, the remaining exams have been evaluated solely through multiple-choice questions. While the advantage is that results can be measured objectively and quantitatively, it&#39;s also true that this format sometimes doesn&#39;t reflect hands-on implementation skills. AWS seems aware of this, and they&#39;ve introduced a new question format starting with the AIF and MLA exams.</p>
<p>According to the <a href="https://d1.awsstatic.com/ja_JP/training-and-certification/docs-ai-practitioner/AWS-Certified-AI-Practitioner_Exam-Guide.pdf">AIF exam guide</a>, the following types of questions may appear.</p>
<blockquote>
<ul>
<li>Ordering: has a list of 3–5 responses to complete a specified task. To earn points for the question, you must select the correct answers and place them in the correct order.</li>
<li>Matching: has a list of responses to match with a list of 3–7 prompts. To get points, you must match all the pairs correctly</li>
<li>Case study: has one scenario with two or more related questions.  The scenario is the same for each question in the case study. Each question in the case study will be evaluated separately and points are awarded for each correct answer.</li>
</ul>
</blockquote>
<p>These three types didn&#39;t appear very frequently in my exam, but just as the guide describes, they were included. The difficulty level was similar to that of regular multiple-choice questions. Due to AWS exam confidentiality, I can&#39;t share exact question formats, but here&#39;s how I&#39;d describe the types based on my experience: For sorting and matching, you can&#39;t rely on option similarity to guess the right answer. You really need to know the required steps and how the given terms or descriptions logically connect.</p>
<p>As for case studies, while the format is essentially multiple choice, they bundle several questions into one shared scenario. This format allows you to approach the case from multiple angles, and it also helps avoid situations where you&#39;re tested more on reading comprehension than on applying your actual knowledge. In the real world, we don&#39;t just answer one question at a time. We usually simulate each case and think through it as a whole. That&#39;s why I think the case study format is a great approach for test takers.</p>
<p>When it comes to AWS certifications, the question formats will likely continue to evolve. For example, like the hands-on labs in the SOA exam, we can expect more questions along the lines of, &quot;Can you actually implement this?&quot;  These kinds of changes won&#39;t happen just once: they&#39;ll gradually be introduced into other certifications as well. So, if you&#39;re preparing for an AWS exam, it&#39;s important to stay up to date and be ready!</p>
<h2>3. Mindset: Preparing for the Challenge</h2>
<p>This is something I often hear from people around me, regardless of their job title:</p>
<blockquote>
<p>&quot;I don&#39;t work in anything related to AWS, but will this actually be useful if I study it?&quot; &quot;If I want to get AWS certified, where should I start?&quot; &quot;What are you using to study?&quot;</p>
</blockquote>
<p> I&#39;m certified as a cloud engineer, which means I already needed to have the knowledge to work in the cloud. Because I use it in my actual job, I interact with cloud services far more often than most people. That&#39;s why getting certified doesn&#39;t automatically mean you&#39;ll be ready to work in cloud-related roles right away.</p>
<p>If you haven&#39;t used the cloud before, it&#39;s rare to suddenly become able to use it just because you passed an exam. A certification is kind of like a coupon. Even if you have a coupon that gives you 10% off at a gas station on purchases over 10,000 yen, there are lots of reasons you might not be able to use it: you don&#39;t have a car, the station is too far away, or you don&#39;t have enough money to hit the discount threshold. Seen this way, the conditions for using the coupon are pretty clear:</p>
<blockquote>
<ol>
<li>You or someone close to you owns a car or is planning to.</li>
<li>The gas station that accepts the coupon is within reach.</li>
<li>You&#39;re in a position to make use of the discount.</li>
</ol>
</blockquote>
<p>So before jumping in, check whether you actually have a reason to want that coupon.</p>
<p>In other words, &quot;Are you in a position to take action and make use of the qualification?&quot;  Even if you get a coupon, it&#39;s not like a car will magically appear, or a gas station will pop up right in front of your house, or the money to use it will suddenly fall into your lap—those things just don&#39;t happen in real life, right? The same goes for the cloud and AWS. For those who feel the cloud doesn&#39;t really apply to their work, examples might include:</p>
<ul>
<li>Business professionals outside of IT</li>
<li>Developers who don&#39;t specialize in infrastructure</li>
<li>Infrastructure specialists focused solely on on-premises systems</li>
</ul>
<p>Now, what would you recommend to someone who says they can&#39;t afford to buy a car? With car leasing or subscriptions, as long as you can pay the monthly fee, you can still drive a car. That&#39;s exactly what the cloud is in IT. I believe that &quot;borrowing technology&quot; is the essence of the cloud. If learning the tech is too expensive, you can just borrow it. Of course, the specifics vary by field, but I truly believe that just understanding this concept can completely change how you view technology.</p>
<p>If someone says, &quot;The gas station&#39;s too far,&quot; then that&#39;s a perfectly valid reason. There&#39;s no need to force yourself to go. But what if the station is close enough to swing by on your daily commute? For developers, the cloud isn&#39;t really that far away. In fact, just shifting your perspective a little might reveal a whole world of possibilities right next to you.</p>
<p>Finally, no matter how good a coupon you have, it&#39;s no use if you don&#39;t use it. Even if you already own a car and a gas station opens up right outside your home, you still won&#39;t be able to use the coupon if you always stick to your usual station. People may have all kinds of reasons:
maybe they can&#39;t pay the 10,000 yen up front, they already have a different gas card, or they&#39;re unsure about the store. But the undeniable fact is that infrastructure professionals are more naturally positioned to get into the cloud than anyone else. If you’ve only worked in on-prem environments, the cloud, whether IaaS (Infrastructure as a Service) or PaaS (Platform as a Service), might feel unfamiliar. Still, the fundamentals of the cloud are built on infrastructure knowledge. That&#39;s why, compared to people in business or development roles, it&#39;s actually much more accessible. So instead of saying, &quot;I don&#39;t have the ability to work with the cloud, &quot;how about saying, ‘Let&#39;s build the ability to work with the cloud’&quot;?</p>
<p>I started my career in development, but thanks to the cloud knowledge I gained through self-study, I was trusted with cloud-related tasks as well. After that, I earned certifications and was able to transition into a cloud-focused role. Honestly, if I had only stuck to what I was already doing or aiming to do, I probably wouldn&#39;t have made it this far. Getting all 12 AWS certifications really feels like it&#39;s opened up more doors for me. Since joining KTC, I&#39;d say about 50% of the knowledge I gained from certifications has been directly applicable in practice. As for the other 50%, I&#39;m continuing to work on ways to put it to good use. KTC has set &quot;AI First&quot; as its key goal for this year, and I plan to contribute actively to our AI initiatives.</p>
<p>If you&#39;re interested in KTC&#39;s AI First direction, I highly recommend checking out the article written by our Vice President, Kageyama.</p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/</a> </p>
<p>The official <a href="https://d1.awsstatic.com/ja_JP/training-and-certification/docs/AWS_certification_paths.pdf">AWS認定パス</a> are also recommended. Please take a look for reference!</p>
<h2>4.Strategy: How I Passed the Exam</h2>
<p>There are plenty of people out there who recommend different study methods, so instead of repeating the same advice, I&#39;d like to talk about strategies for efficiently tackling AWS certification from a different angle.</p>
<h3>The Straightforward Approach</h3>
<p>Studying seriously is actually very simple. The key is to start from scratch and work through the content outlined in the previously mentioned <a href="https://d1.awsstatic.com/ja_JP/training-and-certification/docs-ai-practitioner/AWS-Certified-AI-Practitioner_Exam-Guide.pdf">AIF exam guide</a> This method is ideal for those who don&#39;t have basic knowledge and want to learn properly without rushing, or for anyone who prefers to take their time with exam prep. The process can be broken down into five stages:</p>
<ol>
<li><p>Information gathering: look through sources like search engines, social media, YouTube, blogs, etc. to find your preferred sources.</p>
</li>
<li><p>Choose your source: from the available options, pick the one that fits you best.</p>
</li>
</ol>
<ul>
<li><p>Official AWS documents: The documentation provided by AWS is always up to date, highly reliable, and of high quality. Even when I&#39;m using other learning methods, I always go back to check the official docs. The AWS Training Center, which offers some content for free, is also a great help—definitely take advantage of it. I haven&#39;t used any paid services, but from what I&#39;ve seen, they have a similar effect to that of external learning website introduced below.</p>
</li>
<li><p>YouTube: offers the largest collection of free content, but the quality and accuracy can vary greatly depending on the uploader, and the information isn&#39;t always up to date. That said, if you&#39;re comfortable learning through video and audio and don&#39;t have language barriers, these downsides become less of an issue. It&#39;s also great that you can just give it a try and stop anytime you like.</p>
</li>
<li><p>Books: If you like analog-style studying, books are a solid choice. Their strength lies in offering focused content with a certain level of quality assurance. The advantage is that you can get a general idea of the contents before buying, and everything you need is typically packed into one volume. However, be aware that books tend to lag behind on updates, so they&#39;re not always ideal for something like AWS certification, where things change quickly. Unless you&#39;re planning to dive into study right away and take the exam before the certification updates, it may be better to hold off.</p>
</li>
<li><p>External learning websites: let&#39;s look at paid learning resources, such as external website like Udemy. These are paid resources, but they offer high-quality content and are generally updated faster than YouTube. In short, they combine the strengths of both YouTube and books, which is why I personally use them often. One thing to be careful about AWS certification is that many topics overlap across different certifications, so be sure to check the table of contents carefully before purchasing a course.</p>
</li>
</ul>
<ol start="3">
<li><p>Start studying: study time varies depending on the exam, but in most cases, planning for about 2 to 3 months worked best for me.</p>
</li>
<li><p>Verification: use AWS official mock exams to assess your current level.</p>
</li>
<li><p>Exam: be sure to schedule your exam in advance. You can take it either online or at a testing center offline, but I recommend offline for a more stable test environment. If any problems arise during an offline exam, the test center staff can help resolve them, and as a bonus, offline exam results tend to arrive faster too.</p>
</li>
</ol>
<h3>Quick-Attack Method</h3>
<p>Some people think: &quot;I already know AWS, so I don&#39;t need to start from scratch. I just want to focus on the parts I&#39;m unfamiliar with.&quot; &quot;I&#39;m fine with learning as I go. Just want to get certified as fast as possible!&quot;  I&#39;ve approached a few exams this way myself, so let me share the most cost-effective strategy I&#39;ve used.</p>
<p>:::message
Generative AI: I used Perplexity, but any generative AI will do! The prompts I introduce here are just examples, so feel free to use whatever prompt that suits you.
:::</p>
<h4>Keyword Summary</h4>
<p>Use AI to summarize service content and extract only the key points you need to know.</p>
<p><img src="/assets/blog/authors/you/01-04.jpeg" alt="Input Prompt"></p>
<p>:::details Answer</p>
<p>Here is a summary centered on CloudWatch, AWS&#39;s monitoring service, along with a comparison to related services such as CloudTrail, Config, and WAF. I&#39;ll also briefly mention a few other monitoring tools not covered in detail here.</p>
<p><strong>About CloudWatch</strong></p>
<p><strong>Overview</strong></p>
<p>Amazon CloudWatch is a service for monitoring AWS resources and applications in real time. It provides the following key features:</p>
<ul>
<li><p>Metrics collection: collects resource data such as CPU usage, disk I/O, and network traffic.</p>
</li>
<li><p>Log monitoring: collects and analyzes application and system logs.</p>
</li>
<li><p>Alarm settings: sends notifications or triggers automated actions based on defined thresholds.</p>
</li>
<li><p>Event management: automates responses to specific events (e.g., sending notifications when an instance is launched).</p>
</li>
</ul>
<p><strong>Major use cases</strong></p>
<ul>
<li><p>Monitoring resource performance</p>
</li>
<li><p>Detecting anomalies and sending alert notifications</p>
</li>
<li><p>Visualizing the overall system operation status</p>
</li>
</ul>
<p><strong>Comparison between CloudWatch and its related services</strong></p>
<table>
<thead>
<tr>
<th>Service name</th>
<th>Main objective</th>
<th>Features</th>
<th>Major use cases</th>
</tr>
</thead>
<tbody><tr>
<td>CloudWatch</td>
<td>Metrics &amp; log monitoring</td>
<td>Monitor AWS resources and applications in real time.<br/>Receive near real-time notifications.</td>
<td>Resource monitoring, performance optimization, and anomaly detection.</td>
</tr>
<tr>
<td>CloudTrail</td>
<td>Recording operation history</td>
<td>Record who did what and when (API calls).<br/>For compliance and security audits.</td>
<td>Security audits and detecting unauthorized operations.</td>
</tr>
<tr>
<td>AWS Config</td>
<td>Tracking and evaluating configuration changes</td>
<td>Record changes to resource configurations and detect rule violations.<br/>Save the setting state as a snapshot.</td>
<td>Configuration management, ensuring compliance, and troubleshooting.</td>
</tr>
<tr>
<td>AWS WAF</td>
<td>Web application layer security</td>
<td>Protect web applications from threats like SQL injections and DDoS attacks.<br/>Can be integrated with CloudFront.</td>
<td>Strengthening security and blocking attacks</td>
</tr>
</tbody></table>
<p><strong>Basis of comparison</strong></p>
<ol>
<li>Aims and scope:</li>
</ol>
<ul>
<li><p>CloudWatch monitors the overall system status.</p>
</li>
<li><p>CloudTrail specializes in operation history.</p>
</li>
<li><p>Config is used for changing settings and evaluating rules.</p>
</li>
<li><p>WAF is dedicated to security measures.</p>
</li>
</ul>
<ol start="2">
<li>Real-time capabilities:</li>
</ol>
<ul>
<li><p>CloudWatch can send notifications in near real time.</p>
</li>
<li><p>CloudTrail may have some delay in reporting.</p>
</li>
</ul>
<ol start="3">
<li>Overlapping use cases:</li>
</ol>
<ul>
<li>Data recorded by udTrail and Config can also be sent to CloudWatch Logs for centralized management.</li>
</ul>
<hr>
<p><strong>Comparisons among external monitoring tools applicable to AWS</strong></p>
<p>There are several monitoring tools available outside of AWS, including:</p>
<table>
<thead>
<tr>
<th>Tool name</th>
<th>Features</th>
<th>Advantages</th>
<th>Disadvantages</th>
</tr>
</thead>
<tbody><tr>
<td>Zabbix</td>
<td>Open source<br/>Highly customizable</td>
<td>Available for free<br/>Supports a wide range of environments</td>
<td>Complex to set up and operate</td>
</tr>
<tr>
<td>Mackerel</td>
<td>Japanese SaaS-based tool</td>
<td>Easy to set up<br/>Centralized management across multiple environments</td>
<td>Limited AWS-specific features</td>
</tr>
<tr>
<td>Site24x7</td>
<td>Multi-cloud support</td>
<td>Supports hybrid cloud environments</td>
<td>More difficult to configure in detail compared to AWS-specific tools</td>
</tr>
</tbody></table>
<p><strong>Selection criteria</strong></p>
<p>If you&#39;re using AWS only: CloudWatch is the best fit.</p>
<p>For multi-cloud setups: Site24x7 or Mackerel work well.</p>
<p>If you need deep customization: Zabbix is the way to go.</p>
<hr>
<p><strong>Keys to successful preparation for AWS certification exams</strong></p>
<ol>
<li><p>Clearly understand the role and use cases of each monitoring service.</p>
</li>
<li><p>Be familiar with integration examples involving CloudWatch and other services like CloudTrail, Config, and WAF.</p>
</li>
<li><p>Topics such as pay-as-you-go pricing and real-time monitoring may also appear on the exam.</p>
</li>
</ol>
<p>If you have any questions, feel free to ask!
:::</p>
<h4>Practice Question Generation</h4>
<p>Once you grasp the concepts of key terms, try creating simple questions to test your understanding.</p>
<p><img src="/assets/blog/authors/you/01-05.jpeg" alt="Input Prompt"></p>
<p>:::details Answer</p>
<blockquote>
<p>Question 1: You want to receive a notification when an EC2 instance&#39;s CPU utilization exceeds a specific thresholds. Which AWS service should you use to achieve this purpose? Options: • A. AWS CloudTrail • B. Amazon CloudWatch • C. AWS Config • D. AWS WAF</p>
</blockquote>
<blockquote>
<p>Question 2: Your security team wants to track API calls made to AWS resources over the past three months to investigate a specific user&#39;s activity. Which service is best suited for this purpose? Options: • A. Amazon CloudWatch • B. AWS CloudTrail • C. AWS Config • D. Amazon Athena</p>
</blockquote>
<blockquote>
<p>Question 3: A company wants to track configuration changes to its AWS resources and detect compliance violations. Which service is best suited for this purpose? Options: • A. AWS CloudTrail • B. Amazon CloudWatch Logs • C. AWS Config • D. AWS WAF</p>
</blockquote>
<blockquote>
<p>Question 4: You want to protect a web application from SQL injection and cross-site scripting (XSS) attacks. Which AWS service is best suited for this purpose? Options: • A. Amazon CloudWatch Logs • B. AWS WAF • C. AWS Shield • D. AWS Config
:::</p>
</blockquote>
<p>I only explained the two main ways I use as quick-attack methods, but it&#39;s a generative AI learning approach that anyone can customize endlessly. Besides the methods I introduced, I also often use it to throw in dozens of detailed questions as reminders. If you give the AI some reference material, it&#39;ll generate richer keyword summaries and mock questions. I used the quick-attack method for about 70% of my AIF and MLA prep, and passed in 1 or 2 rounds, so I can guarantee you that it works!</p>
<h2>5. 1st-art: Every Start is Art</h2>
<p>After earning all 12 AWS certifications, I tweeted some thoughts about the journey.</p>
<p>You may have noticed that I hid a little trick in both the title and the body. Go back to the beginning, and you&#39;ll see it right away.</p>
<blockquote>
<ol>
<li>Starting Point: Overview -&gt; 5. 1st-art: Every Start is Art</li>
</ol>
</blockquote>
<p>The reason I added this quirky twist is because my 12-cert, 1-year-and-4-month journey became a single picture after all the pieces came together, one I could only complete because I started (art) this canvas. You never know what the final image will be when you first pick up the brush. When I was in elementary school, I was asked to draw my future, and I drew myself as a firefighter. In junior high school, it was a novelist. Now, I work as a cloud engineer, which is completely different from either of those.</p>
<p>But does that mean the pictures I drew as a child had no meaning? I believe drawing them had meaning because I was facing my dreams. Now, I&#39;ve completed a picture called &quot;12 AWS Certifications.&quot; I intend to keep drawing new pictures as I move forward. This article I wrote on the Tech Blog is one picture, and I think my work at KTC can become another in the series.</p>
<p>Thank you very much for reading!</p>
<hr>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Developing a CMDB Chatbot Function with Generative AI and Text-to-SQL]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL-en/</guid>
            <pubDate>Wed, 16 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Developing a CMDB Chatbot Function with Generative AI and Text-to-SQL]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello! I am Yamada, and I develop and operate in-house tools in the Platform Engineering Team of KINTO Technologies&#39; (KTC) Platform Group. If you want to know more about the CMDB developed by the Platform Engineering team, please check out the article below!
 <a href="https://blog.kinto-technologies.com/posts/2023-12-14-CMDB/">https://blog.kinto-technologies.com/posts/2023-12-14-CMDB/</a></p>
<p>This time, I would like to talk about how we implemented a CMDB data search function and CSV output function in a chatbot, one of the CMDB functions, using <strong>generative AI and Text-to-SQL</strong>.</p>
<p>The CMDB chatbot allows you to ask questions about how to use the CMDB or about the data managed in the CMDB. Questions about the CMDB data had been originally answered using a RAG mechanism using ChromaDB, but we moved to a Text-to-SQL implementation for the following reasons:</p>
<h3>Advantages of Text-to-SQL over RAG</h3>
<ul>
<li>Data accuracy and real-time availability<ul>
<li>The latest data can be retrieved in real time directly from the CMDB database.</li>
<li>No additional processing is required to update data.</li>
</ul>
</li>
<li>System simplification<ul>
<li>No infrastructure for vector DB or embedding processing is required (ChromaDB and additional batches for embedded data are no longer required).</li>
</ul>
</li>
</ul>
<p>For these reasons, we decided that Text-to-SQL is more suitable for a system that handles structured data such as CMDB.</p>
<h2>What Is Text-to-SQL?</h2>
<p>Text-to-SQL is a technology for converting natural language queries into SQL queries. This allows even users without knowledge of SQL to easily extract the necessary information from the database.</p>
<p>This makes it possible to retrieve data such as products, domains, teams, users, and vulnerability information including ECR and VMDR managed in the CMDB database from natural language queries.</p>
<p>The following are some examples of matters that could be utilized within KTC:</p>
<ul>
<li>Retrieving a list of domains that have not been properly managed (domains not linked to products in the CMDB)</li>
<li>Retrieving Atlassian IDs of all employees<ul>
<li>This is because the MSP (Managed Service Provider) team creates tickets for requests such as addressing PC vulnerabilities, by mentioning (tagging) the relevant individuals.</li>
</ul>
</li>
<li>Aggregation of the number of vulnerabilities detected in resources related to the products for which each group is responsible</li>
<li>Extraction of products for which the AWS resource start/stop schedule has not been set.</li>
</ul>
<p>Previously, when a request to extract such data came to the Platform Engineering team, a person in charge would run a SQL query directly from the CMDB database to extract and process the data, then hand it over to the requester. When requesters become able to extract data using Text-to-SQL in the CMDB chatbot, they will be able to easily extract data without having to go through the trouble of asking a person in charge, as shown in the figure below:</p>
<p><img src="/assets/blog/authors/masaki_yamada/text-to-sql/data_extraction.png" alt="data_extraction"></p>
<p>Text-to-SQL is a convenient feature, but you must be aware of the risk of insecure SQL generation. While the following figure illustrates an extreme case, since SQL is generated from natural language, there is a risk of unintentionally generating SQL statements that update or delete data or modify table structures.</p>
<p><img src="/assets/blog/authors/masaki_yamada/text-to-sql/sql_injection.png" alt="sql_injection"></p>
<p>So, you need to avoid generating unsafe SQL by the following methods:</p>
<ul>
<li>Connecting to a Read Only DB endpoint</li>
<li>Set DB users to Read Only permissions</li>
<li>Carrying out a validation check to ensure that commands other than <code>SELECT</code> are not executed in application implementation</li>
</ul>
<h2>System Configuration</h2>
<p>Here is the architecture of the CMDB. Resources that are not relevant to this article have been excluded. As I explained at the beginning, we had originally used ChromaDB as a vector DB, obtained information on how to use the CMDB from Confluence (implemented with LlamaIndex), and retrieved CMDB data from a database (implemented with Spring AI), then entered both into ChromaDB.</p>
<p>This time, we have migrated answers to questions about CMDB data from the RAG feature in Spring AI + ChromaDB to a feature using Text-to-SQL.</p>
<p><img src="/assets/blog/authors/masaki_yamada/text-to-sql/qa_architecture.png" alt="qa_architecture"></p>
<h2>Text-to-SQL Implementation</h2>
<p>From here on, I would like to explain the implementation while showing you the actual code.</p>
<h3>CMDB Data Search Function</h3>
<p><strong>Retrieving Schema Information</strong> 
First, retrieve the schema information required to generate SQL in LLM. The less schema information there is, the higher the accuracy, so we have adopted a method of specifying only the necessary tables.</p>
<p>Since the comments for table columns are important as judgment criteria when the LLM generates SQL statements, all of them need to be added beforehand.</p>
<pre><code class="language-python">def fetch_db_schema():
    cmdb_tables=[&#39;table1&#39;, &#39;table2&#39;, ...]
    cmdb_tables_str = &#39;, &#39;.join([f&quot;&#39;{table}&#39;&quot; for table in cmdb_tables])

    query = f&quot;&quot;&quot;
    SELECT
        t.TABLE_SCHEMA,
        t.TABLE_NAME,
        t.TABLE_COMMENT,
        c.COLUMN_NAME,
        c.DATA_TYPE,
        c.COLUMN_KEY,
        c.COLUMN_COMMENT
    FROM information_schema.COLUMNS c
    INNER JOIN information_schema.TABLES t ON c.TABLE_SCHEMA = t.TABLE_SCHEMA AND c.TABLE_NAME = t.TABLE_NAME
    WHERE t.TABLE_SCHEMA = &#39;cmdb&#39; AND t.TABLE_NAME IN ({cmdb_tables_str})
    ORDER BY
        t.TABLE_SCHEMA,
        t.TABLE_NAME,
        c.COLUMN_NAME
    &quot;&quot;&quot;

    connection = get_db_connection()
    try:
        cursor = connection.cursor()
        cursor.execute(query)
        return cursor.fetchall()
    finally:
        cursor.close()
        connection.close()
</code></pre>
<p>Example of retrieved results</p>
<table>
<thead>
<tr>
<th>TABLE_SCHEMA</th>
<th>TABLE_NAME</th>
<th>TABLE_COMMENT</th>
<th>COLUMN_NAME</th>
<th>DATA_TYPE</th>
<th>COLUMN_KEY</th>
<th>COLUMN_COMMENT</th>
</tr>
</thead>
<tbody><tr>
<td>cmdb</td>
<td>product</td>
<td>Product table</td>
<td>product_id</td>
<td>bigint</td>
<td>PRI</td>
<td>Product ID</td>
</tr>
<tr>
<td>cmdb</td>
<td>product</td>
<td>Product table</td>
<td>product_name</td>
<td>varchar</td>
<td></td>
<td>Product name</td>
</tr>
<tr>
<td>cmdb</td>
<td>product</td>
<td>Product table</td>
<td>group_id</td>
<td>varchar</td>
<td></td>
<td>Product&#39;s responsible department (group) ID</td>
</tr>
<tr>
<td>cmdb</td>
<td>product</td>
<td>Product table</td>
<td>delete_flag</td>
<td>bit</td>
<td></td>
<td>Logical deletion flag 1=deleted, 0=not deleted</td>
</tr>
</tbody></table>
<p><strong>Formatting the retrieved schema information into text for the prompt to be passed to the LLM</strong></p>
<pre><code class="language-python">def format_schema(schema_data):
    schema_str = &#39;&#39;
    for row in schema_data:
        schema_str += f&quot;Schema: {row[0]}, Table Name: {row[1]}, Table Comment: {row[2]}, Column Name: {row[3]}, Data Type: {row[4]}, Primary Key: {&#39;yes&#39; if row[5] == &#39;PRI&#39; else &#39;no&#39;}, Column Comment: {row[6]}\n&quot;
    return schema_str
</code></pre>
<p>Convert each column into the following text and pass the schema information to LLM.</p>
<pre><code>Schema: cmdb, Table Name: product, Table Comment: プロダクトテーブル, Column Name: product_id, Data Type: bigint, Primary Key: PRI, Column Comment: プロダクトID
Schema: cmdb, Table Name: product, Table Comment: プロダクトテーブル, Column Name: product_name, Data Type: varchar, Primary Key: no, Column Comment: プロダクト名
Schema: cmdb, Table Name: product, Table Comment: プロダクトテーブル, Column Name: group_id, Data Type: varchar, Primary Key: no, Column Comment: プロダクトの担当部署(グループ)ID
Schema: cmdb, Table Name: product, Table Comment: プロダクトテーブル, Column Name: delete_flag, Data Type: bit, Primary Key: no, Column Comment: 論理削除フラグ 1=削除, 0=未削除
</code></pre>
<p><strong>Generating SQL queries from questions and schema information from the CMDB chatbot, using LLM</strong>
This is the Text-to-SQL portion, where SQL queries are generated from natural language. Based on the questions and schema information, we specify various conditions in the prompt and have LLM generate SQL.</p>
<p>For example, the following conditions can be specified:</p>
<ul>
<li>Generate valid queries for <code>MySQL:8.0</code></li>
<li>Use fuzzy search for condition expressions other than ID</li>
<li>Basically, exclude logically deleted data from search</li>
<li>Do not generate anything other than SQL statements</li>
<li>Addition of context information<ul>
<li>Convert questions in the forms of &quot;... of KTC&quot; and &quot;... of CMDB&quot; into &quot;All...”</li>
<li>Convert questions about region to those about AWS region<ul>
<li>Convert <code>Tokyo region</code> to <code>ap-northeast-1</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The instruction &quot;Do not generate anything other than SQL statements&quot; is particularly important. When this was not conveyed properly, responses often ended up including unnecessary text such as: &quot;Based on the provided information, the following SQL has been generated: SELECT~”</p>
<p> So, a prompt is needed that ensures SQL statements only in the form of &quot;SELECT~&quot; are generated without generating unnecessary text, explanations, or markdown formatting.</p>
<pre><code class="language-python">def generate_sql(schema_str, query):
    prompt = f&quot;&quot;&quot;
    Generate a SQL query based on the given MySQL database schema, system contexts, and question.
    Follow these rules strictly:

    1. Use MySQL 8.0 syntax.
    2. Use `schema_name.table_name` format for all table references.
    3. For WHERE clauses:
    - Primarily use name fields for conditions, not ID fields
    - Primarily use name fields for conditions, not ID fields
    - Use LIKE &#39;%value%&#39; for non-ID fields (fuzzy search)
    - Use exact matching for ID fields
    - Use exact matching for ID fields

    - Include &quot;delete_flag = 0&quot; for normal searches
    - Use &quot;delete_flag = 1&quot; only when the question specifically asks for &quot;deleted&quot; items
    CRITICAL INSTRUCTIONS:
    - Output MUST contain ONLY valid SQL query.
    - DO NOT include any explanations, comments, or additional text.

    - DO NOT use markdown formatting.
    - DO NOT generate invalid SQL query.
    - DO NOT generate invalid SQL query.
    Process:

    1. Carefully review and understand the schema.
    2. Generate the SQL query using ONLY existing tables and columns. 3. Double-check query against schema for validity.
    System Contexts: - Company:
    KINTO Technologies Corporation (KTC) - System:

    Configuration Management Database (CMDB)
    - Regions: AWS Regions (e.g., Tokyo region = ap-northeast-1)
    Interpretation Rules:
        - &quot;KTC&quot; or &quot;CMDB&quot; in query:Refer to all information in the database
        Examples:
        &quot;
    Employees in KTC&quot; -&gt; &quot;All users&quot; &quot;KTC&#39;s products&quot; -&gt; &quot;All products&quot;
    &quot;Domains on CMDB&quot; -&gt; &quot;All domains&quot;
        - Region mentions:Interpret as AWS Regions

    Example:
    &quot;

    ECR repositories in Tokyo region&quot; -&gt; &quot;ECR repositories in ap-northeast-1&quot;
    Database Schema:
    {schema_str}

    Question:
    {query}
    &quot;&quot;&quot;
    return llm.complete(prompt).text.strip()
</code></pre>
<p><strong>Perform validation checks on SQL generated by LLM and Text-to-SQL to allow only <code>SELECT</code> statements</strong>
To prevent the risk of unsafe SQL generation, we connect to a read-only DB endpoint, but check whether any SQL other than queries has been generated.</p>
<p><strong>Execute the SQL query generated by LLM</strong></p>
<p><strong>Generate an answer in LLM based on the SQL query generated by LLM, the results of SQL execution, and the question.</strong>
Pass the last executed SQL query, the results of SQL execution, and the question to LLM to generate an answer.
Unlike the Text-to-SQL prompt, which includes many instructions, this prompt includes fewer instructions but still specifies not to include the DB schema configuration or physical names in the answer.</p>
<pre><code class="language-python">def generate_answer(executed_sql, sql_result, query):
    prompt = f&quot;&quot;&quot;

    Generate an answer based on the provided executed SQL, its result, and the question.
    Ensure the answer does not include information about the database schema or the column names.

    Respond in the same language as the question.
    Executed SQL:
    {executed_sql}

    SQL Result:
    {sql_result}

    Question:
    {query}
    &quot;&quot;&quot;

    return llm.stream_complete(prompt)
</code></pre>
<h3>Execution Result</h3>
<p>Question: Tell me the product of the platform group.
Based on this question and the database schema, LLM will generate SQL as follows:</p>
<p>Execution Result Question: Tell me the product of the platform group.
Based on this question and the database schema, LLM will generate SQL as follows:</p>
<pre><code>SELECT product_name FROM product WHERE group_name LIKE &#39;%プラットフォーム%&#39; AND delete_flag = 0;
</code></pre>
<p>This information and the results of the SQL execution are then passed to the LLM to generate an answer.</p>
<p><img src="/assets/blog/authors/masaki_yamada/text-to-sql/text_to_sql_result1.png" alt="text_to_sql_result1"></p>
<p>This is the vulnerability information retrieved from the ECR scan results.</p>
<p><img src="/assets/blog/authors/masaki_yamada/text-to-sql/text_to_sql_result2.png" alt="text_to_sql_result2"></p>
<p><strong>Generating a JSON object containing an SQL query using LLM based on the output request and schema information from the CMDB chatbot</strong>
Based on the natural language describing the CMDB data to be output as CSV, we will use LLM to generate a JSON object containing the column names to be output and the SQL statement to search for them. The conditions are basically the same as those for the CMDB data search function prompt, but they emphasize the instructions for generating a JSON object according to the template.</p>
<p>Here is the prompt:</p>
<pre><code class="language-python">prompt = f&quot;&quot;&quot;
Generate a SQL query and column names based on the given MySQL database schema, system contexts and question.
Follow these rules strictly:

1. Use MySQL 8.0 syntax.
2. Use `schema_name.table_name` format for all table references.
3. For WHERE clauses:
- Primarily use name fields for conditions, not ID fields
- Use LIKE &#39;%value%&#39; for non-ID fields (fuzzy search)
- Use exact matching for ID fields
- Include &quot;delete_flag = 0&quot; for normal searches
- Use &quot;delete_flag = 1&quot; only when the question specifically asks for &quot;deleted&quot; items

Process:
1. Carefully review and understand the schema.
2. Generate the SQL query using ONLY existing tables and columns.
3. Extract the column names from the query.
4. Double-check query against schema for validity.

System Contexts:
- Company: KINTO Technologies Corporation (KTC)
- System: Configuration Management Database (CMDB)
- Regions: AWS Regions (e.g., Tokyo region = ap-northeast-1)

Interpretation Rules:
- &quot;KTC&quot; or &quot;CMDB&quot; in query: Refer to all information in the database
Examples:
    &quot;Employees in KTC&quot; -&gt; &quot;All users&quot;
    &quot;KTC&#39;s products&quot; -&gt; &quot;All products&quot;
    &quot;Domains on CMDB&quot; -&gt; &quot;All domains&quot;
- Region mentions: Interpret as AWS Regions
Example:
    &quot;ECR repositories in Tokyo region&quot; -&gt; &quot;ECR repositories in ap-northeast-1&quot;

Output Format:
Respond ONLY with a JSON object containing the SQL query and column names:
{{
    &quot;sql_query&quot;: &quot;SELECT t.column1, t.column2, t.column3 FROM schema_name.table_name t WHERE condition;&quot;,
    &quot;column_names&quot;: [&quot;column1&quot;, &quot;column2&quot;, &quot;column3&quot;]
}}

CRITICAL INSTRUCTIONS:
- Output MUST contain ONLY the JSON object specified above.
- DO NOT include any explanations, comments, or additional text.
- DO NOT use markdown formatting.

Ensure:
- &quot;sql_query&quot; contains only valid SQL syntax.
- &quot;column_names&quot; array exactly matches the columns in the SQL query.

Database Schema:
{schema_str}

Question:
{query}
&quot;&quot;&quot;
</code></pre>
<p><strong>Performing validation checks on SQL generated by LLM and Text-to-SQL to allow only <code>SELECT</code> statements</strong>.
<strong>Execute SQL queries generated by LLM</strong></p>
<p>This is the same as with the CMDB data search function.</p>
<p><strong>Outputting a CSV file using the execution results</strong>
Use the SQL results and column names generated by LLM to output a CSV file.</p>
<pre><code class="language-python">column_names = response_json[&quot;column_names&quot;] # LLMで生成したJSONオブジェクトからカラム名を取得
sql_result = execute_sql(response_json[&quot;sql_query&quot;]) # LLMで生成したSQLの実行結果

csv_file_name = &quot;output.csv&quot;
with open(csv_file_name, mode=&quot;w&quot;, newline=&quot;&quot;, encoding=&quot;utf-8-sig&quot;) as file:
    writer = csv.writer(file)
    writer.writerow(column_names)
    writer.writerows(sql_result)

return FileResponse(
    csv_file_name,
    media_type=&quot;text/csv&quot;,
    headers={&quot;Content-Disposition&quot;: &#39;attachment; filename=&quot;output.csv&quot;&#39;}
)
</code></pre>
<h3>Execution Result</h3>
<p>By specifying the content and columns you want to output and posting it in the chat, you can now output a CSV file as shown below.</p>
<p>First, LLM creates a JSON object like the one below from the chat messages and the database schema.</p>
<pre><code class="language-json">{
    &quot;sql_query&quot;: &quot;SELECT service_name, group_name, repo_name, region, critical, high, total FROM ecr_scan_report WHERE delete_flag = 0;&quot;,
    &quot;column_names&quot;: [&quot;プロダクト名&quot;, &quot;部署名&quot;, &quot;リポジトリ名&quot;, &quot;リージョン名&quot;, &quot;critical&quot;, &quot;high&quot;, &quot;total&quot;]
}
</code></pre>
<p>The following is the process of executing SQL based on the above information and outputting a CSV file: <img src="/assets/blog/authors/masaki_yamada/text-to-sql/csv_result1.png" alt="csv_result1"></p>
<table>
<thead>
<tr>
<th>Product name</th>
<th>Division name</th>
<th>Repository name</th>
<th>Region name</th>
<th>critical</th>
<th>high</th>
<th>total</th>
</tr>
</thead>
<tbody><tr>
<td>CMDB</td>
<td>Platform</td>
<td>×××××</td>
<td>ap-northeast-1</td>
<td>1</td>
<td>2</td>
<td>3</td>
</tr>
<tr>
<td>CMDB</td>
<td>Platform</td>
<td>×××××</td>
<td>ap-northeast-1</td>
<td>1</td>
<td>1</td>
<td>2</td>
</tr>
<tr>
<td>CMDB</td>
<td>Platform</td>
<td>×××××</td>
<td>ap-northeast-1</td>
<td>1</td>
<td>1</td>
<td>2</td>
</tr>
</tbody></table>
<h2>Next Steps</h2>
<p>So far, we have utilized generative AI and Text-to-SQL to implement a CMDB data search function and a CSV data output function. However, there is still room for improvement, as outlined below:</p>
<ul>
<li>The CMDB data search function calls LLM twice, which makes it slow.</li>
<li>Weak at answering complex and ambiguous questions<ul>
<li>Natural language is inherently ambiguous, allowing multiple interpretations of a question.</li>
</ul>
</li>
<li>Accurate understanding of schema<ul>
<li>Schema information is complex, and it is difficult to make the system understand the column relationship between the tables.</li>
</ul>
</li>
<li>Addition of context information<ul>
<li>Currently, the first prompt adds minimal context information. In anticipation of the future, when more context information will be added, we are considering methods to transform the question content from a large amount of context information into an appropriate question before the first LLM call. We are also exploring the possibility of fine-tuning with a dataset that includes KTC-specific context information for additional training.</li>
</ul>
</li>
<li>Implementing query routing
Since the APIs called from the front end are divided into two—one for CMDB data search and one for CSV output—we want to unify them into a single API and improve it so that it can determine which operation to call based on the content of the question.</li>
</ul>
<h2>Conclusion</h2>
<p>This time, I discussed the CMDB data search function and CSV output function using generative AI and Text-to-SQL. It&#39;s difficult to keep up with new generative AI-related technologies as they continue to emerge every day. But as AI will be more involved in application development than ever before in the future, I would like to actively utilize any technologies that interest me or that seem applicable to our company&#39;s products.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Migrating from Docker Hub to ECR Public Gallery]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-28-migrate_from_dokcerhub_to_publicecr-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-28-migrate_from_dokcerhub_to_publicecr-en/</guid>
            <pubDate>Tue, 15 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article explains how to switch your setup from pulling public images on Docker Hub to using the ECR Public Gallery instead.]]></description>
            <content:encoded><![CDATA[<h1>Self-Introduction</h1>
<p>Hi, I&#39;m Tetsu. I joined KTC in March 2025. I worked as an infrastructure engineer handling both on-premises and cloud environments. At KTC, I&#39;ve joined the team as a platform engineer.</p>
<p>I&#39;m a big fan of travel and nature, so I usually head out somewhere far during long holidays.</p>
<h1>Overview</h1>
<p>In this article, I’ll walk you through how to update your GitHub Actions workflow to pull public container images—such as JDK, Go, or nginx, from the ECR Public Gallery instead of Docker Hub.</p>
<p>Starting April 1, 2025, Docker Hub will tighten the rules on pulling public container images for unauthenticated users. More specifically, unauthenticated users will be limited to 10 image pulls per hour per source IP address. Learn more <a href="https://docs.docker.com/docker-hub/usage/pulls/">here</a>.</p>
<p>The virtual machines that run GitHub Actions workflows are shared across all users, which means Docker Hub sees only a limited set of source IP addresses. Because of this, the above limits became a bottleneck when building containers with GitHub Actions, so we&#39;ll need to find a workaround.</p>
<h1>Prerequisites</h1>
<p>At our company, we used GitHub Actions with the following configuration to automate container builds (this is a roughly abstracted configuration).</p>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_DockerHub_Arch1.png" alt="Diagram"></p>
<h1>Considering Countermeasures</h1>
<p>We explored a few ways to deal with the Docker Hub pull limit.</p>
<h2>Using a Personal Access Token (PAT) to Log In to Docker Hub and Pull</h2>
<p>You might be thinking, &quot;Why not just authenticate with Docker Hub in the first place?&quot; Fair point. You can generate a Docker Hub PAT and use it in your GitHub Actions workflow with docker login to authenticate. That way, you can get around the pull limit.</p>
<p>Just keep in mind, PATs are tied to individual users. Since our team shares GitHub Actions workflows, linking tokens to individual users isn’t ideal from a license management standpoint.</p>
<h2>Log in to Docker Hub with your Organization Access Token (OAT) and pull</h2>
<p>It&#39;s basically the same method as above, but the key difference is that you&#39;re authenticating with a shared token tied to your OAT.</p>
<p>To use this shared token, you&#39;ll need a Docker Desktop license for either the Team or Business plan.</p>
<h2>Migrating to GitHub Container Registry (GHCR)</h2>
<p>Here, I&#39;ll cover how to pull container images from GitHub Container Registry (GHCR), which is provided by GitHub. By using {{ secrets.GITHUB_TOKEN }} in your GitHub Actions workflow, you can authenticate and pull container images. That said, searching for images can be a bit tricky, especially if you&#39;re trying to compare versions with what&#39;s available on Docker Hub.</p>
<h2>Transition to ECR Public Gallery</h2>
<p>Here&#39;s how you can pull container images from the ECR Public Gallery provided by AWS. Restrictions differ depending on whether you use IAM to authenticate with ECR Public Gallery, but it&#39;s basically free to use.</p>
<p>For unauthenticated users, the following limits apply per source IP address when using the ECR Public Gallery:</p>
<ul>
<li>1 pull per second</li>
<li>500GB of pulls per month</li>
</ul>
<p>On the other hand, authenticated users are subject to the following restrictions on an account-by-account basis.</p>
<ul>
<li>10 pulls per second</li>
<li>Transfers over 5TB/month are charged at $0.09 per GB (the first 5TB is free)</li>
</ul>
<p>You can find more details in the official documentation below.
<a href="https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/public/public-service-quotas.html">https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/public/public-service-quotas.html</a></p>
<p><a href="https://aws.amazon.com/jp/ecr/pricing/">https://aws.amazon.com/jp/ecr/pricing/</a></p>
<blockquote>
<p>If you are not using an AWS account, data transferred from a public repository is restricted based on the source IP.</p>
</blockquote>
<p>The ECR Public Gallery includes official Docker images, which are equivalent to those on Docker Hub. That makes it easier to use in practice and simplifies the migration process.</p>
<h1>Case Comparison</h1>
<p>I reviewed the proposals above and evaluated them based on QCD. Here&#39;s the comparison table:</p>
<table>
<thead>
<tr>
<th>Proposals</th>
<th>Quality</th>
<th>Cost</th>
<th>Delivery</th>
</tr>
</thead>
<tbody><tr>
<td>Log in to Docker Hub using PAT</td>
<td>× <br> - Relies on personal tokens, which isn&#39;t ideal for organizations <br> - No change in convenience from the current setup</td>
<td>〇 No additional cost</td>
<td>〇 Easy to implement with less workload</td>
</tr>
<tr>
<td>Log in to Docker Hub using OAT</td>
<td>○ No change from the current setup</td>
<td>× License costs increase depending on the number of users</td>
<td>× License changes take time to process</td>
</tr>
<tr>
<td>Transition to GHCR</td>
<td>△ Hard to find equivalent images currently used on Docker Hub</td>
<td>〇 No additional cost</td>
<td>〇 Easy to implement with less workload</td>
</tr>
<tr>
<td>Transition to ECR Public Gallery</td>
<td>〇 Easy to find matching currently used on Docker Hub</td>
<td>〇 No additional cost</td>
<td>〇 Easy to implement with less workload</td>
</tr>
</tbody></table>
<ul>
<li>One advantage of using PAT or OAT is that it keeps things as convenient as they are now.</li>
<li>GHCR can be easily set up using GitHub&#39;s {{ secrets.GITHUB_TOKEN }}, but it&#39;s harder to search for container images compared to ECR Public Gallery.</li>
<li>ECR Public Gallery requires some IAM policy changes, but since they&#39;re minor, the extra workload is minimal.</li>
</ul>
<p>Based on these points, we decided to go with the plan of &quot;migrating to ECR Public Gallery,&quot; as it&#39;s low-workload, cost-free, and offers good usability.</p>
<p>Note: Depending on your environment or organization, this option may not always be the best fit.</p>
<h1>Settings for Migrating to ECR Public Gallery</h1>
<p>To migrate, you&#39;ll need to update the container image source, set up the YAML file for the GitHub Actions workflow, and configure AWS accordingly.</p>
<h2>Diagram</h2>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_DockerHub_Arch2.png" alt="Diagram"></p>
<h2>Fixing the Container Image Source</h2>
<h3>Searching for Container Images</h3>
<p>In most cases, you probably define where to pull container images from in files like your Dockerfile or docker-compose.yml. This time, we&#39;ll walk through how to migrate the source of a JDK container image from Docker Hub to ECR Public Gallery using a Dockerfile.</p>
<p>Let&#39;s say your Dockerfile includes a FROM clause like this:</p>
<pre><code>FROM eclipse-temurin:17.0.12_7-jdk-alpine
</code></pre>
<p>Search <a href="https://gallery.ecr.aws/search?page=1">here</a> to check if the image is available on ECR Public Gallery. In this case, search for the official Docker Hub image like <code>eclipse-temurin</code>before the &quot;:&quot; and pick the one labeled &quot;by Docker.&quot;</p>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_image_tag1.png" alt="image_tag1"></p>
<p>Select &quot;image tags&quot; to display the image list.</p>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_image_tag2.png" alt="image_tag2"></p>
<p>Type the tag of the official Docker Hub image (in this case, <code>17.0.12_7-jdk-alpine</code>) into the image tags search field to find the image you&#39;re looking for. Then copy the &quot;Image URI&quot;.</p>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_image_tag3.png" alt="image_tag3"></p>
<h3>Fixing the Container Image Source</h3>
<p>Paste the modified container image URI into the FROM line.</p>
<p>In this case, the updated URI looks like the example below (note the addition of public.ecr.aws/docker/library/ compared to the original).</p>
<pre><code>FROM public.ecr.aws/docker/library/eclipse-temurin:17.0.12_7-jdk-alpine
</code></pre>
<p>With this change, your setup will now pull images from ECR Public Gallery.</p>
<h2>AWS Configuration</h2>
<p>To pull from ECR Public Gallery while authenticated, you&#39;ll need to set up an IAM role and policy.</p>
<h3>IAM Role</h3>
<p>You can follow the steps in GitHub&#39;s official documentation for this.
<a href="https://docs.github.com/ja/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services">https://docs.github.com/ja/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services</a></p>
<p>Start by setting up the identity provider, then create the IAM role.</p>
<h3>IAM Policy</h3>
<p>Create an IAM policy that allows the action to pull from the ECR Public Gallery. I referred to the following docs for this:
<a href="https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/public/docker-pull-ecr-image.html">https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/public/docker-pull-ecr-image.html</a></p>
<pre><code>{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Sid&quot;: &quot;GetAuthorizationToken&quot;,
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: [
        &quot;ecr-public:GetAuthorizationToken&quot;,
        &quot;sts:GetServiceBearerToken&quot;
      ],
      &quot;Resource&quot;: &quot;*&quot;
    }
  ]
}
</code></pre>
<p>Attach this IAM policy to the IAM role you created above.</p>
<h2>Added Login Process to ECR Public Gallery in Github Actions</h2>
<p>To log in to the ECR Public Gallery with authentication, add a login process to the YAML file that defines the Github Actions workflow.</p>
<p>In our setup, we add the following before the Docker Build step.</p>
<pre><code>    ## ECR Public Galleryへログイン
    - name: Login to ECR Public Gallery
      id: login-ecr-public
      run: |
        aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws
</code></pre>
<p>*Since the ECR Public Gallery is located in the us-east-1 region, make sure to explicitly set <code>--region us-east-1</code>.</p>
<h1>Conclusion</h1>
<p>In this article, we walked through how to set up your GitHub Actions workflow to pull public container images (like JDK, Go, nginx, etc.) from the ECR Public Gallery instead of Docker Hub.</p>
<p>Hope this helps with your development and daily tasks!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Streamlining SQL Creation Using GitHub Copilot Agent!]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-25-GithubCopilotAgentを使ってSQL作成を効率化-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-25-GithubCopilotAgentを使ってSQL作成を効率化-en/</guid>
            <pubDate>Mon, 14 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I will explain how to use Github Copilot Agent and Python to smoothly create SQL.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello, this is Hirata from the Analysis Production Group!  </p>
<p>As an analyst, I’d like to talk about how to streamline the SQL creation tasks I handle every day.<br>In this article, I will talk about how I used Github Copilot Agent and &quot;Python&quot; to streamline the task of writing complex SQL consisting of hundreds of lines, its trial-and-error process and results, and future improvements.</p>
<p>【Summary】<br>✔︎ Preparing table information in advance and having the generative AI create SQL<br>✔︎ Implementing a system to automatically execute and check the created SQL using Python<br>✔︎ Having the AI automatically fix errors upon their occurrence to improve work efficiency</p>
<h2>Background: Daily SQL Creation Tasks and Their Challenges</h2>
<p>I face the following problems daily:   </p>
<ul>
<li><p><strong>Complicated interactions with the generative AI</strong><br>  It was necessary to repeatedly explain table information, data types, date formats, and so on to the generative AI each time, which was a time-consuming task.  </p>
</li>
<li><p><strong>Creation of massive SQL</strong><br>  I have to write hundreds of lines of SQL for tasks such as extracting users for marketing purposes or creating data for analysis, with complex processing logic scattered throughout. </p>
</li>
<li><p><strong>Repeated trial-and-error (Loop)</strong><br>  The repetitive cycle of copying and executing the generated SQL, and when an error is encountered, I forward the error log to request a correction…this has become a bottleneck.<br>  If I fix myself, differences from the latest version created by GitHub Copilot arise, and when I request the next fix, it sometimes reverts to a previous state.</p>
</li>
</ul>
<h2>Trial and Error! Building an Automated Workflow Using Generative AI and Python</h2>
<p>I sought to enhance work efficiency by adopting the following process.</p>
<h3>Overview of the Automation Flow</h3>
<ol>
<li><p><strong>Registration of preliminary information</strong><br> I compile the structure of each table, data types, sample data, sample SQL, and processing tips into respective prompt files.  </p>
</li>
<li><p><strong>SQL generation using generative AI</strong><br> I give the generative AI a prompt describing the full flow: &#39;Generate SQL based on table info, save it, and verify execution,&#39; and it automatically produces the SQL file.  </p>
</li>
<li><p><strong>Execution and checking with python</strong><br> I execute the generated SQL using a Python script. If an error occurs, I feed back the error log and prompt automatic correction.</p>
</li>
</ol>
<h2>Key Points of the Approach</h2>
<p>Below are the directory structure and example files I actually built:</p>
<p><img src="/assets/blog/authors/hirata/github_copilot_sql/directory_sample.png" alt="directory_sample.png"></p>
<ul>
<li><p>rules / conversation_rules.prompt.md<br>  Basic conversation rules Generate and save SQL based on table information, then execute the Python file to check whether the SQL is correct. Also, describe the rules for SQL creation and the preferred conversation style.  </p>
</li>
<li><p>tables / <table_name>.prompt.md<br>  Enter table information. By including sample data, have the generative AI judge the characteristics of the data.</p>
<pre><code>## テーブル名
users

## 説明
ユーザー情報のテーブル。 user_idをキーにorderテーブルと紐付け可能

## sample SQL
```sql
select
  user_id as &quot;顧客ID&quot;,
  name as &quot;顧客名&quot;,
  address as &quot;住所&quot;
from
  users
```

## sample data
| 顧客ID | 顧客名   | 住所                             |
| ------ | -------- | -------------------------------- |
| 1      | 山田太郎 | 東京都新宿区西新宿        |
| 2      | 佐藤花子 | 大阪府大阪市中央区難波    |
| 3      | 鈴木次郎 | 愛知県名古屋市中区栄       |
</code></pre>
</li>
<li><p>sql /<br>  Have the generated SQL saved as a file.</p>
</li>
<li><p>utils / query_checker.py<br>  After creating the SQL, execute this Python file to check whether the SQL is correct.</p>
</li>
</ul>
<h2>Example of Actual GitHub Copilot Agent Response</h2>
<p>Here’s a simple example to check its actual operation.</p>
<pre><code>[in] Write an SQL query to count how many emails were sent this month, grouped by user.
</code></pre>
<pre><code>[out]
First, let’s check the schema information, then create an SQL query to analyze email delivery.
 We’ll look for table information related to email delivery.

&quot;› Searched for files matching /prompts/tables/**, 14 matches&quot; 

We’ve located schema information related to email delivery history! 
Let’s review the details and write an SQL query to extract the number of deliveries made this month.

Read メール配信履歴.prompt.md 

Schema information has been confirmed. It appears the data is stored in a table named [メール配信履歴テーブル] (Email Delivery History Table). 
We’ll now create an SQL query to aggregate the number of email deliveries for this month (April 2025). 

Key columns in this table include: 
 • email_id： メルマガID 
 • delivery_timestamp： 配信日時 
 • delivery_status： 配信ステータス 

Now, let’s create the SQL query to aggregate the number of email deliveries for this month and save it to a file named メール配信集計_今月.sql. 


This SQL query will: 
 ・ Filter emails delivered between April 1 and April 30, 2025 
 ・ Include only rows where delivery the status is &quot;Delivered&quot; 
 ・ Calculate the number of deliveries, successes, failures, and total deliveries per user 
 ・ Display overall totals at the end

Would you like to validate the query using Python?
Let’s run the SQL we just created for verification:  

python3 query_checker.py メール配信集計_今月.sql

SQL validation complete!  The query was successfully validated and is executable.  
The test run showed no syntax errors and confirmed a correct execution plan.
</code></pre>
<h2>Results and Future Prospects</h2>
<p>Fortunately, the entire process operated smoothly, marking the first step toward automating SQL creation!<br>That said, the process is not perfect. What challenges and improvements should I work on?</p>
<h3>● Current Challenges</h3>
<ul>
<li><p><strong>Review workload</strong><br>  I tend to miss errors more easily when reviewing AI-generated SQL compared to writing SQL manually.<br>  I am still in the trial-and-error stage of developing a verification mechanism and methods that would enable even non-SQL users to effectively review queries, and I hope to improve this going forward. I also hope that advances in generative AI will help address these challenges!</p>
</li>
<li><p><strong>Checking whether data has been extracted as intended</strong><br>  There are cases where the requirement definitions are incomplete or I fail to verbalize the information in my mind accurately, making it difficult to automatically determine whether the processing aligns with my intent. There is still room for improvement in conveying subtle nuances and intentions.</p>
</li>
</ul>
<h3>● The Next Challenges</h3>
<ul>
<li><p><strong>Automation of number check</strong><br>  As a first step in sophisticating reviews, I would like to implement a function to check whether the number of extracted items is as I intended.</p>
</li>
<li><p><strong>Accumulation of data processing methods that can be called the &quot;secret sauce&quot;</strong><br>  I want to keep adding to the prompts effective data processing techniques that become more obvious as I use them more.</p>
</li>
<li><p><strong>Expansion to analysis automation</strong><br>  Ultimately, I aim to create a system that can automate, to some extent, the workflow from SQL creation to analysis of extracted data!</p>
</li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Web ComponentsとTailwind CSSの併用が難しい理由を少し深掘りする]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-14-web-components-and-tailwind-css-dont-mix/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-14-web-components-and-tailwind-css-dont-mix/</guid>
            <pubDate>Mon, 14 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Web ComponentsのShadow DOMが持つ「スタイルのカプセル化」と、Tailwind CSSの「グローバルなCSS生成」という仕組みが根本的に衝突するため、両者の併用がなぜ技術的に困難で非推奨なのかを解説します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは！
KINTOテクノロジーズのプロジェクト推進グループでWebエンジニアをしている亀山です。
フロントエンドを勉強中です。</p>
<p>モダンなWeb開発においては<strong>コンポーネント指向</strong>が主流となっています。UIを再利用可能な部品に分割することで、開発効率や保守性が向上します。<strong>Web Components</strong>と<strong>Tailwind CSS</strong>は、どちらもコンポーネント指向のフロントエンド開発を支援する強力なツールです。</p>
<p>Web Componentsは、標準仕様に基づいてカプセル化された再利用可能なカスタム要素を作成できる近年注目を集めている技術です。一方、Tailwind CSSは、ユーティリティファーストのアプローチで高速なUIスタイリングを実現するCSSフレームワークです。最近だとTailwind CSSもパフォーマンスが向上したv4が登場しておりアップデートも活発です。</p>
<p>一見すると、これらの技術は相性が良いように思えるかもしれません。「コンポーネントごとにカプセル化されたマークアップとロジック（Web Components）に、ユーティリティクラスで手軽にスタイルを当てる（Tailwind CSS）」という組み合わせは魅力的、、、だと思っていました。</p>
<p>いざ開発を始めると、どうしてもうまくいかない。調べていくとWeb Componentsの根幹をなす<strong>Shadow DOM</strong>と、Tailwind CSSのスタイリングメカニズムには、お互いの根本的な思想が衝突していることがわかりました。本記事では、特にShadow DOMの観点から、両者がなぜ相性が悪いのか、そしてなぜ併用するべきではないのか私が勉強したことをまとめていきます。</p>
<h2>Web Componentsについて</h2>
<h3>Shadow DOMとは何ぞや</h3>
<p>まずWeb Componentsは主に以下の3つの技術から構成されてます。</p>
<ul>
<li><strong>Custom Elements:</strong> 独自のHTML要素（例: <code>&lt;my-button&gt;</code>）を定義するAPI</li>
<li><strong>Shadow DOM:</strong> コンポーネント内部のDOMツリーとスタイルを、外部から隔離（カプセル化）する技術</li>
<li><strong>HTML Templates:</strong> 再利用可能なマークアップの断片を保持するための<code>&lt;template&gt;</code>要素と<code>&lt;slot&gt;</code>要素</li>
</ul>
<p>この中でも、Tailwind CSSとの相性の悪さに直結するのが<strong>Shadow DOM</strong>の存在です。</p>
<p>Shadow DOMを要素にアタッチすると、その要素は<strong>Shadow Host</strong>となり、内部に隠されたDOMツリー（<strong>Shadow Tree</strong>）を持ちます。Shadow Tree内の要素に対するスタイルは、原則としてShadow Treeの外部（メインドキュメントや親のShadow Tree）からは影響を受けません。逆に、Shadow Treeの外部で定義されたスタイルも、原則としてShadow Tree内部には適用されません。</p>
<p>これは、コンポーネントのスタイルが外部のCSSルールに汚染されたり、コンポーネント内部のスタイルが外部に漏れ出たりするのを防ぐ、強力な<strong>スタイルのカプセル化機能</strong>を提供します。これにより、異なるCSS設計手法が混在する環境でも、コンポーネントの見た目が予期せず崩れるといった心配がなくなります。</p>
<p>下記に併用した場合のソースコードの例を記載します。</p>
<pre><code class="language-typescript">class MyStyledComponent extends HTMLElement {
  constructor() {
    super();
    // Shadow DOMをアタッチ（openモードで外部からアクセス可能に）
    const shadowRoot = this.attachShadow({ mode: &#39;open&#39; });

    // Shadow DOM内部のHTML構造
    const template = document.createElement(&#39;template&#39;);
    template.innerHTML = `
      &lt;div class=&quot;container mx-auto p-4 bg-blue-200&quot;&gt; // Tailwindクラスを使用
        &lt;p class=&quot;text-xl font-bold text-gray-800&quot;&gt;Hello from Shadow DOM!&lt;/p&gt; // Tailwindクラスを使用
        &lt;button class=&quot;bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded&quot;&gt;
          Click me
        &lt;/button&gt;
      &lt;/div&gt;
    `;
    shadowRoot.appendChild(template.content.cloneNode(true));

    // ★ ここが問題 ★ Shadow DOM内部にスタイルを適用するには...？
    // 外部のスタイルシートは原則届かない
  }
}
customElements.define(&#39;my-styled-component&#39;, MyStyledComponent);
</code></pre>
<p>上記の例のように、<code>MyStyledComponent</code> の Shadow DOM 内で <code>&lt;div class=&quot;container mx-auto p-4 bg-blue-200&quot;&gt;</code> のようなTailwindクラスを使用しても、デフォルトではこれらのスタイルは適用されません。</p>
<h2>Tailwind CSSについて</h2>
<h3>ユーティリティファーストとグローバルCSS</h3>
<p>Tailwind CSSは、<code>flex</code>, <code>pt-4</code>, <code>text-blue-500</code> といった低レベルなユーティリティクラスをHTMLに直接記述することで、高速にUIを構築するアプローチを採用しています。</p>
<p>ビルドプロセスにおいて、TailwindはプロジェクトのHTML、JavaScript、TypeScriptファイルなどをスキャンし、使用されているユーティリティクラスに対応するCSSルールを生成します。生成されたCSSは、通常、<strong>グローバルな単一のスタイルシート</strong>として出力され、HTMLドキュメントの<code>&lt;head&gt;</code>などに読み込まれます。</p>
<p>例えば、HTMLに <code>&lt;div class=&quot;flex pt-4&quot;&gt;</code> があれば、Tailwindは以下のようなCSSルールを生成し、グローバルスタイルシートに含めます。</p>
<pre><code class="language-css">/* Tailwindによって生成されるCSS（の例） */
.flex {
  display: flex;
}
.pt-4 {
  padding-top: 1rem;
}
</code></pre>
<p>このTailwindのスタイリングメカニズムにおける重要な点は、CSSルールが<strong>グローバルスコープ</strong>で定義されるという点です。</p>
<h2>2つの絶望的な相性の悪さ</h2>
<h3>Shadow DOMのカプセル化 vs. Tailwindのグローバルスタイル</h3>
<p>ここで問題の核心部分です。</p>
<ul>
<li><strong>Shadow DOM</strong> は、内部の要素に外部のスタイルが適用されないように<strong>カプセル化</strong>する</li>
<li><strong>Tailwind CSS</strong> は、使用されているユーティリティクラスに対応するCSSルールを<strong>グローバルスコープに生成</strong>する</li>
</ul>
<p>この二つは根本的に矛盾します。Tailwindがグローバルに生成した<code>.flex { display: flex; }</code>のようなCSSルールは、Shadow DOMの境界を越えてShadow Tree内の要素に到達しないのです。</p>
<p>先ほどのTypeScriptの例で、<code>&lt;div class=&quot;container mx-auto p-4 bg-blue-200&quot;&gt;</code> にTailwindのスタイルが当たらないのは、これらのクラスに対応するCSSルールがShadow DOMの外部（メインドキュメントのグローバルスコープ）に存在し、Shadow DOMがそのルールの適用をブロックしているからです。</p>
<p><strong>Tailwind CSS v4について補足:</strong> Tailwind CSS v4では、新しいエンジンによるパフォーマンス向上などが謳われていますが、基本的なスタイリングのメカニズム（プロジェクトファイルをスキャンしてユーティリティクラスに対応するCSSをグローバルに生成する）という点では変わりません。したがって、v4を使用してもShadow DOMとの相性の悪さは解消されません。</p>
<h2>どうにかできんのか？（解決策はあるのか？）</h2>
<p>この問題を解決するために、色々調べていると、この衝突の回避策はあるにはあるが、どれもWeb ComponentsやTailwind CSSのメリットを損なう、あるいは実装コストが非常に高いものになり、根本的な解決策は見つかりませんでした。苦し紛れなものですが回避策をいくつか紹介します。</p>
<h3>ビルドしたTailwindのCSSをShadow DOM内にコピー＆ペーストする</h3>
<p>各Web ComponentのShadow DOM内に、そのコンポーネントで使用しているTailwindクラスに対応するCSSルールを手動、あるいはビルドツールで抽出して<code>&lt;style&gt;</code>タグとして埋め込む方法です。</p>
<p><strong>デメリット:</strong></p>
<ul>
<li>非常に手間がかかり、メンテナンス性が低い</li>
<li>コンポーネントごとに重複したCSSを持つことになり、ファイルサイズが増大する</li>
<li>TailwindのJITコンパイル（使っているクラスだけを生成する）のメリットが活かせない</li>
<li>Tailwindの運用ワークフロー（設定ファイル、プラグインなど）と乖離する</li>
</ul>
<h3>Shadow DOMを使用しない</h3>
<p>Web ComponentsでShadow DOMを使わず、Light DOMに要素を配置する方法です。この場合、要素はメインドキュメントのDOMツリーの一部と見なされるため、グローバルなTailwindスタイルが適用されます。</p>
<p><strong>デメリット:</strong></p>
<ul>
<li>Web Componentsの最大のメリットである「スタイルのカプセル化」が失われ、外部のCSSがコンポーネントに影響を与えたり、コンポーネントのスタイルが外部に漏れ出たりする可能性が生じてコンポーネントの独立性が損なわれる</li>
</ul>
<p>これらのアプローチを見てもわかるように、Shadow DOMによる強力なカプセル化と、グローバルスタイルシートに依存するTailwind CSSは、根本的に思想が異なるため、無理に併用しようとするとどちらかの技術のメリットを大きく損なうことになります。</p>
<h2>結論：Web ComponentsとTailwind CSSは併用するべきではない</h2>
<p>これまで見てきたように、Web Components（特にShadow DOMを利用する場合）とTailwind CSSの併用は、両者のメリットを打ち消し合ってしまうため、基本的には避けるべきです。</p>
<p>その理由は、2つの技術が持つスタイリングの<strong>根本的な思想・仕組みが衝突</strong>するからです。</p>
<ul>
<li><strong>Web Components (Shadow DOM)</strong> は、コンポーネントのスタイルを外部から完全に**隔離（カプセル化）**することを目的としている</li>
<li>一方、<strong>Tailwind CSS</strong> は、ユーティリティクラスに対応するCSSを<strong>グローバルなスタイルシート</strong>として生成し、ページ全体に適用することを前提としている</li>
</ul>
<p>このため、Tailwindが生成した便利なユーティリティクラスのスタイルは、Shadow DOMの強固な壁を越えることができず、コンポーネント内部には適用されません。</p>
<p>回避策は存在するものの、いずれもコンポーネントの独立性を犠牲にしたり、開発の複雑さを増大させたりと、本末転倒な結果を招きがちです。それぞれの技術の長所を最大限に活かすためには、併用しないという選択が賢明と言えるでしょう。</p>
<p>今回の記事が、Web ComponentsとTailwind CSSの併用を検討されている方の参考になれば幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/soju.kameyama/20250714/wordcloud_coverimage_web-components-and-tailwind-css-dont-mix.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Re-engaging Dormant Users with Automated Emails Using React Email]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-24-react-email-user-retention-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-24-react-email-user-retention-en/</guid>
            <pubDate>Fri, 11 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[In order to re-engage dormant users of our app, we have implemented initiatives using React Email. This article explains how we implemented React Email and its effects.]]></description>
            <content:encoded><![CDATA[<p>Hello, I am Udagawa, an engineer working on <a href="https://ppap.kinto-jp.com/prismjapan">Prism Japan</a>. I would like to introduce you to our marketing initiatives that use React Email to automatically send emails.</p>
<h2>Challenges We Faced in Our Marketing Initiatives</h2>
<p>Prism Japan was launched in August 2022, and since the beginning of the service, it has acquired users through various marketing initiatives. However, there is no guarantee that once we acquire users, they will continue to use the service. Although it has been about two and a half years since the service started, the number of dormant users is still on the rise.</p>
<p>To address this issue, we implemented a re-visitation (re-engagement) initiative using push notifications, but we faced several challenges.</p>
<ul>
<li>Push notifications do not reach users who have turned off their notification settings.</li>
<li>Even if we send push notifications encouraging users to revisit the app, they do not reach users who have uninstalled it, so we cannot achieve the desired effect.</li>
</ul>
<p>In fact, the push notification consent rate at only about 48%, and considering this rate along with uninstalled users, the number of users who actually receive notifications is quite limited. Furthermore, because they receive notifications from other apps as well, ours tend to get buried among them. In this way, there were limits to the effectiveness of our re-visitation initiative using push notifications.</p>
<p>On the other hand, we ask users to register their email addresses at the time of their membership registration. The consent rate for emails registered in this way remains at a very high level of about 90%. Even if users have deleted the app, emails can still reach those who have not canceled their membership, making this a suitable marketing channel for the re-visitation initiative.</p>
<p>However, from an operational perspective, there were several challenges to this initiative.</p>
<p>First, marketing resources were limited, with a single staff member handling a wide range of tasks from planning initiatives to managing social media. Creating email content requires a lot of man-hours for manually tabulating rankings, selecting appropriate images, designing layouts, and so on. Considering the limited resources of the marketing staff, frequent delivery was difficult. Therefore, although we recognized frequent email delivery as an effective marketing method, it was not realistic due to the operational burden.</p>
<h2>Using React Email To Automate Email Creation</h2>
<p>Thus, we came up with the idea of <strong>automating the entire process from email creation to delivery</strong>.</p>
<p>If we can build a system that automatically collects the information to be displayed in the content, creates email content automatically, and sends emails automatically at scheduled dates and times with a predetermined layout, we can send emails tailored to users even with limited human resources.</p>
<p>However, we, as engineers, struggled with how to implement the process of automatically creating HTML emails.</p>
<p>If we implement processing that directly manipulates HTML, reusability will become low, and issues such as differences in display depending on the receiving mailer will occur. Looking ahead to future content replacement, it is necessary to implement a solution with high reusability that allows flexible addition of new content.</p>
<p>Amid these challenges, we discovered a library called “React Email.”</p>
<p>This “React Email” has the following features:</p>
<ul>
<li>Ability to create HTML emails using JSX</li>
<li>Real-time preview function</li>
<li>High reusability through componentization</li>
</ul>
<p>What is especially important is that reusable componentization allows for easy addition of new content when its creation is required. Because React Email is written with React, dynamically replacing the content becomes easier. These advantages enable the <strong>delivery of personalized content</strong> at low cost by dynamically replacing content based on user behavior and interests. Instead of sending the same content to all users simultaneously, delivering content tailored to each user&#39;s interests can be expected to achieve <strong>high revisit rates and improved engagement</strong>.</p>
<p>By utilizing React Email, we gained a clear prospect of effectively resolving the challenges in our email delivery initiatives, enabling us to move forward with efficient user re-visitation initiatives.</p>
<h2>HTML Generation Using React Email</h2>
<p>From here, I will cover the implementation details. In the implementation, we use React Email to generate the HTML for emails. We adopted a process in which HTML is generated from JSX using the render function of React Email.</p>
<p>First, we created the following components:</p>
<pre><code class="language-javascript">import React from &quot;react&quot;;

const AppCheckSection = () =&gt; {
  return (
    &lt;div style={{ padding: &quot;20px 0&quot;, borderBottom: &quot;1px dashed #cccccc&quot; }}&gt;
      &lt;div&gt;
        &lt;p&gt;
          詳しいスポットの情報やアクセス情報はアプリで確認してみましょう。
          &lt;br /&gt;
          他にも、アプリではあなたにだけのおすすめスポットを掲載中！
        &lt;/p&gt;
        &lt;a
          style={{
            padding: &quot;10px 70px&quot;,
            background: &quot;rgb(17,17,17)&quot;,
            borderRadius: &quot;5px&quot;,
            textAlign: &quot;center&quot;,
            textDecoration: &quot;none&quot;,
            color: &quot;#fff&quot;,
            display: &quot;inline-block&quot;,
            marginBottom: &quot;10px&quot;,
          }}
        &gt;
          &lt;span&gt;アプリをチェック&lt;/span&gt;
        &lt;/a&gt;
        &lt;br /&gt;
        &lt;a href=&quot;https://deeplink.sample.hogehoge/&quot;&gt;
          うまく開かない方はこちら
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default AppCheckSection;
</code></pre>
<p>In this way, we created components for constructing emails.</p>
<p>Then, simply combining the created components in the parent component completes the email template.</p>
<pre><code class="language-typescript">import React from &#39;react&#39;;
import AppCheckSection from &#39;../shared/AppCheckSection&#39;;
import FooterSection from &#39;../shared/FooterSection&#39;;
import RankingHeaderSection from &#39;./RrankingHeader&#39;;
import RankingItems from &#39;./RankingItem&#39;;

export type RankingContents = {
  imageURL: string;
  name: string;
  catchPhrase: string;
};

export type WeeklyRankingProps = {
  areaName: string;
  contents: RankingContents[];
};

const WeeklyRanking: React.FC&lt;WeeklyRankingProps&gt; = ({ areaName, contents }) =&gt; {
  return (
    &lt;div style={{ backgroundColor: &#39;#f4f4f4&#39;, padding: &#39;20px 0&#39; }}&gt;
      &lt;div&gt;
        &lt;RankingHeaderSection /&gt;
        &lt;RankingItems areaName={areaName} contents={contents} /&gt;
        &lt;AppCheckSection /&gt;
        &lt;FooterSection /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default WeeklyRanking;
</code></pre>
<p>To generate the email HTML, React Email&#39;s render function is used.</p>
<p>Using fetchRegionalRankingData, it is possible to obtain different content information for each residential area and create emails accordingly.</p>
<pre><code class="language-typescript">import { render } from &#39;@react-email/render&#39;;
import { WeeklyRankingEmail } from &#39;../emails/weekly-ranking&#39;;
import { fetchRegionalRankingData } from &#39;./ranking-service&#39;;

export async function generateWeeklyRankingEmail(areaName: string): Promise&lt;string&gt; {
  const contents = await fetchRegionalRankingData(region);
  
  const htmlContent = await render(await WeeklyRanking({ areaName, contents }));  
  return emailHtml;
}
</code></pre>
<p>The HTML generated by the render function is used as the e-mail body sent via the SaaS service&#39;s API.</p>
<p>In batch processing, ECS is activated at the timing scheduled by EventBridge to execute the email creation and sending processes.</p>
<p>The content of emails actually sent is like the following one:</p>
<p>   <img src="/assets/blog/authors/udagawa/react-email-user-retention/react-email-user-retention-02.png" alt="email example"></p>
<p>The images show content focused on the Kanto region, but we implement a system capable of flexibly changing the content according to the region set by the user. Therefore, if the user’s residence is Osaka, the ranking for the Kansai region will be delivered to the user by email.</p>
<p>   <img src="/assets/blog/authors/udagawa/react-email-user-retention/react-email-user-retention-04.png" alt="react email preview"></p>
<p>React Email has a preview function that allows us to proceed with email implementation just like when developing normally with React. Implementation without the preview would be extremely difficult, so this function was extremely helpful. By leveraging this function, we were able to proceed with implementation work while checking layouts with the marketing staff.</p>
<p>   <img src="/assets/blog/authors/udagawa/react-email-user-retention/react-email-user-retention-03.png" alt="email used component"></p>
<p>Through componentization, we structured various elements such as footers and app launch promotion sections, in addition to ranking, as reusable parts. By mixing existing components also when creating new content, efficient and consistent email delivery becomes possible.</p>
<p>Scheduled email delivery may result in repeatedly sending similar content, which can lead to a decline in user interest or, in the worst case, the emails may be marked as spam and rejected. Even in an automated system, <strong>the delivery of content that continuously attracts user interest</strong> should be required.</p>
<p>Considering such a situation, we believe that a highly reusable design through componentization, which enables quick changes to the content to be delivered, is important.</p>
<h2>Effect of Automated Email Delivery</h2>
<p>As a result of starting automated email delivery using React Email and batch processing, the number of installations increased starting around the day we started the delivery (February 22). We believe that this has made dormant users who saw our emails become interested in the app and <strong>encouraged them to reinstall it</strong>. In addition, the number of daily active users (DAUs) around email delivery dates significantly increased and has shown a sustained upward trend since the start of the automated email delivery initiative.</p>
<p>In this way, we succeeded in encouraging dormant users, including those who had uninstalled the app, to revisit. <img src="/assets/blog/authors/udagawa/react-email-user-retention/react-email-user-retention-01.png" alt="email result"></p>
<h2>Summary</h2>
<p>By automated email delivery utilizing React Email, we succeeded in <strong>reviving dormant users</strong> and <strong>increasing DAUs</strong> without manual intervention.</p>
<p>Many marketing staff may be struggling with the issue of having many dormant users and limited marketing resources in app development. Automation of email creation using React Email reduces the burden of coming up with email content weekly and enables <strong>efficient and effective marketing activities</strong>. Furthermore, we found &quot;React Email” highly useful for continuously improving and <strong>quickly releasing</strong> content.</p>
<p>We found that, even in today’s world with diversified communication methods, email delivery can still function effectively as a marketing channel if we deliver <strong>content aligned with user interests</strong>.</p>
<p>If you&#39;re struggling with stagnant revisit rates or looking for ways to revive dormant users, this approach is definitely worth considering.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[KINTO Unlimited appのサウンドデザイン]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-11-app_sound_design/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-11-app_sound_design/</guid>
            <pubDate>Fri, 11 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[アプリ体験を高めるためのサウンド導入と実装プロセスのご紹介]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>KINTO Unlimited appのクリエイティブを担当している中村屋です。この度、アプリにサウンドを実装することになったので、そのプロセスや考え方などをお話ししていこうと思います。</p>
<p>KINTO Unlimitedプロジェクトのビジネス担当の方から、アプリを継続利用するユーザーを増やすべく、「つい気持ちよくなって続けてしまうような音」を入れたりできないですかねと軽い相談（クリエイティブに完全にお任せ！）がありました。</p>
<p>サウンドを搭載したアプリは様々にありますが、なんかいい感じって思うアプリってサウンドも洒落てますよね？ここぞとばかりに「<strong>ほほう、あのサウンドデザイナー／アーティストに依頼して、、</strong>」と思ったのも束の間、オリジナルで作る予算はないよと。。大きく思い描いたのに残念。。</p>
<p>とはいえ、クオリティは譲れなかったので、品質が高いと言われるサウンドサービスを色々調べた結果、Spliceという有料サービス（<a href="https://splice.com/sounds">https://splice.com/sounds</a>）を利用することにしました。</p>
<p>メインの業務の傍らの案件かつ、経験があまりない領域ではありましたが、</p>
<ul>
<li>膨大にあるサウンドからどうやって選んで組み立てるのか？</li>
<li>サウンド実装までのデザインプロセスを知りたい</li>
<li>クリエイティブが開発にどう関わってんの？</li>
</ul>
<p>と思う方はぜひ見ていってください。</p>
<h2>Unlimitedのサウンド世界観は？</h2>
<p><img src="/assets/blog/authors/matsuura/250711/2_app_image.jpg" alt="app_image"></p>
<p>まずはディレクションです。</p>
<p>ここが後工程に大きく響いてくるキモとなる部分です。アプリのサウンド世界観を言語化し説得力のある進め方にすること、サウンドの検討に判断軸を持ち、膨大な時間をかけないようにするためにも必要です。</p>
<ul>
<li>スコープを定義：実験的な実装という位置付けのため、最小単位での特定の一連の体験に実装します。ユーザー操作のフィードバックSE（Sound Effect）、BGMをターゲットとします。</li>
<li>Unlimitedのサービスは、購入後のクルマが、技術とともにアップグレードしていく新しいクルマの持ち方であり、そのキーワードは<strong>未来的・革新的・最適化・スマート・安心感</strong>です。その「らしさ」をサウンドに込めます。</li>
</ul>
<p>ここから連想するサウンドは、「<strong>環境に溶け込みながらもモダンで心地よいデジタルサウンド</strong>」（<strong>仮説</strong>）でした。アイデアや表現の幅を狭めないよう、遊びが効くレベルで仮説コンセプトを立てていきます。心が落ち着く要素もありながら、クールでハリのある感じ、、とイメージを膨らませながらサウンドを検索していきます。</p>
<p>そしてすぐに、これではNGだと気づきます。音のプロでない者が感覚で選んだサウンド群が、調和の取れた一貫性のあるものになるわけがないと。しかし、、ありました！質を担保し、効率的な方法が。</p>
<p>Spliceには<strong>サウンドパック</strong>が提供されていて、ゲームやUIなどのアプリ向けのパックがあったのです。そこで、モダンでSFの要素がありつつ心地よいテーマを持つサウンドパックを選び、サウンド候補を選んでいきます。そして、Adobe Premiere Proを用いてアプリの操作動画にSEを当てこみ、さらに候補を絞り込みます。</p>
<p>:::message
Tips：制作サウンドデザイナー名がクレジットに入っているサウンドパックが特に優れています。コンセプトが一貫して明確、音質・音量が安定（ノーマライズ）していて、余計な調整なく実装しやすいと感じました。
:::</p>
<h2>方向転換</h2>
<p>完成度を高めず、早めにプロジェクトメンバーにバリエーションを聞いてもらって、方向性の意見をもらいます。基本はいいねという意見でしたが、「いいんだけど、もうちょっと俗的な感じの方がいいのかな？」という意見に目が止まりました。</p>
<p>アプリ・サービスに深く関わっているメンバーからの意見です。この感覚をクリエイティブが拾い上げ、言葉にできない違和感を解釈する必要があると思いました。</p>
<p>俗的＝一般的、洗練されていない、ありきたりということですが、言葉通りに受け取らず、デザイン的に思考します。洗練された未来的なサウンドは適していない→ユーザーに提供する価値はそこではない→<strong>ビジョンよりもリアルなユーザー</strong>に寄り添い、共感を呼ぶべきであると解釈します。</p>
<p>冒頭の「アプリ継続利用促進のため」にもあるように、初心者向けコンテンツやゲーミフィケーションをベースにした施策などをアプリでは行ってきており、一方的な価値提供ではなく、リアルなユーザーにフォーカスした利用促進を行っています。</p>
<p>当初考えたコンセプトは間違いではないが、アプリコンセプトが緩やかに変化しており、それに伴ったアップデートが必要ということがわかりました。そこで、サウンドコンセプトを「<strong>最新の技術を親しみやすく、共に成長していく安心感のある体験</strong>」<strong>の提供</strong>と再構築しました。
<img src="/assets/blog/authors/matsuura/250711/3_sound_concept.jpg" alt="sound_concept"></p>
<p>このコンセプトで再度サウンドを練り直したサンプルの一部がこちら。
<a href="https://www.youtube.com/watch?v=oeGNNqRJs50">https://www.youtube.com/watch?v=oeGNNqRJs50</a>
自分の記憶にあるような親しみのあるサウンド、遊び心が効いてクセになりそうなイメージになったのではないでしょうか。</p>
<h2>実装する前に</h2>
<p>決まったサウンドデータをエンジニアさんに渡してあとはよろしく！では終わりません。ユーザー体験を形作る上で、ここからの設計フェーズも非常に重要です。</p>
<p>例えば、アニメーションの見せどころの視覚変化にサウンドが密接に同期するととても気持ちいいですよね（例：コインがキラッと光った瞬間に音が鳴る）。逆に、ここにズレがあると違和感が生まれ、ストレスを与えます。</p>
<p>また、ボタンを押した際に鳴るSEを考えた時に、押した瞬間0.00秒ジャストに鳴ると硬い印象になり、数十ミリ秒のわずかな遅延再生させるとより自然で洗練された印象になります。※テーマによって考え方が変わります。</p>
<p>このような考え方を取り入れて、どこで・いつ・どのように再生されるのかを、再現性を担保できるように仕様書にまとめます。（まずは、フィジビリを考えすぎずユーザー体験の理想として落とし込んでいきます）特にサウンドの専門アプリではないので、専門的な概念まで踏み込まず、以下のように実装仕様書をまとめます。</p>
<ul>
<li>管理ID／サウンドファイル名／対象画面</li>
<li>再生トリガー：「〇〇ボタンタップ時」「△△アニメーション表示時」など、どのようなユーザー操作やイベントで音が鳴るかを明記。</li>
<li>ループ再生の有無</li>
<li>音量：BGMやキャンセル音などは抑えめになど、サウンドの意味や関係性を元に設計。</li>
<li>遅延再生：この項目はトリガーを起点として再生のタイミングを調整できるので、トリガー内容が複雑になるのを防ぎます。</li>
<li>フェードイン：音の始まりの調整、SEとBGMの競合回避に役立てることもできます。</li>
<li>フェードアウト：BGMが突然途切れるのではなく、余韻を残して停止させると丁寧な印象です。</li>
<li>備考：再生タイミングの意図など、疑問が生まれないように記載していきます。
<img src="/assets/blog/authors/matsuura/250711/5_explanatory_note1.jpg" alt="explanatory_note1.jpg"></li>
</ul>
<p>そして、データについてです。アプリがインストールされるデバイスはユーザーのもの、つまりユーザーデバイスに負荷をかけないよう、アプリ容量には気を配らなければなりません。以下のデータ仕様は最上位品質ではないものの、高品質なラインで定めています。</p>
<ul>
<li>SE：WAV形式 または AAC形式*</li>
<li>BGM：AAC形式
*重要なサウンド（ブランドSE）や頻度の高いSEはWAV推奨、200KB超え+1秒以上のSEはAACを検討
AAC圧縮後の基本ライン：ステレオ音源256 kbpsの可変ビットレート（VBR）、サンプリングレート44.1/48kHz</li>
</ul>
<p>SEは瞬間に再生される用途なので、データがそのまま再生されるWAV（非圧縮・最高品質）が適しており、AAC（圧縮）は再生にデコード処理が走るためほんの少し遅延が起きるようです。※近年のスマートフォンの処理では、プロ以外にはその差は感じられないと思われますが。</p>
<p>この他にもオーディオの割り込み、プリロード（事前メモリ読み込み）など事細かに定義しなければならないこともありますが、ある程度のところでプロデューサー・エンジニアさんと共有し、詳細を詰めていきます。よくわからないところは悩む前に知見のある人と一緒に前に進める、内製開発のメリットです。</p>
<h2>おわりに</h2>
<p>開発の内容としてはまだ続きますが、一つの区切りとして、ここまでとさせていただきます。</p>
<p>熟知しない領域でここまで進められたわけは、ChatGPTをはじめとしたAIの活用でした。必要な観点を洗い出し、壁打ち相手として利用していき、説得力のある形になるまで考えを深めることができました。しかし、サウンド理論など掘っても掘っても全く底が見えない。。そこで、私には<strong>社内で共通認識を持つことのできる範囲での定義</strong>をすることが重要でした。専門的になりすぎず、プロジェクト内で理解されやすい仕様書を作ることやコミュニケーションに気を配っています。（例えば、音量はdBFS値を使わず、基準点を設けて相対スケール値で表し、理解しやすい0.0-1.0の数値で定義するなど）</p>
<p>それでもなお、サウンドは非常に奥深く、ここでは欠けた内容も多いことは承知しています。また、音楽は人によって（もっというとその時の精神状態によって）感じ方が異なる感性の塊のようなものです。そういう類のものをユーザー体験の中に落とし込んでいったプロセスを紹介しました。</p>
<p>最後に、KINTOテクノロジーズの開発ではMVP（Minimum Viable Product）の考えが浸透していますので、共感を得られれば、アイデアをスピーディに組み立てて開発まで進めることができます。そして、ユーザーの反応を見ながら、アップデートを繰り返していくことができます。これはその一つの事例でもあり、そのような開発にクリエイティブがどう関わっているか、その一端を感じていただけたなら嬉しく思います。最後までお読みいただき、ありがとうございました。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/matsuura/250711/1_unlimited_sound_top.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Corporate Website Renewal: Insights from the Design Field]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-17-corporate-site-renewal-story-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-17-corporate-site-renewal-story-en/</guid>
            <pubDate>Thu, 10 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A behind-the-scenes look at how we created a website that captures the spirit of KTC with recruitment in mind, from the designers' perspective.]]></description>
            <content:encoded><![CDATA[<p>I&#39;m feeling a bit nervous writing this blog after ages. I&#39;m Sugimoto from the Creative Office at KINTO Technologies (KTC for short). </p>
<p>In 2024, on our third year since the company was founded, we gave our corporate website a full redesign. Three years after our founding, the project began when the HR team requested a new recruitment-focused website to help attract more people to join us in the future. </p>
<p>Since the corporate website is centered around recruitment, we interviewed not only the Human Resources team but also members of management to understand the company&#39;s direction, as well as engineers from the Developer Relations Group to capture voices from the front lines. </p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/01_introduction.jpg" alt="introduction"> </p>
<p>Questions like “What kind of people does the company truly want?” and “Who do we genuinely want to work with?” guided our conversations. As we listened to various perspectives—along with their challenges and aspirations—the purpose of the corporate website gradually came into focus. </p>
<p>&quot;Let&#39;s create a website that shows what KTC is all about to engineers and creators who stay curious about technology, keep up with the latest trends, and take initiative.&quot; That goal shaped our concept. </p>
<p>The concept is &quot;The Power of Technology &amp; Creativity.&quot; We picked this word to reflect our drive to lead Toyota&#39;s mobility services through technology and creativity. </p>
<p>Setting a concept might feel like an extra step, but it gives everyone a shared point to return to, especially important when different roles are involved and the project starts to drift. </p>
<p>&quot;Do we really need that feature?&quot; &quot;Can&#39;t we make it more engaging?&quot; With this concept in place, even questions from a different angle make it easier to say, &quot;That’s why we&#39;ll do it.&quot; Or, &quot;That&#39;s why we won&#39;t.&quot; </p>
<h1>Personality Settings</h1>
<p>The next step for us was to define a brand personality, a clear picture of what kind of person the company would be, and how it would behave if it were human. (More on brand personality <a href="/posts/2025-04-17-corporate-site-renewal-story/#%E2%80%BB%E3%83%96%E3%83%A9%E3%83%B3%E3%83%89%E3%83%91%E3%83%BC%E3%82%BD%E3%83%8A%E3%83%AA%E3%83%86%E3%82%A3%E3%81%A8%E3%81%AF">below</a>.) </p>
<p>Creating a brand personality from the ground up takes time and effort, often requiring input from across the company. However, since the main goal of launching the corporate website was recruitment, speed was a priority. So we built on what was already in place within our company: our vision, values, culture, and work attitude. </p>
<p>The personality we landed on for KTC is, simply, &quot;creator.&quot; As creators, we define ourselves as those who use technology and creativity to build the best products for our users: products that are intuitive, clear, thoughtful, and useful. </p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/02_personality.jpg" alt="personality"> </p>
<h1>Creating an Exciting Mood Board</h1>
<p>With the brand personality set, the next step is figuring out how to reflect that in the design of the corporate website. So, one more step! Before the lead designer jumped in, the whole Creative Office came together to build a mood board. </p>
<p>This gives us a visual anchor to return to; just like the concept itself, which helps keep things on track and makes the rest of the process smoother.</p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/03_moodbord.jpg" alt="moodboard"> </p>
<p>Each designer brought in visuals they felt captured the KTC vibe, and the mood board session turned into a lively exchange. </p>
<p>Creating a mood board also led to some new discoveries. I imagined the output would reflect the vibe of a shiny, fast-paced California tech company. But when we shifted our perspective to ask, &#39;Who are we, really?&#39;, the answer became clear: we are (or aspire to be) a professional engineering group that embraces the spirit of Japanese craftsmanship rooted in the Toyota Group’s <i>gemba</i> philosophy. </p>
<p>The mood board was inspired by globally recognized modern systems and our defined brand personality. Our goal was to create a corporate website that offered high usability while visually expressing our brand identity. </p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/04_base.jpg" alt="base"> </p>
<h1>Achieving a Jump in Creativity and Efficiency</h1>
<p>By clearly defining the website’s &quot;personality,&quot; &quot;mood,&quot; and &quot;purpose,&quot; everything came together with a strong sense of consistency—from the photo tone and interview content to the copywriting and implementation. It really highlighted how that clarity can enhance both creativity and efficiency. It also made it easier to explain the design logically to non-designers, helping us put even abstract ideas into words. </p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/05_jumpup.jpg" alt="jumpup"> </p>
<h1>Honored to Receive International Recognition</h1>
<p>Our newly redesigned corporate website has received several international web design awards, including the prestigious CSS Design Awards. </p>
<p>We&#39;d love for you to take a look. And if something clicks, we hope it sparks your interest in us! </p>
<p>Check out the website here!
<a href="https://www.kinto-technologies.com/">https://www.kinto-technologies.com/</a></p>
<h4>※What is brand personality?</h4>
<p>It represents what kind of traits and personality a brand (or company) would have if it were a person. This is called its archetype. We use a common framework that breaks &quot;personality&quot; into 12 types. This helps us explore a company&#39;s character, thinking, behavior, and distinctive features. Having a clear brand personality makes it easier to present a consistent image. Even if the audience isn’t consumers, you can still leave a strong and unified impression on your target—whether it’s through a corporate website like this or event giveaways.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aya_sugimoto/250417/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[新たなステージへ！Osaka Tech Lab 2.0始動]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-10-OsakaTechLab2/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-10-OsakaTechLab2/</guid>
            <pubDate>Thu, 10 Jul 2025 09:00:00 GMT</pubDate>
            <description><![CDATA[新たなステージへ！Osaka Tech Lab 2.0始動]]></description>
            <content:encoded><![CDATA[<p>こんにちは！KINTOテクノロジーズ株式会社の大阪採用担当、Okaです。
このたび、私たちOsaka Tech Labは新しいオフィスに移転しました。この記事では、その舞台裏と新オフィスの魅力をお届けします！</p>
<h2>Osaka Tech Labとは</h2>
<p>Osaka Tech Labは、2022年に心斎橋で開設した西日本のエンジニアリング拠点です。このたび、JR大阪駅直結のビルに移転し、さらにアクセスが良くなりました。</p>
<p>ソフトウェア開発、クラウドインフラ、データ分析など、さまざまな分野のエンジニアが集まり、自社プロダクトの開発・改善に取り組んでいます。</p>
<h2>みんなで作り上げた「Osaka Tech Lab 2.0」</h2>
<p><strong>コンセプト誕生の経緯</strong></p>
<p>オフィス移転をきっかけに、「Osaka Tech Lab 2.0」プロジェクトがスタート！
このプロジェクトは、最初から誰かが用意していたものではありません。メンバー自身が「こんな場所にしたい」と想いを持ち寄って、みんなで作り上げたものです。</p>
<p>その中で生まれたのが、「集GO！発SHIN！CO-LAB」というコンセプト。</p>
<p>「単なる業務スペースではなく、大阪らしさや文化を活かしながら、新しい価値を“みんなで創っていく”場にしたい。」そんな気持ちを込めて、これまでの活動を振り返りながら、みんなで名前をつけました。</p>
<p>![](/assets/blog/authors/oka/osakarenewal/1.png =600x) </p>
<p><strong>「この指とまれ」という文化</strong></p>
<p>Osaka Tech Labでは、もうひとつ、私たちらしい合言葉が生まれました。それが「この指とまれ」です。やりたいことがある人が、「やってみたい」と声をあげる。そこに、「いいね」「一緒にやろう」と自然に人が集まってくる。そんな場面が、私たちの周りではよくあります。</p>
<p>この動き方を、みんなで「この指とまれ」と呼ぶようになりました。</p>
<p>![](/assets/blog/authors/oka/osakarenewal/2.png =600x)  </p>
<p>実行委員会形式で進めた新オフィスづくりも、この「この指とまれ」スタイルがきっかけ。誰かが声をかけて、そこに集まったメンバーで、一緒に手を動かしながら作り上げてきました。そんな想いが詰まった、新しいオフィス。ここからは私たちのオフィスの一部をご紹介します！</p>
<h2>新オフィスの魅力をご紹介！</h2>
<p>![](/assets/blog/authors/oka/osakarenewal/3.png =600x)<br>オフィスの床には、会議室へと続く道路のラインがあしらわれています。</p>
<p><strong>🛝 PARKエリア | 靴を脱いで、ほっと一息</strong>
![](/assets/blog/authors/oka/osakarenewal/4.png =600x)  </p>
<p>靴を脱いで、ゆったり過ごせる土足禁止のリラックス空間をつくりました。カジュアルなミーティングやちょっと一息つきたいときにぴったりの場所です。さっそく全社MTGでも、自然とみんなが集まるお気に入りの場所になっています。</p>
<p>![](/assets/blog/authors/oka/osakarenewal/5.png =600x)  </p>
<p><strong>🚗 会議室の名前も、Osaka Tech Lab流</strong></p>
<p>会議室には、ガレージやピットをモチーフにした名前をつけています。その中でも「モータープール」など、大阪らしさとモビリティを掛け合わせたユニークな名前も。</p>
<p>※「モータープール」：大阪でよく使われている「駐車場」を意味する言葉です。</p>
<p>![](/assets/blog/authors/oka/osakarenewal/6.png =600x)<br>Slackでブレストを重ねる中で、雑談から自然と生まれたこのネーミング。みんなで楽しみながら決めた、“私たちらしい”名前になりました。</p>
<p>![](/assets/blog/authors/oka/osakarenewal/7.png =600x)<br>（ちなみに、大阪ならではのユーモアも交えながら、アツい議論が繰り広げられながら決まりました！）</p>
<p>![](/assets/blog/authors/oka/osakarenewal/8.png =600x)  </p>
<p><strong>🛣️OSAKA JCT</strong></p>
<p>KINTOの室町オフィス同様「OSAKA JCT」という、発信スペースも誕生しました。壁のデザインはOsaka Tech Labのデザイナーが、みんなで考えたコンセプトをカタチにした、自慢のクリエイティブです。</p>
<p>![](/assets/blog/authors/oka/osakarenewal/9.png =600x)<br>オフィスの開所式は、このJCTを活用しながら「この指とまれ」でメンバーを募り、実行委員会形式で企画・運営しました。移転式もメンバー主導で進め、マネージャー陣を招いて社内の決起会を実施。すべてが「みんなで作った」手作りのイベントでした。</p>
<p>![](/assets/blog/authors/oka/osakarenewal/10.png =600x)<br>新オフィスについて、メンバーからはこんな声も届いています。</p>
<ul>
<li>仕事へのモチベーションが自然と上がり、背筋が伸びる感覚になります。</li>
<li>共有スペース「PARK」は開放感があり、大人数でも自然と集まれる心地よい場所。</li>
<li>モビリティをモチーフにした工夫がオフィスのあちこちに。場所の名前や標識、道を模した床、タイヤの机、クルマ型の移動式ベンチなど、細部にまで遊び心が散りばめられていて、歩いているだけでわくわくします。</li>
</ul>
<p>開所式でメンバーにインタビューしたところ、「自分たちの声がオフィスに反映されているのが嬉しい」「”自分たちの場所”として愛着が持てる」といった声がたくさん届きました。</p>
<h2>Osaka Tech Labで感じたこと</h2>
<p>実は、この「みんなでつくる」という空気は、旧オフィスの頃から変わっていません。</p>
<p>![](/assets/blog/authors/oka/osakarenewal/11.jpeg =600x)  </p>
<p>旧オフィスの閉所式は、みんなでお酒を持ち寄って乾杯する、あたたかくてゆるい飲み会でした。部署も肩書きも関係なく、ふらっと集まって、気づけばわらわらと飲み会が始まっている——そんな文化が、Osaka Tech Labには自然と根付いています。</p>
<p>採用担当として、この距離感や、自分たちの声を大事にできる文化こそ、Osaka Tech Labの大きな魅力だと感じています。新オフィスになった今も、この雰囲気はきっと変わりません。</p>
<p>これからも、未来のことを気軽に語り合える、そんな場所であり続けたいと思っています。</p>
<h2>一緒に「集GO！発SHIN！CO-LAB」しませんか？</h2>
<p><strong>イベント開催情報</strong></p>
<p>Osaka Tech Labでは、私たちのカルチャーを体験できるイベントを定期的に開催しています。「この指とまれ」にピンときた方は、ぜひ気軽に遊びにきてください。コンセプトの具体的な取り組みとして、Osaka Tech Labのメンバーが日々の開発で得た知見やノウハウを共有するイベント「CO-LAB Tech Night」を開催いたします。</p>
<p><strong>CO-LAB Tech Night  vol.1 , 全部内製化 大阪でクラウド開発やってるで！ #1</strong></p>
<ul>
<li>開催日時：2025年7月10日(木) 19:00-21:30</li>
<li>概要：クラウド開発をテーマに、クラウドインフラ、SRE、データ分析基盤を取り上げ、Osaka Tech Lab のメンバーが、現在の取り組みや、そこから得た知見を共有します。</li>
<li>詳細：<a href="https://www.kinto-technologies.com/news/20250702">https://www.kinto-technologies.com/news/20250702</a></li>
</ul>
<p><strong>CO-LAB Tech Night vol.2 , Cloud Security Night #3</strong></p>
<ul>
<li>開催日時：2025年8月7日(木) 19:00-21:30</li>
<li>概要：AWS、Google Cloud、Azureなどのマルチクラウド環境におけるクラウドセキュリティに関する話題を中心に、各社の取り組みを通じて、クラウドセキュリティの知識を深めるイベントです。今回は、東京で開催している「Cloud Security Night」の第3回目を大阪で開催いたします！</li>
<li>詳細：<a href="https://www.kinto-technologies.com/news/20250709">https://www.kinto-technologies.com/news/20250709</a></li>
</ul>
<p>![](/assets/blog/authors/oka/osakarenewal/12.png =600x)  </p>
<p><strong>最新情報はOsaka Tech Labの特設サイトで！</strong></p>
<p>Osaka Tech Labでは、今後もエンジニアリング・クラウド・データ分析など、さまざまなテーマでイベントやTech Blogを通じて発信を続けていきます。イベント情報は、Osaka Tech Lab特設サイトにて随時更新予定です。気になる方は、イベント一覧(CO-LAB events)からぜひチェックしてみてください！</p>
<p>▼Osaka Tech Lab 特設サイトはこちら
<a href="https://www.kinto-technologies.com/company/osakatechlab/">https://www.kinto-technologies.com/company/osakatechlab/</a></p>
<p>![](/assets/blog/authors/oka/osakarenewal/13.png =600x)  </p>
<p><strong>(Osaka Tech Labの特設サイトも、「この指とまれ」から生まれました)</strong></p>
<p>この記事と同時に公開されるOsaka Tech Labの特設サイトも、「この指とまれ」から生まれた取り組みのひとつです。</p>
<p>「もっと発信したい」「大阪のリアルな雰囲気をもっと届けたい」そんなメンバーの声から自然と人が集まり、企画・デザイン・執筆・公開まで、みんなで手を動かしながら作り上げました。東京のクリエイティブ室のメンバーも巻き込んで、まさにCO-LABで実現した、大阪らしい挑戦です。この特設サイトにも、私たちのカルチャーがぎゅっと詰まっています。ぜひ、のぞいてみてください。</p>
<h4>カジュアル面談も実施中</h4>
<p>「ちょっと話を聞いてみたい」「Osaka Tech Labの雰囲気をもっと知りたい」という方、どうぞお気軽に下記URLからお申し込みください！</p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/1859151978603163665">https://hrmos.co/pages/kinto-technologies/jobs/1859151978603163665</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/oka/osakarenewal/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Event Report: DBRE Summit 2023]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-09-13-イベントレポート-DBRE-Summit-2023-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-09-13-イベントレポート-DBRE-Summit-2023-en/</guid>
            <pubDate>Wed, 09 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A report on the DBRE Summit 2023, held on August 24, 2023!]]></description>
            <content:encoded><![CDATA[<p>Hello. My name is Hoshino, a member of the DBRE team at KINTO Technologies.</p>
<p>In my previous job, I worked as an infrastructure and backend engineer at a web production company. Over time, I developed a strong interest in databases and found the work of DBRE especially compelling, so I decided to join the DBRE team at KINTO Technologies in August 2023.</p>
<p>The Database Reliability Engineering (DBRE) team operates as a cross-functional organization, tackling database-related challenges and building platforms that balance organizational agility with effective governance. Database Reliability Engineering (DBRE) is a relatively new concept, and only a few companies have established dedicated DBRE organizations. Among those that do, their approaches and philosophies often differ, making DBRE a dynamic and continually evolving field.</p>
<p>For examples of our DBRE initiatives, check out the tech blog by Awache (<a href="https://twitter.com/_awache">@_awache</a>) titled <a href="https://blog.kinto-technologies.com/posts/2022-10-04-DBRE_Guardrail/">Efforts to Implement the DBRE Guardrail Concept</a>, as well as the presentation at this year&#39;s <a href="https://speakerdeck.com/_awache/cus-11-aws-summit-2023-dbre-practice?slide=2">AWS Summit</a> and p2sk&#39;s (<a href="https://twitter.com/_p2sk_">@<em>p2sk</em></a>) <a href="https://speakerdeck.com/masakihirose/kinto-tekunorozizudeno-dbre-huo-dong-nogoshao-jie">talk at the DBRE Summit 2023</a>.</p>
<p>In this article, I&#39;d like to share a report on the DBRE Summit 2023, which was held on August 24, 2023!</p>
<h2>What is DBRE Summit 2023?</h2>
<p>This event is for learning about the latest DBRE topics and practices, as well as networking in the DBRE community. A total of 186 people signed up in advance via <a href="https://kinto-technologies.connpass.com/event/292402/">connpass</a>, both online and offline, and many of them also participated on the day.</p>
<p>Thank you to all the speakers and attendees for taking the time out of your busy schedules to help make the DBRE Summit a success!</p>
<h2>Linkage&#39;s Initiatives to Making DBRE a Culture, Not Just a Role</h2>
<p><strong>Taketomo Sone/Sodai <a href="https://twitter.com/soudai1025">@soudai1025</a>, Representative member of Have Fun Tech LLC, CTO of Linkage, Inc., and Co-organizer of the DBRE Users Group (DBREJP)</strong></p>
<p>@<a href="f2cf15bd3abf417a9864cff86a57c7f4">speakerdeck</a></p>
<p>DBRE is not just a role, but a database-centered operational philosophy and a culture of maintaining databases as part of everyday product development activities.</p>
<p>When a hero who can handle all databases emerges, it creates the risk of becoming overly dependent on that person.</p>
<p>To prevent this, we should strive for a peaceful environment where stable operations don&#39;t rely on heroes. To achieve that, we need to build a strong organizational culture at the company level.</p>
<p>While individual skill and enthusiasm are necessary, they alone can&#39;t build a culture. So, the first step is to create the environment.</p>
<p>In addition, because design is directly linked to the security and operation of the database, there needs to be a culture in which developers practice DBRE.</p>
<p>Database Reliability Engineering is a philosophy, and an operational style that aims to solve problems through systems rather than craftsmanship. DBRE focuses not on reacting to issues, but on preventing them in the first place.</p>
<p>It&#39;s never too late to start!</p>
<p><strong><Thoughts></strong> I realized that when putting DBRE into practice, it is very important to involve others rather than trying to do it all by ourselves. DBRE = Philosophy and Culture! To help build a company culture, I want to proactively engage in cross-functional communication!</p>
<h2>Current State of Mercari&#39;s DBRE and a Comparison of Query Replay Tools</h2>
<p><strong>Satoshi Mitani <a href="https://twitter.com/mita2">@mita2</a>, DBRE, Mercari, Inc.</strong></p>
<p>Mercari&#39;s DBRE team was established about a year ago. Until then, the SRE team was in charge of the database.</p>
<p>Initially, the system architecture consisted of just a monolithic API and a single database, but now it has been split into a monolith and microservices.</p>
<p>The main responsibilities of the DBRE team include providing support for the databases owned by each microservice, answering various DB enquiries to resolve developers&#39; concerns, and researching tools to increase productivity.</p>
<p>When we started providing support for MicroService DB, we faced challenges, such as wanting to act proactively but not being able to see the issues easily and the DBRE team not being recognized.</p>
<p>To address these,</p>
<ul>
<li>Developer Survey conducted, with multiple choice questions about what developers expect from DBRE</li>
<li>DBRE Newsletter published every six months, with active communication from the DBRE team.</li>
</ul>
<p>These efforts have gradually raised awareness across the company, leading to an increase in requests.</p>
<p>Other DBRE responsibilities include operational tasks related to the Monolith DB, and efforts toward modernization.</p>
<p>To select a query replay tool capable of mirroring production queries, we defined key evaluation criteria and then conducted a survey.</p>
<p>What is a replay tool?</p>
<ul>
<li>A replay tool reproduces production queries or traffic in a separate environment. It is used to investigate the impact of database migrations or version upgrades.</li>
</ul>
<p>Tools compared</p>
<ul>
<li>Percona query Playback<ul>
<li>A log-based, easy-to-use replay tool.</li>
</ul>
</li>
<li>MySQL-query-replayer (MQR)<ul>
<li>MQR is a tool built for large-scale replays, and you can really sense the creator Tombo-san&#39;s passion.</li>
</ul>
</li>
</ul>
<p><strong><Thoughts></strong> I got the impression that the DBRE team is actively sharing organizational challenges through Developer Surveys and DBRE Newsletters. It was also very insightful to hear about the criteria and process used in evaluating replay tools.</p>
<h2>Introducing DBRE Activities at KINTO Technologies</h2>
<p><strong>Masaki Hirose <a href="https://twitter.com/_p2sk_">@<em>p2sk</em></a>, DBRE, KINTO Technologies</strong></p>
<p>@<a href="c27dda9dea9049e8886e4903fcfb642b">speakerdeck</a></p>
<p>The DBRE team is part of a company-wide cross-functional organization called the Platform Group.</p>
<p>The roles of DBRE are divided into two categories:</p>
<ul>
<li>Database Business Office<ul>
<li>Responsible for solving problems based on requests from development teams and stakeholders, as well as promoting the use of DBRE-provided platforms.</li>
</ul>
</li>
<li>Cloud Platform Engineering<ul>
<li>Responsible for providing database-related standards and platforms to promote effective cloud utilization while ensuring governance compliance.</li>
</ul>
</li>
</ul>
<p>DBRE&#39;s activities are determined by defining four pillars and then deciding on specific activities based on the current state of the organization.</p>
<p>Actual Activities</p>
<ul>
<li>Building a system to collect information on DB clusters</li>
<li>DB secret rotation</li>
<li>Validation: Aurora zero-ETL integrations with Redshift (preview)</li>
</ul>
<p>KINTO Technologies&#39; DBRE team is building platforms to enhance the reliability of databases.</p>
<p>To achieve this, we&#39;ve chosen to solve the challenges through engineering.</p>
<ul>
<li>By using the cloud effectively to balance agility with database security and governance.</li>
<li>By evolving these efforts into a company-wide platform, continue to drive positive impact on the business.</li>
</ul>
<p>We&#39;re proceeding these with an approach called Database Reliability Engineering.</p>
<p><strong><Thoughts></strong> I was very impressed by how the team clearly defines the role of DBRE and leverages that definition to design organizational systems that both improve database reliability and contribute to the business. In the future, I hope to contribute to building even better systems based on the four DBRE pillars.</p>
<h2>Implementing DBRE with OracleDB: We tried it at Oisix ra daichi ~</h2>
<p><strong>Tomoko Hara <a href="https://twitter.com/tomomo1015"> @tomomo1015</a>, DBRE, Oisix ra daichi Inc. and Co-organizer of the DBRE Users Group (DBREJP)</strong></p>
<p>@<a href="8980f8a3503b497c888e9b701ad9b042">speakerdeck</a></p>
<p>Among the many aspects of visibility that SRE/DBRE can provide, cost visibility tends to be overlooked. So, we&#39;re taking on the challenge of managing infrastructure costs across the entire company.</p>
<p>Our approach involves reviewing the list of invoices to understand the actual state of the system and identify potential issues.</p>
<p>Additionally, by evaluating cost-effectiveness, we contribute to improving business profit margins.</p>
<p>Database costs make up a significant portion of overall infrastructure expenses. While databases are critical enough to warrant that investment, they must not be neglected or treated with complacency.</p>
<p>To reduce database costs, we&#39;re implementing measures such as stopping databases used in development environments on days when they are not in use, and considering the most cost-effective approach.</p>
<p>When using a commercial database, knowing the license type and its associated cost is very important in embodying DBRE.</p>
<p>Conduct a license inventory to understand whether the licenses your company has contracted are appropriate.</p>
<p>Take the time to think about how we can improve reliability, grow, and enjoy what we do, both now and in the future.</p>
<p>By visualizing costs, many things become clear, so we encourage you to start by making costs visible as an approach to contributing to the business and improving reliability.</p>
<p><strong><Thoughts></strong> It was very interesting to hear about cost visualization, which is something I don&#39;t often get to hear about. As mentioned in the talk, the database accounts for a large proportion of infrastructure costs and is a critical part of the system, so I felt it was very important to visualize it and evaluate its cost-effectiveness. Including cost aspects, I found it helpful and hope to contribute to solving such challenges as part of DBRE going forward.</p>
<h2>ANDPAD&#39;s Initiatives to Automate Table Definition Change Review and Create Guidelines</h2>
<p><strong>Yuki Fukuma <a href="https://twitter.com/fkm_y">@fkm_y</a>, DBRE, ANDPAD Inc.</strong></p>
<p>@<a href="aae21d0e462a49bab607ee6edac214b7">speakerdeck</a></p>
<p>At ANDPAD, when a product team makes changes to table definitions, the DBRE team is responsible for reviewing them, and several issues have arisen in the process. For this reason, we felt the need to create a scalable mechanism to improve review efficiency.</p>
<p>As part of our investigation, we decided to categorize the review comments from DBRE to the developers, and release small, incremental changes starting with those that we could address.  We adopted this approach in order to get early results while moving forward.</p>
<ul>
<li>Automating Access Paths<ul>
<li>Although the database terms of use had already been created, it was hypothesized that they weren&#39;t being read much until they were actually needed. So, we created an access path that would display them at the necessary timing. As a result, the number of views increased and the frequency of comments during reviews decreased.</li>
</ul>
</li>
<li>Automating Table Definition Reviews<ul>
<li>A system was built to automatically review items that can be mechanically checked. This reduced the review costs for DBRE.</li>
</ul>
</li>
</ul>
<p>By creating such a system, we not only improved review efficiency, but also made it possible to apply the process to products that had not previously been reviewed, enabling DBRE to automate table definition reviews.</p>
<p><strong><Thoughts></strong> I found it impressive how the automation of access paths and table definition reviews made the process highly efficient and easy to use at the right time. This was very helpful and I hope to build something similar myself in the future.</p>
<p><strong>Michi Kubo <a href="https://twitter.com/amamanamam">@amamanamam</a>, DBRE, ANDPAD Inc.</strong></p>
<p>@<a href="f4ef1eb1a6e04c688b98bb2b9415b355">speakerdeck</a></p>
<p>A story about creating a course of action to ensure that table definition changes are implemented uniformly and of higher quality in production by all teams.</p>
<p>One of the issues was that the quality of validation during table definition changes varied between teams, leading to migrations being carried out without sufficient validation, potentially causing service disruptions or failures.</p>
<p>To address this, we conducted interviews and analyzed the causes. We then created clear guidelines to ensure the quality of validations.</p>
<p>Overview of the guidelines</p>
<ul>
<li>Create a list of tasks to be completed before the actual executing</li>
<li>Create a list of items to be included in pull requests</li>
<li>Create a flow for considering release timing</li>
</ul>
<p>As a result of implementing these guidelines, validation results became more comprehensive and unified.</p>
<p><strong><Thoughts></strong> I found it impressive how the team clearly identified the issues and organized guidelines and processes to improve quality, which helped raise awareness across the team and enhance reliability. As a DBRE team member, I&#39;d like to organize guidelines in a way that motivates the whole team to empathize with the issues and collaborate in solving them.</p>
<h2>Panel Discussion: &quot;The Future of DBRE&quot;</h2>
<p><strong>Taketomo Sone/Sodai <a href="https://twitter.com/soudai1025">@soudai1025</a></strong></p>
<p><strong>Satoshi Mitani <a href="https://twitter.com/mita2">@mita2</a></strong></p>
<p><strong>Tomoko Hara <a href="https://twitter.com/tomomo1015">@tomomo1015</a></strong></p>
<ul>
<li><p>What&#39;s the best way to get started with DBRE?</p>
<ul>
<li>It might be a good idea to start by setting a goal and then determining what to do based on that.</li>
<li>Identifying challenges and working to build a culture around addressing them is important.</li>
<li>Database standardization might be a good topic to tackle first.</li>
</ul>
</li>
<li><p>What unique skills are required to practice DBRE?</p>
<ul>
<li>Since DBRE activities span across different teams, communication skills are essential.</li>
<li>You need a personality that can respond positively under pressure.</li>
<li>The ability to build trust is important.</li>
</ul>
</li>
<li><p>What makes DBRE an attractive career?</p>
<ul>
<li>This will enhance your DB expertise.</li>
<li>Since the core technologies of databases don&#39;t change rapidly, the knowledge and experience you gain can be used for a long time.</li>
<li>It&#39;ll broaden your perspective beyond databases to include applications as well.</li>
</ul>
</li>
<li><p>What are you looking to work on in the future?</p>
<ul>
<li>I&#39;d like to engage in community activities as a DBRE.</li>
<li>I&#39;d like to accumulate more success stories as a DBRE.</li>
<li>I hope DBRE will become a more widely recognized.</li>
</ul>
</li>
</ul>
<p><strong><Thoughts></strong> I was a bit surprised to learn that DBRE requires more than just database knowledge. Of course, database knowledge is essential, but I realized that communication skills and a positive mindset are just as important for building a cross-organizational culture. I personally hope that DBRE becomes a role more and more people aspire to.</p>
<h2>Summary</h2>
<p>So, how was it for you?</p>
<p>DBRE itself is still a developing field, and only a limited number of companies have adopted it so far. That&#39;s why the DBRE Summit was such a valuable opportunity to learn about the DBRE initiatives of various companies. Having recently transitioned from backend engineering to DBRE, I&#39;m not yet a database specialist. However, through this summit, I came to recognize that working on database improvement tasks and building cross-functional cultural foundations are also important activities of DBRE.</p>
<p><a href="https://youtube.com/live/C2b93fgn05c">https://youtube.com/live/C2b93fgn05c</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/hoshino/dbre_summit_2023/dbre_summit_2023.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: December 2024 Update]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-23-newcomers-introduction-24dec-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-23-newcomers-introduction-24dec-en/</guid>
            <pubDate>Tue, 08 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Introducing the new members who joined KINTO Technologies in December 2024.]]></description>
            <content:encoded><![CDATA[<h1>Hello</h1>
<p>Hi there—this is MakiDON, joining the company in December 2024! In this article, I asked our December 2024 new joiners to share their first impressions right after joining. I&#39;ve put their thoughts together here. I hope this content will be useful for those who are interested in KINTO Technologies, and serve as a reflection for the members who participated in the interview!</p>
<h1>Fsk</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/newcomers/Fsk.png" alt="Fsk"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I work on frontend development in the Business System Group, part of the Business Systems Development Department. So far, I&#39;ve been doing frontend using Nextjs, always aiming to build user-friendly interfaces. There&#39;s still plenty for me to learn, but I&#39;ll do my best to be helpful in any way I can.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>There are five of us, including me. We&#39;ve got one PM, two front-end engineers, and two back-end engineers.</li>
</ul>
</li>
<li><strong>What was your first impression of KINTO Technologies when you joined? Were there any surprises?</strong><ul>
<li>Using generative AI tools like Copilot and ChatGPT has been a huge help. I was a bit nervous before joining, but everyone was so warm and welcoming that I quickly felt at ease.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on site?</strong><ul>
<li>I really appreciate how easy it is to ask for help when I run into something.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I think it&#39;s great to have the opportunity to share my thoughts and feelings with everyone.</li>
</ul>
</li>
<li><strong>Question from Frank to Fsk</strong><ul>
<li><strong>If you could hand off just one boring daily task to a robot, what would it be?</strong><ul>
<li>Definitely, cleaning! It eats up time every day, and I&#39;d much rather spend that time doing something else.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Takahashi</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/newcomers/takahashi.png" alt="takahashi"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I work as a project manager for the Owned Media Group and the Marketing Product Development Group. I focus on helping everyone move toward a shared goal—acting as a good partner to our clients and internal teams, and as a bridge between engineers and business divisions.</li>
<li>In my previous job, I gained experience as a web designer. Later, I transferred to the Development Department, where I managed a range of platform-related areas, including membership systems, payments, points, and facility information.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>The Owned Media Group has one project manager and two engineers. The Marketing Product Development Group focuses on static content and includes a team leader, a project manager, a tech lead, and two engineers.</li>
</ul>
</li>
<li><strong>What was your first impression of KINTO Technologies when you joined? Were there any surprises?</strong><ul>
<li>My first impression was how quiet the office was. At my previous job, the sales team was on the same floor and right nearby, so it was always noisy.</li>
<li>As for any gaps or surprises, I&#39;d say that each group has its own development style. You kind of need to stay flexible and ready to adapt your mindset.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on site?</strong><ul>
<li>It&#39;s quiet. So quiet that I feel like I need to be a little mindful when tossing a can into the trash.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>During my self-intro at work, I think the only thing really came across was that I&#39;m into Monster Hunter. So I&#39;m glad to get the chance to write the article.</li>
</ul>
</li>
<li><strong>Question from Fsk to Takahashi</strong><ul>
<li><strong>Do you prefer World or Wilds? lol</strong><ul>
<li>I&#39;d say Wilds, especially with all the upcoming updates to look forward to! Hoping it becomes something we can enjoy for over 10 years, just like World!</li>
</ul>
</li>
<li><strong>Generative AI is currently being used in the design field, and many engineers are being called &quot;AI prompt engineers.&quot; What do you think about this trend?</strong><ul>
<li>As long as people are careful not to infringe on copyright or image rights, I think it&#39;s totally fine to let generative AI handle certain tasks. That said, I don&#39;t think it&#39;s suitable in contexts like contests or competitions where creativity is what&#39;s being judged.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Lyu</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/newcomers/Lyu.png" alt="Lyu"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I currently belong to the Business System Group in the Business System Development Division, where I mainly work on backend system development. My day-to-day work involves designing, implementing, operating and maintaining various systems that help streamline internal operations and improve data integration. I always keep stability and scalability in mind when developing systems.</li>
<li>Previously, I worked at IBM, where I was involved in developing medical information systems for major hospitals in Japan. I&#39;ve had hands-on experience across the entire process—everything from requirements gathering and design to development, rollout, and after-sales support. I&#39;ve always aimed to build systems that truly meet the needs of users on the ground.</li>
<li>Drawing on that experience, I continue to work on deepening both my technical skills and understanding of the business so I can deliver systems that are even more practical and valuable.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>There are five of us, including me. We&#39;ve got one PM, two front-end engineers, and two back-end engineers. Everyone was a pro in their own area, and I learned a lot from being part of the team.</li>
</ul>
</li>
<li><strong>What was your first impression of KINTO Technologies when you joined? Were there any surprises?</strong><ul>
<li>The first thing that stood out was how warm and welcoming everyone was. There&#39;s a relaxed atmosphere where people communicate freely, without being overly concerned about hierarchy. I was also impressed by the wide range of in-house events and active club activities—there&#39;s always something going on. The benefits are really employee-friendly too, which makes it a great place to work.</li>
<li>There wasn&#39;t a big gap between what I expected and what I actually experienced. If anything, the work environment turned out to be even better than I had imagined.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on site?</strong><ul>
<li>It&#39;s bright and really enjoyable. Of course, we talk about work, but it&#39;s also easy to share fun ideas or little things that happen during the day. The team members are all close to each other and it&#39;s easy to get along with anyone, so you can work with peace of mind.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I&#39;m really glad to have the chance to share my experiences like this. I hope that something from my daily work or thoughts can help someone out there, even just a little.</li>
</ul>
</li>
<li><strong>Question from Takahashi to Lyu</strong><ul>
<li><strong>If you were to buy a car through KINTO, which car would you like to drive?</strong><ul>
<li>I&#39;d definitely go for the Crown. I&#39;ve always thought it looked cool. Plus, I actually use this model a lot when creating test data at work, so I&#39;ve kind of grown attached to it. lol</li>
<li>The employee discount program also makes it possible to get a Crown at a really reasonable price, which is a big plus. On top of that, the range of customer-friendly services, like the comprehensive insurance plan, really makes the whole package feel impressive.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>MakiDon</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/avatar.jpg" alt="Yuya"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>My name is MakiDon, and I joined the company in December. I belong to the Marketing Product Development Group in the Mobility Product Development Division. I mainly handle data analysis and machine learning tasks. My main role is to identify issues through data analysis, propose strategic solutions and exit plans, and support system design using machine learning.</li>
<li>Before this, I worked as a project manager at a startup focused on architecture and IT.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>I&#39;m in the the Data Analysis and ML Utilization Team. We&#39;re a group of eight: one PjM (Project Manager)/PdM (Product Manager), one Scrum Master, and six engineers.</li>
</ul>
</li>
<li><strong>What was your first impression of KINTO Technologies when you joined? Were there any surprises?</strong><ul>
<li>Since KTC is part of a large corporation, my first impression was that it&#39;d be a pretty traditional and stable company. But once I joined, I saw generative AI being used in Slack and AI actively integrated into various systems. It quickly became clear that the company is a tech company and has a fast-moving, startup-like energy—much more than I expected.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on site?</strong><ul>
<li>It&#39;s a very open and supportive environment. Not only within the team but across departments, people are quick to offer help. You can ask for advice anytime, which makes it easy to work with peace of mind.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I Actually, I got to write a <a href="https://blog.kinto-technologies.com/posts/2025-02-25-cursor-vs-copilot/">Tech blog</a> before this main post. I&#39;d never written a blog before, but thanks to the support and advice from my team, I was able to write it without any worries. It turned out to be a really valuable experience. I&#39;ll continue to do my best to share my new knowledge and experience both inside and outside the company!</li>
</ul>
</li>
<li><strong>Question from Lyu to MakiDon</strong><ul>
<li><strong>What are you most proud of in your work so far?</strong><ul>
<li>By bringing in-house the output we&#39;d previously generated using machine learning tools, we managed to cut costs and boost click-through rates!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Frank Neezen</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/newcomers/Frank.png" alt="Frank"></p>
<ul>
<li><strong>Self-introduction</strong><ul>
<li>I’m Frank Neezen, a member of the Business Development Department, </li>
<li>Officially titled Business Development Manager. My primary role, however, is as the Technical Architect, where I help guide the design and implementation of our core global full-service products.</li>
<li>My background lies in consulting, where I’ve focused on advising clients on leveraging Salesforce to meet their technical and operational needs.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong><ul>
<li>My direct team consists of 4 team members with a diverse skillset. We collaborate closely with our engineering team to develop software solutions for the global full-service lease business.</li>
</ul>
</li>
<li><strong>What was your first impression of KINTO Technologies when you joined? Were there any surprises?</strong><ul>
<li>My transition from Salesforce in Amsterdam to KTC in Tokyo was remarkably smooth! I had some initial concerns about adapting to the cultural differences, but the exceptional onboarding process and the warm, supportive team at the Jimbocho office made all the difference. From day one, their welcoming attitude helped me settle in effortlessly. My main hurdle, however, was organizing all my personal affairs, for example sorting out banking or registering within the neighborhood without being able to speak Japanese. I had lots of help though from KTC with these activities.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on site?</strong><ul>
<li>Our team is based together in the Jimbocho office, next to many of the engineers. The vibe is open and professional, but also relaxed. The atmosphere is open, professional but relaxed. There is a good team feeling, we all want to succeed with our work.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong><ul>
<li>I have written articles in the past for other topics though mainly related to Salesforce. Always happy to write up and share my personal story of joining KTC!</li>
</ul>
</li>
<li><strong>Question from MakiDon to Frank</strong><ul>
<li><strong>Was there anything that surprised you when you came to Japan?</strong><ul>
<li>I&#39;m amazed by how safe Japan is, walking around anywhere in Tokyo, the biggest city in the world feels completely secure! Also what truly surprising is that if you lose something, like a wallet or phone, it almost always finds its way back to you. I have had a few times when I did not even realize I lost something but then someone would randomly come up to me with my lost item. Such a refreshing experience!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Finally</h1>
<p>Thank you everyone for sharing your thoughts on our company after joining it!</p>
<p>There are more and more new members at KINTO Technologies every day! We&#39;ll be posting more new-joiner stories from across divisions, so stay tuned!</p>
<p>And yes — we&#39;re still hiring! KINTO Technologies is looking for new teammates to join us across a variety of divisions and roles. For more details, check it out <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[KINTOテクノロジーズOsaka Tech Labにご来場のかたへ]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-09-office-access-osaka/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-09-office-access-osaka/</guid>
            <pubDate>Tue, 08 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズOsaka Tech Labにご来場のかたへ]]></description>
            <content:encoded><![CDATA[<h2>まいど、おおきに( º∀º )/</h2>
<p>技術広報G イベントチームのゆかちです。</p>
<p>2025年7月、Osaka Tech Labにも念願のイベント会場が…！<br>今回はそんな<strong>Osaka Tech Lab JCT</strong>の行き方を簡単にですが紹介しちゃいます！</p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/jct.png =600x)<br><em>Osaka Tech Lab JCT、40人ほど着席可能</em></p>
<h4><strong>住所：〒530-0001　大阪府大阪市北区梅田三丁目１番３号ノースゲートビルディング２０階</strong></h4>
<ul>
<li>JR大阪『中央口改札(1F)』、『連絡橋口(3F)』　2分</li>
<li>OsakaMetro 梅田駅 『北改札』5分</li>
<li>阪急電車 梅田駅『2階中央改札』7分</li>
<li>阪神電車 梅田駅（連絡橋）7分</li>
</ul>
<p>場所はルクア1100の隣になります。
<strong>案内看板の『ルクアイーレ』や『オフィスタワー』が目指す先です！</strong>
阪急、阪神電車で来る方はまずは『JR大阪駅』方面へ！
![accessosaka1](/assets/blog/authors/uka/accessosaka/0.png =600x)<br><em>目印に！</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/2.png =600x)<br><em>JR大阪駅は『中央口改札』もしくは『連絡橋口改札』を出てオフィスタワー方面へ</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/1.png =600x)<br><em>Osaka Metroの場合は『北改札』を出てオフィスタワー方面へ</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/3.png =600x)<br><em>1階から来る方はこちら</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/4.png =600x)<br><em>3階から来る方はこちら</em></p>
<p>:::message alert
現在、ルクアイーレ前3階からオフィスエリアへの入口が工事中です。
![入口自動ドア付近遠景の写真](/assets/blog/authors/uka/accessosaka/underconstruction01.jpg =600x)
![入口自動ドアの写真](/assets/blog/authors/uka/accessosaka/underconstruction02.jpg =600x)
入口がわかりづらいのでご注意ください。
:::</p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/5.png =600x)<br><em>エスカレーターを登り4階が連絡通路になります、3階から乗るともっと短いヨ</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/6.png =600x)<br><em>正面の自動ドアまでまっすぐ</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/front.jpg =600x)<br><em>正面入口を入り右へ</em>
:::message
<strong>ビルのセキュリティ上、時間帯によっては正面入口が施錠されている場合がございます。</strong><br>その際は、正面入口に設置している「QRコード付きの案内ボード」からご連絡ください！スタッフが順次お迎えにまいります。
:::</p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/7.png =600x)<br><em>手前のエレベーターにて20階までお越しください</em></p>
<p>![accessosaka1](/assets/blog/authors/uka/accessosaka/8.png =600x)<br><em>エスカレーター降りてすぐがKINTOテクノロジーズです！Welcome！</em></p>
<h2>さいごに</h2>
<p>以上、Osaka Tech Labでのお時間を楽しく過ごしていただけたら嬉しいです！
足を運んでいただきありがとうございました！
またのお越しをお待ちしております(^_^)/</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uka/accessosaka/accessosaka.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Building a Speedy Analytics Platform with Auto-Expansion ETL Using AWS Glue]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-08-22-AWS-Glue-analysis-platform-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-08-22-AWS-Glue-analysis-platform-en/</guid>
            <pubDate>Mon, 07 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Building a Speedy Analytics Platform with Auto-Expansion ETL Using AWS Glue]]></description>
            <content:encoded><![CDATA[<p>My name is Nakagawa, and I am the team leader of the data engineering team in the analysis group at KINTO Technologies. Recently, I have become interested in golf and have started to pay attention to the cost per ball. My goal this year is to make my course debut!<br/>
In this article, we would like to introduce the efforts of our data engineering team in efficiently developing KINTO&#39;s analytics platform and providing the data necessary for analysis in line with service launches.</p>
<h1>Data Engineering Team’s Goal</h1>
<p>The data engineering team develops and operates an analytics platform. An analytics platform plays a behind-the-scenes role that involves collecting and storing data from internal and external systems, and providing it in a form that can be utilized for business. Our goal is as follows so that data can be utilized immediately upon the launch of services:<br/>
__ &quot;In line with the launch of various services, we will aggregate data on our analytics platform and provide it immediately!&quot;__</p>
<p><img src="/assets/blog/authors/a.nakagawa/202308/analysis_platform_01.png" alt="Analytics platform conceptual diagram"></p>
<h1>Challenges</h1>
<p>However, with the expansion of the KINTO business and while we set the above-mentioned roles and goals, the following challenges have arisen.</p>
<ul>
<li>Limited development resources (as we are a small, elite team)</li>
<li>An increase in systems to be linked due to business expansion</li>
<li>An increase in modifications is proportional to an increase in the number of linked systems. (Note: The increase in modifications is also influenced by our agile business style of &quot;starting small and growing big.&quot;)</li>
</ul>
<h1>Solutions</h1>
<p>To solve the above challenges, we use AWS Glue for ETL. From the perspective of reducing workloads, we have focused on two aspects―operations and development. We have approached the challenges using the following methods.</p>
<ul>
<li>Standardization aimed at no-code</li>
<li>Automatic column expansion for a faster, more flexible analytics platform</li>
</ul>
<h1>Our company’s AWS Analytics Platform Environment</h1>
<p>Before explaining the two proposed improvements, I would like to explain our analytics platform environment. Our analytics platform uses AWS Glue for ETL and Amazon Athena for the database. In the simplest pattern, its structure is as shown in the diagram below. The structure involves loading data from source tables, accumulating raw data in a data lake in chronological order, and storing it in a data warehouse for utilization. <img src="/assets/blog/authors/a.nakagawa/202308/analysis_platform_02.png" alt="Analytics platform environment"></p>
<p>When developing workflows and jobs for data linkage using AWS Glue, KINTO Technologies use CloudFormation to deploy a series of resources, including workflows, triggers, jobs, data catalogs, Python, PySpark, and SQL. The main resources required for deployment are as follows:</p>
<ul>
<li>YAML file (workflow, job, trigger, and other configuration information)</li>
<li>Python shell (for job execution)</li>
<li>SQL file (for job execution)</li>
</ul>
<p>As mentioned above, the development work workloads increased in proportion to an increase in services, tables and columns. This began to strain our development resources. As described in the previous solutions, we addressed the challenges by implementing two main improvements. I would like to introduce the methods we used.</p>
<h1>Standardization aimed at no-code</h1>
<p>&quot;Standardization aimed at no-code&quot; was carried out in the following steps.</p>
<ul>
<li>Step 1 in 2022: Standardization of Python programs</li>
<li>Step 2 in 2023: Automatic generation of YAML and SQL files</li>
</ul>
<p><img src="/assets/blog/authors/a.nakagawa/202308/analysis_platform_03.png" alt="Standardization aimed at no-code "> In the improvement related to Python shell in Step 1, we focused on the fact that, up until now, workflow development was performed on a per-service basis, and the Python shell was also developed, tested, and reviewed on a per-workflow basis. This approach led to an increase in workloads. We moved forward with program standardization by unifying parts of the code that had been reused with slight modifications across different workflows, and by making them more general-purpose to accommodate variations in data sources. As a result, while we are currently focusing on intensive development and review of the common code, there is no longer any need to develop source code for each workflow. If the data source is Amazon RDS or BigQuery, all processing, including data type conversion to Amazon Athena, can now be handled within the standardized part. Therefore, when starting data linkage for each service, it is now possible to achieve no-code data linkage by simply writing settings in a configuration file.<br/></p>
<p>Step 2, the automatic generation of YAML and SQL files, improves the configuration files that remained as necessary parts in Step 1, as well as View definitions required for linkage with the source side. We improved these by using GAS (Google Apps Script) to automatically generate configuration files such as YAML and SQL for the View. This minimizes the development work by simply setting the minimal necessary definitions, such as workflow ID and table names that need to be linked, on a Google Spreadsheet, which automatically generates YAML files for configuration and SQL files for the View.</p>
<h1>Automatic column expansion for a faster, more flexible analytics platform</h1>
<p>In &quot;Automatic Column Expansion for a Faster, More Flexible Analytics Platform,&quot; before the improvement, table definitions and item definitions that have been already defined at the data linkage source had been also defined on the analytics platform side in YAML.[^1] Therefore, at the time of initial establishment, it was necessary to define as many items on the analytics platform side as on the data linkage source side, resulting in a need for approximately 800 to 1,200 item definitions per service on average (20 to 30 tables × 20 items × both lake and DWH). Our company is constantly expanding its services based on the philosophy of “starting small and growing big,” which frequently results in backend database updates. This update process also requires carefully identifying and modifying relevant portions from among the previously set 800 to 1,200 definition items, which has significantly increased development workloads.</p>
<p><img src="/assets/blog/authors/a.nakagawa/202308/analysis_platform_04.png" alt="Automatic Column Expansion for a Faster, More Flexible Analytics Platform"> So what we came up with was a method in which, when accessing the data linkage source for data linkage, the item definition information is also linked at the same time, allowing automatic updates of the item definitions on the analytics platform. The idea is that since the properly developed information is already present on the source side, there is no reason not to take advantage of it!</p>
<p>The specific implementation method for column auto-expansion is carried out using the following steps.</p>
<ul>
<li><code>glue_client.get_table</code> Retrieve table information from the AWS Glue Data Catalog.</li>
<li>Replace <code>table[&#39;Table&#39;][&#39;StorageDescriptor&#39;][&#39;Columns&#39;]</code> with the item list <code>col_list</code> obtained from the data linkage source.</li>
<li>Update AWS Glue&#39;s Data Catalog with <code>glue_client.update_table</code>.</li>
</ul>
<pre><code class="language-python:">def update_schema_in_data_catalog(glue_client: boto3.client, database_name: str,
                                table_name: str, col_list: list) -&gt; None:
    &quot;&quot;&quot;
    Args:
        glue_client (boto3.client): Glue client
        database_name (str): Databse naem
        table_name (str): Table name
        col_list (list): Column list of dictionary
    &quot;&quot;&quot;
    #AWS Glueのデータカタログからテーブル情報を取得
    table = glue_client.get_table(
                            DatabaseName = database_name,
                            Name = table_name
                        )

    #col_listでColumnsを置換え
    data = table[&#39;Table&#39;]
    data[&#39;StorageDescriptor&#39;][&#39;Columns&#39;] = col_list

    tableInput = {
        &#39;Name&#39;: table_name,
        &#39;Description&#39;: data.get(&#39;Description&#39;, &#39;&#39;),
        &#39;Retention&#39;: data.get(&#39;Retention&#39;, None),
        &#39;StorageDescriptor&#39;: data.get(&#39;StorageDescriptor&#39;, None),
        &#39;PartitionKeys&#39;: data.get(&#39;PartitionKeys&#39;, []),
        &#39;TableType&#39;: data.get(&#39;TableType&#39;, &#39;&#39;),
        &#39;Parameters&#39;: data.get(&#39;Parameters&#39;, None)
    }

    #AWS Glueのデータカタログを更新
    glue_client.update_table(
                        DatabaseName = database_name,
                        TableInput = tableInput
                    )
</code></pre>
<p>In addition to these, when creating an item list obtained from the linkage source, we also perform mapping of different data types for each database in the background. By doing so, we can generate item definitions on the analytics platform based on the schema information from the source side.</p>
<p>One point we paid attention to with the automatic updating of item definitions on the analytics platform side is that the table structure of the analytics platform under our management could change unexpectedly without our knowledge. To address this concern, we have implemented a system that sends a “notification” to Slack whenever a change occurs. By doing this, we can prevent the issue of the table structure changing unexpectedly without our knowledge. The system detects changes, and after checking the changes with the source system, linkage of the changes to subsequent systems as needed is possible.</p>
<p>[^1]: I won’t go into details here, but AWS Glue includes a crawler that updates the data catalog. However, due to issues such as the inability to update with sample data or perform error analysis, we have decided not to use it.</p>
<h1>Conclusion</h1>
<p>What are your thoughts? This time, I have introduced two methods of using AWS Glue in our analytics platform: “standardization aimed at no-code” and “automatic column expansion for faster, more flexible analytics platform.” By improving these two points, we have succeeded in reducing the development workloads. Now, even for a data linkage job involving 40 tables, the development workloads can be reduced to about one person-day, which has enabled us to achieve our goal of &quot;aggregating data into the analytics platform and providing it immediately in line with the launch of various services!&quot; I hope this will serve as a useful reference for those who wish to reduce development workloads in a similar way!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Kaizen Cases and the Kaizen Mindset at KINTO Technologies]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-07-18-Kaizen_mind-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-07-18-Kaizen_mind-en/</guid>
            <pubDate>Fri, 04 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Internal kaizen is implemented daily in even the smallest administrative tasks. I'd like to share a few examples of kaizen initiated voluntarily at our Jinbocho Office, along with the mindset behind them.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello! My name is Miura and I work in the Development Support Department at KINTO Technologies, assisting the Global Development Department. My day-to-day work includes managing tools for the Global Development Division, supporting office operations to create a smoother working environment for team members, and handling various inquiries. Lately, I&#39;ve been really into following my favorite band. They&#39;re only active for one year, so I&#39;ve been chasing their shows wherever I can!</p>
<p>Now, back to the topic. Since most of my work involves a lot of detailed admin tasks, I try to find ways to make small improvements every day. In this article, I&#39;ll introduce some of the <em>kaizen</em> initiatives I&#39;ve implemented at KINTO Technologies.</p>
<h1><em>Kaizen</em> So Far</h1>
<p>At KINTO Technologies, being part of the Toyota Group, we often use the term <em>kaizen</em> rather than improvement. Here&#39;s how we define it:🔻</p>
<blockquote>
<p><em>Kaizen</em> refers to the practice of eliminating waste in tasks or workflows and continuously improving the way we work to focus on higher-value activities. <a href="%E5%BC%95%E7%94%A8%E5%85%83%EF%BC%9A%5B%E3%82%AB%E3%82%A4%E3%82%BC%E3%83%B3%E3%81%A8%E3%81%AF%EF%BC%9F%E6%94%B9%E5%96%84%E3%82%84KAIZEN%E3%81%A8%E3%81%AE%E9%81%95%E3%81%84%E3%82%845S%E6%B4%BB%E5%8B%95%E3%83%BB%E3%83%88%E3%83%A8%E3%82%BF%E7%94%9F%E7%94%A3%E6%96%B9%E5%BC%8F%E3%81%A8%E3%81%AE%E9%96%A2%E4%BF%82%5D(https://kaizen-base.com/column/31127/)">^1</a></p>
</blockquote>
<p>Since joining the company, I&#39;ve carried out the following <em>kaizen</em> activities: [1] Revising and updating mailing list management [2] Revising the logbook and approval route for lending security cards [3] Managing test devices [4] Creating name tags for shared umbrellas</p>
<p>Let&#39;s take a closer look at the background, actions taken, and effects.</p>
<h2>[1]  Revising and Updating Mailing List Management📧</h2>
<p>This initiative began in my very first month after I joined the company, when I tried to call members for a meeting, but I had no idea who was on the mailing list. Although the Development Support Division where I belong, had an internal mailing list, the Global Development Division didn&#39;t have anything like that! So I thought, why not create a similar one?</p>
<p>But first, I had to identify which mailing lists even existed. Once I pulled the data, I was shocked! There were 94 mailing lists currently in use! Are we really using all of these? This question led me to carry out a full audit.</p>
<p>First, I followed the example set by the Development Support Division and created a similar list in Excel. I set up a matrix with registered members on the vertical (Y) axis, mailing lists on the horizontal (X) axis, and used a ● for registrants.
<img src="/assets/blog/authors/M.Mori/20230725/GK_ML_list_new.jpg" alt="GK_MLlist">
<em>Mailing List (Excuse the heavy redactions🤣)</em></p>
<p>Each team leader reviewed the table, and I carried out an audit by confirming list administrators, clarifying the purpose of each list, and verifying registered members. To make the mailing list information accessible to everyone, I shared the table via our cloud storage, BOX. To prevent the list from becoming outdated, I set up a process where any update requests must be submitted through a JIRA ticket, and I retained sole editing rights.</p>
<p>Having a list makes it easy to check who was registered to which list and what types of lists existed. It also helped raise awareness across the Global Development Division that mailing lists don&#39;t update automatically. Another benefit of visualizing all the mailing lists was the ability to check for duplicates created for similar purposes. This <em>yokoten</em> (horizontal deployment) was possible because, although I belong to the Development Support Division, I also support the Global Development Division.</p>
<h2>[2] Revising the Logbook and Approval Route for Lending Security Cards</h2>
<p>At the Jinbocho Office, external vendors who come in more than twice a week are given security cards. It&#39;s a simple process, but the Excel file used for tracking didn&#39;t keep any history. So, I updated it to support change tracking and made it possible to easily identify which cards were currently unused.</p>
<p>By using conditional formats and functions, only available cards could be selected. This prevents the accidental deletion of user information and makes audits much easier.</p>
<p><img src="/assets/blog/authors/M.Mori/20230725/security_card.png" alt="Jimbocho_securitycard"><em>Now automatically display the number of cards and available card numbers.</em></p>
<p>Regarding the change in approval route, because I belong to the Development Support Division, I couldn&#39;t submit requests for security card issuance on my own. I had to ask a member from the Global Development Division to do it on my behalf, just to follow the correct approval route. This roundabout process was not in line with the actual work, so I raised the question with the relevant division, &quot;Shouldn&#39;t we change this odd workflow?&quot;  After that, we organized the role and system of concurrent duties of the two divisions. Now, when I submit a request, I can select either the Development Support or Global Development approval route. This change eliminated the need for others to step in on my behalf and reduced the time spent on individual coordination.✨</p>
<h2>[3] Managing Test Devices📱</h2>
<p>Until now, test and verification devices such as smartphones used during system development were managed in a table on Confluence. But this made it difficult to see at a glance who was using which device, and the table often went out of date. In some cases, certain devices ended up being managed informally by individuals. At one point, someone almost purchased a new device without realizing we already had one. Around the same time, I found out that company-purchased books were being centrally managed using JIRA. That got me thinking, could we manage test devices the same way?</p>
<p> ➡️ <a href="https://blog.kinto-technologies.com/posts/2023-01-17-book-management/">How We Made Book Management Easier</a></p>
<p>As we transitioned to JIRA, I took the opportunity to do a full inventory check. This gave us visibility into whether anything was missing, broken, or not in use. (Some devices were even locked with unknown passwords.🔒)</p>
<p>Because test devices are used on a daily basis, we physically checked each one during the audit. We recorded the password settings and uploaded photos of each device to their respective JIRA tickets. This helped resolve confusion when device names alone weren&#39;t clear enough. By managing the devices in JIRA, all members can check the rental status at a glance, and by setting rental expiration dates, we can now track usage.</p>
<p><img src="/assets/blog/authors/M.Mori/20230725/GKDV_Ticket.jpg" alt="GK_test_device_jira"><em>Visualization of lending conditions, detailed device information is included in the ticket.</em></p>
<p>In addition, there is no longer the hassle of forgetting to update Confluence when borrowing or returning, or having to contact them via Slack every time.</p>
<p>Most importantly, by linking devices to specific users and assigning return dates, I feel that all members have become more aware that they are <strong>&quot;borrowing&quot;</strong> the device. I also set up JIRA to send reminders to the admin when return deadlines are approaching. Rina-san helped me implement this based on the existing book management system. Thank you so much for your support!</p>
<h2>[4] Creating Name Tags for Shared Umbrellas☔</h2>
<p>It all started with a request to &quot;clean up umbrellas that have been left in the umbrella stand at the entrance.&quot; So, I checked the other umbrella stands as well. Any umbrellas that had been left for several days were announced internally and then disposed of.</p>
<p>One comment I received in response to that announcement mentioned the idea of making the umbrellas available to anyone for shared use by putting a plastic tape over the clean umbrellas to be disposed of and repurpose them as office loaners.</p>
<p>I noticed that many of the umbrellas in the office stands were clear plastic or plain designs. I figured the number of abandoned umbrellas would probably keep growing, and people might start grabbing the wrong ones by mistake. That reminded me of writing my name on some masking tape and made a name tag with a rubber band for my umbrella in the past. lol</p>
<p>That worked fine for me, but I thought it would be nice if everyone had a name tag if possible, so I prepared Keychains.</p>
<p><img src="/assets/blog/authors/M.Mori/20230725/Jimbocho_Umbrella.jpg" alt="Jimbocho_Umbrella"><em>A keychain with your name on it to secure your umbrella.👍</em></p>
<p>This <em>kaizen</em> is not yet widespread, but I hope it will be used more and more, not only for umbrellas, but also as name tags to be attached to personal items stored in the refrigerator.</p>
<h1>Where Does the <em>Kaizen</em> Mindset Come From?</h1>
<p>Let me share the origins of my <em>kaizen</em> mindset.</p>
<p>I&#39;ve always enjoyed imagining things ever since I was a child. On my way to school, I used to often imagine things like, &quot;Wouldn&#39;t it be cool if the road just moved on its own? ✨&quot; or, &quot;What if a shield popped up automatically when it rained?✨&quot; (Kind of like something out of Doraemon, right?😅) I think <em>kaizen</em> is just an extension of that kind of thinking.</p>
<p>I believe that great people follow that imagination into careers in research or engineering, but in my case, since I&#39;m at an average level, it&#39;s more about solving the problems right in front of me. When I find myself thinking &quot;If only this were easier…🤔&quot;— this is when <em>kaizen</em> starts.</p>
<p>When it comes to work, the fundamental principle is <strong>&quot;Making work easier&quot; means &quot;making work enjoyable.&quot;</strong> Who wouldn&#39;t be happier if their job got just a little bit easier? Eventually, those easier ways of working become the norm.</p>
<p>The starting point is to make things easier for myself, but I also take the other person or people who will use it into consideration as I go along. Whenever I’m doing something repetitive or routine, I find myself thinking, &quot;Wouldn&#39;t it be nice if this were easier?” It may be difficult to fully realize that idea by myself, but ideally, the things that have already become easier now will eventually become norms, and whoever takes over from me will go on to make them even better. I&#39;d be thrilled if the improvements I made didn&#39;t stay as the final version, but went beyond me and continued to evolve in someone else&#39;s hands. Something like this is exciting to imagine, isn&#39;t it?</p>
<h1>Next <em>Kaizen</em> - The Next Issue I Want to Tackle</h1>
<p>Some recurring tasks are still handled in Excel, and I want to streamline them further, possibly by using macros. So, I&#39;ve recently started trial and error using Sherpa<a href="Sherpa%E3%81%AFSlack%E3%81%A7ChatGPT%E3%81%8C%E5%88%A9%E7%94%A8%E3%81%A7%E3%81%8D%E3%82%8B%E6%A9%9F%E8%83%BD%E3%81%A7%E3%81%99%E3%80%82%E7%A4%BE%E5%86%85%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%8C%E9%96%8B%E7%99%BA%E3%81%97%E3%81%A6%E3%81%8F%E3%82%8C%E3%81%BE%E3%81%97%E3%81%9F%EF%BC%81%E8%BF%91%E6%97%A5%E3%81%9D%E3%81%AE%E8%A8%98%E4%BA%8B%E3%82%82%E5%85%AC%E9%96%8B%E4%BA%88%E5%AE%9A%E3%81%AA%E3%81%AE%E3%81%A7%E3%81%8A%E6%A5%BD%E3%81%97%E3%81%BF%E3%81%AB%EF%BC%81">^2</a> which was just released internally, as well as ChatGPT. With a <em>kaizen</em> mindset at the core, I&#39;ll continue working to make things better!✨</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Post-Event Interview: Digital Talent Development Seminar in Chubu by Central Japan Economic Federation & Digital Literacy Council]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-04-18-EventReport20230208-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-04-18-EventReport20230208-en/</guid>
            <pubDate>Thu, 03 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Post-event interview: digital talent development seminar in Chubu]]></description>
            <content:encoded><![CDATA[<p>I am Aritome from the Development Support Division at KINTO Technologies.<br>I am in charge of organizing all-hands meetings, supporting engineer development and training programs.</p>
<p>At KINTO Technologies (KTC), we support our engineers&#39; growth through their work at the company. For this reason, we actively encourage participation in communities outside the company and speaking at external events. (President Kotera and Vice President Kageyama also frequently speak at externally hosted events.)  </p>
<p>On February 8, 2023, Wada-san, a young engineer from our data analytics team, joined a panel discussion as a guest speaker at the <a href="https://www.chukeiren.or.jp/news/p18639/">Digital Human Capital Development Seminar in Chubu</a>, hosted by the Central Japan Economic Federation and the Digital Literacy Council.</p>
<ul>
<li>What did you talk about?</li>
<li>What is your role at our company?</li>
</ul>
<p>I interviewed Wada-san after the seminar to find out more.</p>
<h2>To start with, could you introduce yourself?</h2>
<p><strong>Wada</strong>:<br>Hello!<br>My name is Wada and I work as a data scientist at KINTO Technologies. My main job is responding to analysis requests from both inside and outside the company, and developing AI functions for in-house apps.</p>
<p>Thank you for having me today!  </p>
<p><strong>Aritome</strong>:<br>Thank you!</p>
<h2>Can you tell us about your career path before joining KINTO Technologies?</h2>
<p><strong>Wada</strong>:<br>I majored in social informatics at university. It&#39;s not a familiar term, but basically, it&#39;s an applied field of informatics that focuses on using information and communication technologies to solve social issues.<br>After graduating from university, I joined an automotive parts manufacturer in 2019, where I worked on production management systems. Then in 2022, I made the move to my current role.</p>
<h2>What was the theme of the event, and what led to you speaking at the event?</h2>
<p><strong>Wada</strong>:<br>The <a href="https://www.chukeiren.or.jp/news/p18639/">Digital Human Capital Development Seminar in Chubu</a> was aimed at management and mid-level employees of various companies in the Chubu region, which stressed the importance of all employees acquiring digital literacy from now on.<br>At the event, three specific qualifications that will lead to acquiring digital literacy were recommended. The Information Technology Passport Examination, Data Scientist Certificate, and JDLA Deep Learning for GENERAL (G-certificate)</p>
<p>In the latter half of the event, a panel discussion was held featuring Ryutaro Okada, Board Director and Secretary General of the Japan Deep Learning Association, along with four panelists who had gained digital literacy by obtaining certifications. The discussion covered what they found beneficial about earning the certifications, challenges they faced, and how the experience has influenced their work.   </p>
<p>I also hold the JDLA Deep Learning for ENGINEER certification (commonly known as the E-Certificate) There was a call for panelists for the event within the certification holders&#39; community, and that&#39;s how I got the opportunity to take part in the event.</p>
<p><img src="/assets/blog/authors/s.wada/kaijo.jpg" alt=" Photo of the venue">
<em>Photo of the event venue</em></p>
<p><strong>Aritome</strong>:<br>I&#39;ve been hearing a lot about the G-Certificate lately.<br>Can you tell us more about it?</p>
<h2>Can you tell us more about the G-Certificate?</h2>
<p><strong>Wada</strong>:<br>The G-Certificate is a qualification that tests basic knowledge of deep learning.<br>The G stands for &#39;Generalist,&#39; and the test covers not only the meaning of technical terms, but also knowledge of the history of technology and legal regulations. It does not require much knowledge of math or coding, so it is also recommended for non-engineers! There&#39;s also a related qualification called E-Certificate, which is more focused on deep learning theory and implementation skills.</p>
<p>If you hold either, you can join a community called CDLE (Community of Deep Learning Evangelists). That&#39;s the community where I found the call for panelists for this event.</p>
<blockquote>
<p>CDLE is a community exclusively for people who&#39;ve passed either the G-Certificate or the E-Certificate, both run by the Japan Deep Learning Association (JDLA). It&#39;s a space for certified members to connect and share knowledge. It operates entirely on a non-profit basis.</p>
</blockquote>
<p>*Quoted from the CDLE guidelines, <a href="https://cdle.jp/about">CDLE community website</a>.</p>
<p><strong>Aritome</strong>:<br>So, there&#39;s a community of certified members.<br>With that shared learning experience, the conversation&#39;s sure to be lively!</p>
<h2>What motivated you to get certified in the first place?</h2>
<p><strong>Wada</strong>:<br>I thought that obtaining a certification would be the most efficient way to acquire systematic knowledge! When I first started learning about AI, I was mostly referencing sample code I found online and diving into machine learning and deep learning without really understanding how anything worked. At first, it was fun to see things run, but gradually I became interested in the mechanics behind. That&#39;s when I began reading more advanced books and technical blogs.  </p>
<p>However, learning this way gave me only bits of knowledge.<br>It was tough to learn the field in a way that was both systematic and comprehensive.  </p>
<p>So I decided to take the certification exam, since its syllabus was packed with carefully curated content and suited for obtaining systematic knowledge. To put it in an analogy, it&#39;s like filling a container with your favorite pebbles, each representing bits of knowledge, but there are still gaps. The syllabus is like water that fills those gaps with structured learning! (Does that make sense?) </p>
<p><img src="/assets/blog/authors/s.wada/image.png" alt="Image of knowledge">
<em>Image of knowledge acquisition</em></p>
<p><strong>Aritome</strong>:<br>I totally get that feeling of not knowing where to start when trying something new. When you&#39;re self-taught, it&#39;s hard to feel confident if your knowledge is all over the place.</p>
<h2>What challenges did you face and how did you approach studying for the certification?</h2>
<p><strong>Wada</strong>:<br>I had a certain level of understanding of how to use the technology from my self-study, but I had to re-learn the background, basic technology, history leading up to the technology, and legal frameworks.<br>In addition, at that time, the E-certificate exam didn&#39;t use any specific frameworks, and the questions were based on scratch implementations using NumPy. Since I had been working with scikit-learn and Keras, getting used to the unfamiliar syntax was definitely a challenge. But I wanted to fill in the gaps in my knowledge, so it was a perfect match for my original goal, worth the effort (laughs).</p>
<p><strong>Aritome</strong>:<br>Because it&#39;s a certification, I imagine you really have to study the full scope of the field, even areas you&#39;re not as comfortable with. It sounds like a challenge!</p>
<h2>Did getting the certification or studying a new field lead to any changes for you?</h2>
<p><strong>Wada</strong>:<br>Learning all the key terminology around AI gave me the confidence to start tackling more advanced books, including academic papers I wouldn&#39;t have dared to touch before. I can&#39;t say I breeze through them, but &quot;Ohhh! I can read! I&#39;m reading!&quot;(laughs)</p>
<p><img src="/assets/blog/authors/s.wada/yomeru.png" alt="I can read it!"></p>
<p><strong>Aritome</strong>:<br>That sense of growth must make all the effort feel worthwhile!</p>
<h2>What were some of the best things about being certified?</h2>
<p><strong>Wada</strong>:<br>Nowadays, AI is being integrated into many different areas, creating significant value<br>I think having the ability to look at different areas and ask, &quot;What if I combined AI with this?&quot;will become one of my personal strengths. With tools like ChatGPT lowering the barrier to entry, I believe we&#39;ll see even more accessible AI services emerging, and this trend will only continue to grow.</p>
<h2>At KINTO Technologies, are there any systems or cultural elements in place to support learning?</h2>
<p><strong>Wada</strong>:<br>There&#39;s a strong culture of sharing what we learn. We have study sessions across different scopes, within teams, across departments, and company-wide. Even small information sharing is encouraged. Our tech news Slack channel is constantly buzzing with interesting updates.<br>You can also easily request to purchase books that are useful for work, and you can access a variety of books on the <a href="https://blog.kinto-technologies.com/posts/2023-01-17-book-management/">online bookshelf</a> shared between offices.<br>If the opportunity comes up, like my case, you&#39;re free to speak at external events, too!</p>
<h2>What kind of employees are there at KINTO Technologies?</h2>
<p><strong>Wada</strong>:<br>My first impression after joining was, &quot;There are all kinds of people here!&quot;(lol)<br>At my previous job, almost everyone was a new graduates, so coming into a company where everyone is mid-career was a big change. Everyone brings their own specialty from past experience, and it&#39;s really inspiring to see those strengths complement each other to get things done!<br>I am expected to work as a specialist in the AI field, which makes it a really rewarding environment where I can keep growing.</p>
<h2>Is there anything you personally do to promote a learning culture?</h2>
<p><strong>Wada</strong>:<br>I try to be open about my own skills, what I&#39;ve been learning, and what I&#39;m interested in. It leads to people saying things like &quot;I found this article&quot; or asking &quot;Can you explain this?&quot; While I&#39;m explaining, I often learn something new, too. It creates a great feedback loop.  </p>
<h2>Lastly, do you have a message for our readers?</h2>
<p><strong>Wada</strong>:<br>I wasn&#39;t able to talk much about technical side this time, but I&#39;d like to write more about the AI products I work on in the future!</p>
<p>Thank you for reading all the way to the end!</p>
<h1>We Are Hiring!</h1>
<p>We are looking for people to work with us to create the future of mobility together. If you are interested, please feel free to contact us for a casual interview.</p>
<p>@<a href="https://www.kinto-technologies.com/recruit/">card</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/s.wada/wordcloud_coverimage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[KINTOテクノロジーズは SRE NEXT 2025 にプラチナスポンサーとして協賛します]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-07-03-sre-next-2025/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-07-03-sre-next-2025/</guid>
            <pubDate>Thu, 03 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズは SRE NEXT 2025 にプラチナスポンサーとして協賛します]]></description>
            <content:encoded><![CDATA[<p>こんにちは！SREチームのkasaiです。</p>
<p>KINTOテクノロジーズ株式会社（以下、KTC）は、2025年7月11日（金）〜12日（土）にTOC有明で開催される「SRE NEXT 2025」にて、プラチナスポンサーとして協賛いたします！
KTCがSRE NEXTのスポンサーになるのは今回が初めてです。</p>
<p>弊社SREチームは昨年から再スタートを切りました。 SREを実践する難しさを日々感じつつも、サービスの信頼性を高めるための活動に取り組んでいます。みなさんも同じように試行錯誤を重ねているのではないかと思います。
そんなSREの方々が集まる場を支えられればと思い、スポンサーに立候補いたしました！</p>
<h1>SRE NEXTとは</h1>
<blockquote>
<p>信頼性に関するプラクティスに深い関心を持つエンジニアのためのカンファレンスです。 同じくコミュニティベースのSRE勉強会である「SRE Lounge」のメンバーが中心となり運営・開催されます。
SRE NEXT 2025のテーマは「Talk NEXT」です。SRE NEXT 2023で掲げた価値観 Diversity、Interactivity、Empathyを大切にしつつ、SREの担う幅広い技術領域のトピックや組織、人材育成に対してディスカッションやコミュニケーションを通じて、新たな知見や発見を得られる場にします。</p>
</blockquote>
<p><a href="https://sre-next.dev/2025/">Home | SRE NEXT 2025</a></p>
<h3>開催概要</h3>
<p><strong>開催日：2025年7月11日（金）・12日（土）</strong>
<strong>会場：TOC有明及びオンライン</strong>
<strong>公式サイト：<a href="https://sre-next.dev/2025/">https://sre-next.dev/2025/</a></strong></p>
<h1>スポンサーセッションあります！</h1>
<p>DAY 2 (7/12) 13:00 - 13:20にTrack Bにて「ロールが細分化された組織でSREは何をするか？」というタイトルで長内がスポンサーセッションをする予定です。</p>
<p>細分化された組織の中においてロールが重なり合う中「自分たちは何をすべきか？」「SREとしての価値はどこにあるのか？」といった問いにSREチームがどのように向き合ってきたのかをお話しします。</p>
<p><img src="/assets/blog/authors/kasai/20250703/session.png" alt="セッション">
<em>詳細：<a href="https://sre-next.dev/2025/schedule/#slot081">https://sre-next.dev/2025/schedule/#slot081</a></em></p>
<h1>ブース出展もします！</h1>
<p>ブースでは簡単に答えられるアンケートを用意しています。</p>
<p>ご回答いただくとガチャガチャが回せて、オリジナルノベルティーが当たりますので、ぜひブースに遊びにきてください！</p>
<p>当日はSREのメンバーもブースにいますので、SREについてTalkしましょう！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/kasai/20250703/cover_image.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Quickly Building a Backend for a Payment Management Tool from Scratch with AWS SAM]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-06-19-awssam-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-06-19-awssam-en/</guid>
            <pubDate>Wed, 02 Jul 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Here's how we built a backend for an internal payment operations system using AWS SAM.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I am Nishida, a member of the payment platform development team at KINTO Technologies.
In this article, I&#39;d like to share how we used AWS SAM to build the backend for an internal payment operations system, which was also introduced earlier <a href="https://blog.kinto-technologies.com/posts/2022-12-06-RemoteMobProgramming/">in this article</a>.</p>
<h1>What is AWS SAM?</h1>
<p>First off, AWS SAM (Serverless Application Model) is a tool that makes it easy to build and deploy serverless services like Lambda and API Gateway. With AWS SAM, developers no longer need in-depth knowledge of infrastructure and can focus on building applications using a serverless architecture.</p>
<h1>Why We Chose AWS SAM</h1>
<p>Right after joining KINTO Technologies, I became involved in developing a payment operations system. Given the short development timeline of just 2 to 3 months, we needed to select backend technologies that supported rapid iteration Since it was an internal system with limited traffic, we decided to go with AWS SAM, leveraging my prior experience with it from a previous role.</p>
<h1>How to Use AWS SAM</h1>
<p>I&#39;d like to use AWS SAM to build a REST API using API Gateway and Lambda in a serverless setup. <img src="/assets/blog/authors/m.nishida/20230619/sam_diagram.png" alt="sam_diagram"></p>
<p>Here&#39;s what the directory structure looks like:</p>
<pre><code>.
├── hello_world
│   ├── __init__.py
│   └── app.py
└── template.yaml
</code></pre>
<p>First, install AWS SAM from the official <a href="https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/install-sam-cli.html">documentation</a>.</p>
<p>AWS SAM uses a file called a template to manage AWS resources.</p>
<pre><code class="language-yaml:">AWSTemplateFormatVersion: &#39;2010-09-09&#39;
Transform: AWS::Serverless-2016-10-31
Description: &gt;
  sam-app

  Sample SAM Template for sam-app
Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: HelloWorldFunction
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Events:
        HelloWorld:
          Type: Api
          Properties:
            Path: /hello
            Method: get
</code></pre>
<pre><code class="language-python:">import json

def lambda_handler(event, context):
    body = {
        &quot;message&quot;: &quot;hello world&quot;,
    }

    response = {
        &quot;statusCode&quot;: 200,
        &quot;body&quot;: json.dumps(body)
    }

    return response
</code></pre>
<p>We deploy using the sam command. This time, I&#39;ll try deploying interactively using the <code>--guided</code> option.</p>
<pre><code class="language-bash">sam deploy --guided
</code></pre>
<p>Enter the stack name, region, etc.</p>
<pre><code class="language-bash">Stack Name [sam-app]: # デプロイするスタック名を入力
AWS Region [ap-northeast-1]: # デプロイするリージョンを入力
#Shows you resources changes to be deployed and require a &#39;Y&#39; to initiate deploy
Confirm changes before deploy [y/N]: # 変更内容を確認するかを入力
#SAM needs permission to be able to create roles to connect to the resources in your template
Allow SAM CLI IAM role creation [Y/n]: # SAM CLI が IAM ロールを作成するかを入力
#Preserves the state of previously provisioned resources when an operation fails
Disable rollback [y/N]:　# ロールバックを無効にするかを入力
HelloWorldFunction may not have authorization defined, Is this okay? [y/N]: # Lambda に対する認可を設定するかを入力
Save arguments to configuration file [Y/n]: # 設定を保存するかを入力
SAM configuration file [samconfig.toml]: # 設定ファイルの名前を入力
SAM configuration environment [default]: # 環境名を入力
</code></pre>
<p>Once the deployment is complete, check the Lambda console to confirm that <code>HelloWorldFunction</code> has been created.</p>
<p><img src="/assets/blog/authors/m.nishida/20230619/lambda_cosole_functions.png" alt="lambda_cosole_functions"></p>
<p>You can also find the endpoint by selecting the API Gateway that triggers Lambda. <img src="/assets/blog/authors/m.nishida/20230619/lambda_cosole.png" alt="lambda_cosole"></p>
<p>Let&#39;s try sending a request using curl.</p>
<pre><code class="language-bash">curl https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello
</code></pre>
<p>If the request is successful, you&#39;ll get a response like this:</p>
<pre><code class="language-json">{&quot;message&quot;: &quot;hello world&quot;}
</code></pre>
<h1>After Trying It Out</h1>
<p>As I had prior experience with AWS SAM, I was able to get the basic infrastructure up and running in just a day, which helped us stay on track with the development schedule.<br>Once you&#39;re familiar with it, one of the best things about AWS SAM is how easy it makes building APIs in a serverless setup.<br>In addition to API Gateway and Lambda, we also use AWS SAM to build EventBridge and SQS, which are used for periodic processing such as batch processing. The official documentation has also improved a lot, which I think has lowered the barrier to getting started.</p>
<h1>Conclusion</h1>
<p>In this article, I shared how we quickly built the backend for a payment operations system from scratch using AWS SAM.<br>Since it&#39;s a tool provided by AWS, it has high compatibility, reduces the overhead of environment setup, and allows you to focus more on actual development. If you&#39;re interested, I highly recommend giving it a try.  </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/m.nishida/20230619/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Achieving Auto Provisioning in ECS Environments]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-05-30-AutoProvisioning-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-05-30-AutoProvisioning-en/</guid>
            <pubDate>Tue, 01 Jul 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello. My name is <a href="https://twitter.com/sokasanan">Shimamura</a>, and I used to be a DevOps engineer in the Platform Group, but now I&#39;m on the Operation Tool Manager team within the same gorup, where I&#39;m responsible for Platform Engineering and tool-related development and operations.</p>
<p>KINTO Technologies&#39; Platform Group promotes IaC using Terraform. We define design patterns that are frequently used within the company and provide them as reference architectures, and each environment is built based on those patterns. For the sake of control, each environment from development to production is built upon ticket-based requests.  Before building the development environment, we prepare a sandbox environment (AWS account) for the application department&#39;s verification. However, this is often built manually and there are many differences with the environment built by Platform Group.</p>
<p>If a design pattern were available, the environment could be automatically built upon developer request, which would eliminate the waiting time between the request and the creation of the environment, and improve development efficiency. I think this kind of request-based automated building is a common requirement in DevOps, but it seems that Kubernetes is still the most commonly used application execution platform.</p>
<p>KINTO Technologies uses Amazon ECS + Fargate as its application execution platform, so I would like to introduce this as a (probably) rare example of automated environment building for ECS.</p>
<h1>Background</h1>
<h2>Challenges</h2>
<p><img src="/assets/blog/authors/JumpeiShimamura/20230530/sabun.png" alt="Something&#39;s different"></p>
<ul>
<li>The system is not around when application developers need it (during verification/launch)<ul>
<li>As part of the DevOps activities, I researched AutoProvisioning (automated environment building) and felt that it was common, but it is not present within our company.</li>
</ul>
</li>
<li>There is a large difference between an environment built in a sandbox environment with a relatively high degree of freedom and an environment built according to the design patterns provided by Platform Group.<ul>
<li>IAM permissions and security</li>
<li>Presence of common components such as VPC/Subnet/NAT Gateway</li>
<li>etc.</li>
</ul>
</li>
<li>As a result, the communication costs becomes higher for both parties when requesting a build.</li>
</ul>
<h2>Solution</h2>
<p><strong>Why not create an automated building mechanism?</strong></p>
<p>Since this is a design pattern, there are some AWS services that may be missing, but it&#39;s tolerable and presumably they will be added manually.</p>
<p>As a first step, it&#39;s worthwhile to automatically build an environment on AWS in about an hour so that you can check the operation of your application and prepare for CICD.</p>
<h1>Let&#39;s Make It</h1>
<p>Thankfully, Terraform is becoming more modular so we can build environments in a variety of patterns by simply writing a single file (locals.tf), so I think of the below as a base:</p>
<ul>
<li><strong>Used in-house created modules (Must)</strong></li>
<li><strong>Built with in-house design patterns as a base (Must)</strong></li>
<li>Made sure that DNS is automatically configured and communication is possible via HTTPS.</li>
<li>It should be able to automatically generate locals.tf<ul>
<li>Prototyped the application to see if it can be structured and generated using Golang&#39;s HCLWrite</li>
<li>After prototyping, I found that structuring was difficult, so I eventually gave up on automatic generation.</li>
<li>I took care of it by replacing some parameters from the template file</li>
<li>Since the process was about replacing, detailed settings for each component are not possible.</li>
</ul>
</li>
</ul>
<h1>The Final Result</h1>
<p><img src="/assets/blog/authors/JumpeiShimamura/20230530/gui.png" alt="GUI"></p>
<p>From the GUI on the CMDB select</p>
<ul>
<li>product</li>
<li>design patterns</li>
</ul>
<p>When you select this and click Create New, the specified configuration will be built in the sandbox environment of the department associated with the product in 10 to 40 minutes (depending on the configuration).</p>
<h2>Overall Configuration</h2>
<p><img src="/assets/blog/authors/JumpeiShimamura/20230530/kousei-1.png" alt="Provisioning overall configuration"></p>
<h2>Individual Explanation</h2>
<p>I separated the part that creates the Terraform code from the part that actually builds it in the sandbox environment so that they could be tested separately.</p>
<h3>Terraform Code Generation Parts</h3>
<ul>
<li>ProvisioningSourceRepo<ul>
<li>Issue management</li>
<li>GitHub Actions execution</li>
<li>Terraform code for the created sandbox environment</li>
<li>CIDR list for each sandbox environment</li>
</ul>
</li>
<li>ProvisioningAppRepo<ul>
<li>Template for design pattern</li>
<li>Yaml (buildspec.yml) in CodeBuild</li>
<li>Various ShellScripts running on CodeBuild</li>
</ul>
</li>
<li>InfraRepo<ul>
<li>TerraformModule</li>
</ul>
</li>
</ul>
<h3>AWS Environment Building Part</h3>
<ul>
<li>S3<ul>
<li>Source and Artifact in CodePipeline</li>
</ul>
</li>
<li>EventBridge<ul>
<li>CodePipeline Trigger</li>
</ul>
</li>
<li>CodePipeline/CodeBuild<ul>
<li>Actual construction environment</li>
</ul>
</li>
<li>Route53 (Dev)<ul>
<li>Delegate authority from the production DNS and use Route53 in the Dev environment</li>
</ul>
</li>
</ul>
<h3>Terratest (Apply)</h3>
<p>The Terratest sample looks like this. The test is nested so that if any of the Init, Plan, or Apply steps fail, the test will end. If the Apply step fails midway, Destroy what was applied up to that point. I think you will be able to write it more neatly if you have knowledge of Golang.</p>
<pre><code class="language-go:apply_test.go">package test

import (
	&quot;github.com/gruntwork-io/terratest/modules/terraform&quot;
	&quot;testing&quot;
)

func TestTerraformInitPlanApply(t *testing.T) {
	t.Parallel()

	awsRegion := &quot;ap-northeast-1&quot;

	terraformOptions := &amp;terraform.Options{
		TerraformDir: &quot;TerraformファイルがあるPATH&quot; + data.uuid,

		EnvVars: map[string]string{
			&quot;AWS_DEFAULT_REGION&quot;: awsRegion,
		},
	}

	// InitでErrorがなければPlan、PlanでErrorがなければApplyと
	// IFで入れ子構造の対応を実施(並列だとInitで失敗してもテストとしてすべて走る)
	if _, err := terraform.InitE(t, terraformOptions); err != nil {
		t.Error(&quot;Terraform Init Error.&quot;)
	} else {
		if _, err := terraform.PlanE(t, terraformOptions); err != nil {
			t.Error(&quot;Terraform Plan Error.&quot;)
		} else {
			if _, err := terraform.ApplyE(t, terraformOptions); err != nil {
			    t.Error(&quot;Terraform Apply Error.&quot;)
			    terraform.Destroy(t, terraformOptions)
			} else {
				// 正常終了
			}
		}
	}	
}
</code></pre>
<h2>Elements</h2>
<table>
<thead>
<tr>
<th>Name</th>
<th>Overview</th>
</tr>
</thead>
<tbody><tr>
<td>CMDB (in-house production)</td>
<td>Configuration Management Database to manage databases  Since rich functions were unnecessary, KINTO Technologies has developed an in-house CMDB. On top of that, we are creating a request form for automatic building. In addition, after being built, FQDN and other information are automatically registered in the CMDB.</td>
</tr>
<tr>
<td>Terraform</td>
<td>A product for coding various services, AWS among them. IaC. In-house design patterns and modules are created with Terraform.</td>
</tr>
<tr>
<td>GitHub</td>
<td>A version control system for storing source code. Build requests are logged by raising an Issue. Also, since Terraform code is required for deletion, etc., we also save each code for the sandbox environment.</td>
</tr>
<tr>
<td>GitHubActions</td>
<td>The CI/CD tool included in GitHub. At KINTO Technologies, we utilize GitHub Actions for tasks such as building and releasing applications In this case, we are using the issue filing as a trigger to determine whether to Create/Delete, select the necessary code group, compress it, and connect to AWS.</td>
</tr>
<tr>
<td>CodePipeline/CodeBuild</td>
<td>CICD-related tools provided by AWS. Using it to run Terraform code. We could run Terraform/Terratest on GitHubActions, but since we use GitHubActions daily for application builds, we chose to use this to avoid the impact on each product team due to usage limits, etc.</td>
</tr>
<tr>
<td>Terratest</td>
<td>A Go library for testing infrastructure code, etc. You can also test modules, but in this case we are using it to recover from failures in the middle of Terraform Apply. <a href="https://terratest.gruntwork.io/">Click here for the official site</a></td>
</tr>
</tbody></table>
<h2>Restrictions</h2>
<ul>
<li>We target multiple sandbox environments (AWS accounts) associated with each development team, but only one can be created at a time (exclusive).<ul>
<li>Since CodePipeline/CodeBuild are running in the same environment due to DNS</li>
</ul>
</li>
<li>We also create parts that are not run in the application.<ul>
<li>It may seem like there is a lot of waste, but this is due to the build design pattern.</li>
<li>It is built as a seamless line from FQDN to DB.</li>
</ul>
</li>
<li>You need to set the VPC, etc. in the Module beforehand.<ul>
<li>You need to build a set of common components such as VPC beforehand.</li>
</ul>
</li>
</ul>
<h1>What to Do if There Are No Modules</h1>
<p>KINTO Technologies has been working on design patterns for some time, so we have the advantage of being able to easily use Terraform to build everything from CloudFront to RDS. What can you do if you haven&#39;t progressed that far but still want to implement AutoProvisioning using ECS?</p>
<h2>I Thought About It</h2>
<p>Create up until the ECS Cluster in advance.</p>
<ul>
<li>ECS Service</li>
<li>ECR Repository</li>
<li>ALB TargetGroup</li>
<li>ALB ListenerRule</li>
<li>IAM Role</li>
<li>Route53</li>
</ul>
<p>I think it would be easier to prepare a Terraform file with the above, and then build it. TaskDefinition can be created if you have permission, so it&#39;s up to the user.</p>
<h2>Configuration Proposal</h2>
<p>I think CodePipeline/CodeBuild would be fine instead of GitHubActions, but when you consider the need to prepare a GUI like CodeCommit, wouldn&#39;t it be easier to just put it all together on GitHub? So, here is the configuration. I haven&#39;t used AWS Proton yet, so I haven&#39;t considered it.</p>
<p><img src="/assets/blog/authors/JumpeiShimamura/20230530/non_service.png" alt="Service Provisioning Configuration"></p>
<p>I think it would be possible to separate the Parameter parts such as locals.tf and create them using the sed command or Golang&#39;s HCL library. Once you have confirmed the build using Terratest, etc., add any FQDN to the ALB alias and match it with the ListenerRule.</p>
<h1>Next Steps</h1>
<p>Originally, we had hoped to offer it in advance to get feedback, but at present it hasn&#39;t been used much. We have provided a GUI for this purpose, and we plan to start by having a variety of people use it and receive feedback.</p>
<p>However, I think there are many things we can do, such as increasing the number of compatible design patterns and simplifying the associated CICD settings. I would really like to introduce Kubernetes and then move on to AutoProvisioning, which has many applications.</p>
<p>｜・ω・`) Is that not possible?</p>
<h1>Impressions</h1>
<p>To be honest, I tried hard to automatically generate templates using Golang, but gave up because the HCL structure of our in-house design patterns was difficult to analyze and reconstruct. There was some talk internally about this being a reinvention of the console, but if we could get that far, I think we might be able to automate not only the sandbox environment but also the STG environment. For Platform Group, the environment can be created simply by tapping and selecting a few items on the GUI. It&#39;s really simple.</p>
<p>To be honest, I wanted to reach that level, but I think it was good that I was able to take even the first step.</p>
<p>In Kubernetes, I think it might be possible to create something similar by preparing a Helm chart as a template. I would like to consider alternative methods and try various things.</p>
<h1>Summary</h1>
<p>The Operation Tool Manager Team oversees and develops tools used internally throughout the organization. As I wrote in my previous <a href="https://blog.kinto-technologies.com/posts/2022-12-09-AWS_Prometehus_Grafana_o11y/">O11y article</a>, we organize the mechanisms and present them to application developers so that they can use them on a self-service basis, supporting the creation of value by these developers.</p>
<p>A PlatformEngineering meet up was held a little while ago, and it&#39;s reassuring to know that this is in line with the direction we&#39;re moving forward in. The Operation Tool Manager team also has an in-house tool building department, allowing developers to quickly and intensively create value for their applications.</p>
<p>Please feel free to contact us if you are interested in any of these activities or would like to hear from us.</p>
<p>@<a href="https://hrmos.co/pages/kinto-technologies/jobs/1811937538128224304">card</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/JumpeiShimamura/20230530/0530_title.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Reporting Back from the Sysdig Kraken Hunter Workshop]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-14-Sysdig_Kraken_Hunter_ワークショップ_参加レポート-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-14-Sysdig_Kraken_Hunter_ワークショップ_参加レポート-en/</guid>
            <pubDate>Mon, 30 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Reporting Back from the Sysdig Kraken Hunter Workshop]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello! I&#39;m Tanachu from the Security &amp; Privacy Group at KINTO Technologies! I usually work on log monitoring and analysis using SIEM, building monitoring systems, and handling cloud security tasks as part of some projects in the SCoE group (here you can read about<a href="https://blog.kinto-technologies.com/posts/2024-05-13-SCoE/">what&#39;s the SCoE group?</a>) . <a href="https://blog.kinto-technologies.com/posts/2025-02-26-newcomers-introduction-oct-nov/">Here</a> is my self-introduction.</p>
<p>In this article, we share a report on our visit to the &quot; <a href="https://go.sysdig.com/kraken-hunter">Sysdig Kraken Hunter Workshop </a>,&quot; held on March 26, 2025, at the <a href="https://corp.collabo-style.co.jp/">Collaboration Style</a> event space near Nagoya Station.</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%81%AE%E6%A7%98%E5%AD%90.png" alt="The Event Space.png"> <em>The Event Space</em></p>
<h1>Using Sysdig Secure at KINTO Technologies</h1>
<p>At KINTO Technologies, we mainly use Sysdig Secure for Cloud Security Posture Management (CSPM) and Cloud Detection and Response (CDR). I&#39;ve put together more details in this blog, so feel free to take a look.</p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-12-14-CloudSecurityEngineer/">A Day in the Life of a KTC Cloud Security Engineer</a></p>
<h1>What is the Sysdig Kraken Hunter Workshop?</h1>
<p>Sysdig is a company founded by Loris Degioanni, the co-developer of the well-known network capture tool, Wireshark. It offers security solutions for cloud and container environments, built around <a href="https://falco.org/">Falco</a>, an open-source standard for cloud-native threat detection developed by Sysdig. We use Sysdig Secure to monitor cloud activities such as permission settings and account or resource creation in cloud environment.</p>
<p>The Sysdig Kraken Hunter Workshop is a hands-on session where you run simulated attacks on a demo Amazon EKS environment. You go through a series of modules using Sysdig to practice detection, investigation, and response. If you pass the post-workshop exam, you&#39;ll earn a Kraken Hunter certification badge.</p>
<p>In this blog, I&#39;ll walk you through three modules that stood out the most.</p>
<h1>Module 1: Simulated Attack and Event Investigation</h1>
<p>In this module, we carried out a simulated attack on a demo Amazon Elastic Kubernetes Service (Amazon EKS) environment and used Sysdig Secure to detect and investigate the event.</p>
<p>First, following the provided documentation, we simulated a remote code execution (RCE) attack on the Amazon EKS demo environment. The simulated actions included:</p>
<ul>
<li>Reading, writing, and executing arbitrary files on the system</li>
<li>Downloading files onto the system</li>
</ul>
<p>After running the simulated attack, we accessed the Sysdig Secure console via browser. By checking the status of the targeted resources, we could confirm that Sysdig had detected events related to the attack.</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/insights2.png" alt="insights2.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">Reference: sysdig-aws workshop-instructions-JP</a></em></p>
<p>Digging deeper, we confirmed that Sysdig Secure had picked up the simulated attack in real time.</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/insights3.png" alt="insights3.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">Reference: sysdig-aws workshop-instructions-JP</a></em></p>
<p>This hands-on flow let us try out a simulated attack and see exactly how Sysdig Secure handles detection and investigation through its console.</p>
<p>By running the attack myself and going through the investigation process with Sysdig Secure, I felt like I got a solid understanding of what the tool is capable of.</p>
<h1>Module 2: Host and Container Vulnerability Management</h1>
<p>In this module, we explored Sysdig Secure&#39;s features for managing vulnerabilities in both hosts and containers. Since our own products use containers and follow a microservices architecture, this topic is especially relevant to us.</p>
<p>Sysdig Secure offers several types of vulnerability scans: Runtime Vulnerability Scanning, Pipeline Vulnerability Scanning, and Registry Vulnerability Scanning.</p>
<p>The Runtime Vulnerability Scan lists all containers that have run in your monitored environment in the past 15 minutes, along with all hosts/nodes that have the Sysdig Secure Agent installed. Resources are automatically sorted by severity based on the number and risk level of vulnerabilities, making it easy to spot what needs your attention first.</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/vuln1.png" alt="vuln1.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">Reference: sysdig-aws workshop-instructions-JP</a></em></p>
<p>You can also click on any listed item to drill down and view vulnerability details.</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/vuln4.png" alt="vuln4.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">Reference: sysdig-aws workshop-instructions-JP</a></em></p>
<p>Pipeline Vulnerability Scan checks container images for vulnerabilities before they&#39;re pushed to a registry or deployed to a runtime environment. Registry Vulnerability Scan targets images already stored in your container registry. This way, you can check for vulnerabilities at each phase of the container image lifecycle, from development to production.</p>
<p>There are plenty of security tools out there for vulnerability management, but the Sysdig Secure console stood out to me for its sophisticated UI and intuitive usability.</p>
<h1>Module 3: Container Posture &amp; Compliance Management</h1>
<p>In this module, we experienced how Sysdig Secure helps manage posture and compliance in cloud environments. As you may have probably seen or heard in the news, misconfigurations in the cloud are a major cause of security incidents. Since we build our products in a fully cloud-native setup, this isn&#39;t just others problem—it&#39;s something we take seriously. That&#39;s why this feature caught our attention.</p>
<p>As a posture and compliance management feature, Sysdig Secure allows you to check if your environment complies with common standards like CIS, NIST, SOC 2, PCI DSS, and ISO 27001.</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/posture1.png" alt="posture1.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">Reference: sysdig-aws workshop-instructions-JP</a></em></p>
<p>It also highlights non-compliant resources and shows you how to fix them. While it&#39;s hard to say whether the steps will be practical in every situation, having that guidance readily available does save workloads on researching fixes. As an admin, that&#39;s a huge plus.</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/posture2.png" alt="posture2.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">Reference: sysdig-aws workshop-instructions-JP</a></em></p>
<h1>Kraken Hunter Certification Exam</h1>
<p>The Kraken Hunter certification exam had about 30 to 40 questions on a dedicated web page. The questions covered topics from the workshop, so if you paid attention, you had a solid shot at passing.</p>
<p>I struggled a bit with some of the finer details introduced at the start of the workshop, but I managed to pass the exam!</p>
<p>Here&#39;s the certification badge awarded to those who pass:</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/KrakenHunter_CertBadge.png" alt="KrakenHunter_CertBadge.png"> <em>Kraken Hunter Certification Badge</em></p>
<h1>Using Sysdig Secure Going Forward</h1>
<p>We&#39;re exploring and pushing the following ways to get the most out of Sysdig Secure:</p>
<ul>
<li>CSPM: Creating custom policy rules in <a href="https://www.openpolicyagent.org/docs/latest/policy-language/">Rego</a> based on our governance framework to ensure cloud security that aligns with our internal policies.</li>
<li>CDR: Building custom rules using <a href="https://falco.org/">Falco</a> to expand threat detection tailored to our environment.</li>
<li>CWP: Testing and implementing Cloud Workload Protection (CWP) to secure our container workloads.</li>
</ul>
<h1>Summary</h1>
<p>In the Sysdig Kraken Hunter workshop, we conducted a simulated attack against an Amazon EKS demo environment and got hands-on with Sysdig Secure—detection, investigation, response, and more.</p>
<p>Since we&#39;ve only used a limited set of Sysdig Secure&#39;s features at our company, most of what was introduced was new to us. While we fumbled a bit at first, it was a great chance to see what the tool is truly capable of.</p>
<p>Joining the in-person workshop also gave us the chance to hear real stories from other companies—their challenges and efforts in the field. Big thanks to the organizers for making this happen.</p>
<h1>Conclusion</h1>
<p>Our Security and Privacy Group, along with the SCoE group who joined this workshop, are looking for new teammates. We welcome not only those with hands-on experience in cloud security but also those who may not have experience but have a keen interest in the field. Please feel free to contact us.</p>
<p>For more information, please <a href="https://hrmos.co/pages/kinto-technologies/jobs?category=2037393016523087875">check here</a>.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[社内フロントエンド勉強会について]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-30-frontend-study-group/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-30-frontend-study-group/</guid>
            <pubDate>Mon, 30 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[社内フロントエンド勉強会の活動報告について本記事で共有します]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは！<br>新車サブスク開発G、Osaka Tech Lab 所属の high-g（<a href="https://x.com/high_g_engineer">@high_g_engineer</a>）です。<br>今回は、社内で立ち上げたフロントエンド勉強会について紹介します。</p>
<h2>開催経緯</h2>
<p>ある日、新車サブスク開発Gの上長との1on1で、<a href="https://2024.tskaigi.org/talks">TSKaigi 2024のタイムテーブル</a>を見せる機会がありました。
それを見た上長からは「幅広いテーマが扱われていて、非常にいい教材だね。これを使って、各部署のフロントエンドエンジニア同士で知見を共有し、横のつながりを作る勉強会があったらいいね」という前向きな提案をもらいました。</p>
<p>その言葉をきっかけに、参加意欲の高そうなフロントエンドエンジニアを募り、社内勉強会の企画がスタートしました。</p>
<h2>勉強会の目的</h2>
<p>この勉強会では、以下の3つを目的に掲げています。</p>
<ol>
<li><strong>学習</strong>：外部カンファレンスの発表内容やWeb標準仕様を共有、議論を通じて理解を深める</li>
<li><strong>実践</strong>：学んだ内容を実際のプロダクトに適用する</li>
<li><strong>共有</strong>：実践から得た知見や課題を参加者間で共有し、組織全体のナレッジとして蓄積する</li>
</ol>
<p>単に知識を得て終わるのではなく、「実際に業務で使える状態になる」ことを目指した実践的な勉強会です。
週に1回、1時間の枠を確保し、読み合わせ・モブプログラミング・ハンズオンなど、内容に応じた形式で実施しています。</p>
<h2>主なテーマと発展経緯</h2>
<p>2024年9月30日から現在までに34回開催し、以下のような内容を取り組みました。</p>
<h3>カンファレンスや技術イベントの知見共有（第1〜17回）</h3>
<p>まずは、勉強会を定着させるために、<a href="https://2024.tskaigi.org/">TSKaigi 2024</a>や<a href="https://jsconf.jp/2024/">JSConf JP 2024</a>の発表内容を中心に、フロントエンド技術の最新動向を参加者で学習しました。</p>
<ul>
<li><strong>Prettierの未来を考える</strong>（第1回）：コードフォーマッターの今後の方向性</li>
<li><strong>TypeScriptのパフォーマンス改善</strong>（第2回）：実際の業務コードでの課題と照らし合わせ</li>
<li><strong>全てをTypeScriptで統一したらこうなった！</strong>（第3回）：フルスタック開発事例</li>
<li><strong>TypeScript化の旅: Helpfeelが辿った試行錯誤と成功の道のり</strong>（第5回）</li>
<li><strong>TanStack Routerで型安全かつ効率的なルーティング</strong>（第7回）</li>
<li><strong>Storybook駆動開発 UI開発の再現性と効率化</strong>（第9回）</li>
<li><strong>TypeScriptで型定義を信頼しすぎず「信頼境界線」を設置した話</strong>（第10回）</li>
<li><strong>mizchiさんによる「LAPRAS 公開パフォーマンスチューニング」</strong>（第12〜13回）：外部事例から学ぶパフォーマンス改善</li>
<li><strong>Yahoo! JAPANトップページにおけるマイクロフロントエンド</strong>（第15回）：大規模組織での開発事例</li>
<li><strong>JavaScript のモジュール解決の相互運用性</strong>（第16回）</li>
<li><strong>You Don&#39;t Know Figma Yet - FigmaをJSでハックする</strong>（第17回）</li>
</ul>
<h3>チーム間知見共有と相互理解（第18〜24回）</h3>
<p>参加者が増えたことで、個人のスキル共有や各部署のフロントエンドチームの状況共有を実施しました。
これにより、今後の勉強会の方向性を検討するとともに、各チームのプロジェクトをコードベースで確認し、KINTOテクノロジーズ全体のフロントエンド課題を共有できました。</p>
<ul>
<li><strong>ここまでのふりかえり</strong>（第18回）：勉強会の方向性を議論</li>
<li><strong>自己紹介 〜これまでのキャリアを添えて〜</strong>（第19回）：メンバー間の相互理解促進</li>
<li><strong>各チームのフロントエンド開発状況共有会</strong>（第20〜24回）：5回シリーズで各チームの技術スタック、課題、取り組みを詳細に共有</li>
</ul>
<h3>Web標準の理解と実践（第25〜28回）</h3>
<p>ふりかえり会で、Web仕様を理解したいという声があったため、Baselineを全員で掘り下げながら理解する取り組みをしました。</p>
<p><a href="https://web.dev/baseline?hl=ja">https://web.dev/baseline?hl=ja</a></p>
<ul>
<li><strong>Baselineの理解</strong>（第25〜27回）：3回シリーズでWeb標準について体系的に学習</li>
<li><strong>Baselineの振り返り＆次回以降やること議論</strong>（第28回）：学習内容の整理と今後の方向性検討</li>
</ul>
<h3>実践的なパフォーマンス改善（第29回〜現在）</h3>
<p><a href="https://www.youtube.com/watch?v=j0MtGpJX81E">mizchiさんの公開パフォーマンスチューニングの動画</a>を参考にし、実際のプロダクトのパフォーマンスチューニングを実践しました。</p>
<p><a href="https://www.youtube.com/watch?v=j0MtGpJX81E">https://www.youtube.com/watch?v=j0MtGpJX81E</a></p>
<ul>
<li><strong>FACTORYパフォーマンス改善</strong>（第29〜30回）：2回シリーズで具体的な改善施策と結果を共有</li>
<li><strong>TSKaigi 2025 登壇内容シェア会</strong>（第31回）：社内メンバーの登壇内容を事前共有</li>
<li><strong>KINTO ONE パフォーマンス改善</strong>（第32〜34回）：3回シリーズでモブプログラミング形式により全員で実際の改善作業を実施、最も実践的な学習を実現</li>
</ul>
<h2>継続的な開催による成果</h2>
<h3>参加者数の拡大と定着</h3>
<p>当初5名から始まった勉強会は、継続開催を通じて、コンスタントに10名以上が参加する勉強会へと成長しました。参加者は新車サブスク開発Gだけでなく、他のグループからも参加者が増えてきて、横の繋がりを実現できています。
初期メンバーの多くが現在も参加を続けており、新しく参加したメンバーも定着率が高いことから、勉強会が参加者にとって価値のある時間になっていることが伺えます。</p>
<h3>組織的な技術力の段階的向上</h3>
<p>普段、カンファレンスや技術イベントで得た知識は、個人視点での学習範囲にとどまりがちですが、各部署にいる異なる課題感を持ったフロントエンドエンジニアたちで同時に学ぶ事により、組織全体にメリットがあるだけでなく、個人では気づきにくい観点からの学びにもつながっています。</p>
<p>BaselineシリーズでWeb標準について体系的に学習した継続参加メンバーは、これまで「なんとなく」使っていた技術について仕様レベルで理解できるようになり、業務での技術選択においてもより根拠を持った判断ができるようになっています。</p>
<h3>実践的な問題解決能力の獲得</h3>
<p>直近の勉強会では、KINTO ONE や KINTO FACTORY などの実際のプロダクトを対象に、モブプログラミング形式でパフォーマンス改善に取り組みました。</p>
<p>勉強会で得た知見をそのままプロダクトに適用し、実際に手を動かして確認することで、パフォーマンスチューニングへの苦手意識を払拭。売上向上にもつながる、実践的かつ価値のある取り組みとなりました。</p>
<h3>チーム間の技術的連携の深化</h3>
<p>開発状況の共有会を通じて、これまで把握しきれていなかった他チームの技術的な取り組みや課題を知る機会が増えました。
その結果、類似した課題を持つチーム同士が勉強会後に個別で相談し合うケースも増加しています。</p>
<p>勉強会は、単なる学習の場にとどまらず、実際の業務課題を解決するためのハブとしても機能し始めています。</p>
<h2>まとめ</h2>
<p>社内のフロントエンドエンジニア同士の横のつながりを強化する目的で始まったこの勉強会は、外部カンファレンスの知見共有からスタートし、現在では実プロダクトを題材にしたパフォーマンス改善へと発展しています。</p>
<p>今後も継続開催を通じて、つながりを育みながら、組織全体の技術力向上に貢献する場を目指していきます。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/high-g/20250630/bg_frontend_study_group.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How I Designed the Logo for Our Internal Event]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-09-Chohonbukai-logo-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-09-Chohonbukai-logo-en/</guid>
            <pubDate>Fri, 27 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A laid-back behind-the-scenes look at the process and what mattered most to us]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello! My name is K, and I work as a designer at KINTO Technologies. I usually work mainly on UI/UX design for e-commerce sites, but sometimes I also have the opportunity to work on communication design.</p>
<p>Back in November 2024, I was responsible for designing the logo that became the face of our internal event, the CHO All-Hands Meeting! Since this was a special opportunity, I would like to take this opportunity to casually introduce the behind-the-scenes aspects of the production and the points I paid particular attention to.</p>
<h1>What is a Logo?</h1>
<p>A logo is more than just decoration; it serves as the face of a brand or event.</p>
<p>At a glance, people can recognize it and think, &quot;Oh, that&#39;s the event!&quot; or &quot;Hey, I&#39;ve seen this before!&quot; It plays a big role in shaping the impression it leaves on them.</p>
<p>Instead of just creating something because it seems cool, I ask myself, &quot;What kind of vibe will this event have?&quot; &quot;What message is it trying to send?&quot; &quot;What impression do I want people to walk away with?&quot; It was a good reminder of how important it is to design with intention.</p>
<h1>Defining the Concept: Understanding the Core of the Event</h1>
<p>First, I had a chat with the art director in charge of the event&#39;s overall artwork. As we clarified the event&#39;s purpose and key message, I started shaping the concept behind the logo.</p>
<p>The concept of the CHO All-Hands Meeting event was &quot;initiative&quot; and &quot;connection.&quot;</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_002.jpg" alt="concept"></p>
<ul>
<li>A space where everyone proactively connects with their colleagues</li>
<li>A place where we truly feel that our company encourages taking initiative</li>
<li>An energetic, lively atmosphere with colleagues that fuels our motivation for the next challenge</li>
</ul>
<p>I wanted to capture all of that through a design that feels &quot;free and fun,&quot; and had &quot;an energetic vibe that brings people together.&quot;</p>
<h1>Exploring Design Directions</h1>
<p>The next phase was to find out what kind of visuals would fit the concept. How do I express &quot;a design that feels free and fun,&quot; and &quot;an energetic vibe that brings people together.&quot;? When I thought about this, one theme came to mind: &quot;otaku culture x technology.&quot;</p>
<p>The event draws lots of people from development and creative teams. For many of them, things like anime, games, mecha, and manga are not only familiar, but also genuinely exciting. By blending that with a futuristic tech vibe, I felt we could create a world that feels even more open, energetic, and full of positive momentum.</p>
<p>Some visual elements we considered were:</p>
<ul>
<li><strong>Elements of mecha, robots, and tokusatsu-inspired details</strong>: To bring in that mechanical, industrial edge.</li>
<li><strong>Manga and comic-style elements</strong>: To experiment with bold, energetic lettering and speech bubble shapes.</li>
<li><strong>Digital-style typography</strong>: To add a subtle futuristic vibe.</li>
</ul>
<p>I started by sketching out rough ideas by hand, letting the concepts flow freely from there.</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_003.jpg" alt="sketch"></p>
<h1>From Sketch to Digital</h1>
<p>Once the direction became clear from the sketches, I moved into Illustrator to start digitizing the design.</p>
<ul>
<li>Cleaned up the rough drafts and created the base shape.</li>
<li>From there, made several variations, each with subtle tweaks in nuance.</li>
<li>Discussed with the art director which design best embodies the concept.</li>
</ul>
<p>Of course, plenty of ideas didn&#39;t make the cut—but going through that trial-and-error process really reminded me how essential it is for creating great design.</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_004.jpg" alt="date"></p>
<h1>Polishing the Details</h1>
<p>Once the rough draft was locked in, it was time to move into the final phase. At this stage, I kept refining things, tweaking the details until everything felt just right.</p>
<p>And, <strong>one of the key elements that really shapes the impression of a logo is the font.</strong></p>
<p>For example, rounded fonts can give off a soft, friendly vibe, while sharper fonts feel more sleek and polished. Even small differences like that can completely change the overall tone of the logo.</p>
<p>This time, instead of relying on existing fonts, I created an original font. While keeping the event&#39;s core themes—autonomy and interaction—in mind, I made adjustments with a focus on the following points:</p>
<ul>
<li><strong>Improve readability</strong>: Adjust letter width, proportions, and spacing to make the text easier to read and give the overall design a cohesive feel.</li>
<li><strong>Refine curves</strong>: Reduce the number of paths to create a smoother, more polished shapes.</li>
<li><strong>Harmony between kanji and katakana</strong>: Be mindful of consistent shapes so the characters would feel balanced when placed together.</li>
</ul>
<p>When compared to the original red guidelines, the final shape has changed significantly. This process really reminded me how even the smallest design choices, like font style and tiny shape tweaks, can greatly affect the impression a logo gives.</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_005.jpg" alt="beforeafter"></p>
<h1>Finalizing the Logo: Balancing Playfulness and Versatility</h1>
<p>And finally—the logo was complete!</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_006.jpg" alt="finalize"></p>
<ul>
<li>It strikes a nice balance between playful vibes and practical versatility, making it easy to use in all kinds of contexts.</li>
<li>The mix of subculture and tech came through naturally in the design.</li>
<li>By sticking to the shapes and fonts, the overall finish and quality really leveled up!</li>
</ul>
<h1>Summary</h1>
<p>Logo design is something where even the tiniest details can totally change the impression it gives. This project reminded me how important it is to keep pushing until you hit that &quot;This is it!&quot; moment.</p>
<p>This time, I think I managed to go beyond just making &quot;something that looks cool.&quot; I created a design that really &quot;captures the spirit of the event and works across different contexts.&quot;</p>
<p>If this article sparks even a little inspiration or insight for someone out there, I&#39;ll be happy!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/kanae_kato/250430/techblog_001.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[2025年4月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-27-newcomer-202504/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-27-newcomer-202504/</guid>
            <pubDate>Fri, 27 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年4月に入社した5人の皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1><strong>はじめに</strong></h1>
<p>こんにちは、2025年4月入社の hiro です！
本記事では、2025年4月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。
KINTO テクノロジーズ(以下、KTC)に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1><strong>Minami</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/image-20250613-072106.png" alt="Minami"></p>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>モビリティプロダクト開発部に所属します</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>分析、事業戦略/戦術の提案、施策の実行まで一貫して向き合うチームです</li>
<li>とても優秀なアナリストやデータサイエンティスト、エンジニアの皆さんと一緒に事業成長に取り組めるので、いまから楽しみです</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>働き方やオフィスでの過ごし方など、自由度の高さに驚きました</li>
<li>落ち着いてる中に密かに熱い気持ちを持ってる方もいらっしゃるので、これからが楽しみです</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>チームは比較的若く、仲良く明るい雰囲気です</li>
<li>新しい体制でチームの強みを発揮できるように頑張ります</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>入社前に読んだので、KTCに入社される方の参考になったら良いなと思いました</li>
</ul>
</li>
<li><p><strong>MAoさん ⇒ Minamiさんへの質問</strong></p>
<blockquote>
<p>国内国外問わず、おすすめの旅行先があったら教えてください！</p>
</blockquote>
<ul>
<li>昨年行ったハワイのカウアイ島がすごく素敵でした！</li>
</ul>
</li>
</ul>
<h1><strong>H.N</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/malta.jpeg" alt="H.N"></p>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>業務システム開発部で販売店業務領域を主に担当しています。</li>
<li>最近は室町オフィス近辺の美味しいお店を探すのが趣味です。</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>業務システム開発部の中でもNimbusチームに所属してまして、プロパー3人とパートナーさんで日々業務にあたっています。</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>想像していたよりも社内イベントや食事会への参加機会が多くて、他部署の方と関われるきっかけ作りができそうなので良い意味のギャップでした！</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>忙しい中でも困った事や不明点を聞きやすい雰囲気づくりをしてくださる先輩方が多く、日々キャッチアップするにあたって大変助かっています！</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>テックブログも含めてこういった記事を書くことに慣れていないのですが、これを機会に楽しめるようになればいいなと思ってます！</li>
</ul>
</li>
<li><p><strong>Minami ⇒ H.Nへの質問</strong></p>
<blockquote>
<p>参加されて面白かった社内イベントを教えてください！</p>
</blockquote>
<ul>
<li>まだ社内イベントには参加できてませんが、以下の社内イベントに参加してみたいと思ってます！<ul>
<li>KTCBeerBash</li>
<li>生成AI系の社内勉強会</li>
<li>部署やチームを横断して交流が深められそうなイベントがあれば！</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1><strong>K.S</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/haiace.jpg" alt="K.S"></p>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>my routeアプリのUI/UX改善を主に担当しています。趣味は家族でキャンプに行くことです。</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>7月から新体制になる予定です。PDMと開発チーム一体となってアプリをよりよくしていきます。</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>入社したタイミングで神保町オフィスがリニューアルされていてとにかくオフィスが綺麗です。さらに休憩スペースの椅子や机はアウトドアメーカーに統一されていてオシャレ！</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>my routeチームは皆さん優しく、困った事は全てサポートしてくれます。歓迎会も素晴らしかったです！</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>過去の先輩方の紹介ブログをよく読むようになり、さらに会社の事を知れて良い機会になりました。</li>
</ul>
</li>
<li><p><strong>H.Nさん ⇒ K.Sさんへの質問</strong></p>
<blockquote>
<p>家族でこれまで行った中でおすすめのキャンプ場や旅先があれば教えてください！</p>
</blockquote>
<ul>
<li>都内から近場＆子供&amp;ペット同伴が喜ぶ&amp;高規格という点で</li>
<li><a href="https://tacoglamp.com/">TACO GLAMP</a></li>
<li><a href="https://www.moroyama-yuzunosato.com/">毛呂山町ゆずの里オートキャンプ場</a></li>
<li><a href="https://www.mikabo.co.jp/camp/">みかぼ高原オートキャンプ場</a></li>
<li>です！他にも沢山あります。</li>
<li>ですが、ぶっちゃけ焚き火さえできれば何処でもOKですw</li>
</ul>
</li>
</ul>
<h1><strong>ちる</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/02.jpg" alt="ちる"></p>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>はじめまして！　ちるです。　IT/IS部　コーポレートITグループに所属しています。　</li>
<li>これまではWEB系の開発エンジニアとしてのキャリアを積んできたのですが、KTCではコーポレートITとして社内情報システム、販売店様の情シス業務支援など組織を強くしていくためにエンジニアスキルを発揮できるよう日々奮闘しています！</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>チームはInnovation Driveチームに所属しています！私を含め９名のチームでそれぞれのメンバーが得意領域で活躍しています！</li>
<li>チームの目標として「KTCのIT環境を最高のモノにする。」「KTCで生み出された価値を社外に届ける。」といった目標があり、社内だけにはとどまらず「KTCの価値を最大化し対外的な価値に繋げる技術者集団」を目標としているチームになります。</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>第一印象は、元気な人が多いな！　でした</li>
<li>入社してすぐに社内イベントがあったり、定期的に開催されるビアバッシュがあったり交流が多い会社だなって思いました。</li>
<li>ギャップに関してはカジュアル面談や採用面接で色々とお話伺っていた内容がそのままだったので大きなギャップは特になかったです。ただ大企業のグループ会社なので、業務フローなどが固いイメージ、制約などが厳しいと勝手に想像していたのですが、そういった事は特になく、むしろスピード感がすごくて驚きました！</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>所属チームのメンバーは拠点が別なこともあり、全員が揃うのはオンライン上がほとんどですが不思議なくらい距離感を感じないです。　困ったときの相談や質問もSlackやZoom等ですぐコミュニケーション取れるのでとても良い雰囲気で仕事できています。</li>
<li>障害発生時にみんなが「どうした？どうした？」って集まってくる感じや、チームMTGで相談すると意見交換が盛り上がって時間足りなくなることあったりと、とても活発なチームでよいな！と感じています。</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>入社前からテックブログは読んでいて書くことはわかっていたので、ついに来たか・・・と思いました笑</li>
</ul>
</li>
<li><p><strong>K.Sさん ⇒ ちるさんへの質問</strong></p>
<blockquote>
<p>名古屋オフィスから近くて美味しい定食屋さんあったら教えてください！</p>
</blockquote>
<ul>
<li>オフィス近くの柳橋中央市場内にある　天ぷらとワイン小島　揚げたての天ぷら定食おすすめです</li>
<li>名古屋にいらっしゃる際には是非一緒にいきましょ！</li>
</ul>
</li>
</ul>
<h1><strong>MAo</strong></h1>
<p><img src="/assets/blog/authors/h.nakai/image.png" alt="MAo"></p>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>IT/IS部　コーポレートITグループに所属しています。主に視える化担当でBI作成しつつ、その前後の業務改善をしたりと現場に近い距離で業務しています。</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>Innovation Driveチームに所属しており10名ほどのチームです。</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>周囲の人がすごく話しかけてくれる！！</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>それぞれの考えを持ち寄り、より良い解決策を考える雰囲気です。</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>何を書くのかドキドキしました。</li>
</ul>
</li>
<li><p><strong>ちるさん ⇒ MAoさんへの質問</strong></p>
<blockquote>
<p>最近のマイブーム教えて下さい！</p>
</blockquote>
<ul>
<li>道路を走っている車を眺めながらお茶を飲むこと！「みんな、動いてるなぁ。私も頑張ろう！」と思えます。</li>
</ul>
</li>
</ul>
<h1><strong>さいごに</strong></h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTOテクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[iOS] It's quick and easy! CI Credit Super Stingy Savings Tips]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-21-SavingCICreditTips-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-21-SavingCICreditTips-en/</guid>
            <pubDate>Thu, 26 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Introducing ways to save CI credits on the my route app (iOS)]]></description>
            <content:encoded><![CDATA[<p>My name is Ryomm and I work at KINTO Technologies developing my route (iOS). Here are some things I&#39;ve done to save CI credits:</p>
<h1>Introduction</h1>
<p>In our project, we use Bitrise as a CI tool. Last year, in addition to regular unit testing, we introduced <a href="https://blog.kinto-technologies.com/posts/2024-04-17-SnapshotTest/">snapshot testing</a> and <a href="https://blog.kinto-technologies.com/posts/2024-12-24-myroute-ios-spm/">moved to SPM</a>. Before we knew it, the time it took for each Bitrise CI run had ballooned to around 25 minutes, and in months when a lot of implementation was happening, we often ended up exceeding our budget. Bitrise becomes expensive if you exceed the contracted amount, so at the exchange rate at the time of writing, it costs about 400 yen for each excess CI run. That&#39;s expensive! For this reason, when credits were about to exceed the limit, a trend was created to limit PR merging to the bare minimum in order to avoid moving CI.</p>
<p>In order to overcome this situation, we have been working on some credit saving techniques for our project.</p>
<h1>Reviewing the CLI Tool Setup</h1>
<p>By looking at the Bitrise Build results, you can see how long each step took.</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/01.png" alt="Bitrise Build Result (Before)"> <em>Bitrise Build Result</em></p>
<p>Looking at this, we can see that it took 12 minutes in &quot;Script Runner&quot;. This is the step where we set up swiftLint and LicensePlist. <a href="https://blog.kinto-technologies.com/posts/2024-12-24-myroute-ios-spm/#swiftlint%E3%82%84licenseplist%E3%82%92spm%E3%81%AB%E4%B9%97%E3%81%9B%E3%82%8B">As I mentioned in my previous article</a>, the libraries can be downloaded and used in a package created separately from the workspace in order to be executed in the Build Phase.</p>
<p>Well, this certainly does take time, so I&#39;ll try to shorten it. Fortunately, the library we want to use here is compatible with the Build Tool Plugin, so we can skip this step by transferring it to that library.</p>
<p>Since the settings such as license_plist.yml and .swiftlint.yml have already been made, all you need to do is add the package to the project&#39;s Package Dependencies and add the plugin to Run Build Tool Plug-ins in the target Build Phase.</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/02.png" alt="Build Phase settings"> <em>Build Phase settings</em></p>
<p>Since the location of LicensePlist cannot be specified by <code>outputPath</code> when it is a plugin, you need to include it in BuildPhase so that the license file is moved under Settings.bundle as described in <a href="https://github.com/mono0926/LicensePlist?tab=readme-ov-file#integrate-into-build---build-tool-plugin">README</a>. Also, the package needs to be included in the app itself, not just a package linked via Frameworks.</p>
<p>This completely eliminated the &quot;Script Runner&quot; step, saving me 12 minutes... and cutting my credit costs in half!🎉 <img src="/assets/blog/authors/ryomm/2025-04-21/03.png" alt="Bitrise Build Result (After)"> <em>Bitrise Build Result</em></p>
<p>Additionally, project configuration has been simplified and there is no longer any need to run separate shells for setup or version updates.</p>
<p>In this case, everything was compatible with the Build Tool Plugin, so I changed the configuration, but I also tried <a href="https://github.com/mtj0928/nest">nest</a> as a different approach. This allows you to reduce CI time while still managing your existing CLI tools as separate packages.</p>
<p>Replace the package for installing the CLI tool under the tools directory with nest.</p>
<pre><code>Project/
├── Hoge.xcworkspace
├── Hoge.xcodeproj
├── Test/
│   └── ...
├── ...
└── tools
    └── nextfile.yaml // ここを置き換える
</code></pre>
<p>When you run <code>nest bootstrap nestfile.yaml</code>, you can see that the binary is installed in <code>tools/.nest/bin</code>, so set it to be executed in the Build Phase.</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/04.png" alt="Running swiftlint using nest"> <em>Configuring swiftlint in the build phase</em></p>
<p>This may be useful if the Build Tool Plugin is not supported.</p>
<h1>Review the Test</h1>
<p>In our project, all tests were packed into one test target, so all tests were always run.</p>
<p>Furthermore, the snapshot tests were very heavy tests that took about an hour to run, so the method that compared them with a reference image was commented out on the CI so that it would not be executed. However, because asynchronous drawing processes such as waiting before comparison are executed, if the process fails, timeouts accumulate and you have to wait for a long time, which is also one of the factors that eats up credits.</p>
<p>Therefore, I separated the snapshot tests that were not running on CI into a separate test target and tried to control the tests that were executed using TestPlan.</p>
<p>First, create a test target for the long-running snapshot: <img src="/assets/blog/authors/ryomm/2025-04-21/05.png" alt="Create a test target"> <em>Create a test target</em></p>
<p>After configuring the targets using the existing test targets as reference, move the snapshot tests to the newly created target via Target Membership in the Compile Sources under Build Phases or in the File inspector for each test file. In this case, if the moved test file has a dependency on a test file in the original test target, it will not be able to be built, so you will need to separate the dependencies each time.</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/06.png" alt=" Change the target "> * Change the target *</p>
<p>Next, create a TestPlan. A TestPlan is a collection of tests to be run and their configuration. In this case, the tests you want to run can be specified on a test target basis. For this purpose we have created a separate test target.</p>
<p>TestPlans can be linked to schemas, and in our app we have a one-to-one relationship between schemas and TestPlans. And in the TestPlan for the schema you want to use on CI, make sure you don&#39;t run snapshot tests.</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/07.png" alt="TestPlan settings"> <em>TestPlan settings</em></p>
<p>When you actually run it, the execution time doesn&#39;t change significantly on CI unless there is a failure. However, the local testing experience has improved significantly. Snapshot tests used to be executed even when only the logic was changed, but now they can be prevented from being executed by simply unchecking the box, resulting in a significant reduction in time.</p>
<p>We have decided not to run tests that were not originally run on CI in the first place, but we would like to make it possible to run snapshot tests as well after adjusting the balance with credit usage.</p>
<h1>Conclusion</h1>
<p>In addition to the steps introduced here, other measures that can shorten build times include fixing code that takes a long time to infer types and deleting unused assets. These efforts reduced the average time per CI run from about 22 minutes to about 12 minutes, resulting in a savings of about 45% in credits.</p>
<p>This time, we focused on reducing the time before and after the build, which is something we can do immediately, but next time we would like to reduce the build time itself even more.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ryomm/2025-04-21/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Expanding the Reach of Technical Documents! Automating Markdown Conversion with Generative AI]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-11-droidlab-markdownGpt-blog-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-11-droidlab-markdownGpt-blog-en/</guid>
            <pubDate>Wed, 25 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I will introduce a case study in which GPT was used to automatically convert Confluence and PPT format materials into Markdown format in order to widely share technical materials of the Android team's study group, Droid Lab.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Nice to meet you. My name is Yena, and I’m working on Android app development at KINTO Technologies. My career began with Android development, and I have been involved in a wide range of areas, including smart TV apps, web backend and frontend development, and API development. While currently working on Android development, I also plan and run an in-house study group called &quot;Droid Lab&quot; to support the growth of both myself and my team.</p>
<p>To share the technical content from the study group more broadly outside the team, we considered publishing the materials as a tech blog, but we faced the following challenges:</p>
<p>Since the materials are mainly created in Confluence or PPT format, they need to be converted to Markdown format before being posted on a blog, which makes it impossible to publish them as is and requires significant effort.</p>
<p>This conversion work was a significant burden, making it difficult to secure enough time to write blog articles, which led to delays in disseminating information as intended.</p>
<p>To solve these challenges, we introduced a mechanism that leverages generative AI ChatGPT&#39;s <strong>Custom GPT</strong> to automatically convert source materials, such as those in Confluence and PPT formats, into Markdown format.</p>
<p>We are currently running trial operations, and this mechanism is expected to bring benefits such as reducing the burden of conversion work and improving the efficiency of information sharing.</p>
<p>Here, I will explain the creation procedure and settings of the GPT that we actually introduced.</p>
<h2>How To Create a GPT</h2>
<h3>1. Creating a GPT</h3>
<ul>
<li><ol>
<li>After logging in, click &quot;Explore&quot; on the top left. 
<img src="/assets/blog/authors/yena/Screenshot01.png" alt="Open Screenshot 2025-04-10 at 14.48.58.png"></li>
</ol>
</li>
<li><ol start="2">
<li>Select &quot;Create GPT&quot; from the My GPT category. 
<img src="/assets/blog/authors/yena/Screenshot02.png" alt="Open Screenshot 2025-04-11 at 12.04.41.png"></li>
</ol>
</li>
<li><ol start="3">
<li>Set a Name and profile picture in GPT Builder. 
   DALL·E can also automatically generate an image that matches the name you created (the image below was generated by AI based on the name we created in DALL·E). 
<img src="/assets/blog/authors/yena/DALLE.png" alt="Open DALLE.png"> 
<img src="/assets/blog/authors/yena/Screenshot03.png" alt="Open Screenshot 2025-04-10 at 15.05.52.png"></li>
</ol>
</li>
<li><ol start="4">
<li>Setting GPT details</li>
</ol>
</li>
</ul>
<p><img src="/assets/blog/authors/yena/Screenshot04.png" alt="Open Screenshot 2025-04-10 at 15.07.11.png"></p>
<ul>
<li><p><strong>1 Description</strong>
Here is a brief one- or two-line description of what this GPT does.</p>
<blockquote>
<p>Example:
This GPT automatically generates technical blog articles in Markdown format based on Droid Lab materials. The branch used for creating blog articles should follow this format: (branch name: sample/YYYY-MM-DD-<theme>).</p>
</blockquote>
</li>
<li><p><strong>2 Instructions</strong>
Here, you should describe in detail how  GPT should behave, what kind of output it should generate, and any constraints. This is the core of the prompt. Below are the output rules that GPT should follow:</p>
<ul>
<li>Output the Markdown body text below YAML (*In our case, we have a fixed format, so we define it in YAML.)</li>
<li>Heading -&gt; <code>## Heading</code></li>
<li>List -&gt; <code>- Item</code></li>
<li>Inline code -&gt; <code>Code</code></li>
<li>Code block -&gt; Written in open/closed sets such as <code>kotlin</code></li>
<li>Image -&gt; <code>![description](URL)</code></li>
<li>⚠️ Do not change the structure, style, or order of sentences.</li>
<li>The output must <code>always start with Markdown</code> and be enclosed within a code block.</li>
<li>If the internal API URL or a confidential name is included, clearly state &quot;【Non-public information may be included】&quot; and provide suggested corrections.</li>
<li>Even if the output is long, do not stop midway and return the entire output in one go.</li>
</ul>
</li>
<li><p><strong>3 Conversation starters</strong>
Register example sentences to show how users should use it.</p>
<blockquote>
<p>Example: Convert the Confluence memo into a technical blog in Markdown format.</p>
</blockquote>
</li>
<li><p><strong>4 Knowledge</strong>
Specify supplementary materials (such as style guides, sample articles, etc.) to be uploaded to the GPT.</p>
<ul>
<li>Internal blog style guide</li>
<li>e.g. samples of previously published articles * Be sure to confirm that no confidential information is included before uploading.</li>
</ul>
</li>
<li><p><strong>5 Capabilities</strong></p>
<ul>
<li>Web Search</li>
<li>Canvas</li>
<li>DALL·E</li>
<li>Code Interpreter -&gt; The function can be turned on and off as needed! <img src="/assets/blog/authors/yena/ScreenshotOn.png" alt="Screenshot05.png"></li>
</ul>
</li>
<li><ol start="5">
<li>Setting GPT details
After entering all the information, click the “Create” button in the upper right, and GPT creation will be <strong>completed</strong>. 
<img src="/assets/blog/authors/yena/Create.png" alt="Open Screenshot 2025-04-10 at 15.07.11.png"></li>
</ol>
</li>
</ul>
<h2>2. Setting up GPT</h2>
<p>Here, I will explain the operating rules and constraints set for GPT. Below are the main functions we have set up to comply with our internal blog format and ensure proper Markdown conversion via GPT:</p>
<table>
<thead>
<tr>
<th>Function</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>✍️ Conversion to Markdown format</td>
<td>Converts the pasted text directly to Markdown syntax without changing the style or structure.</td>
</tr>
<tr>
<td>📄 Automatic addition of YAML meta information</td>
<td>Automatically generates postId, title, and excerpt to match the company&#39;s blog format.</td>
</tr>
<tr>
<td>🧱 Preservation of syntax and unification of output format</td>
<td>Outputs everything in a single code block to prevent Markdown syntax from being broken.</td>
</tr>
<tr>
<td>🔐 Security check function</td>
<td>Detects internal APIs and internal code names, marks them as 【Possibly confidential information】 and suggests corrections.</td>
</tr>
<tr>
<td>⚠️ Output interruption prevention logic</td>
<td>Outputs everything in one go, even for long text, without stopping midway to prevent syntax from being broken.</td>
</tr>
<tr>
<td>⚠️ Japanese/English check</td>
<td>Detects variations in notation, typos, and unnatural expressions, and suggests corrections as necessary.</td>
</tr>
</tbody></table>
<h2>2-1. 🧠 Detailed specifications of GPT (prompt settings)</h2>
<p>This GPT functions as a professional tech blog writer and a Markdown converter tool. When a user pastes text from Confluence or an internal memo, it will be output according to the format below, without altering the original structure, order, or style.</p>
<h3>1. YAML Meta Information Output Rules</h3>
<p>To publish it as a blog post, YAML meta information (such as title, date, and category) must be defined at the beginning.<br>This GPT automatically generates a YAML header from the pasted text, according to the following rules:</p>
<ul>
<li>YAML must be output in a <code>yaml</code> code block in the prescribed format.</li>
<li>The &quot;title&quot; and &quot;excerpt&quot; fields must be automatically completed by inferring and extracting them from the text.</li>
<li>If there is no title, it must be output as <code>&#39;Article title here&#39;</code>.</li>
<li>If the article is very long, <code>Markdown</code> code block must be automatically split into multiple blocks, and output must continue until completion, without the need for the user to prompt by saying &quot;continue.”</li>
</ul>
<h4>Automatic Category Judgment Rules</h4>
<p>If your post contains the following keywords, automatically replace <code>category</code> with the corresponding one:</p>
<ul>
<li>Kotlin, Compose, MVVM, KMP -&gt; &quot;Android&quot;</li>
<li>GitHub, CI/CD, CodeBuild -&gt; &quot;DevOps&quot;</li>
<li>Lint, architecture, coding standards -&gt; &quot;Architecture&quot;</li>
<li>Confluence, Markdown, GPT -&gt; &quot;Tooling&quot;</li>
<li>Firebase, AWS, S3 -&gt; &quot;Cloud&quot;</li>
<li>Study group, internal sharing, knowledge -&gt; &quot;Team&quot;</li>
<li>AI, ChatGPT, Prompt, Natural Language Processing -&gt; &quot;Generative AI&quot; 
If multiple categories apply, <strong>prioritize the category that appears most frequently</strong>.<blockquote>
<p>💡 Below is an example of the YAML header format used for our internal blog (it may not be usable in external environments): [Example]</p>
</blockquote>
</li>
</ul>
<pre><code>---
postId: &quot;&lt;自動生成されたID&gt;&quot;
title: &quot;&lt;記事タイトル&gt;&quot;
excerpt: &quot;&lt;要約文&gt;&quot;
coverTitle: &quot;&lt;表紙用の見出し&gt;&quot;
coverImage: &quot;&lt;画像パス（社内ブログ用）&gt;&quot;
date: &quot;&lt;ISO形式の日付&gt;&quot;
category: &quot;&lt;カテゴリ名&gt;&quot;
....
---
</code></pre>
<h3>2. Markdown Body Text Output Rules</h3>
<p>Markdown conversion rules:</p>
<ul>
<li>Heading -&gt; <code>## Heading</code></li>
<li>List -&gt; <code>- Item</code></li>
<li>Inline code -&gt; <code>Code</code></li>
<li>Code block -&gt; Example: <code>kotlin</code> (※ Be sure to close after opening.) </li>
<li>Image -&gt; <code>![explanation](image URL)</code></li>
</ul>
<p>⚠️ Do not change sentence structure, order, or style.<br>⚠️ Output everything accurately up to the closing tag to avoid breaking the Markdown syntax.</p>
<h3>3. Post-Output Automatic Check Function</h3>
<ul>
<li><strong>Put all the output (YAML + Markdown body text) in one code block (<code>Markdown</code>)</strong>.</li>
<li>The output must begin with <code>Markdown</code> and <strong>end with the corresponding closing code block tag</strong>.</li>
<li>Even if the text is long, the output must not stop midway, and <strong>the entire text must be returned in one go</strong>.</li>
</ul>
<h4>3-1. 🔐Security Check</h4>
<p>Check that the following items are not included:</p>
<ul>
<li>Internal API URL</li>
<li>Internal library name</li>
<li>Project code name</li>
<li>If confidential information such as a customer ID is detected, mark it as “【Possible confidential information】” and present <strong>proposed revision for publication</strong> at the same time.</li>
</ul>
<p><strong>Detection example (screenshot)</strong> 
<img src="/assets/blog/authors/yena/Screenshot05.png" alt="Open Screenshot05.png"></p>
<h4>3-2.⚠️ Japanese/English Check</h4>
<ul>
<li>Japanese typos, misused particles, awkward phrasing, and inconsistencies in sentence endings, among other issues.</li>
<li>English spelling mistakes, grammatical errors, unnatural expressions, etc.</li>
</ul>
<p>If necessary, output the <strong>proposed revision</strong> under the heading “⚠️ Text check:”<br>However, do not change the order, structure, or style of the sentence in any way; simply <strong>point out the mistakes</strong>.</p>
<p><strong>Detection example (screenshot)</strong> 
<img src="/assets/blog/authors/yena/Screenshot06.png" alt="Open Screenshot05.png"></p>
<h2>4. Usage Demo</h2>
<h3>4-1. Execution Steps</h3>
<p>💬 <strong>Step 1</strong>: Enter the TEXT you want to convert 
💬 <strong>Step 2</strong>: Paste into custom GPT and send 
<img src="/assets/blog/authors/yena/Test01.png" alt="Open Test01.png"> 
💬 <strong>Step 3</strong>: Output in Markdown format 
<img src="/assets/blog/authors/yena/Test02.png" alt="Open Test02.png"></p>
<h2>5. Benefits Gained from the Implementation</h2>
<p>The implementation of this GPT has brought several benefits, including increased efficiency in Markdown conversion and improved quality of information sharing.<br>Incidentally, ** this article has also been automatically converted to Markdown format using this GPT.**</p>
<table>
<thead>
<tr>
<th>Item</th>
<th>Before</th>
<th>After</th>
<th>Benefit</th>
</tr>
</thead>
<tbody><tr>
<td>Markdown conversion task</td>
<td>30 minutes to 2 hours or more</td>
<td>Tens of seconds to several minutes</td>
<td>Reduced workloads by more than 80%</td>
</tr>
<tr>
<td>Format unification</td>
<td>Individual differences exist</td>
<td>Automatic, stable output</td>
<td>Improved quality and readability</td>
</tr>
<tr>
<td>Security verification</td>
<td>Manual verification</td>
<td>Automatic detection and marking</td>
<td>Safe to publish</td>
</tr>
<tr>
<td>Sentence verification</td>
<td>Manual verification</td>
<td>Automatic detection and marking</td>
<td>Safe to publish</td>
</tr>
<tr>
<td>Users</td>
<td>Mainly those familiar with the Markdown format</td>
<td>Members who are unfamiliar with the Markdown format can also use it</td>
<td>Expanded scope of use</td>
</tr>
</tbody></table>
<h2>6. Summary</h2>
<p>We introduced this GPT to reduce the effort involved in Markdown conversion, aiming to make it easier to casually and widely share the knowledge gained at Droid Lab, both within the team and beyond.</p>
<p>By leveraging generative AI, time-consuming conversion tasks have been streamlined, enabling us to share information more efficiently while maintaining confidence in both quality and security.</p>
<p>Moving forward, we plan to improve usability by adding features like direct PowerPoint uploads and automatic meeting summaries.<br>We aim for a future where knowledge sharing across the entire development team becomes even smoother!</p>
<h2>🚀 Potential Applications</h2>
<ul>
<li><p><strong>Addition of Japanese-to-English conversion function</strong><br>Support for global communication and sharing with international members.</p>
</li>
<li><p><strong>Support for PPT uploads</strong><br>We are currently developing a system that will eliminate the need for manual copying and allow conversion simply by uploading files.</p>
</li>
<li><p><strong>Introduction of GPT for meeting summarization</strong><br>Optimization to automatically extract summaries and ToDos by simply pasting meeting logs and minutes</p>
</li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yena/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[The Future of Generative AI: Unsolved challenges]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-11-AI_future_challenges-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-11-AI_future_challenges-en/</guid>
            <pubDate>Fri, 20 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Exploring the challenges generative AI faces and potential solutions ahead]]></description>
            <content:encoded><![CDATA[<p>The other day, during a casual chat with a colleague, he suddenly said: &quot;AI has come so far in the blink of an eye. I can&#39;t even imagine what it&#39;ll be like in five years.&quot;</p>
<p>I&#39;m not an AI expert, but it just so happens I&#39;ve looked into it a little bit. And the answer to that question isn&#39;t as simple as saying &quot;It&#39;ll get way better.&quot;</p>
<p>The thing is, there are some deep-rooted challenges in how the technology actually works.</p>
<p>I’m no expert, but here’s my take. I&#39;d like to share what some of these challenges are, and what might help us get past them.</p>
<h2>Humans as the Bottleneck</h2>
<p>As you know, generative AI, including Large Language Models, needs a whole lot of data to learn.<br>And by &quot;a whole lot,&quot; I mean <strong>enormous</strong>.</p>
<p>That data is collected from publicly available sources through web crawling and scraping, as well as from books, code repositories, and so on.<br>And the key is that all of that content is <strong>created by humans</strong>.
But we humans just aren&#39;t fast enough. We can&#39;t produce new data at the rate AI is consuming it now.</p>
<p>According to a <a href="https://arxiv.org/abs/2211.04325">paper</a> by Pablo Villalobos from the <a href="https://x.com/epochairesearch">research institute Epoch AI</a>, if current trends continue, we could run out of high-quality, publicly available human-generated text data sometime <strong>between 2026 and 2032</strong>.<br>In other words, &quot;scaling up with more data&quot; may not work anymore beyond that point. Simply because there isn&#39;t enough new human-generated content left to feed these models.</p>
<p><img src="/assets/blog/authors/vorona/ai_challenges/trends_plot.png" alt="trends"> <em>Forecasting Human-Generated Public Text and Data Consumption in LLMs</em></p>
<p>Reusing data (a technique called <strong>multi-epoch learning</strong>) has some effect, but it&#39;s not a fundamental solution.</p>
<p>To make matters worse, a lot of the data currently proliferating is of poor quality. For example, spam, social media comments, extremely biased information, misinformation, even illegal content.</p>
<p>Also, it&#39;s worth pointing out that in languages less commonly used than English, the pace at which human-generated content accumulates is naturally much slower.<br>Therefore, in such languages, the gap between the amount of data created by humans and the data needs of AI could become an even bigger problem.</p>
<p>So, what should we do about it? Here are a few of the proposed solutions:</p>
<ul>
<li><p><strong>Using synthetic data (i.e., data generated by AI itself) for training</strong><br>While this can be effective in some areas, it also comes with a risk of &quot;model collapse.&quot; I&#39;ll get into the details in the next section.</p>
</li>
<li><p><strong>Utilizing non-public data</strong><br>This means using proprietary data held by companies for AI training. This obviously raises serious legal and ethical questions. In fact, some companies, such as the <a href="https://arstechnica.com/information-technology/2023/08/the-new-york-times-prohibits-ai-vendors-from-devouring-its-content/?utm_source=chatgpt.com">New York Times</a>, have already banned AI vendors from scraping their content.</p>
</li>
<li><p><strong>Improving model efficiency</strong><br>Instead of just making models bigger, the idea is to train them to <strong>learn smarter</strong>.<br>Actually, we&#39;re starting to see signs of this shift. When using tools like ChatGPT, we can see something like &quot;reasoning&quot; where the model links multiple steps logically, rather than just recalling memorized information.</p>
</li>
</ul>
<h2>Inbreeding in Generative Models</h2>
<p>As mentioned earlier, one way to increase the amount of training data is to <strong>generate</strong> more of it.<br>But this comes with its own risks.</p>
<p><a href="https://arxiv.org/abs/2305.17493">In this paper</a>, Zakhar Shumaylov from the University of Cambridge investigates the question: &quot;What happens when we train a next-generation model on data generated by past AI models, rather than by humans?&quot; </p>
<p>The authors point to a dangerous feedback loop called <strong>model collapse</strong>.<br>When AI-generated data is used over and over again for training, the model gradually drifts away from the original distribution of real-world data.<br>As a result, its outputs become more generic, monotonous, and distorted. In particular, rare and subtle features are more likely to get lost.<br>This mainly happens for two reasons:</p>
<ol>
<li><strong>Statistical errors</strong> build up over generations, due to a limited sample  </li>
<li><strong>Functional errors</strong> emerge because the model can&#39;t perfectly reproduce complex data distributions</li>
</ol>
<p><img src="/assets/blog/authors/vorona/ai_challenges/dogs_example.png" alt="dogs"><br><em>Visual images of model collapse</em></p>
<p>Interestingly, keeping just 10% of the original human-generated data can help reduce model collapse to some extent.<br>However, <strong>it cannot be completely prevented</strong>.
Unless we make a deliberate effort to preserve real, human-generated data, AI models will increasingly end up trapped in a narrow, self-reinforcing worldview.<br>It&#39;s effectively <strong>digital inbreeding</strong>.</p>
<p>Furthermore, Gabrielle Stein from East Carolina University <a href="https://thescholarship.ecu.edu/server/api/core/bitstreams/c16ab41b-44e2-4bce-a33e-ccd80110676f/content">explored</a> whether this issue could be avoided by using &quot;cross-model learning,&quot; in which AIs exchange and learn from each other&#39;s data.<br>The conclusion? <strong>It didn&#39;t really make much of a difference</strong>.</p>
<p>In her study, she trained models on different proportions of human data: 100%, 75%, 50%, 25%, and 0%.<br>The results showed the following trends:</p>
<ul>
<li>As the proportion of synthetic data increased, linguistic diversity steadily declined  </li>
<li>No &quot;tipping point&quot; was observed where performance suddenly collapsed at a specific percentage  </li>
<li>Even a small amount of human data helped slow the rate of degradation</li>
</ul>
<p>She suggests that to avoid early-stage model collapse, <strong>at least half</strong> of the training data should reliably come from confirmed human-written content.</p>
<p>Considering that much of the data we see online is generated by AI, and that most AI training data is scraped from the Internet, it paints a somewhat bleak picture for the future of AI.<br>As AI-generated content increasingly makes its way into training data, the risk of causing model collapse in the future keeps growing.<br>Still, there are some fresh, innovative approaches are emerging that could lead to a breakthrough.</p>
<h2>What Comes Next?</h2>
<p>To address the challenges I&#39;ve covered so far, a few relatively new approaches have started to emerge.<br>While they&#39;re not permanent solutions, they might be able to <strong>delay</strong> the <em>collapse caused by inbreeding and data shortages</em> for a while.</p>
<p>One example already mentioned is AI <em>reasoning</em>.
This refers to behavior in models like ChatGPT, where the model goes through multiple steps of internal reasoning and judgment before producing a final answer.</p>
<p>Another promising method is called <a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation"><strong>Retrieval-Augmented Generation (RAG)</strong></a>.<br>Put simply, this approach lets AI models generate responses not just from their training data, but also by pulling in <strong>external documents</strong>.<br>For example, feeding a PDF into an LLM or letting it search the Internet before answering a question would fall into this category.</p>
<p>That said, as you can probably guess, this <strong>doesn&#39;t solve the underlying problem of a lack of data</strong>.<br>After all, the amount of new, reliable information we can feed into a model is still limited.</p>
<p>So, what are promising approaches that haven&#39;t been fully realized yet?<br>One example is the trend toward <strong>synthetic reality and embodied agents</strong>.<br>This is a completely different approach to AI development.</p>
<p>Instead of learning passively from static datasets, the idea is to place AI agents in dynamic virtual environments where they act, explore, and adapt to achieve goals.</p>
<p><strong>The data obtained is self-generated</strong>, produced through experiencing results, testing hypotheses, and planning strategies.<br>It&#39;s contextual, diverse, grounded in interaction, and extremely high in quality.<br>This method enables <strong>sustainable and self-renewing learning</strong> in environments with near-infinite variation.<br>Regardless of the exhaustion of human-written text, it helps avoid the trap of AI being stuck in its own output.</p>
<p>...However, we&#39;re not there yet.</p>
<p>Sure, we&#39;ve been successful at offloading all sorts of boring tasks onto AI.
But for now, it looks like we still have a fair amount of work to do ourselves.</p>
<p>Thanks for reading!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/vorona/ai_challenges/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Let’s Start a New Development Approach, "TDD × AI," in the AI Era]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-02-tdd_x_ai-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-02-tdd_x_ai-en/</guid>
            <pubDate>Thu, 19 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article introduces a new development approach combining AI and TDD, explaining its effectiveness and practical methods.]]></description>
            <content:encoded><![CDATA[<p>In 2025, as AI continues to evolve rapidly, being able to effectively use AI has become a key skill for engineers.</p>
<p>However, to do so, it is essential to understand prompts appropriately (how to give instructions), requiring experience and knowledge.</p>
<p>As the first step in coding with AI, I will introduce you to development utilizing TDD (test-driven development) and AI, which is the theme of this article. </p>
<h2>Benefits of TDD × AI</h2>
<p>✅<strong>Drastically reduced implementation cost! Engineers “only need to write tests”</strong> 
Other than writing tests, they do not need to give complex instructions or prompts. After that, AI automatically generates code.</p>
<p>✅<strong>Development speed skyrockets! Dramatic reduction in detailed back-and-forth communication</strong> AI can instantly generate code at each step of TDD, significantly accelerating development efficiency and improving consistency across the codebase.</p>
<p>✅ <strong>Exceptional code quality! AI output can be controlled with proper testing</strong> Proper testing ensures control over AI generative code. The result is code with fewer bugs.</p>
<h2>What is TDD?</h2>
<p>Here is a brief explanation of TDD (Test-Driven Development), which is a fundamental premise. </p>
<p><img src="/assets/blog/authors/HiroyaHinomori/2025_03_tdd_x_ai_01.png" alt="chart1"></p>
<p><a href="https://www.amazon.co.jp/dp/4274217884">https://www.amazon.co.jp/dp/4274217884</a></p>
<p>&quot;TDD (Test-Driven Development)&quot; is a methodology proposed by Kent Beck in his book👆 over 20 years ago.  By repeating the simple cycle shown in the diagram above — first, write a test, then implement code to pass the test, and finally, refactor — you can produce high-quality, maintainable code. Since testing serves as the starting point for implementation, <strong>development can proceed while ensuring a testable structure.</strong></p>
<h2>Practice TDD</h2>
<p>:::message This is performed using the Agent mode of GitHub Copilot in VSCode. ::: <a href="https://code.visualstudio.com/docs/copilot/copilot-edits#_use-agent-mode-preview">https://code.visualstudio.com/docs/copilot/copilot-edits#_use-agent-mode-preview</a></p>
<p>With this method, an engineer writes tests and generative AI handles implementation and refactoring, enabling efficient, high-quality development.</p>
<p><img src="/assets/blog/authors/HiroyaHinomori/2025_03_tdd_x_ai_03.png" alt="chart2"></p>
<p>This time, I will explain the flow of TDD × AI using <strong>password validation</strong> as an example.  </p>
<p>Let&#39;s start with <strong>character count validation</strong>.  </p>
<p>:::message alert<br><strong>It is important to proceed in small steps, without filling in all the specifications from the beginning!</strong><br>:::</p>
<h3>Step 1: Creating a Character Count Validation Test</h3>
<p>Prepare a target password validation class and test files. You can leave both files empty at this stage.</p>
<p>Once the files are created, you might be tempted to immediately write the password validation class. But since we’re using TDD, we’ll start by writing tests.</p>
<pre><code class="language-swift:PasswordValidatorTests.swift">@Test(&quot;パスワードの文字数が8文字以上16文字以下でバリデーションが成功すること&quot;)
func testPasswordLengthValidation() async throws {
    let sut = PasswordValidator()
    let validPasswordShort = &quot;Valid123&quot;
    let validPasswordLong = &quot;ValidPassword123&quot;
    let invalidPasswordShort = &quot;Short01&quot;
    let invalidPasswordLong = &quot;ThisPasswordIsWay&quot;

    #expect(sut.validate(validPasswordShort) == true)
    #expect(sut.validate(validPasswordLong) == true)
    #expect(sut.validate(invalidPasswordShort) == false)
    #expect(sut.validate(invalidPasswordLong) == false)
}
</code></pre>
<p>Since the PasswordValidator class and validate method aren&#39;t implemented yet, you’ll naturally get an error</p>
<p>Next, input a prompt to the AI and ask it to implement.</p>
<p>![input prompt](/assets/blog/authors/HiroyaHinomori/2025_03_tdd_x_ai_02.png =500x)</p>
<p>Then, after a few tens of seconds, the AI generates an implementation.</p>
<pre><code class="language-swift:PasswordValidator.swift">struct PasswordValidator {
    // パスワードの長さの制約を定数として定義
    private let minimumPasswordLength = 8
    private let maximumPasswordLength = 16

    /// パスワードが有効かどうかを検証します
    /// - Parameter password: 検証するパスワード
    /// - Returns: パスワードが有効な場合はtrue、そうでない場合はfalse
    func validate(_ password: String) -&gt; Bool {
        let passwordLength = password.count
        return passwordLength &gt;= minimumPasswordLength &amp;&amp; passwordLength &lt;= maximumPasswordLength
    }
}
</code></pre>
<p>Next, let&#39;s add a feature to validate the mix of numbers, letters, and symbols.</p>
<h3>Step 2: Adding Validation of Numbers, Letters, and Symbols</h3>
<pre><code class="language-swift:PasswordValidatorTests.swift">@Test(&quot;パスワードの文字列の中に数字、文字、記号が含まれている場合にバリデーションが成功すること&quot;)
func testPasswordCharacterValidation() async throws {
    let sut = PasswordValidator()
    let validPassword = &quot;ValidPassword12!&quot;
    let invalidPasswordNoNumber = &quot;InvalidPassword!&quot;
    let invalidPasswordNoCharacter = &quot;1234567890!&quot;
    let invalidPasswordNoSymbol = &quot;InvalidPassword1&quot;

    #expect(sut.validate(validPassword) == true)
    #expect(sut.validate(invalidPasswordNoNumber) == false)
    #expect(sut.validate(invalidPasswordNoCharacter) == false)
    #expect(sut.validate(invalidPasswordNoSymbol) == false)
}
</code></pre>
<p>At this stage, an error will occur, but don&#39;t worry about it; just provide the test case to the AI and let it generate the implementation..</p>
<pre><code class="language-swift:PasswordValidator.swift">struct PasswordValidator {
    // パスワードの長さの制約を定数として定義
    private let minimumPasswordLength = 8
    private let maximumPasswordLength = 16

    /// パスワードが有効かどうかを検証します
    /// - Parameter password: 検証するパスワード
    /// - Returns: パスワードが有効な場合はtrue、そうでない場合はfalse
    func validate(_ password: String) -&gt; Bool {
        let passwordLength = password.count
        guard passwordLength &gt;= minimumPasswordLength &amp;&amp; passwordLength &lt;= maximumPasswordLength
        else {
            return false
        }

        let hasNumber = password.rangeOfCharacter(from: .decimalDigits) != nil
        let hasLetter = password.rangeOfCharacter(from: .letters) != nil
        let hasSymbol =
            password.rangeOfCharacter(from: .symbols) != nil
            || password.rangeOfCharacter(from: .punctuationCharacters) != nil

        return hasNumber &amp;&amp; hasLetter &amp;&amp; hasSymbol
    }
}
</code></pre>
<p>Up to this point, we completed the implementation in under <strong>10 minutes</strong>.</p>
<p>In traditional coding, you need to explicitly define various conditions and specifications in the prompt, but with this method, you simply ask AI to <code>implement something that meets the test conditions</code>. Since all the implementation details have been written in the test, there is almost no need for complex prompt instructions.</p>
<h3>To Further Streamline Communication with AI</h3>
<p>If you write implementation rules and constraints in advance in &quot;copilot-instructions.md,&quot; there&#39;s no need to provide detailed instructions to the AI each time.</p>
<pre><code class="language-markdown:.github/copilot-instructions.md">日本語で返答してください。

### コーディングルール

- テストはswift-testingを使用してください。
- 実装には基本的にマジックナンバーは使わないこと
- DRYの原則に則って実装してください
- KISSの原則に則って実装してください
- YAGNIの原則に則って実装してください
</code></pre>
<h2>To Become an Engineer Who Thrives in the Age of AI</h2>
<p>AI is not omnipotent. But that’s no reason to give up! It is important to <strong>calmly determine what &quot;AI is good at&quot; and what &quot;humans should handle.&quot;</strong> With “TDD × AI,” let&#39;s understand the coding habits of AI and reach new levels of speed and quality in development!🚀</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/HiroyaHinomori/2025_03_tdd_x_ai_03.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[Server-side Kotlin] Using Kotlin’s Built-in Result Type for Error Management]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-31-ErrorHandlingInServerSideKotlin-en /</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-31-ErrorHandlingInServerSideKotlin-en /</guid>
            <pubDate>Wed, 18 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[An unified error handling approach using Kotlin Result type.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello. My name is Shiode, and I do payment-related backend development in the Toyota Woven City Payment Solution Development Group. As mentioned in <a href="https://blog.kinto-technologies.com/posts/2022-09-27-kotlin-moshi/">my previous article</a>, our group uses Kotlin for development, with Ktor as our web framework and Exposed as the ORM. We also adopt Clean Architecture in our code architecture.</p>
<p>Initially, we used Kotlin&#39;s <a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-result">Result type</a> for error handling, but as the number of developers increased, we started seeing a mix of Result and throw used in code. Mixing throw into code that uses Result defeats the purpose of expressing error handling through types, as it still requires try-catch blocks.</p>
<p>Since Kotlin doesn&#39;t have Java&#39;s checked exceptions, it&#39;s easy to forget to call a try-catch block, which can lead to unhandled errors. To improve this situation, we discussed within the team and decided to standardize our error handling using Kotlin&#39;s Result type.</p>
<p>In this article, I&#39;ll walk you through how our group writes error handling in practice.</p>
<p>This article does not include the following.</p>
<ul>
<li>Explanation of Clean Architecture</li>
<li>Explanation of Ktor and Exposed</li>
<li>Comparison between <code>kotlin-result</code> and Kotlin&#39;s official Result type</li>
</ul>
<h2>Application Directory Structure</h2>
<p>Before getting into the main topic, I will explain the directory structure of the application in this group. Below is the well-known diagram of Clean Architecture along with our group&#39;s directory structure. As we&#39;ve adopted Clean Architecture, our application&#39;s directory structure is generally organized in line with its principles.</p>
<p><img src="/assets/blog/authors/reona-shiode/error-handling/CleanArchitecture.jpg" alt="Clean Architecture"> <em>(Source: <a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">The Clean Code Blog</a>)</em></p>
<pre><code class="language-text">App Route/
├── domain
├── usecase/
│   ├── inputport
│   └── interactor
└── adapter/
    ├── web/
    │   └── controller
    └── gateway/
        ├── db
        └── etc
</code></pre>
<p>The correspondence between our directory structure and the Clean Architecture diagram is as follows:</p>
<ul>
<li><code>domain</code> directory: <code>entities</code></li>
<li><code>usecase</code> directory: <code>Use Cases</code></li>
<li><code>adapter/web/controller</code> directory: <code>Controllers</code></li>
<li><code>adapter/gateway</code> directory: <code>Gateways</code></li>
</ul>
<p>The terminology doesn&#39;t match exactly, but basically the <code>domain</code> directory sits at the core, the <code>usecase</code> directory surrounds it, and everything under <code>adapter</code> forms the outermost layer.</p>
<p>Therefore, the allowed direction of dependency is as follows:</p>
<ul>
<li><code>usecase</code> -&gt; <code>domain</code></li>
<li>Everything under <code>adapter</code> -&gt; <code>usecase</code> or <code>domain</code></li>
</ul>
<p>This direction of dependency makes it possible to develop business logic without being affected by factors such as web frameworks or database types.</p>
<h1>Error Handling Policy</h1>
<p>Basically, our error handling is based on the following policies:</p>
<ul>
<li>Use Result type instead of throw in case of processing failure</li>
<li>When a function returns a Result type, do not use throw</li>
<li>When returning an exception, use a custom-defined exception type</li>
</ul>
<p>In the next section, I&#39;ll go over each of these policies in more detail, with code examples.</p>
<h2>Use the Result type When a Function May Fail</h2>
<p>Since Kotlin doesn&#39;t have checked exceptions like Java, there&#39;s no mechanism to force the caller to handle errors. By using the Result type, you can explicitly indicate that an error may be returned to the caller, reducing the chances of error handling being missed. However, in cases like <code>Result&lt;Unit&gt;</code>, where the return value isn&#39;t used, error handling cannot be enforced unless a custom lint rule is defined. But as of now, we haven&#39;t defined one yet.</p>
<h3>Code Examples</h3>
<p>Below is a simple code example. When defining a function that performs division, it typically results in an error if the denominator is zero. If a function might fail, specify Result as its return type. In this example, <code>Result&lt;Int&gt;</code> is specified.</p>
<pre><code class="language-kotlin">fun divide(numerator: Int, denominator: Int) : Result&lt;Int&gt; {
    if (denominator == 0) {
        return Result.failure(ZeroDenominatorException())
    }
  return Result.success(numerator/denominator)
}
</code></pre>
<h2>When Returning an Exception as a Result Type, Wrap the Exception in a Custom-defined Exception</h2>
<p>Repositories are defined as interfaces in the domain layer, with their implementations residing in the adapter layer. If a use case layer calls a repository function and handles errors, and the adapter layer returns a third-party library exception as is, then the use case layer must be aware of that third-party exception. In that case, the use case layer becomes dependent on the adapter layer. Here&#39;s what that looks like in a diagram:</p>
<p>![依存関係](/assets/blog/authors/reona-shiode/error-handling/dependency.png =400x) <em>Interface-based Dependency and Exception-based Dependency (bad example)</em></p>
<p>To avoid this, we always make sure to wrap any exception returned via Result in a custom-defined exception.</p>
<p>One tricky point when applying Clean Architecture is deciding which layer exceptions belong to, but I personally think it should be in the domain layer. Our group uses a shared set of custom exceptions across multiple services, so we&#39;ve extracted them into a separate domain library.</p>
<p>Another tricky point with Kotlin&#39;s official Result type is that it doesn&#39;t allow you to specify the exception type, which means you can&#39;t enforce returning only custom exceptions. In cases like this, it may be worth considering the use of <a href="https://github.com/michaelbull/kotlin-result"><code>kotlin-result</code></a>. However, we chose not to adopt it in order to avoid introducing third-party types into the domain code.</p>
<h3>Code Examples</h3>
<p>Let&#39;s say the following interface is defined in the domain layer.</p>
<pre><code class="language-kotlin">data class Entity(val id: String)
interface EntityRepository {
  fun getEntityById(id: String): Result&lt;Entity&gt;
}
</code></pre>
<p>Now, consider a case where a third-party library exposes a method like the one shown below, and it&#39;s used as-is.</p>
<pre><code class="language-kotlin">fun thirdPartyMethod(id: String): Entity {
    throw ThirdPartyException()
}
</code></pre>
<h4>Bad Example</h4>
<p>If the implementation in the adapter layer returns the exception from the third-party library directly. As shown below, this causes it to leak into the caller such as <code>UseCase</code>.</p>
<pre><code class="language-kotlin">class EntityRepositoryImpl : EntityRepository {
  override fun getEntityById(id: String): Result&lt;Entity&gt; {
      return kotlin.runCatching {
        thirdPartyMethod(id)
      } // This returns the third party exception
  }
}
</code></pre>
<h4>Good Example</h4>
<p>To prevent third-party exceptions from leaking to the caller, wrap them in a custom-defined exception.</p>
<pre><code class="language-kotlin">class EntityRepositoryImpl : EntityRepository {
  override fun getEntityById(id: String): Result&lt;Entity&gt; {
    return kotlin.runCatching {
      thirdPartyMethod(id)
    }.onFailure { cause -&gt;
      // wrap with our own exception
      CustomUnexpectedException(cause)
    }
  }
}
</code></pre>
<h2>Avoid Using <code>throw</code> in Functions that Return Result</h2>
<p>If a function returns a Result type or throws an exception, the caller must handle both. Even if the function author thinks a particular exception doesn&#39;t need to be handled by the caller, there may be cases where the caller wants to handle it. For this reason, we have standardized on using the Result type and avoid throwing exceptions explicitly.</p>
<p>In some cases such as database connection errors, it might seem acceptable to throw an exception from the adapter layer and let it propagate directly to the API response, since recovery at the use case level is impossible. However, for issues like failure to update the database, we may still want to log inconsistencies with third-party SaaS. In that case, if we scope out using throw, there&#39;s a risk that alerts won&#39;t be triggered appropriately.</p>
<p>We believe it&#39;s up to the caller to decide whether error handling is necessary, so even if the function author considers it unnecessary, the exception is returned using a Result.</p>
<h3>Code Examples</h3>
<p>Let&#39;s take the save function of a repository as an example. The save function receives the entity class and returns the result as a <code>Result&lt;Entity&gt;</code>.</p>
<h4>Examples of what not to do</h4>
<p>As shown below, assume that a connection error was thrown, while other errors are returned using the Result type.</p>
<pre><code class="language-kotlin">class EntityRepository(val db: Database) {
    fun saveEntity(entity: Entity): Result&lt;Entity&gt; {
        try {
            db.connect()
            db.save(entity)
        } catch (e: ConnectionException) {
            // return result instead
            throw OurConnectionException(e)
        } catch (e: throwable) {
            return Result.failure(OurUnexpectedException(e))
        }
        return Result.success(entity)
    }
}
</code></pre>
<p>Now, suppose the use case layer wants to take some kind of action if an error occurs during the save operation. In this case, you must use <code>runCatching</code> (which internally uses try-catch to convert to Result type).</p>
<pre><code class="language-kotlin">class UseCase(val repo: EntityRepository) {
    fun createNewEntity(): Result&lt;Entity&gt; {
        val entity = Entity.new()
        return runCatching { // need this runCatching in order to catch an exception
            repo.saveEntity(entity).getOrThrow()
        }.onFailure {
            // some error handling here
        }
    }
}
</code></pre>
<h4>Good Example</h4>
<p>In the good example, all exceptions are wrapped in a custom-defined exception and returned using the Result type. This allows the caller to remove <code>runCatching</code>, simplifying the code.</p>
<pre><code class="language-kotlin">class EntityRepository(val db: Database) {
    fun saveEntity(entity: Entity): Result&lt;Entity&gt; {
        try {
            db.connect()
            db.save(entity)
        } catch (e: ConnectionException) {
            return Result.failure(OurConnectionException(e))
        } catch (e: Exception) {
            return Result.failure(OurUnexpectedException(e))
        }
        return Result.success(entity)
    }
}
class UseCase(val repo: EntityRepository) {
    fun createNewEntity(): Result&lt;Entity&gt; {
        val entity = Entity.new()
        return repo.saveEntity(entity).onFailure {
            // some error handling here
        }
    }
}
</code></pre>
<h1>Useful Custom Function for Using the Result Type</h1>
<h2><code>andThen</code></h2>
<p>When using a Result type, there are often times when you want to use that value of a successful Result to return a different Result type. For example, updating the status of a specific entity might look like this:</p>
<pre><code class="language-kotlin">fun UseCaseImpl.updataStatus(id: Id) : Result&lt;Entity&gt; {
  val entity = repository.fetchEntityById(id).getOrElse {
    return Result.failure(it)
  }
  val updatedEntity = entity.updateStatus().getOrElse {
    return Result.failure(it)
  }
  return repository.save(updatedEntity)
}
</code></pre>
<p>In such cases, writing code becomes easier when the operations can be connected by a method chain. While the <code>kotlin-result</code> provides the <code>andThen</code> function for this purpose, Kotlin&#39;s official Result type does not. Therefore, our group defined and uses the following method:</p>
<pre><code class="language-kotlin">inline fun &lt;T, R&gt; Result&lt;T&gt;.andThen(transform: (T) -&gt; Result&lt;R&gt;): Result&lt;R&gt; {
    if (this.isSuccess) {
        return transform(getOrThrow())
    }
    return Result.failure(exceptionOrNull()!!)
}
</code></pre>
<p>By using this, the previous example can be rewritten as shown below. The result is a bit cleaner, with less repetitive code.</p>
<pre><code class="language-kotlin">fun UseCaseImpl.updataStatus(id: Id) : Result&lt;Entity&gt; {
  return repository.fetchEntityById(id).andThen { entity -&gt;
    entity.updateStatus()
  }.andThen { updatedEntity -&gt;
    repository.save(updatedEntity)
  }
}
</code></pre>
<h2><code>doInTransaction</code> for Exposed</h2>
<p>Our group uses <a href="https://github.com/JetBrains/Exposed">Exposed</a> as the ORMapper. With Exposed, all database operations must be written within the lambda scope called <code>transaction</code>. If an exception is thrown within this <code>transaction</code> scope, it automatically performs a rollback. Since using the Result type avoids throwing exceptions, we created a function that performs a rollback automatically when the Result indicates failure.</p>
<pre><code class="language-kotlin">fun &lt;T&gt; doInTransaction(db: Database? = null, f: () -&gt; Result&lt;T&gt;): Result&lt;T&gt; {
    return transaction(db) {
        f().onFailure {
            rollback()
        }.onSuccess {
            commit()
        }
    }
}
</code></pre>
<p>Applying this to the previous example of <code>UseCaseImpl</code>, it can be used as follows.</p>
<pre><code class="language-kotlin">fun UseCaseImpl.updataStatus(id: Id) : Result&lt;Entity&gt; {
  return doInTransaction {
    repository.fetchEntityByIdForUpdate(id).andThen { entity -&gt;
      entity.updateStatus()
    }.andThen { updatedEntity -&gt;
      repository.save(updatedEntity)
    }
  }
}
</code></pre>
<h2><code>respondResult</code> for Ktor</h2>
<p>Our group uses <a href="https://github.com/ktorio/ktor">Ktor</a> as the web framework. A function called <code>respondResult</code> was created to allow Result types from use cases to be returned directly as HTTP responses.</p>
<pre><code class="language-kotlin">suspend inline fun &lt;reified T : Any&gt; ApplicationCall.respondResult(code: HttpStatusCode, result: Result&lt;T?&gt;) {
    result.onSuccess {
        when (it) {
            null, is Unit -&gt; respond(code)
            else -&gt; respond(code, it)
        }
    }.onFailure {
        // defined below
        respondError(it)
    }
}

suspend fun ApplicationCall.respondError(error: Throwable) {
    val response = error.toErrorResponse()
    val json = serializer.adapter(response.javaClass).toJson(response)
    logger.error(json, error)

    respondText(
        text = json,
        contentType = ContentType.Application.Json,
        status = e.errType.toHttpStatusCode(),
    )
}
</code></pre>
<p>Although it&#39;s simple, using this function eliminates the need to call <code>Result.getOrThrow</code>, making the code a bit cleaner.</p>
<pre><code class="language-kotlin">fun Route.route(useCase: UseCase) {
  val result = useCase.run()
  call.respondResult(HttpStatusCode.OK, result.map {it.toViewModel()} )
}
</code></pre>
<p>By the way, <code>respondError</code> is a function that returns an error response from the throwable. We use this function to handle exceptions thrown in the Ktor pipeline and return appropriate responses. We&#39;ve also created a custom Ktor plugin to handle exceptions.</p>
<pre><code class="language-kotlin">val ErrorHandler = createApplicationPlugin(&quot;ErrorHandler&quot;) {
    on(CallFailed) { call, cause -&gt;
        call.respondError(cause)
    }
}
</code></pre>
<h1>Conclusion</h1>
<p>I introduced how our group handles errors, along with some helpful custom functions for the Result type. From what I&#39;ve seen in various tech blogs, many companies seem to use <code>kotlin-result</code>, while there&#39;s relatively little information out there on using Kotlin&#39;s official Result type. We&#39;ve found Kotlin&#39;s official Result type to be sufficient for error handling, so we encourage you to give it a try!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Behind the Scenes of Novelty Production for Creating a Sense of Unity at KINTO Technologies]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025_03_21_Novelty-Design-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025_03_21_Novelty-Design-en/</guid>
            <pubDate>Tue, 17 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A Unveiling the process behind creating novelties that foster employee communication!]]></description>
            <content:encoded><![CDATA[<p>Hello! My name is Mayu, and I work as a designer in the Creative Office at KINTO Technologies. I usually focus on UI/UX design for apps, but this time, I was in charge of creating novelty items to distribute at a company event. In this article, I&#39;ll share a behind-the-scenes look at the process, from planning to design. I hope this will offer some helpful insights for those involved in novelty production.</p>
<h1>Novelty Selection</h1>
<h4><strong>The theme is &quot;something that gives a sense of unity&quot;</strong></h4>
<p>For this event, we aimed to create a novelty item that fosters a sense of unity. We developed ideas based on the following conditions:</p>
<ul>
<li>Creates opportunities to communicate with people you don&#39;t usually interact with.</li>
<li>Strengthens a sense of unity.</li>
<li>Promotes innovation.</li>
<li>Appeals to all, regardless of age or gender.</li>
<li>Meets the needs of multiple people.</li>
<li>Easy for anyone to use immediately.</li>
<li>Budget: a few hundred to around a thousand yen per person.</li>
<li>Offers lasting value.</li>
</ul>
<p>After considering various ideas, we ultimately decided to produce a &quot;<strong>Magnetic Card Stand</strong>&quot; and an &quot;<strong>Original Name Card</strong>&quot;.</p>
<h1>Reasons for choosing the &quot;Magnetic Card Stand&quot; and &quot;Name Card&quot;</h1>
<h4><strong>Magnetic Card Stand:</strong></h4>
<ul>
<li>Placing it on the desk makes it easier to naturally engage with others, promoting communication.</li>
<li>Featuring the KINTO Technologies logo and car shape helps foster attachment to the company and boost motivation.</li>
<li>Its simple design makes it easy for anyone to use in daily situations.</li>
</ul>
<h4><strong>Name Card:</strong></h4>
<ul>
<li>Creating name cards with each employee&#39;s name makes it easier to approach one another even on first meetings, promoting communication across the company.</li>
<li>The cut-off KTC lettering design visually expresses a sense of unity throughout the organization.</li>
<li>The cards can be used as name tags during the event and placed on desks afterward for continued use.</li>
</ul>
<h1>Production of Magnetic Card Stand</h1>
<h4>**1. Contractor Selection and Request **</h4>
<p>We commissioned the production of the magnetic card stand to the original goods specialty website &quot;<a href="https://novelty-moku.com/product/jb278/">MOKU</a>.&quot; The deciding factor was MOKU&#39;s high level of customization, which made it possible to create original magnetic card stands simply by submitting design data.</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/moku.jpg" alt="moku">
<br></p>
<h4><strong>2. Prototyping</strong></h4>
<p>We created a simple paper prototype to check the size and usability. We then placed it on an actual desk to evaluate visibility and practicality.</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/prototyping.jpg" alt="prototyping">
<br></p>
<h4>**3. Design of Magnetic Card Stand **</h4>
<p>Using Adobe Illustrator, we created a design with the logo positioned on a specified template. The result is a simple design that highlights the KINTO Technologies logo.</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/stand_design-1.jpg" alt="stand_design-1">
<br></p>
<h4><strong>4. Design of Instruction Manual</strong></h4>
<p>To ensure ease of use, we created an original instruction manual. Here too, we used Adobe Illustrator and produced the design data based on the specified template.</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/stand_design-2.jpg" alt="stand_design-2">
<br></p>
<h4><strong>5. Data Submission and Delivery</strong></h4>
<p>The design data was submitted, and delivery was completed in about three weeks! (Order quantity: 500 pieces)</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/card-stand.jpg" alt="card-stand"></p>
<h1>Production of Name Card</h1>
<h4><strong>1. Create a Name Card Design</strong></h4>
<p>Using Figma, we created an original design featuring names, division, and custom Slack emojis. We made a prototype to ensure it fit properly with the magnetic card stand.</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/card_design-1.jpg" alt="card_design-1">
<br></p>
<p>The key feature is this <strong>half-cut KTC lettering</strong>. KTC stands for &quot;KINTO Technologies.&quot; The small squares represent employees, symbolizing the idea that &quot;each individual comes together to form KTC.&quot; With a simple, stylish black-based design, it also brings out the essence of a tech company.</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/card_design-2.jpg" alt="card_design-2">
<br></p>
<h4><strong>2. Automatic Data Generation</strong></h4>
<p>Creating data manually for everyone would have been overwhelming, so we enlisted our in-house engineers to help automatically generate the data in HTML. We built a system that imports employee information from a CSV file and automatically populates it into a template.</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/html.jpg" alt="html">
<br></p>
<h4><strong>3. Printing and Cutting</strong></h4>
<p>Printed the materials using the office printer and cut them all by hand. It was tough, but it saved a lot of money! lol</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/cutting.jpg" alt="cutting"></p>
<h1>Project Results and Learnings</h1>
<p>After distributing the giveaways, we received a lot of encouraging feedback from employees, such as:</p>
<ul>
<li>&quot;It&#39;s easier to start conversations now!&quot;</li>
<li>&quot;The design is so cute!&quot;</li>
<li>&quot;The Slack icon makes it feel even more personal!&quot;</li>
</ul>
<p>The giveaways helped foster a sense of unity, and the project was incredibly rewarding for me as well. This experience also reinforced the importance of not just designing something attractive, but thinking carefully about how it will actually be used. I believe we were able to showcase the true power of purpose-driven design.</p>
<h1>Finally</h1>
<p>I hope to apply what I&#39;ve learned from this project to future design work. If you thought, &quot;KINTO Technologies sounds like a fun place!&quot; please check out our <a href="https://www.kinto-technologies.com/recruit/">recruitment page</a>! Looking forward to hearing from you! Thank you for reading!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/mayu_jibu/250321/main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Appium Meetup Tokyo Presentation Report]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-18-Appium-Meetup-Tokyo-report-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-18-Appium-Meetup-Tokyo-report-en/</guid>
            <pubDate>Mon, 16 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Appium Meetup Tokyo Presentation Report]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1>Presentation Report by Oka (okapi)</h1>
<p>On February 20, 2025, I had the chance to speak at the &quot;<a href="https://autifyjapan.connpass.com/event/342867/">Appium Meetup Tokyo</a>.&quot;</p>
<p>Here are the slides I presented: Guidelines and Practices for Efficient App Automation(<a href="https://speakerdeck.com/kintotechdev/xiao-lu-de-naapurizi-dong-hua-notamenogaidoraintoshi-jian-fang-fa">https://speakerdeck.com/kintotechdev/xiao-lu-de-naapurizi-dong-hua-notamenogaidoraintoshi-jian-fang-fa</a>). Feel free to take a look!</p>
<h1>Background of Appium Meetup Tokyo</h1>
<p>Our newly developed apps have been showing a higher bug rate compared to our web services (sometimes nearly 10 times higher)</p>
<p>↓</p>
<p>Since the testing workload was so heavy, automating app testing became a must.</p>
<p>↓</p>
<p>Automation work started with Appium.</p>
<p>↓</p>
<p>Finding good places to learn about Appium wasn&#39;t easy.</p>
<p>↓</p>
<p>Then, Why not just host our own events?</p>
<p>And that led me to this talk!</p>
<p><img src="/assets/blog/authors/pannnu.wai/Presentation.jpg" alt="Presentation"></p>
<h1>Things I Paid Attention to in the Presentation</h1>
<ol>
<li>Our company has many development engineers who are familiar with QA processes, which makes collaboration smoother and helps us work together more effectively to improve quality. To emphasize this point, I mainly talked about how our QA and development teams collaborate <em>to build apps that are easy to automate</em>.&quot;</li>
</ol>
<p><img src="/assets/blog/authors/pannnu.wai/oka_img1.png" alt="Interact with the development team"></p>
<ol start="2">
<li>I also made sure the slides were easy to follow, even for those new to Appium, which led to a lively Q&amp;A with 14 questions, 7 online and 7 offline!</li>
</ol>
<h3>Sample Questions</h3>
<p>Here&#39;s a step-by-step guide to logging in to the KINTO Kantan Moushikomi App (KINTO Easy Application App).</p>
<h5>Q1.</h5>
<p>When working with development and QA, how do you assign IDs on pages with many similar elements? For example, on the screen you showed in your slides a layout with multiple car models displayed. How do you handle such cases?</p>
<h5>A1.</h5>
<p>An ID is assigned per object and screen name, making sure each one is unique. On pages with multiple car models, we assign IDs according to the specifications document the development team uses for car information.</p>
<h5>Q2.</h5>
<p>Do you define separate IDs for iOS and Android?</p>
<h5>A2.</h5>
<p>For the IDs used in Appium, we always set a shared ID that works across both iOS and Android.</p>
<h1>Reflections on the Presentation</h1>
<p>Although this was my first time presenting externally, practicing beforehand at an internal joint study session really helped me feel fully prepared and deliver the talk smoothly. For anyone new to public speaking like we were, I definitely recommend starting by practicing with people around you or inside your company first.</p>
<p>Here&#39;s a photo from my practice session.</p>
<p>↓</p>
<p><img src="/assets/blog/authors/pannnu.wai/oka_img2.jpg" alt="Okasan&#39;s practice"></p>
<h1>Looking Ahead</h1>
<p>Since there are very few places to learn about Appium in Japan, we&#39;re planning to keep sharing our insights and experiences through &quot;Appium Meetup Tokyo&quot; (<a href="https://autifyjapan.connpass.com/event/342867/">https://autifyjapan.connpass.com/event/342867/</a>) to help build a supportive community!</p>
<h1>Presentation Report by Pann Nu Wai</h1>
<p>This time I&#39;d like to share about my Appium efforts.</p>
<h1>Background of Appium Meetup Tokyo</h1>
<p>Hi, I&#39;m Pann Nu Wai, primarily working on in-house test automation.</p>
<p>To help expand the use of Appium, I&#39;ve been involved in various initiatives, one of which was giving a talk at an internal study session.</p>
<p>I took this opportunity to share how our team is advancing test automation. After refining the content based on the joint study session, I was able to present it at Appium Meetup Tokyo.</p>
<h1>Things I Paid Attention to in the Presentation</h1>
<p>I explained the four steps that our team took to use Appium to automate mobile app testing.</p>
<p>The documentation is designed so that even non-automation engineers can understand the source code simply by reading the test specifications, covering everything from spec writing to test performance. <img src="/assets/blog/authors/pannnu.wai/pannnu_img1.png" alt="Automation testing"></p>
<h1>Reflections on the Presentation</h1>
<p>As a non-native Japanese speaker, pronunciation was a challenge. But after practicing many times, the talk went smoothly. I also got great feedback and had a truly rewarding experience.</p>
<p>Here&#39;s a photo from my practice session.</p>
<p>↓</p>
<p><img src="/assets/blog/authors/pannnu.wai/pannnu_img2.jpg" alt="Pann Nu&#39;s practice"></p>
<h1>Looking Ahead</h1>
<p>I also hope to continue sharing Appium-related insights beyond our company.</p>
<p>By communicating our internal efforts to the public, I hope to exchange knowledge and experience with more people and help advance the field of test automation.</p>
<p>I&#39;ll keep working to improve my presentation content for future sessions to provide even more value. I appreciate your continued support!</p>
<h1>Conclusion</h1>
<p>This article is a report on our presentations at Appium Meetup Tokyo. I&#39;ve also written one on the first event (<a href="https://blog.kinto-technologies.com/posts/2025-02-20-Appium-Meetup-Tokyo-%E9%96%8B%E5%82%AC%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88/">https://blog.kinto-technologies.com/posts/2025-02-20-Appium-Meetup-Tokyo-開催レポート/</a>) — feel free to check it out as well!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/pannnu.wai/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[【実践】Midjourney v7「Omni-Reference」で、キャラクターの見た目を統一した話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-13-omni-reference/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-13-omni-reference/</guid>
            <pubDate>Mon, 16 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Midjourney v7の新機能「Omni-Reference」を使って、オリジナルキャラクターの見た目を一貫して再現し、ビジュアル表現をアップデートしたプロセスを紹介する実践レポートです。]]></description>
            <content:encoded><![CDATA[<p>こんにちは。KINTOテクノロジーズのクリエイティブ室でデザイナーをしている桃井（<a href="https://x.com/momoitter">@momoitter</a>）です。</p>
<p>この記事では、Midjourney v7に新たに搭載された「Omni-Reference」機能を使って、オリジナルキャラクターの見た目を統一したプロセスをご紹介します。</p>
<p>この記事はこんな方におすすめです</p>
<ul>
<li>Midjourney v7のOmni-Referenceについて詳しく知りたい</li>
<li>キャラクターの一貫性を保った画像生成に挑戦したい</li>
<li>AIツールで高クオリティなビジュアル制作をしたい</li>
</ul>
<p>実際のプロンプトや生成画像も交えながら、Omni-Referenceの活用ポイントや調整のコツを解説していきます。</p>
<h2>Omni-Referenceとは？</h2>
<p><img src="/assets/blog/authors/momoi/250613/01_midjourney.jpg" alt="midjourney">
2025年5月、Midjourney v7に「Omni-Reference」という待望の機能が登場しました。</p>
<p>これは、特定の画像を参照しながら一貫性のあるビジュアルを維持しつつ、新しい画像を生成できる機能です。
これまでv7では難しかった「キャラクター性の維持」が可能になり、人物だけでなく物体や乗り物にも対応しています。</p>
<p>また、「Omni Strength（ow）」というパラメータによって、どの程度参照画像に忠実に生成するかを数値で調整できます。</p>
<h2>「社内向け生成AIツール」について</h2>
<h3>社内向け生成AIツールとは？</h3>
<p><img src="/assets/blog/authors/momoi/250613/02_old_character.jpg" alt="社内向け生成AIツール">
2024年11月、KINTOテクノロジーズの社内イベント「超本部会」のオープニングムービーに、AIで生成されたキャラクターが登場しました。</p>
<p>その後、エンジニア向けのイベントや人事採用イベントなどで、KINTOテクノロジーズを説明するナビゲーター的存在として活躍しています。</p>
<p>誕生のエピソードはこちら
<a href="https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/">https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/</a></p>
<h3>なぜ見た目を統一したかったのか？</h3>
<p>キャラクターの誕生以降、画像生成AIや動画生成AIは急速に進化しました。
その結果、当初のビジュアルはやや古さを感じさせるものになってしまいました。</p>
<p>ちょうどその少し後に、Midjourney v7やRunway Gen-4がリリースされ、生成クオリティが飛躍的に向上。オリジナルキャラクターのビジュアルもこのタイミングでアップデートすることにしました。</p>
<p>アップデートの過程はこちら
<a href="https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making/">https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making/</a></p>
<p>ただし、v7の表現力は非常に高い一方で、リリース当時は同じキャラクターの見た目を一貫して再現する機能がなく、生成するたびに微妙に顔が異なるという課題がありました。</p>
<p><img src="/assets/blog/authors/momoi/250613/03_difference.jpg" alt="difference"></p>
<p>しかもアップデートによりクオリティが上がった分、初期のオリジナルキャラクターが持っていた「キャラクターとしての親しみやすさ」が薄れてしまうという副作用も。</p>
<p>そんな中、2025年5月、Midjourney v7に「Omni-Reference」という新機能が追加されました。</p>
<p>私はこの機能を使って、元の見た目を活かしながら、v7らしい美しさとリアルさを兼ね備えた「進化したキャラクター」の再構築に挑戦しました。</p>
<h2>いざ実践！</h2>
<h3>ChatGPTに相談</h3>
<p><img src="/assets/blog/authors/momoi/250613/04_chatgpt.jpg" alt="chatgpt"></p>
<p>まずはChatGPTにこう相談しました。</p>
<blockquote>
<p>「Midjourney v7のOmni-Referenceに添付の画像をアップして、このピンク髪の女性のキャラクターの見た目を固定しながら、クリーンな背景を付けた正面向きの画像をMidjourneyで生成したいです。Midjourney用のプロンプトを教えてください。」</p>
</blockquote>
<p>そうすると、このようなプロンプトが返ってきました。</p>
<blockquote>
<p>upper body portrait of a female virtual operator in a clean futuristic interior, facing camera directly, symmetrical composition, looking straight ahead, centered, gentle expression, slightly relaxed face, subtle smile, brightly illuminated face, soft front lighting, high key lighting on the face, studio-style lighting setup, clear and vivid facial features, softly lit background, minimalistic sci-fi control room, white and silver tones, crisp details</p>
</blockquote>
<h3>Omni-Referenceの使い方</h3>
<h4>Step1：画像をドラッグ＆ドロップ</h4>
<p><img src="/assets/blog/authors/momoi/250613/05_01_omnireference.jpg" alt="omnireference">
参照させたい画像を、Midjourneyのプロンプト入力欄へドラッグすると、画面上部のバーに「Omni-Reference」が表示されるので、そこにドロップ。</p>
<h4>Step2：Omni Strength（ow）の調整</h4>
<p><img src="/assets/blog/authors/momoi/250613/05_02_omnistrength.jpg" alt="omnistrength">
「Omni Strength」では、一貫性の強度（=元画像への忠実度）を調整できます。数値はowというパラメータで指定します。</p>
<h4>Step3：生成開始</h4>
<p><img src="/assets/blog/authors/momoi/250613/05_03_start.jpg" alt="start">
ChatGPTから取得したプロンプトを入力して、生成スタート！</p>
<h3>「ow」値による変化</h3>
<p><img src="/assets/blog/authors/momoi/250613/06_01_ow.jpg" alt="ow"></p>
<ul>
<li>owが低い（例：100〜200） → 元画像とはあまり似ないが、Midjourney特有の繊細で美しい描写が得られる</li>
<li>owが高い（例：800〜1000）→ 元画像には似るが、引っ張られすぎてMidjourneyらしさが失われてしまう。</li>
</ul>
<p><img src="/assets/blog/authors/momoi/250613/06_02_ow100.jpg" alt="ow100">
<em>ow100</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_03_ow200.jpg" alt="ow200">
<em>ow200</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_04_ow400.jpg" alt="ow400">
<em>ow400</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_05_ow600.jpg" alt="ow600">
<em>ow600</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_06_ow800.jpg" alt="ow800">
<em>ow800</em></p>
<p><img src="/assets/blog/authors/momoi/250613/06_07_ow1000.jpg" alt="ow1000">
<em>ow1000</em></p>
<h3>最適なバランスを探して</h3>
<p><img src="/assets/blog/authors/momoi/250613/07_01_2000.jpg" alt="2000">
試行錯誤の末、アップデートにはow 200〜400が最適という結論に。 
このあたりの数値だと、元の面影を保ちつつもMidjourneyらしさのある美しい描写が可能でした。</p>
<p><img src="/assets/blog/authors/momoi/250613/07_02_fix.jpg" alt="fix">
ある瞬間、「これだ！」と思える1枚が現れました。 元のキャラクターの面影を残しつつ、Midjourneyらしい美しさと繊細さもある理想のビジュアルです。</p>
<h3>シーン展開と世界観づくり</h3>
<p>決定画像をベースに、Omni-Referenceで構図や背景を展開していきました。
ChatGPTにも再度相談し、シーンのアイデアやMidjourney用のプロンプトを取得。</p>
<p><img src="/assets/blog/authors/momoi/250613/08_another01.jpg" alt="another01">
<img src="/assets/blog/authors/momoi/250613/08_another02.jpg" alt="another02">
しっかりしたベースがあると、それに沿った世界観の展開もスムーズでした。</p>
<p>こうしてアップデートした新しい見た目は、会社紹介動画の冒頭にも使われています。
<a href="https://www.youtube.com/watch?v=8Df_0StDAiw">https://www.youtube.com/watch?v=8Df_0StDAiw</a></p>
<h3>応用：社内の他コンテンツにも展開</h3>
<p>さらに先日の社内勉強会の登壇資料でも、オリジナルキャラクターを活用。
Omni-Referenceのおかげで「似てる・似てない」を気にする手間が省け、資料やイベントビジュアルなどにもスムーズに導入できるようになりました。
<img src="/assets/blog/authors/momoi/250613/09_application01.jpg" alt="application01">
<img src="/assets/blog/authors/momoi/250613/09_application02.jpg" alt="application02">
<img src="/assets/blog/authors/momoi/250613/09_application03.jpg" alt="application03"></p>
<h3>実践でわかったコツ</h3>
<p>Omni-Referenceは非常に強力な機能ですが、その分「参照が強すぎる」こともあります。</p>
<ul>
<li>服装まで自由にしたい → 顔のみの画像を添付</li>
<li>髪型も変えたい → 目元中心など、必要最低限の情報だけにする</li>
</ul>
<p>このように参照範囲を調整することで、顔の一貫性を保ちつつも、Midjourneyらしい自由な表現の恩恵を受けることができます。</p>
<h2>まとめ：誰でもオリジナルキャラクターを作れる時代へ</h2>
<p>Omni-Referenceの登場によって、キャラクターのビジュアルを一貫して保ちながら高クオリティなシーン展開ができるという制作環境が整いました。</p>
<p>これはつまり、オリジナルキャラクターのような存在を、誰でも再現できる時代が来たということ。</p>
<p>私ひとりの中に閉じこめておくのはもったいない。 だからこそ、これからは社内の皆の創造性でキャラクターを育てていけたらと思っています。</p>
<p>表現を拡張し、オリジナルキャラクターをもっと羽ばたかせていきたい。
AI技術の進化とともに、進化を続けていきます。
<img src="/assets/blog/authors/momoi/250613/10_butterfly.jpg" alt="butterfly"></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/momoi/250613/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[LLM アプリケーションの セキュリティを保護する AI-SPM の取組み]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-16-aispm/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-16-aispm/</guid>
            <pubDate>Mon, 16 Jun 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは、KINTO テクノロジーズ <code>Security CoE</code> グループの多田です。普段は大阪のオフィスで勤務しています。我々のグループでは、マルチクラウド環境の「ガードレール監視とカイゼン活動をリアルタイムで実施する」をミッションに、クラウドセキュリティに関する多くのことにチャレンジしています。メンバが日々、どのような活動を実施しているかは、<a href="https://blog.kinto-technologies.com/posts/2024-12-14-CloudSecurityEngineer/">こちらのブログ</a>にもまとめていますので、ぜひご覧ください。</p>
<h1>背景</h1>
<p>世間の LLM ( 大規模言語モデル ) アプリケーション開発の流れに乗り、当社のプロダクトチームでは、多くの LLM アプリケーションを開発しており、PoC やプロダクトレディの状態に進展しています。一方で、クラウドセキュリティを監視するグループとしては、これらのアプリケーションのセキュリティについても適切な対策を講じる必要があります。</p>
<p>当社の LLM アプリケーションは、主に AWS、Google Cloud、Azure 上で開発されており、クラウドベンダーが提供する生成 AI サービスを活用して構築されることが多い状況です。当グループでは Cloud Security Posture Management ( CSPM ) の監視および運用を行っています。しかし、現時点では、生成 AI 関連サービスに特化した CSPM のコントロールは提供されていないのが現状です。例えば、AWS の場合、AWS Foundational Security Best Practices ( FSBP ) や Center for Internet Security ( CIS ) では、生成 AI サービスに関する直接的なコントロールが提供されていません。</p>
<p>そこで当グループでは、各クラウドベンダーの生成 AI サービスを利用して LLM アプリケーションを開発する際に遵守すべきガイドラインを策定しました。このガイドラインには、クラウドベンダーが提供する生成 AI 関連サービスの利用推奨や設定方法についても記載しています。言い換えれば、ガイドラインに記載した設定方法は、CSPM として監視すべきコントロールとなります。さらに、コントロールを <a href="https://www.openpolicyagent.org/docs/policy-language">Rego 言語</a> で実装し、AWS Bedrock の CSPM として運用を行っています。</p>
<p>タイトルにある、<strong>AI-SPM</strong> は、AI Security Posture Management の略で、「 AI や機械学習 ( ML ) 、生成 AI モデルなどの AI 関連資産のセキュリティリスクやコンプライアンスリスクを可視化・管理・軽減するためのソリューション」というような定義がされているようなので、今回の取組みについても、あえて、AI-SPM という名前にしてみました。</p>
<h1>LLM アプリケーションで遵守すべきセキュリティガイドライン</h1>
<p>ガイドラインを作成するにあたっては、<a href="https://genai.owasp.org/resource/owasp-top-10-for-llm-applications-2025/">OWASP Top 10 for LLM Applications 2025</a> を参考にしました。おそらく、LLM アプリケーションのセキュリティを語るうえでは、最早、鉄板ともいうべき資料かと思います。この OWASP の資料では、LLM アプリケーションでよく見られる最も重大な脆弱性 Top 10 がリストされており、「概要」「脆弱性の例」「防御や軽減策」「攻撃シナリオ」などが記載されています。これらの内容を基に、各クラウドサービスで LLM アプリケーションを開発する場合、利用するサービス、機能の選定やベストプラクティスについて検討を行いました。</p>
<p>Top 10 の最初に記載のあるリスクは「 LLM01：Prompt Injection 」です。</p>
<p>このリスクは、<code>悪意ある入力によって LLM の振る舞いが意図せず操作され、情報漏洩や不正な動作を引き起こすリスク</code> です。このリスクの予防・緩和策としては、<code>LLM への入力の検証・フィルタリング</code> が有効となります。
そして、この予防・緩和策をクラウドサービス上で実装する場合にどうすべきかというと、AWS であれば、<a href="https://aws.amazon.com/jp/bedrock/guardrails/">Amazon Bedrock Guardrails</a> に <code>Prompt Attack</code> をフィルタする機能があるので、この機能を有効化することが対策となります。あとは、CSPM のコントロールとして、この機能が有効化されているかどうかをチェックすることで、可視化とカイゼンが実施できるようになります。</p>
<p>以下に、Top 10 の中の代表的なリスクと AWS サービスでの「予防・緩和策」と「 CSPM コントロールとしての実装」をまとめますので参考にしてください。</p>
<table>
<thead>
<tr>
<th>Top 10</th>
<th>リスク概要</th>
<th>予防・緩和策</th>
<th>AWSでの実装</th>
<th>CSPM コントロールの実装</th>
</tr>
</thead>
<tbody><tr>
<td>LLM01: Prompt Injection</td>
<td>悪意ある入力 ( プロンプト ) によって LLM の振る舞いが意図せず操作され、情報漏洩や不正な動作を引き起こすリスク</td>
<td>モデル動作の制約、入力・出力の検証、フィルタリング、権限制御、人間の承認導入など</td>
<td>Amazon Bedrock Guardrails の content filters「Prompt attacks」を利用する</td>
<td>Amazon Bedrock Guardrails の Prompt attacks <code>Configure prompt attacks filter を有効化</code>し、<code>Block</code> アクション かつ 閾値が <code>HIGH</code> に設定されていることを確認する</td>
</tr>
<tr>
<td>LLM02: Sensitive Information Disclosure</td>
<td>モデルの応答や挙動から、個人情報や機密データなどのセンシティブな情報が漏洩するリスク</td>
<td>出力の検証・フィルタ、学習データの管理、アクセス制御</td>
<td>Amazon Bedrock Guardrails の「sensitive information filters」を利用する</td>
<td>Amazon Bedrock Guardrails<code>Sensitive information filters</code>が <code>Output</code> で <code>有効化</code>されていることを確認する</td>
</tr>
<tr>
<td>LLM06: Excessive Agency</td>
<td>LLM やそのエージェントに過剰な自律性や権限を与えることで、意図しない行動や操作が発生するリスク</td>
<td>最小権限の徹底、人間の承認、権限監査</td>
<td>Amazon Bedrock Builder tools の「Agent」を利用する</td>
<td>Amazon Bedrock Builder tools の Agents を利用する場合は、<code>Guardrail details</code> が<code>関連付けられている</code>ことを確認する</td>
</tr>
<tr>
<td>LLM09: Misinformation</td>
<td>LLM が誤情報やバイアスを含む出力を生成し、ユーザーや社会に悪影響を及ぼすリスク</td>
<td>多様かつ信頼性のあるデータで学習、ファクトチェック、出典表示</td>
<td>Amazon Bedrock Guardrails の「contextual grounding check」を利用する</td>
<td>Amazon Bedrock Guardrail の <code>contextual grounding check</code>が<code>有効化</code>されていることを確認する</td>
</tr>
<tr>
<td>LLM10: Unbounded Consumption</td>
<td>LLM のリソース消費が制御されず、DoS やコスト増大、サービス停止などを招くリスク、リクエストや計算資源の無制限利用が原因</td>
<td>リソース制限、クォータ設定、利用状況の監視</td>
<td>Amazon Bedrock 「Model invocation logging」を利用する</td>
<td>Amazon Bedrock の<code>Model invocation logging</code> が<code>有効</code>になっていることを確認する</td>
</tr>
</tbody></table>
<p>上記の内容については、5 月に実施した共催イベント <a href="https://kinto-technologies.connpass.com/event/350508/">Cloud Security Night #2</a> で登壇していますので、<a href="https://speakerdeck.com/osakatechlab/llm-apurikesiyonnotamenokuraudosekiyuritei-cspm-noshi-zhuang-pointo">こちらの資料</a>も参考していただければと思います。</p>
<h1>Rego による CSPM コントロールの実装</h1>
<p>CSPM のコントロールの定義はできたので、コントロールの内容をチェックする仕組みを開発していきます。当グループでは、CSPM の運用に、AWS であれば、Security Hub、Google Cloud であれば、<a href="https://sysdig.jp/">Sysdig</a>、Azure であれば Defender for Cloud を利用しています。もちろん、統合したツールを使う方が良いのでしょうが、コンソールをゴリゴリ使うというよりは、それぞれ、API 等を通じて、CSPM のアラート状況を確認し、必要に応じてSlack 通知するなどしていますので、ツールが統合されてないことに不自由は感じていません。</p>
<p>LLM アプリケーションの CSPM コントロールについては、Sysdig の CSPM 機能で利用されている Rego で開発することにしました。Rego を採用した理由は、OSS であることと CSPM のようなクラウドインフラの設定の判定ロジックを記載するのであれば、それほど学習コストも必要なく開発できると思ったからです。</p>
<p>以下が <code>LLM01：Prompt Injection</code> コントロールを Rego で実装したものになります。やってることは、<code>risky == true</code> ( リスクあり ) をデフォルト値に設定し、Bedrock Guardrails の設定  <code>ContentPolicy.Filters</code> の値が <code>Type == PROMPT_ATTACK</code> と <code>InputStrength==HIGH</code> であれば、<code>Prompt attack</code> が有効化され、閾値が <code>High</code> に設定されているとして、<code>risky == false</code> とし、リスクなしと判断しています。</p>
<pre><code class="language-HCL">default risky := true

risky := false if {
	some filter in input.ContentPolicy.Filters
    lower(filter.Type) == &quot;prompt_attack&quot;
    lower(filter.InputStrength) == &quot;high&quot;
}
</code></pre>
<p>この Rego を Sysdig のカスタムコントロールとして、Sysdig にデプロイする必要があります。詳細なカスタムコントロールの作成方法やデプロイ手順については、Sysdig 公式ブログで紹介されていますので、<a href="https://sysdig.jp/blog/how-to-build-custom-controls-in-sysdig-secure/">こちら</a> を参考にしてください。我々もこちらを参考にすることで開発を進めました。本ブログに記載していませんが、カスタムコントロールを作成するにあたっての多くのノウハウも貯まりました。</p>
<p>カスタムコントロールは、Sysdig に terraform としてデプロイする必要があります。以下が最終的に作成したカスタムコントロールの main.tf になります。</p>
<pre><code class="language-terraform">terraform {
    required_providers {
        sysdig = {
            source = &quot;sysdiglabs/sysdig&quot;
            version = &quot;&gt;=0.5&quot;
        }
    }
}
variable &quot;sysdig_secure_api_token&quot; {
  description = &quot;Sysdig Secure API Token&quot;
  type        = string
}
provider &quot;sysdig&quot; {
    sysdig_secure_url=&quot;https://app.us4.sysdig.com&quot;
    sysdig_secure_api_token = var.sysdig_secure_api_token
}
resource &quot;sysdig_secure_posture_control&quot; &quot;configure_prompt_attack_strength_for_amazon_bedrock_guardrails&quot; {
  name = &quot;Configure Prompt Attack Strength for Amazon Bedrock Guardrails&quot;
  description = &quot;Ensure that prompt attack strength is set to HIGH for your Amazon Bedrock guardrails. Setting prompt attack strength to HIGH in guardrails helps protect against malicious inputs designed to bypass safety measures and generate harmful content.&quot;
  resource_kind = &quot;AWS_BEDROCK_GUARDRAIL&quot;
  severity = &quot;High&quot;
  rego = &lt;&lt;-EOF
    package sysdig
    
    import future.keywords.if
    import future.keywords.in

    default risky := true

    risky := false if {
		  some filter in input.ContentPolicy.Filters
    	  lower(filter.Type) == &quot;prompt_attack&quot;
    	  lower(filter.InputStrength) == &quot;high&quot;
      }
  EOF
  remediation_details = &lt;&lt;-EOF
    ## Remediation Impact
    This control will help you ensure that your Amazon Bedrock guardrails are configured with high prompt attack strength, which is crucial for protecting against malicious inputs designed to bypass safety measures and generate harmful content.

    ## Remediation Steps
    1. Navigate to the [Amazon Bedrock console](https://console.aws.amazon.com/bedrock/home).
    2. Select the guardrail you want to modify.
    3. In the guardrail settings, locate the &quot;Content Policy&quot; section.
    4. Ensure that the &quot;Prompt Attack&quot; filter is set to &quot;High&quot; for the &quot;Input Strength&quot;.
    5. Save the changes to the guardrail configuration.
    6. Repeat this process for any other guardrails in your AWS environment.

    ## Remediate Using Command Line
    You can use the AWS CLI to update the guardrail configuration. Run the following command to set the prompt attack strength to HIGH for a specific guardrail:

    ```bash
    aws bedrock update-guardrail --guardrail-id &lt;guardrail-id&gt; --content-policy &#39;{&quot;Filters&quot;: [{&quot;Type&quot;: &quot;prompt_attack&quot;, &quot;InputStrength&quot;: &quot;high&quot;}]}&#39;
    ```
    Replace `&lt;guardrail-id&gt;` with the ID of your guardrail.
    Repeat this command for other guardrails in your AWS environment.
    
    ## Additional Information
    For more information on configuring Amazon Bedrock guardrails, refer to the [Amazon Bedrock documentation](https://docs.aws.amazon.com/bedrock/latest/userguide/guardrails.html).

    ## Contact Information
    Slack Channel: #security-coe-group
  EOF
}
</code></pre>
<p>実は当初、Sysdig は、CSPM の対象リソースとして Amazon Bedrock をサポートしていませんでした。Sysdig に相談したところ、ものすごい速さで対応いただき、今回の取組みに繋がりました。機能的な部分の満足度もありますが、この辺りのスピード感は、Sysdig ユーザとして非常に心強いです。</p>
<p>あとは、同じ要領で、<a href="#llm-%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%A7%E9%81%B5%E5%AE%88%E3%81%99%E3%81%B9%E3%81%8D%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AC%E3%82%A4%E3%83%89%E3%83%A9%E3%82%A4%E3%83%B3">LLM アプリケーションで遵守すべきセキュリティガイドライン</a> に記載したコントロールをいくつか Rego で実装し、Sysdig にデプロイしています。</p>
<h1>Sysdig による AI-SPM 運用</h1>
<p>デプロイしたいくつかのカスタムコントロールは、カスタムポリシーとして定義し、AWS Bedrock リソースを可視化しています。可視化の結果、問題があれば、カイゼンするという運用になります。当社の場合、当グループからプロダクト開発グループに対して、カイゼン依頼を行いますが、その時に、カイゼン方法と合わせて、カイゼン依頼をするようにしています。</p>
<p>下の画面は、Sysdig コンソール画面で、<code>LLM01：Prompt Injection</code> の CSPM コントロールを確認している画面となります。コントロール名は、<code>Configure Prompt Attack Strength for Amazon Bedrock Guardrails</code> です。名前は、それっぽく、こちらで付けています。状況としては、AWS Bedrock Guardrail のリソースが 3 つ存在し、1 つが <code>Failling</code>、残り 2 つが <code>Passing</code> していることを示しています。</p>
<p><img src="/assets/blog/authors/tadatomo/ai-spm01.png" alt="ai-spm01"></p>
<p>上記画面をドリルダウンすることで、カイゼンの影響やカイゼン方法などを参照することができます。こちらの内容も main.tf に記載した内容が反映されています。</p>
<p><img src="/assets/blog/authors/tadatomo/ai-spm02.png" alt="ai-spm02"></p>
<p>ただ、実際の運用では、Sysdig コンソール画面にアクセスすることはそれほどなく、アラートの確認等はSysdig API を経由して確認するようにしています。</p>
<h1>まとめ</h1>
<p>今回は、LLM アプリケーションのセキュリティ対策として、Amazon Bedrock の 設定のチェックロジックを Rego で開発し、Sysdig で運用する方法についてご紹介しました。ガイドラインでは、Amazon Bedrock だけでなく、Azure AI Foundry や Google Cloud Vertex AI も整理しているため、今後は、同様に Rego の開発、Sysdig による運用を進めていきます。</p>
<p>また、従来の CSPM に加え、AI-SPM では、クラウドインフラ全体のセキュリティに留まらず、AI 固有の課題やデータ資産の保護など、従来の CSPM ではカバーしきれない領域にも取り組む必要があります。AI 技術は急速に進化しており、最近では MCP や A2A などの新しい概念が登場しています。これらの進展に対応したセキュリティ対策を推進していくことも重要です。</p>
<p>今後も新しい技術や課題に追随しながら、AI アプリケーションのセキュリティを強化する取り組みを続けていきます。</p>
<h1>さいごに</h1>
<p>Security CoE グループでは、一緒に働いてくれる仲間を募集しています。クラウドセキュリティの実務経験がある方も、経験はないけれど興味がある方も大歓迎です。お気軽にお問い合わせください。</p>
<p>詳しくは、 <a href="https://hrmos.co/pages/kinto-technologies/jobs/1811937538128224276">こちらをご確認ください。</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[EncryptedSharedPreferencesからTink + DataStoreに置き換える]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-16-encrypted-shared-preferences-migration/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-16-encrypted-shared-preferences-migration/</guid>
            <pubDate>Mon, 16 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[EncryptedSharedPreferencesがDeprecatedになったため、Android KeyStoreとTinkを比較検証してDataStoreを使ったデータ管理方法に置き換えた話です。]]></description>
            <content:encoded><![CDATA[<h1>EncryptedSharedPreferencesからTink + DataStoreに置き換えた話</h1>
<p>こんにちは。Toyota Woven City Payment 開発グループの大杉です。</p>
<p>私たちのチームでは、 <a href="https://woven.toyota/en">Woven by Toyota</a>の<a href="https://www.woven-city.global/">Toyota Woven City</a> で使用される決済システムの開発をしており、バックエンドからWebフロントエンド、そして、モバイルアプリケーションまで決済関連の機能を幅広く担当しています。</p>
<p>今回は公式にDeprecatedになってしまったEncryptedSharedPreferencesを実装していたAndroidアプリの置き換えをした話をまとめました。</p>
<h2>はじめに</h2>
<p>EncryptedSharedPreferencesがv1.1.0-alpha07からDeprecatedになり、<a href="https://developer.android.com/jetpack/androidx/releases/security?hl=ja#1.1.0-alpha07">Android KeyStoreへの置き換えが公式から推奨</a>されました。</p>
<p>![Updates of security-crypto](/assets/blog/authors/osugi/20250616/security-crypto.png =600x)</p>
<h2>EncryptedSharedPreferencesの代替技術調査</h2>
<p>EncryptedSharedPreferencesがDeprecatedとなったことで、永続化手段と暗号化技術の調査を始めました。</p>
<h3>永続化手段の選定</h3>
<p>私たちのアプリにおけるユースケースでは、設定データの保存にEncryptedSharedPreferencesを使用していただけであったので、SharedPreferencesを使用するだけでも十分ではありました。
ですが、せっかくの置き換えタイミングであったので公式推奨に則り、永続化手段として<a href="https://developer.android.com/topic/libraries/architecture/datastore?hl=ja">DataStore</a>を採用しました。</p>
<h3>暗号ライブラリの選定</h3>
<p>こちらも前述の公式推奨の通り、<a href="https://developer.android.com/privacy-and-security/keystore?hl=ja">Android KeyStore</a>を使用する方針で進めていこうとしたのですが、APIレベルによって機能の制約があるだけでなく、セキュリティレベルの高い実装(StrongBox)を使用するにはデバイスのスペックも関係するため、単純にプログラミングするだけでは想定したセキュリティレベルを担保できない可能性もありました。
今回のアプリは、MDMで管理されたデバイス上で動作する前提であり、StrongBoxにも対応しているデバイスを元々選定していたため、この制約については問題になりませんでした。</p>
<p>また、暗号ライブラリ調査の中で、<a href="https://developers.google.com/tink?hl=ja">Tink</a>というGoogleが提供している暗号ライブラリの存在を知りました。
<a href="https://github.com/tink-crypto/tink/">Tinkのリポジトリ</a>を見ると、マスターキーの保存にAndroid KeyStoreを利用されていることがわかります。</p>
<p>メンテナンスの容易さやパフォーマンスの観点でAndroid KeyStoreとTinkを比較するため、サンプル実装を行いました。</p>
<h4>暗号ライブラリの実装比較</h4>
<p>Android KeyStoreのStrongBoxとTEEを使用した場合とTinkを使用した場合のサンプルコードを以下にまとめています。</p>
<p>両者とも基本的な実装はそこまで苦労せず着手できたと感じました。
一方で、Android KeyStoreは</p>
<ul>
<li>暗号アルゴリズムによってAndroid KeyStoreの鍵発行設定を変える必要がある</li>
<li>初期化ベクトル(IV)の管理が開発者依存になる</li>
<li>実装サンプルが少ない</li>
</ul>
<p>Tinkはこの辺りをうまくラップしてくれている良さがあります。</p>
<p><strong>Android KeyStoreを使用した暗号・復号処理の実装例</strong> </p>
<pre><code class="language-kotlin">
class AndroidKeyStoreClient(
    private val useStrongKeyBox: Boolean = false
) {
    private val keyStoreAlias = &quot;key_store_alias&quot;
    private val KEY_STORE_PROVIDER = &quot;AndroidKeyStore&quot;

    private val keyStore by lazy {
        KeyStore.getInstance(KEY_STORE_PROVIDER).apply {
            load(null)
        }
    }

    private val cipher by lazy {
        Cipher.getInstance(&quot;AES/GCM/NoPadding&quot;)
    }

    private fun generateSecretKey(): SecretKey {
        val keyStore = keyStore.getEntry(keyStoreAlias, null)
        if (keyStore != null) {
            return (keyStore as KeyStore.SecretKeyEntry).secretKey
        }

        return KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE_PROVIDER)
            .apply {
                init(
                    KeyGenParameterSpec.Builder(
                        keyStoreAlias,
                        KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
                    ).setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                        .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                        .setIsStrongBoxBacked(useStrongKeyBox)
                        .setKeySize(256)
                        .build()
                )
            }.generateKey()
    }

    fun encrypt(inputByteArray: ByteArray): Result&lt;String&gt; {
        return runCatching {
            val secretKey = generateSecretKey().getOrThrow()
            cipher.init(Cipher.ENCRYPT_MODE, secretKey)
            val encryptedData = cipher.doFinal(inputByteArray)

            cipher.iv.joinToString(&quot;|&quot;) + &quot;:iv:&quot; + encryptedData.joinToString(&quot;|&quot;)
        }
    }

    fun decrypt(inputEncryptedString: String): Result&lt;ByteArray&gt; {
        return runCatching {
            val (ivString, encryptedString) = inputEncryptedString.split(&quot;:iv:&quot;, limit = 2)
            val iv = ivString.split(&quot;|&quot;).map { it.toByte() }.toByteArray()
            val encryptedData = encryptedString.split(&quot;|&quot;).map { it.toByte() }.toByteArray()

            val secretKey = generateSecretKey()
            val gcmParameterSpec = GCMParameterSpec(128, iv)

            cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec)
            cipher.doFinal(encryptedData)
        }
    }
}
</code></pre>
<p><strong>Tinkを使用した暗号・復号処理の実装例</strong></p>
<pre><code class="language-kotlin">
class TinkClient(
  context: Context
) {
    val keysetName = &quot;key_set&quot;
    val prefFileName = &quot;pref_file&quot;
    val packageName = context.packageName

    var aead: Aead

    init {
        AeadConfig.register()
        aead = buildAead(context)
    }

    private fun buildAead(context: Context): Aead {
        return AndroidKeysetManager.Builder()
            .withKeyTemplate(KeyTemplates.get(&quot;AES256_GCM&quot;))
            .withSharedPref(
                context,
                &quot;$packageName.$keysetName&quot;,
                &quot;$packageName.$prefFileName&quot;
            )
            .withMasterKeyUri(&quot;android-keystore://tink_master_key&quot;)
            .build()
            .keysetHandle
            .getPrimitive(RegistryConfiguration.get(), Aead::class.java)
    }


    fun encrypt(inputByteArray: ByteArray): Result&lt;String&gt; {
        return runCatching {
            val encrypted = aead.encrypt(inputByteArray, null)
            Base64.getEncoder().encodeToString(encrypted)
        }
    }

    fun decrypt(inputEncryptedString: String): Result&lt;ByteArray&gt; {
        return runCatching {
            val encrypted = Base64.getDecoder().decode(inputEncryptedString)
            aead.decrypt(encrypted, null)
        }
    }
}
</code></pre>
<h4>暗号ライブラリのパフォーマンス検証</h4>
<p>Android KeyStoreとTinkの暗号化処理時間のベンチマークを計測しました。
Android KeyStoreについては、<a href="https://developer.android.com/privacy-and-security/keystore?hl=ja#HardwareSecurityModule">StrongBox</a>と<a href="https://source.android.com/docs/security/features/trusty?hl=ja">TEE</a>の2つの実行基盤を利用したケースで評価しています。</p>
<p>テストコードでは、共通の暗号化アルゴリズム(AES_GCM)を設定し、10KBのデータを繰り返し暗号化する処理を<a href="https://developer.android.com/topic/performance/benchmarking/microbenchmark-overview?hl=ja">Microbenchmark</a>を使用して計測しました。Microbenchmarkを使用することで、Google Pixel Tabletの実機上でかつUIスレッド以外のスレッドを利用して計測を行っています。</p>
<p>テストコードは以下です。</p>
<pre><code class="language-kotlin">
import androidx.benchmark.junit4.BenchmarkRule
import androidx.benchmark.junit4.measureRepeated
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class ExampleBenchmark {

    @get:Rule
    val benchmarkRule = BenchmarkRule()

    @Test
    fun benchmarkTinkEncrypt() {
        val context = InstrumentationRegistry.getInstrumentation().context
        val client = TinkClient(context)
        val plainText = ByteArray(1024 * 10)

        benchmarkRule.measureRepeated {
            client.encrypt(plainText).getOrThrow()
        }
    }

    @Test
    fun benchmarkStrongBoxEncrypt() {
        val context = InstrumentationRegistry.getInstrumentation().context
        val client = AndroidKeyStoreClient(context, true)
        val plainText = ByteArray(1024 * 10)

        benchmarkRule.measureRepeated {
            client.encrypt(plainText).getOrThrow()
        }
    }

    @Test
    fun benchmarkTeeEncrypt() {
        val context = InstrumentationRegistry.getInstrumentation().context
        val client = AndroidKeyStoreClient(context, false)
        val plainText = ByteArray(1024 * 10)

        benchmarkRule.measureRepeated {
            client.encrypt(plainText).getOrThrow()
        }
    }
}
</code></pre>
<p>以下に計測結果をまとめました。</p>
<table>
<thead>
<tr>
<th align="left">暗号化基盤</th>
<th align="right">平均処暗号理時間 (ms)</th>
<th align="right">アロケーション数</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Android KeyStore (StrongBox)</td>
<td align="right">209</td>
<td align="right">4646</td>
</tr>
<tr>
<td align="left">Android KeyStore (TEE)</td>
<td align="right">7.07</td>
<td align="right">4786</td>
</tr>
<tr>
<td align="left">Tink</td>
<td align="right">0.573</td>
<td align="right">38</td>
</tr>
</tbody></table>
<p>Android KeyStore (StrongBox)およびAndroid KeyStore (TEE)ではハードウェアへのアクセスが発生するため、ソフトウェア側で暗号化処理を行っているTinkと比べてかなり処理に時間がかかっていることがわかります。
今回採用したデバイスはAndroidの中でも比較的スペックが高いものですが、特にAndroid KeyStore (StrongBox)を採用する場合は、UXの検討が必要になりそうです。</p>
<h2>備考</h2>
<p>ちなみに、実際にAndroid KeyStoreの鍵生成で適用されている実行基盤は以下のコードから判別できます。</p>
<pre><code class="language-kotlin">
val secretKey = generateSecretKey()
val kf = SecretKeyFactory.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEY_STORE_PROVIDER)
val ki = kf.getKeySpec(secretKey, KeyInfo::class.java) as KeyInfo
val securityLevelString = when (ki.securityLevel) {
    KeyProperties.SECURITY_LEVEL_STRONGBOX -&gt; &quot;STRONGBOX&quot;
    KeyProperties.SECURITY_LEVEL_TRUSTED_ENVIRONMENT -&gt; &quot;TEE&quot;
    KeyProperties.SECURITY_LEVEL_SOFTWARE -&gt; &quot;SOFTWARE&quot;
    else -&gt; &quot;UNKNOWN&quot;
}

Log.d(&quot;KeyStoreSecurityLevel&quot;, &quot;Security Level: ${ki.securityLevel}&quot;)
</code></pre>
<h2>まとめ</h2>
<p>EncryptedSharedPreferencesがDeprecatedとなったため、移植先の技術選定を行いました。
公式推奨に則り、永続化手段としてDataStoreを採用しました。
暗号化技術に関してはAndroid KeyStoreとTinkの比較検証を行い、Tinkの方が鍵の発行や暗号化処理が抽象化されていて利用しやすく、また、処理速度も優れていることがわかり、セキュリティ要件としても十分であるためTinkを採用することにしました。
Android KeyStoreを採用する場合は、動作環境のデバイススペックも考慮した実装が求められるため、セキュリティ要件とのバランスを考慮する必要がありそうです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Introducing VDP (Vulnerability Disclosure Program) Kaizen Activities]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-14_VDP-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-14_VDP-en/</guid>
            <pubDate>Fri, 13 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[An overview of KINTO Technologies' efforts to improve the Vulnerability Disclosure Program (VDP) and the results achieved.]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1>Self-Introduction</h1>
<p>My name is Morino, and I work at KINTO Technologies, mainly in product security and security governance. I&#39;m a fan of RB Omiya Ardija and Chiikawa. For the past 10 years, I&#39;ve been involved in cybersecurity and information security. Prior to that, I spent many years as a web application engineer, focusing on the development and operation of web performance measurement systems and front-end platforms for e-commerce sites. In this article, I’d like to share our Kaizen efforts related to the Vulnerability Disclosure Program (VDP).</p>
<h1>What is the Vulnerability Disclosure Program (VDP)?</h1>
<p>The VDP is a system that enables companies and organizations to receive vulnerability reports from external security researchers and white hat hackers. Public awareness grew in October 2023 when Rakuten Group placed a &quot;security.txt&quot; on a public server and launched its VDP.</p>
<p><a href="https://xtech.nikkei.com/atcl/nxt/column/18/00001/08552/">Reference: Rakuten places text on public server — &quot;security.txt&quot; to help improve security</a> (in Japanese)</p>
<h1>What is security.txt?</h1>
<p>Published as RFC 9116 in April 2022, <em>security.txt: A File Format to Aid in Security Vulnerability Disclosure</em> helps standardize how companies and organizations share vulnerability reporting information, making it easier for security researchers to report issues. We also published our own security.txt in November 2023.</p>
<p><img src="/assets/blog/authors/masamorino/security_txt.jpg" alt="security.txt"></p>
<h1>Reactions After Installing security.txt</h1>
<p>Most inquiries were about whether we offer rewards (we don&#39;t), and many reports didn&#39;t clearly indicate whether they actually involved vulnerabilities in KINTO/KTC.</p>
<p><img src="/assets/blog/authors/masamorino/email01.jpg" alt="Email 1"> <img src="/assets/blog/authors/masamorino/email02.jpg" alt="Email 2"></p>
<h1>A White Hat Hacker Reported a Vulnerability!</h1>
<p>In August 2024, we received a report about a vulnerability in our service. After reviewing the report within our team, we confirmed the issue was real and asked the development team to fix it.</p>
<p><img src="/assets/blog/authors/masamorino/email03.jpg" alt="Email 3"></p>
<h1>Kaizen for Our VDP</h1>
<p>Although we recognized the value of the VDP, we identified several issues. As a result, we started using the VDP service offered by IssueHunt in November 2024.</p>
<ul>
<li>Clarify VDP guidelines, including whether a bounty is offered</li>
<li>Specify which web services and applications are covered</li>
<li>Provide a report template</li>
</ul>
<p>As of March 7, 2025, we&#39;ve received six reports; two of which involved vulnerabilities that required action and have been fixed. Honestly, I&#39;m surprised at the results, which exceeded my expectations.</p>
<p>Our company is featured as a case study on the IssueHunt website. Please have a look if you&#39;re interested.</p>
<p><a href="https://issuehunt.jp/cases/kinto-technologies">A fresh perspective on boosting security! VDP success story in the car subscription industry</a> (in Japanese)</p>
<h1>Our Contribution to the P3NFEST Bug Bounty 2025 Winter</h1>
<p>As mentioned above, we do not currently offer a rewards program. However, to evaluate the effectiveness of incentive-based systems and to support students working to secure the future of the internet we’ve decided to contribute some of our services to a student-focused bug bounty program hosted by IssueHunt.</p>
<p>The following services are eligible for bug bounties:</p>
<ul>
<li>KINTO Technologies Corporate Site</li>
<li>KINTO Tech Blog (this website)</li>
</ul>
<p>The event runs from Monday, February 17, 2025 to Monday, March 31, 2025.</p>
<p>For more information, please visit the event page. We welcome students to take on the challenge. <a href="https://issuehunt.jp/events/2025/winter/p3nfestbugbounty">P3NFEST Bug Bounty 2025 Winter</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ChatGPTに全任せ！AIとつくる、バーチャルキャラクター映像（Midjourney・Runway）]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-06-ai-character-movie-making/</guid>
            <pubDate>Fri, 13 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[ChatGPT、Midjourney、RunwayなどのAIを駆使して、バーチャルキャラクター映像をほぼ会話だけで形にしていった過程をまとめています。]]></description>
            <content:encoded><![CDATA[<p>「こんな世界観の映像、作ってみたいな…」<br>そう思ったとき、皆さんならどうしますか？<br>私は迷わず、ChatGPTに丸投げしました。</p>
<p>こんにちは。KINTOテクノロジーズのクリエイティブ室でデザイナーをしている桃井（<a href="https://x.com/momoitter">@momoitter</a>）です。</p>
<p>この記事では、ChatGPT、Midjourney、RunwayなどのAIツールを駆使して、ピンク髪のバーチャルキャラクターの映像を、ほぼ会話だけで形にしていった過程をまとめています。</p>
<p>専門的なスキルや時間がなくても、「こんな映像を作ってみたい」というアイデアさえあれば大丈夫。
AIと一緒に、そのイメージを少しずつかたちにしていくプロセスを体験してみたい方に向けて書きました。</p>
<p>まずは完成した映像をご覧ください。<br><a href="https://www.youtube.com/watch?v=GH9CdNqTyHQ">https://www.youtube.com/watch?v=GH9CdNqTyHQ</a></p>
<h2>きっかけは、1体のキャラクターの“リニューアル”</h2>
<p>このキャラクターは、もともと2024年11月の社内イベント「超本部会」のために誕生したものでした。
<img src="/assets/blog/authors/momoi/250606/01_old_character.jpg" alt="old_character"></p>
<p>制作過程はこちらの記事をご覧ください。<br><a href="https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/">https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/</a></p>
<p>当時としては最先端のAI技術を使い、社内でも注目を集めたキャラクターでした。</p>
<p>……が、あれからわずか4ヶ月。画像生成AIや動画生成AIはさらに進化し、当時「すごい！」と思っていた表現が、いま見ると少し古く感じてしまうように。</p>
<p>そこで、「せっかくなら最新の技術で、このキャラの世界をアップグレードしよう」と思い立ち、ChatGPTと一緒に再構築を始めることにしました。</p>
<h2>Step1 世界観の共有と画像生成</h2>
<p>まず最初に行ったのは、キャラクターとその世界観の共有です。<br>もともと生成していた画像をChatGPTにアップロードし、こう伝えました。</p>
<blockquote>
<p>このキャラクターは少し前の画像生成AI技術で作られたため、見た目をアップデートしたいです。<br>彼女には「バーチャルオペレーター」という設定があります。<br>その設定をもとに世界観を膨らませ、Midjourney v7で表現できるようなシーンのバリエーションとプロンプトを提案してください。</p>
</blockquote>
<p>Midjourneyを選んだ理由は、v7にアップデートされて以降、キャラクター描写の精度や質感が大きく向上したと感じていたからです。
今回のように、既存キャラの見た目をアップグレードしたい場面にぴったりだと思いました。</p>
<p><img src="/assets/blog/authors/momoi/250606/02_chatgpt_input.jpg" alt="chatgpt_input"></p>
<p>するとすぐに、「その世界観なら、こういうシーンはどうでしょう？」といった具体的なシチュエーション案と、それに対応するプロンプトが次々と返ってきました。<br>まるで映像監督とのブレストをしているような感覚です。</p>
<p><img src="/assets/blog/authors/momoi/250606/03_chatgpt_prompt_midjourney.jpg" alt="chatgpt_prompt_midjourney"></p>
<p>試しにそのプロンプトをMidjourneyに入力してみたところ、<br>自分の想像をはるかに超えるビジュアルが次々と生成され、その表現力に驚かされました。</p>
<p><img src="/assets/blog/authors/momoi/250606/04_midjourney01.jpg" alt="midjourney01">
<img src="/assets/blog/authors/momoi/250606/04_midjourney02.jpg" alt="midjourney02">
<img src="/assets/blog/authors/momoi/250606/04_midjourney03.jpg" alt="midjourney03">
<img src="/assets/blog/authors/momoi/250606/04_midjourney04.jpg" alt="midjourney04">
<img src="/assets/blog/authors/momoi/250606/04_midjourney05.jpg" alt="midjourney05"></p>
<p>この映像を作り始めた当初は、Midjourney v7に「Omni-Reference」のようなキャラクターの一貫性を保つ機能がまだ搭載されていませんでした。
そのため、「ピンク髪のショートヘア」という分かりやすい特徴を意識的にプロンプトへ含めることで、「一貫性があるように見せる」工夫をしていました。</p>
<p>もしイメージと違うものが出てきても、<br>「もう少し顔に寄って」「背景を明るくクリーンな雰囲気に」などとChatGPTに伝えるだけで、再調整されたプロンプトを即座に出力してくれます。</p>
<h2>Step2 画像から動画生成へ</h2>
<p>気に入った画像が生成できたら、次はそれをChatGPTに添付し、以下のように依頼します。</p>
<blockquote>
<p>この画像は提案していただいた〇〇のシーンのプロンプトをMidjourneyで生成した画像です。<br>この画像をRunwayのGen-4のキーフレーム機能のファーストフレームとして設定し、動画を生成したいです。<br>よりこのシーンが魅力的になるような、動きを加えたプロンプトを生成してください。</p>
</blockquote>
<p><img src="/assets/blog/authors/momoi/250606/05_chatgpt_input_scene.jpg" alt="chatgpt_input_scene"></p>
<p>ChatGPTは画像の内容を読み取った上で、その魅力を最大化するRunway用プロンプトを作成してくれます。<br><img src="/assets/blog/authors/momoi/250606/06_chatgpt_prompt_runway.jpg" alt="chatgpt_prompt_runway"></p>
<p>Runwayを使った理由は、バージョンがGen-4へと進化したことによって、Midjourneyの高精細なビジュアルの魅力を損なわずに動画化できると感じたからです。  </p>
<p>Runway Gen-4のimage to videoにMidjourneyで生成した画像をアップ。<br>ChatGPTが出力したプロンプトを貼り付けると、画像の世界観を最大限に引き出す高クオリティな映像が生成されました。
<img src="/assets/blog/authors/momoi/250606/07_runway.jpg" alt="runway"></p>
<p>キャラクターやカメラの動きのイメージが違った場合も、<br>ChatGPTに「生成された動画はこうなっていたので、ここをこう変えてほしい」と伝えるだけで、プロンプトを再提案してくれます。</p>
<h2>Step3 BGM選定もChatGPTと一緒に</h2>
<p>映像のBGMを探すときも、ChatGPTが大活躍。  </p>
<blockquote>
<p>この世界観に合うBGMをAdobe Stockでどのようなキーワードで探せばいいですか？</p>
</blockquote>
<p>と聞くと、「futuristic」「sci-fi」「cyberpunk」など、雰囲気に合ったワードをいくつも提案してくれました。</p>
<p><img src="/assets/blog/authors/momoi/250606/08_adobestock.jpg" alt="adobestock"></p>
<h2>Step4 編集して完成</h2>
<p>生成した動画とBGMをPremiere Proでつなぎ合わせ、構成・長さ・テンポ感を調整します。<br>シーンの切り替えにフェードイン・アウトを加えたり、音の入り方に緩急をつけたりすることで、映像全体の完成度がグッと高まります。</p>
<p>Midjourneyで作成した静止画と、Runwayで生まれた滑らかな動きが合わさることで、
静止画だけでは伝わりきらなかった「息づかい」や「空気感」が加わり、世界観が一段とリアルに感じられるイメージビデオが完成しました。
<a href="https://www.youtube.com/watch?v=GH9CdNqTyHQ">https://www.youtube.com/watch?v=GH9CdNqTyHQ</a></p>
<h2>AIと一緒に、想像をかたちにするということ</h2>
<p>今回のプロセスで一番感じたのは、<br>自分の頭の中の曖昧なイメージを、ChatGPTがどんどん「言語化＆具現化」してくれること。</p>
<p>Midjourneyでも、Runwayでも、「ちょっと違う」「もっとこう」と伝えるだけで、理想の表現に近づいていく実感がありました。</p>
<p>AIと一緒に進めることで、創造の幅が大きく広がることを実感できるはずです。
ぜひ一度、体験してみてください。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/momoi/250606/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Exploring the Future of QA and AI: An Idea-Packed Brainstorming Session]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-10-QA-×-AIで切り開く未来-アイデア満載のブレインストーミング--en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-10-QA-×-AIで切り開く未来-アイデア満載のブレインストーミング--en/</guid>
            <pubDate>Thu, 12 Jun 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Hi, this is Nakanishi from the QA Group (though I also wear a few other hats at the Developer Relations Group and the KINTO FACTORY Development Group ^^)</p>
<p>This year at KINTO Technologies, we&#39;re embracing an &quot;AI First, Release First&quot; mindset. As part of this shift, our QA team has been exploring ways to make the most of AI to speed up our release cycles.</p>
<p>This time, a group of QA members who shared the same passion came together for a lively brainstorming session.</p>
<ul>
<li>&quot;Wouldn&#39;t it be awesome if we could do this?&quot;</li>
<li>&quot;I really want to try something like this!&quot;</li>
</ul>
<p>Together, we discussed ideas and possibilities.</p>
<p>In this article, I&#39;ll be sharing some of the most exciting concepts that emerged out of the session, exploring how AI can help transform the future of QA.</p>
<hr>
<h1>Issues That Emerged from Our Discussion</h1>
<p>QA teams produce a huge volume of documents every day, but the sheer amount of information makes it hard to find what&#39;s actually needed. Specification formats also vary by project or person, which complicates sharing across teams. On top of that, there&#39;s no solid system for reviewing incidents or implementing preventive measures—making it tough to stop issues from recurring. The review process itself is also a heavy workload, and there&#39;s growing demand to streamline it.</p>
<h1>A Future Made Possible by AI</h1>
<h2>Effective Information Use (RAG)</h2>
<p>RAG (Retrieval-Augmented Generation) is a cutting-edge method for retrieving and analyzing information using the latest in generative AI technology. It quickly pulls key insights from vast archives of documents and incident data, delivering the right information to users when they need it. For instance, when an incident occurs, AI can instantly scan and analyze similar past cases to suggest effective solutions on the spot. It&#39;s like having a top-tier assistant who remembers every past experience and offers instant advice right when you need it. Already in use across industries like finance and customer support, it&#39;s dramatically speeding up response times and boosting problem-solving efficiency.</p>
<h2>Organizing and Supporting Specifications and Designs</h2>
<p>AI helps identify inconsistencies and omissions in your specifications, clearly highlighting and organizing any issues. By analyzing the inputted specifications, it can also automatically generate suitable test scenarios. For example, if you provide the specs for an e-commerce site cart feature, the AI can instantly create a scenario like: &quot;Add product → Change quantity → Payment → Order confirmation.&quot; It can even generate additional scenarios, such as error handling flows and boundary value tests. This drastically cuts down the time and effort spent on manual scenario creation, boosting both the accuracy and efficiency of QA tasks.</p>
<h2>Automate and Streamline Your QA Process</h2>
<p>AI analyzes user operation logs to catch even the small mistakes that humans might miss. Specifically, it detects frequent user errors and unusual operations, like &quot;errors triggered during specific screen transitions&quot; or &quot;fields often filled in incorrectly on input forms.&quot; With this, you can refine your test scenarios and address potential problem areas in advance. AI also automates the hassle of managing test case numbering on Conflu, cutting down on manual mistakes and saving a significant amount of time.</p>
<h2>Incident Analysis and Prevention</h2>
<p>AI that analyzes past incidents and offers concrete measures to prevent them from happening again. For example, it thoroughly reviews cases like &quot;display issues on specific browsers&quot; or &quot;payment system failures&quot; on e-commerce sites. Based on the findings, it suggests actionable steps such as &quot;regularly checking browser behavior by version&quot; or &quot;enhancing error handling before and after payment processing.&quot; When a critical issue arises, the AI immediately assesses the risk level and sends automatic alerts to the relevant teams, enabling swift, real-time response and resolution.</p>
<h2>Streamlining Test Data Creation</h2>
<p>AI instantly generates the data required for testing. From new vehicle and used car details to user information, it can quickly produce large volumes of diverse data tailored to realistic business scenarios. In addition, by integrating AI with browser automation tools like Selenium and Appium, tasks that once required manual input can now be automated. With just a few simple settings, you can generate massive amounts of test data in no time. This integration not only reduces human error but also slashes the workload required for data creation, greatly improving the overall efficiency of your QA process.</p>
<h2>Tool Integration and Process Automation</h2>
<p>Connect Slack with tools like JIRA or Asana to receive timely, automated updates on what matters. Streamline your workflow by centralizing information from various tools in one place. Let AI serve as the bridge between platforms, smoothing out your entire process flow.</p>
<h2>Using AI Models for QA</h2>
<p>We&#39;re exploring the use of ChatGPT fine-tuned specifically for QA tasks. By building our own in-house AI model, we dramatically improved the response speed of the AI. Furthermore, we&#39;re working to create an environment where developers can easily use AI for quick self-checks.</p>
<hr>
<h1>Future Action Plans</h1>
<ol>
<li>Prioritize organizing and consolidating information using AI.</li>
<li>AI-assisted reviews will help us work more efficiently and lighten the human load.</li>
<li>Continuous data collection for the development of dedicated AI models.</li>
<li>Actively utilizing AI-based incident analysis for process improvement.</li>
<li>Automation between various tools will lead to further improvements in efficiency.</li>
</ol>
<hr>
<p>Even when something feels too big to handle alone, new paths can open up when we think together. Inspired by the ideas from this brainstorming session, we&#39;ll launch a range of QA initiatives powered by AI. If you&#39;re a QA engineer interested in exploring new possibilities with AI or taking on fresh challenges with us, we&#39;d love to connect. Casual chats and info sessions are always welcome. Feel free to reach out!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-03-10-AI-QA/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年3月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-12-newcomer-202503/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-12-newcomer-202503/</guid>
            <pubDate>Thu, 12 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[2025年3月に入社した5人の皆様に入社後の感想を伺い、まとめました。]]></description>
            <content:encoded><![CDATA[<h1><strong>はじめに</strong></h1>
<p>こんにちは、2025年3月入社の tetsu です！
本記事では、2025年3月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。
KINTO テクノロジーズ(以下、KTC)に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1><strong>オサダヨシヒロ</strong></h1>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>グループコアシステム部のオサダです。ビジネスデベロップメントチームとグローバルコミュニティサイトの“TOYOTA Community by KINTO”を担当しています。</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>グループコアシステム部は約40名います。多国籍なメンバーがいて日本語、英語他様々な言語でコミュニケーションしています。ビジネスデベロップメント、システム開発グループ、共通サービス開発グループがあります。</li>
<li>ビジネスデベロップメントはグローバルのリースシステムを担当しています。</li>
<li>システム開発グループは“Global KINTO ID Platform”や“TOYOTA Community by KINTO”の企画/開発/運用しています。</li>
<li>共通サービス開発チームは”会員プラットフォーム”と“決済プラットフォーム”、最近立ち上げた“AIプラットフォーム”の開発/運用をしています。</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>業務外の部活動、イベント(任意参加の“Beer Bash”など)がとても多いと思いました。特に自動車部は活動活発な印象で、レース観戦やサーキットカートイベント等があります。部員でなくても飛び入り参加企画もあり、仕事も部活動も活発な印象です。</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>“Good Morning！”で元気よく１日がはじまる感じです。多国籍で様々な会社を経験しているメンバーなので、和気あいあい議論しながらお仕事進めています。</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>KTC、グループコアシステム部を知ってもらういいチャンスと思いました！　また、自分自身の振り返りにもなりました。</li>
</ul>
</li>
<li><p><strong>KJさん ⇒ オサダヨシヒロさんへの質問</strong></p>
<blockquote>
<p>映画がお好きとのことですが、これ観とかないと損するぞって映画を何点か教えていただきたいです。</p>
</blockquote>
<ul>
<li>KTCの皆さまへは、「タッカー」を観て頂きたいです。監督はフランシス・フォード・コッポラさん、製作総指揮 はジョージ・ルーカスさんと豪華なスタッフ陣で1988年のアメリカ映画です。時代は違いますが、車への熱い想いがあります。</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/t.sugiyama/osada.gif" alt="osada"></p>
<h1><strong>tetsu</strong></h1>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>プラットフォームGでPlatformエンジニアをしています、tetsuと申します！</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>私が所属するプラットフォームG Platform Engineeringチームは、神保町オフィスに6人、Osaka Tech Labに3人の体制となります。</li>
<li>東京と大阪で物理的に距離がありますが、SlackやGatherなどを使ってコミュニケーションをとっています。また東京と大阪のメンバーで一緒に勉強会を企画もしてます！</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>ギャップは特にありませんでした。オフィスが離れている他チームの人とのコミュニケーションが大変なのかなと思っていましたが、BeerBashのようなイベントや社内サークルが活発なので、他チームの人との接点も多く、仕事がしやすいです。</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>真面目に技術の議論をしたり、休憩中には雑談をしたりと和気あいあいとしています。私がKubernetesの勉強をしたいと話していたら、大阪のメンバーも一緒にしたいとなり、一緒に勉強会を企画することになり、今ではCloudInfraグループ、DBREグループとグループ横断で実施しています。</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>入社ブログを見ていて転職するかどうか考えていました。私と同じような転職を考えている人が見ているのかなと思うと、緊張しています(笑)</li>
</ul>
</li>
<li><p><strong>オサダヨシヒロさん ⇒ tetsuへの質問</strong></p>
<blockquote>
<p>神保町オフィス周辺のお勧めランチを教えてほしいです！</p>
</blockquote>
<ul>
<li>麺類が好きなので、「丸亀製麺」や油そばの「春日亭」によく行きます！天丼の「はちまき」という店も気になっているので、神保町でお会いしたときにぜひ行きましょう！</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_Me.jpeg" alt="tetsu"></p>
<h1><strong>YY</strong></h1>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>共通サービス開発G所属で、バックエンドエンジニアです</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>室町オフィスにて13名で開発を行なっています。</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>入社時のフォローアップがしっかり整備されている点がとても安心できました。チームではドキュメントにしっかり残す文化が定着していて、運用ノウハウを蓄積できている点が驚きでした。</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>それぞれが尊重し合って仕事していると思います。チーム方針として、「遠慮しない」という点を明記していたり、困っていたら誰かが力になってくれる環境でとても心強いです。</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>こういうのってなかなか腰が上がらなかったので、機会を与えていただけてとても嬉しいですし、今後執筆するハードルが下がったらいいなと思っています。</li>
</ul>
</li>
<li><p><strong>tetsuさん ⇒ YYさんへの質問</strong></p>
<blockquote>
<p>入社時に車を持っている話をしたと思うのですが、愛車へのこだわりがあれば教えてほしいです！</p>
</blockquote>
<ul>
<li>こだわりで合ってるのか微妙なとこですが、好きな車体カラーを選ぶとこですかね。有料色でもリセールも気にせずビビッときた色を選ぶようにしています！</li>
</ul>
</li>
</ul>
<h1><strong>ナミキ ユウジ</strong></h1>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>コーポレートITG ID (Innovation Drive)チームのナミキ ユウジです。</li>
<li>主にKTCのM365環境の課題解決と新しい技術の検証・導入、販売店様の情シス業務支援を行っています。</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>私を含めて9名のチームです。
室町、神保町、名古屋という複数拠点のメンバーで構成されています。</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>Slackのレスポンスが著しく速い！！と感じたのが第一印象ですね。コミュニケーションのスピード感に驚きました。「Slackの通知をパッと見て、すぐにアクションを返す習慣」が組織全体に根付いているんだなと感じました。</li>
<li>ギャップとしては、設立して間もない組織であるのに、社内向けドキュメントがきちんと整備されていることですね。また、ConfluenceやSharePointが使いこなされていることに感動しました。何かわからないことがあれば、内製の生成AIに聞いたり、社内ポータルにアクセスすれば、たいてい自己解決ができます。そのような環境であることは予想外でした。</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>気軽に声を掛けたり、ご飯に誘える雰囲気です。オフィス周辺には美味しいお店がたくさんあるので、ランチが楽しいです。仕事終わりのまぜそばがこれまた最高です。</li>
<li>チームの別拠点の方とも良い関係性で仕事ができています。数ヶ月に1回、別拠点の方とも直接お会いして仕事できる機会があることが大きいのかなと思っています。</li>
<li>また、社内イベントを通じて他部署の方とも気軽に交流できていることが嬉しいです！(まだ入社3ヶ月目ですが、50～70人近くの他部署の方と交流できています。嬉しい！)</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>エンジニアブログ運営チームに直接声をかけてもらって、素直に嬉しいと思いました。入社後は技術ブログを積極的に書きたいなと思っていましたし、入社同期と再び交流する機会にもなったので。これを機に、今後も継続して技術ブログを投稿していきたいと思います。(めざせ月イチ投稿！)</li>
</ul>
</li>
<li><p><strong>YYさん ⇒ ナミキ ユウジさんへの質問</strong></p>
<blockquote>
<p>同じサウナ部ですが、今気になってるサウナ施設があれば教えて欲しいです！</p>
</blockquote>
<ul>
<li>今年の5月にオープンした<a href="https://everyday-sauna.com/stores/%e8%b6%8a%e8%b0%b7%e5%ba%97/">毎日サウナ越谷店</a>です！
1号店の前橋店、2号店の八王子店に次ぐ3号店となるのですが、越谷店には毎日サウナ初のお風呂(しかも薬草泡風呂！)があるとのことで、メチャメチャ気になっています。サウナ部で行きたいですね！！</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/t.sugiyama/namiki.png" alt="namiki"></p>
<h1><strong>KJ</strong></h1>
<ul>
<li><p><strong>自己紹介</strong></p>
<ul>
<li>ゴルフが大好きでした。血豆ができるまで練習しました。30代後半から始めたのですがシニアプロになりたいと半ば本気で思ってました(笑)。ただ、コロナを境にプレー頻度が減って練習もしなくなり下手になりました。2025年4月、マスターズでのマキロイのキャリアグランドスラム達成に感動し、もう一度、出直そうと思っています。</li>
</ul>
</li>
<li><p><strong>所属チームの体制は？</strong></p>
<ul>
<li>出向先のKINTOでは業務PJ改善Tというリース業務の改善を行うチームに所属しており、そこは4人体制です。KTCでは部付きです。</li>
</ul>
</li>
<li><p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong></p>
<ul>
<li>官僚的・縦割りな組織ではなく、会社全体を見回し、そのうえで自身で出来ること、すべきことを自立的に考えることができる人が多い組織だなというのが第一印象です。
良い意味でトヨタらしさが無いことがギャップでした。</li>
</ul>
</li>
<li><p><strong>現場の雰囲気はどんな感じ？</strong></p>
<ul>
<li>KINTOサービスの根幹であるリース業務を回す部署なのでキッチリカッチリした雰囲気がある半面、メンバーの方々は多様性あふれる面白い人ばかりです。
どうすれば業務の品質が上がり、工数/リードタイムが削減できるだろう、と日々考えており、良い意味で現状を疑う雰囲気を持っていると思います。</li>
</ul>
</li>
<li><p><strong>ブログを書くことになってどう思った？</strong></p>
<ul>
<li>面白い取り組みだと思いました。一般的に埋もれやすい新入社員にスポットライトを当ててもらえるのはありがたいですし、応募・入社検討中の方々にとっても近しい存在の発信として参考になる情報だと思います。</li>
</ul>
</li>
<li><p><strong>ナミキ ユウジさん ⇒ KJさんへの質問</strong></p>
<blockquote>
<p>他部署のメンバーとの関わりなどどのようにされていますか？</p>
</blockquote>
<ul>
<li>自部署では1人だけの名古屋採用で、普段は室町オフィスではなく名古屋オフィスにいます。名古屋オフィスのKTCメンバーは室町に比べるとかなり少ないですが、少数がゆえの距離感の近さもあり他部署の方々とはよく仕事の話や雑談をさせてもらっています。一緒にランチに行くことも多いです。また、私はKINTOの業務部に出向させていただいており、リース業務を通して交わる人も徐々に増えてきいます。向こう1-2年くらいで友達100人を達成したいです(笑)。</li>
</ul>
</li>
</ul>
<h1><strong>さいごに</strong></h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTO テクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Creating an Original Talking Character Using 3 Generative AIs (Adobe Firefly, TTSMaker, and Runway)]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI-en/</guid>
            <pubDate>Wed, 11 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[How to make an original talking character using 3 Generative AIs (Adobe Firefly, TTSMaker, and Runway)]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello! I&#39;m Momoi (<a href="https://x.com/momoitter">@momoitter</a>), a designer at KINTO Technologies.</p>
<p>I belong to the Creative Office where I work on projects like the <a href="https://www.kinto-technologies.com/">corporate website</a> and <a href="https://corp.kinto-jp.com/mascot/">Kumobii</a> (KINTO&#39;s official mascot) related sites, incorporating cutting-edge web design along the way.</p>
<p>In November 2024, our company hosted an internal event called the CHO All-Hands Meeting, and I was in charge of creating the opening movie for it and I used three generative AIs to create a female character who kicks off the event with a voice announcement.</p>
<h3>Actual Video</h3>
<p><a href="https://www.youtube.com/watch?v=pVj_UQ_3-tg">https://www.youtube.com/watch?v=pVj_UQ_3-tg</a> <em>When asked at the event start, &quot;Are you ready?&quot;, she answers &quot;Of course!&quot;</em></p>
<p>In this article, I&#39;ll share the process of creating this original talking character and my thoughts along the way.</p>
<ul>
<li>How I wanted to create our own character that talks</li>
<li>About incorporating AI to easily create a memorable video</li>
</ul>
<p>If you&#39;re like me, then definitely keep reading!</p>
<h2>Background</h2>
<p>The request from the art director overseeing the event&#39;s overall creative direction was to &quot;re-edit the video used in our <a href="https://www.kinto-technologies.com/">corporate website</a>&#39;s key visual and turn it into a one-minute opening movie.&quot;</p>
<p>But just re-editing existing footage felt a bit too familiar for employees, and I wanted something more eye-catching, something that instantly energizes the event from the start.</p>
<p>That&#39;s when I turned to our AI chatbot in our company Slack. I thought, what if I used AI to bring Sherpa to life as a surprise? A personified Sherpa in the video could definitely catch everyone&#39;s eye.</p>
<h2>AIs Used</h2>
<p>To create the original talking character, I used the following three AI tools:</p>
<ol>
<li>Adobe Firefly (character image generation)</li>
<li>TTSMaker (text-to-speech conversion)</li>
<li>Runway (character animation with voice)</li>
</ol>
<p>From here on, I&#39;ll show you how I used these AIs to make the video.</p>
<h2>1. Character Image Generation</h2>
<h3>Adobe Firefly</h3>
<p>An image generation AI tool provided by Adobe. Since Adobe Firefly is trained on copyright-free images like Adobe Stock images, it can be used without worrying about copyright issues.</p>
<p>Many of the well-known image generation AIs claim to be copyright-free, but some operate in a gray area, such as being able to generate images that closely resemble anime characters. Since this was a corporate event, we wanted to avoid any copyright concerns, so we chose an AI that can be used without such worries.</p>
<p><a href="https://www.adobe.com/jp/products/firefly.html">Adobe Firefly</a></p>
<h3>Image Generation</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly01.jpg" alt="firefly01"></p>
<p>&quot;Sherpa,&quot; the original inspiration for this character, is displayed on our company&#39;s Slack with an icon like this:</p>
<p>From the icon we established that:</p>
<ul>
<li>The &quot;pa&quot; sound in Sherpa inspired a feminine feel</li>
<li>The pink color of the icon led us to give her pink hair</li>
<li>She&#39;s an AI chatbot so we wanted to give her a smart, digital vibe in her overall look</li>
</ul>
<p>And by doing so we expanded the character&#39;s image.</p>
<h3>On-screen Operations</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly02.jpg" alt="firefly02"></p>
<p>When you open Firefly, you&#39;ll see a screen like this.</p>
<p>Roughly speaking, you enter the prompts to generate an image in the input area below, and then use the menu on the left to adjust the aspect ratio, composition, style, tone, etc.</p>
<p>This time, after much trial and error, I generated the character using the prompt &quot;3D character, female, pink hair, white background, upper body, white simple digital clothing, facing forward.&quot;</p>
<h3>Mass Generation</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly03.jpg" alt="firefly03"></p>
<p>Once the prompt was more solidified, it was a matter of luck whether I would get a good result, so I just generated 100 to 200 sheets.</p>
<h3>Selection</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly04.jpg" alt="firefly04"></p>
<p>To give the event a fresh start, I aimed for a character that felt like an &quot;AI operator&quot; with an official and trustworthy vibe.</p>
<p>So, I filtered out these images.</p>
<ul>
<li>Look too young</li>
<li>Weird clothing</li>
<li>Scary face</li>
</ul>
<h3>Selected Image</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly05.jpg" alt="firefly05"></p>
<p>After carefully narrowing down the options, I chose this image because it felt cool yet approachable.</p>
<h2>2. Text-to-Speech Conversion</h2>
<h3>TTSMaker</h3>
<p>An AI voice generator that converts typed text into speech.</p>
<p>There are many similar AI-based text-to-speech services, but many of them are paid services or require crediting the source if they are free. I went with this tool because it&#39;s free to use without any credit attribution.</p>
<p><a href="https://ttsmaker.com/ja">TTSMaker</a></p>
<h3>On-screen Operations</h3>
<p><img src="/assets/blog/authors/momoi/250226/02_tts01.jpg" alt="tts01">When you open TTSMaker, you&#39;ll see a screen like this.</p>
<p>The procedure is as follows:</p>
<ol>
<li>Select a language</li>
<li>Enter the text you want to convert to speech</li>
<li>Listen to sample voices and choose the tone</li>
<li>Set the details like speed and pitch</li>
<li>Convert</li>
</ol>
<p>I aimed for a voice that felt like an &quot;AI operator&quot; with an official and trustworthy vibe. After comparing sample voices, I chose &quot;406 - Yuki Tsumiyuki -🇯🇵 Japanese female&quot; and had her read the line: &quot;Mochirondesu. Cho-honbukai o hajimemasu.&quot; (&quot;Of course. We&#39;ll now start the CHO All-Hands Meeting.&quot;)</p>
<h3>Actual Audio</h3>
<p><a href="https://www.youtube.com/watch?v=r4Zw2by669I">https://www.youtube.com/watch?v=r4Zw2by669I</a></p>
<h2>3. Character Animation with Voice</h2>
<h3>Runway</h3>
<p>An AI-powered tool that makes it easy to create and edit high-quality videos.</p>
<p>I chose this tool for its &quot;Lip Sync Video&quot; feature, which syncs character images with voice audio.</p>
<p><a href="https://runwayml.com/">Runway</a></p>
<h3>On-screen Operations</h3>
<h4>1.Open &quot;Lip Sync Video&quot; under the &quot;Generative Audio&quot; section, then drag and drop the previously created character image.</h4>
<p><img src="/assets/blog/authors/momoi/250226/03_runway01.jpg" alt="runway01"></p>
<h4>2. Check that the facial range of the character image is recognized correctly, and if there are no problems, click &quot;upload audio.&quot;</h4>
<p><img src="/assets/blog/authors/momoi/250226/03_runway02.jpg" alt="runway02"></p>
<h4>3. Drag and drop the previously generated voice, then click &quot;Generate.&quot;</h4>
<p><img src="/assets/blog/authors/momoi/250226/03_runway03.jpg" alt="runway03"></p>
<h3>Generated Video</h3>
<p><a href="https://www.youtube.com/watch?v=usY4yB9Z1YA">https://www.youtube.com/watch?v=usY4yB9Z1YA</a></p>
<p>A video of the character speaking in sync with the audio was generated.</p>
<h3>Bonus Tip 1</h3>
<p><a href="https://www.youtube.com/watch?v=raYsnhwZONo">https://www.youtube.com/watch?v=raYsnhwZONo</a></p>
<p>By uploading a song, the character can actually be made to sing.</p>
<h3>Bonus Tip 2</h3>
<p><a href="https://www.youtube.com/watch?v=3edVClgoLug">https://www.youtube.com/watch?v=3edVClgoLug</a></p>
<p>In this way, it&#39;s also possible to make a still image of a real person talk.</p>
<p>At a recent internal study session (held in Tokyo), the speaker was suddenly unable to make the business trip from Osaka to attend, so we asked him to prepare still images and voice memos, and we used this AI-generated video to make our presentation.</p>
<h2>4. Final Touches</h2>
<p><img src="/assets/blog/authors/momoi/250226/04_shiage01.jpg" alt="shiage01"></p>
<p>That&#39;s all for how to create a talking character video. What comes next is a little something extra.</p>
<p>Actually, getting a character to talk using AI has become so easy that anyone can do it with the AIs I introduced above.</p>
<p>But since I&#39;m a designer in the Creative Office, I wanted to go beyond what any other department could produce. So as a final touch, I added a 3D balloon-style CG of Sherpa made in Illustrator, gave it a soft floating motion in After Effects, and composited it into the video. This added a polished finish and added the value only a creator could bring.</p>
<h3>Final Video</h3>
<p><a href="https://www.youtube.com/watch?v=pVj_UQ_3-tg">https://www.youtube.com/watch?v=pVj_UQ_3-tg</a></p>
<p>By adding one final touch, the video evolved from just a talking character to something with added graphical expression.</p>
<h2>Conclusion</h2>
<p><img src="/assets/blog/authors/momoi/250226/05_matome01.jpg" alt="matome01"> <em>How it looked when projected at the event</em></p>
<p>Each creative generative AI has its own characteristics and limitations. By understanding and combining those characteristics, I was able to create a level of quality that wouldn&#39;t have been possible with just one tool.</p>
<p>On the day of the event, this character was projected on a large screen, and I was grateful to hear comments like:</p>
<ul>
<li>&quot;That was amazing! How did you make it?&quot;</li>
<li>&quot;The quality was so high I thought it was outsourced!&quot;</li>
</ul>
<p>It felt great to see the impact it made, just as I aimed for.</p>
<p>All tools used are simple enough that even non-creators can use it. As long as you have an idea, you can create memorable videos like this. If this article sparked your interest, definitely give it a try!</p>
<p>Thank you for reading to the end.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/momoi/250226/00_main_visual.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Creating an Original Talking Character Using 3 Generative AIs (Adobe Firefly, TTSMaker, and Runway)]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-14-creating_a_mascot_with_generative_AI-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-14-creating_a_mascot_with_generative_AI-en/</guid>
            <pubDate>Wed, 11 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[How to make an original talking character using 3 Generative AIs (Adobe Firefly, TTSMaker, and Runway)]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello! I&#39;m Momoi (<a href="https://x.com/momoitter">@momoitter</a>), a designer at KINTO Technologies.</p>
<p>I belong to the Creative Office where I work on projects like the <a href="https://www.kinto-technologies.com/">corporate website</a> and <a href="https://corp.kinto-jp.com/mascot/">Kumobii</a> (KINTO&#39;s official mascot) related sites, incorporating cutting-edge web design along the way.</p>
<p>In November 2024, our company hosted an internal event called the CHO All-Hands Meeting, and I was in charge of creating the opening movie for it and I used three generative AIs to create a female character who kicks off the event with a voice announcement.</p>
<h3>Actual Video</h3>
<p><a href="https://www.youtube.com/watch?v=pVj_UQ_3-tg">https://www.youtube.com/watch?v=pVj_UQ_3-tg</a> 
<em>When asked at the event start, &quot;Are you ready?&quot;, she answers &quot;Of course!&quot;</em></p>
<p>In this article, I&#39;ll share the process of creating this original talking character and my thoughts along the way.</p>
<ul>
<li>How I wanted to create our own character that talks</li>
<li>About incorporating AI to easily create a memorable video</li>
</ul>
<p>If you&#39;re like me, then definitely keep reading!</p>
<h2>Background</h2>
<p>The request from the art director overseeing the event&#39;s overall creative direction was to &quot;re-edit the video used in our <a href="https://www.kinto-technologies.com/">corporate website</a>&#39;s key visual and turn it into a one-minute opening movie.&quot;</p>
<p>But just re-editing existing footage felt a bit too familiar for employees, and I wanted something more eye-catching, something that instantly energizes the event from the start.</p>
<p>That&#39;s when I turned to our AI chatbot in our company Slack. I thought, what if I used AI to bring our chatbot to life as a surprise? A personified version in the video could definitely catch everyone&#39;s eye.</p>
<h2>AIs Used</h2>
<p>To create a talking character, I used the following three AI tools:</p>
<ol>
<li>Adobe Firefly (character image generation)</li>
<li>TTSMaker (text-to-speech conversion)</li>
<li>Runway (character animation with voice)</li>
</ol>
<p>From here on, I&#39;ll show you how I used these AIs to make the video.</p>
<h2>1. Character Image Generation</h2>
<h3>Adobe Firefly</h3>
<p>An image generation AI tool provided by Adobe. Since Adobe Firefly is trained on copyright-free images like Adobe Stock images, it can be used without worrying about copyright issues.</p>
<p>Many of the well-known image generation AIs claim to be copyright-free, but some operate in a gray area, such as being able to generate images that closely resemble anime characters. Since this was a corporate event, we wanted to avoid any copyright concerns, so we chose an AI that can be used without such worries.</p>
<p><a href="https://www.adobe.com/jp/products/firefly.html">Adobe Firefly</a></p>
<h3>Image Generation</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly01.jpg" alt="firefly01"></p>
<p>This character is displayed on our company&#39;s Slack with an icon like this:</p>
<p>From the icon we established that:</p>
<ul>
<li>The pink color of the icon led us to give her pink hair</li>
<li>She&#39;s an AI chatbot so we wanted to give her a smart, digital vibe in her overall look</li>
</ul>
<p>And by doing so we expanded the character&#39;s image.</p>
<h3>On-screen Operations</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly02.jpg" alt="firefly02"></p>
<p>When you open Firefly, you&#39;ll see a screen like this.</p>
<p>Roughly speaking, you enter the prompts to generate an image in the input area below, and then use the menu on the left to adjust the aspect ratio, composition, style, tone, etc.</p>
<p>This time, after much trial and error, I generated the character using the prompt &quot;3D character, female, pink hair, white background, upper body, white simple digital clothing, facing forward.&quot;</p>
<h3>Mass Generation</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly03.jpg" alt="firefly03"></p>
<p>Once the prompt was more solidified, it was a matter of luck whether I would get a good result, so I just generated 100 to 200 sheets.</p>
<h3>Selection</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly04.jpg" alt="firefly04"></p>
<p>To give the event a fresh start, I aimed for a character that felt like an &quot;AI operator&quot; with an official and trustworthy vibe.</p>
<p>So, I filtered out these images:</p>
<ul>
<li>The ones looking too young</li>
<li>Having weird clothing</li>
<li>Scary faces</li>
</ul>
<h3>Selected Image</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly05.jpg" alt="firefly05"></p>
<p>After carefully narrowing down the options, I chose this image because it felt cool yet approachable.</p>
<h2>2. Text-to-Speech Conversion</h2>
<h3>TTSMaker</h3>
<p>An AI voice generator that converts typed text into speech.</p>
<p>There are many similar AI-based text-to-speech services, but many of them are paid services or require crediting the source if they are free. I went with this tool because it&#39;s free to use without any credit attribution.</p>
<p><a href="https://ttsmaker.com/ja">TTSMaker</a></p>
<h3>On-screen Operations</h3>
<p><img src="/assets/blog/authors/momoi/250226/02_tts01.jpg" alt="tts01">When you open TTSMaker, you&#39;ll see a screen like this.</p>
<p>The procedure is as follows:</p>
<ol>
<li>Select a language</li>
<li>Enter the text you want to convert to speech</li>
<li>Listen to sample voices and choose the tone</li>
<li>Set the details like speed and pitch</li>
<li>Convert</li>
</ol>
<p>I aimed for a voice that felt like an &quot;AI operator&quot; with an official and trustworthy vibe. After comparing sample voices, I chose &quot;406 - Yuki Tsumiyuki -🇯🇵 Japanese female&quot; and had her read the line: &quot;Mochirondesu. Cho-honbukai o hajimemasu.&quot; (&quot;Of course. We&#39;ll now start the CHO All-Hands Meeting.&quot;)</p>
<h3>Actual Audio</h3>
<p><a href="https://www.youtube.com/watch?v=r4Zw2by669I">https://www.youtube.com/watch?v=r4Zw2by669I</a></p>
<h2>3. Character Animation with Voice</h2>
<h3>Runway</h3>
<p>An AI-powered tool that makes it easy to create and edit high-quality videos.</p>
<p>I chose this tool for its &quot;Lip Sync Video&quot; feature, which syncs character images with voice audio.</p>
<p><a href="https://runwayml.com/">Runway</a></p>
<h3>On-screen Operations</h3>
<h4>1.Open &quot;Lip Sync Video&quot; under the &quot;Generative Audio&quot; section, then drag and drop the previously created character image.</h4>
<p><img src="/assets/blog/authors/momoi/250226/03_runway01.jpg" alt="runway01"></p>
<h4>2. Check that the facial range of the character image is recognized correctly, and if there are no problems, click &quot;Upload Audio.&quot;</h4>
<p><img src="/assets/blog/authors/momoi/250226/03_runway02.jpg" alt="runway02"></p>
<h4>3. Drag and drop the previously generated voice, then click &quot;Generate.&quot;</h4>
<p><img src="/assets/blog/authors/momoi/250226/03_runway03.jpg" alt="runway03"></p>
<h3>Generated Video</h3>
<p><a href="https://www.youtube.com/watch?v=usY4yB9Z1YA">https://www.youtube.com/watch?v=usY4yB9Z1YA</a></p>
<p>A video of the character speaking in sync with the audio was generated.</p>
<h3>Bonus Tip 1</h3>
<p><a href="https://www.youtube.com/watch?v=raYsnhwZONo">https://www.youtube.com/watch?v=raYsnhwZONo</a></p>
<p>By uploading a song, the character can actually be made to sing.</p>
<h3>Bonus Tip 2</h3>
<p><a href="https://www.youtube.com/watch?v=3edVClgoLug">https://www.youtube.com/watch?v=3edVClgoLug</a></p>
<p>In this way, it&#39;s also possible to make a still image of a real person talk.</p>
<p>At a recent internal study session (held in Tokyo), the speaker was suddenly unable to make the business trip from Osaka to attend, so we asked him to prepare still images and voice memos, and we used this AI-generated video to make our presentation.</p>
<h2>4. Final Touches</h2>
<p><img src="/assets/blog/authors/momoi/250226/04_shiage01.jpg" alt="shiage01"></p>
<p>That&#39;s all for how to create a talking character video. What comes next is a little something extra.</p>
<p>Actually, getting a character to talk using AI has become so easy that anyone can do it with the AIs I introduced above.</p>
<p>But since I&#39;m a designer in the Creative Office, I wanted to go beyond what any other department could produce. So as a final touch, I added a 3D balloon-style CG made in Illustrator, gave it a soft floating motion in After Effects, and composited it into the video. This added a polished finish and added the value only a creator could bring.</p>
<h3>Final Video</h3>
<p><a href="https://www.youtube.com/watch?v=pVj_UQ_3-tg">https://www.youtube.com/watch?v=pVj_UQ_3-tg</a></p>
<p>By adding one final touch, the video evolved from just a talking character to something with added graphical expression.</p>
<h2>Conclusion</h2>
<p><img src="/assets/blog/authors/momoi/250226/05_matome01.jpg" alt="matome01"> 
<em>How it looked when projected at the event</em></p>
<p>Each creative generative AI has its own characteristics and limitations. By understanding and combining those characteristics, I was able to create a level of quality that wouldn&#39;t have been possible with just one tool.</p>
<p>On the day of the event, this character was projected on a large screen, and I was grateful to hear comments like:</p>
<ul>
<li>&quot;That was amazing! How did you make it?&quot;</li>
<li>&quot;The quality was so high I thought it was outsourced!&quot;</li>
</ul>
<p>It felt great to see the impact it made, just as I aimed for.</p>
<p>All tools used are simple enough that even non-creators can use it. As long as you have an idea, you can create memorable videos like this. If this article sparked your interest, definitely give it a try!</p>
<p>Thank you for reading till the end.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/momoi/250226/00_main_visual.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Spring AIを試してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-11-springAI/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-11-springAI/</guid>
            <pubDate>Wed, 11 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Spring AIを試してみた]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは。KINTOテクノロジーズ（以下KTC）プラットフォームグループ Platform Engineeringチームで内製ツールの開発・運用を担当している山田です。普段はアプリケーションエンジニアとして、JavaとSpring Bootを使ったバックエンドや、JavaScriptとReactを使ったフロントエンドでCMDB（Configuration Management Database）の開発をしています。ここ1年ほどは生成AIブームに乗り、CMDBにRAGやText-to-SQLを活用したチャットボットの開発にも取り組んでいます。</p>
<p>こちらはText-to-SQLについて書いた前回の記事です。ご興味ある方はぜひご覧ください！
<a href="https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/">https://blog.kinto-technologies.com/posts/2025-01-16-generativeAI_and_Text-to-SQL/</a></p>
<p>今回は生成AI関連で、2025年5月20日にGAされたSpring AIを試してみた内容をご紹介したいと思います。</p>
<h2>Spring AIの概要</h2>
<p>2025年5月20日にGAされたSpringエコシステムの一つで、生成AI関連のフレームワークです。生成AI分野ではPythonベースのLlamaIndexやLangChainが有名ですが、既存のSpringアプリケーションに生成AI機能を追加したい場合や、Javaで生成AI開発をやってみたい方にとっての選択肢になるかと思います。</p>
<p><a href="https://spring.pleiades.io/spring-ai/reference/">https://spring.pleiades.io/spring-ai/reference/</a></p>
<h2>検証内容</h2>
<p>今回はSpring AIを使って、以下の2つの機能を実装してみました。</p>
<ol>
<li><strong>チャット機能（LLM対話）</strong>: AWS BedrockのClaude 3.7 Sonnetモデルを利用した対話機能</li>
<li><strong>Embedding機能</strong>: CohereのエンベディングモデルとChroma（ベクトルデータベース）を組み合わせた文書埋め込み・類似文書検索機能</li>
</ol>
<h2>技術スタック</h2>
<ul>
<li>Spring Boot 3.5.0</li>
<li>Java 21</li>
<li>Spring AI 1.0.0</li>
<li>Gradle</li>
<li>Chroma 1.0.0</li>
<li>AWS Bedrock<ul>
<li>LLMモデル：Anthropic Claude 3.7 Sonnet</li>
<li>Embeddingモデル：Cohere Embed Multilingual v3</li>
</ul>
</li>
</ul>
<h2>環境構築</h2>
<h3>依存関係の設定</h3>
<p>まずは<code>build.gradle</code>にSpring AIを使うための依存関係を追加します。</p>
<pre><code class="language-gradle">plugins {
    id &#39;java&#39;
    id &#39;org.springframework.boot&#39; version &#39;3.5.0&#39;
    id &#39;io.spring.dependency-management&#39; version &#39;1.1.7&#39;
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

ext {
    set(&#39;springAiVersion&#39;, &quot;1.0.0&quot;)
}

dependencies {
    implementation &#39;org.springframework.boot:spring-boot-starter-web&#39;
    implementation &#39;org.springframework.boot:spring-boot-starter-validation&#39;
    implementation &#39;org.springframework.ai:spring-ai-starter-model-bedrock-converse&#39;
    implementation &#39;org.springframework.ai:spring-ai-starter-model-bedrock&#39;
    implementation &#39;org.springframework.ai:spring-ai-bedrock&#39;
    implementation &#39;org.springframework.ai:spring-ai-starter-vector-store-chroma&#39;
    testImplementation &#39;org.springframework.boot:spring-boot-starter-test&#39;
}

dependencyManagement {
    imports {
        mavenBom &quot;org.springframework.ai:spring-ai-bom:${springAiVersion}&quot;
    }
}
</code></pre>
<h3>アプリケーション設定</h3>
<p><code>application.yml</code>でAWS Bedrockの認証情報やモデル選択、ベクトルストアの接続情報を設定します。</p>
<pre><code class="language-yaml">spring:
  application:
    name: spring-ai-sample
  
  ai:
    bedrock:
      aws:
        region: ap-northeast-1
        access-key: ${AWS_ACCESS_KEY_ID}
        secret-key: ${AWS_SECRET_ACCESS_KEY}
        session-token: ${AWS_SESSION_TOKEN}
      converse:
        chat:
          options:
            model: arn:aws:bedrock:ap-northeast-1:{account_id}:inference-profile/apac.anthropic.claude-3-7-sonnet-20250219-v1:0
      cohere:
        embedding:
          model: cohere.embed-multilingual-v3
    model:
      embedding: bedrock-cohere
    vectorstore:
      chroma:
        client:
          host: http://localhost
          port: 8000
        initialize-schema: true
</code></pre>
<p>設定はこれだけです。あとはアプリケーション内で必要なクラスを簡単にDIできるようになります。</p>
<h2>実装例</h2>
<h3>1. 生成AI（LLM）との対話機能</h3>
<p>Spring AIでは<code>ChatClient</code>インターフェースを使って、簡単にLLMとの対話を実装できます。</p>
<h4>サービス層の実装</h4>
<p>ChatServiceクラスでLLMとの対話機能を実装します。</p>
<pre><code class="language-java">@Service
public class ChatService {
    private final ChatClient chatClient;

    public ChatService(ChatClient.Builder builder) {
        this.chatClient = builder.build();
    }

    public String generate(String message) {
        return this.chatClient.prompt(message).call().content();
    }
}
</code></pre>
<p><code>ChatClient.Builder</code>をDIすることで、設定ファイルに基づいたクライアントを自動的に構築できます。</p>
<h4>コントローラーの実装</h4>
<p>RESTコントローラーでAPIエンドポイントを提供します。</p>
<pre><code class="language-java">@RestController
public class ChatController {
    private final ChatService chatService;
    private final EmbeddingService embeddingService;

    public ChatController(ChatService chatService, EmbeddingService embeddingService) {
        this.chatService = chatService;
        this.embeddingService = embeddingService;
    }

    @PostMapping(&quot;/generate&quot;)
    public ResponseEntity&lt;String&gt; generate(@RequestBody ChatRequest request) {
        return ResponseEntity.ok(chatService.generate(request.getMessage()));
    }

}
</code></pre>
<p>これで<code>/generate</code>エンドポイントにメッセージをPOSTすると、Claude 3.7 Sonnetによる応答を受け取ることができます。</p>
<p>以下が呼び出し例です。</p>
<pre><code class="language-bash">curl -X POST http://localhost:8080/generate -H &quot;Content-Type: application/json&quot; -d &#39;{&quot;message&quot;: &quot;こんにちわわ&quot;}&#39;

こんにちは！何かお手伝いできることはありますか？
</code></pre>
<h3>2. Embedding検索機能の実装</h3>
<p>次に、ドキュメントをベクトル化して検索する機能を実装します。</p>
<h4>Embeddingサービスの実装</h4>
<p>このサービスでは、サンプルテキストを<code>Document</code>オブジェクトとしてベクトル化し、Chromaに保存します。そして「Spring」というクエリに対して類似度の高いドキュメントを検索します。この処理の裏側では、Cohere Embed Multilingual v3モデルによるテキストのベクトル変換が行われています。</p>
<pre><code class="language-java">@Service
public class EmbeddingService {
    private final VectorStore vectorStore;

    public EmbeddingService(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    public List&lt;Document&gt; embed() {
        List&lt;Document&gt; documents = List.of(
            new Document(&quot;Spring AI is a framework for building AI applications with the familiar Spring ecosystem and programming model.&quot;),
            new Document(&quot;LlamaIndex is a data framework for LLM applications to ingest, structure, and access private or domain-specific data.&quot;),
            new Document(&quot;LangChain is a framework for developing applications powered by language models through composability.&quot;)
        );

        vectorStore.add(documents);
        return vectorStore.similaritySearch(SearchRequest.builder().query(&quot;Spring&quot;).topK(5).build());
    }
}
</code></pre>
<h4>APIエンドポイントの実装</h4>
<pre><code class="language-java">@GetMapping(&quot;/embedding&quot;)
public ResponseEntity&lt;List&lt;Document&gt;&gt; embedding() {
    return ResponseEntity.ok(embeddingService.embed());
}
</code></pre>
<p>この実装により、<code>/embedding</code>エンドポイントにアクセスすると、サンプルドキュメントのベクトル化と検索が行われ、その結果が返されます。</p>
<p>以下が呼び出し例です。「Spring」という言葉が入ったドキュメントの類似度（score）が一番高い結果になっていますね。</p>
<pre><code class="language-bash">curl http://localhost:8080/embedding

[
  {
    &quot;id&quot;: &quot;af885f07-20c9-4db4-913b-95406f1cb0cb&quot;,
    &quot;text&quot;: &quot;Spring AI is a framework for building AI applications with the familiar Spring ecosystem and programming model.&quot;,
    &quot;media&quot;: null,
    &quot;metadata&quot;: {
      &quot;distance&quot;: 0.5593532
    },
    &quot;score&quot;: 0.44064682722091675
  },
  {
    &quot;id&quot;: &quot;5a8b8071-b8d6-491e-b542-611d33e16159&quot;,
    &quot;text&quot;: &quot;LlamaIndex is a data framework for LLM applications to ingest, structure, and access private or domain-specific data.&quot;,
    &quot;media&quot;: null,
    &quot;metadata&quot;: {
      &quot;distance&quot;: 0.6968217
    },
    &quot;score&quot;: 0.3031783103942871
  },
  {
    &quot;id&quot;: &quot;336b3e07-1a70-4546-920d-c4869e77e4bb&quot;,
    &quot;text&quot;: &quot;LangChain is a framework for developing applications powered by language models through composability.&quot;,
    &quot;media&quot;: null,
    &quot;metadata&quot;: {
      &quot;distance&quot;: 0.71094555
    },
    &quot;score&quot;: 0.2890544533729553
  }
]
</code></pre>
<h2>さいごに</h2>
<p>簡単ではありますが、今回はSpring AIを使った生成AI機能についてご紹介しました。Spring AIを試してみて、Javaのシステムに生成AI関連の処理を組み込む場合には選択肢の一つになるかなと思いました。また、今回ご紹介したチャット機能やEmbedding検索機能を組み合わせることで、RAG機能も比較的簡単に実装できると感じています。</p>
<p>現時点では、生成AI関連の機能やエコシステムはLlamaIndexやLangChainといったPython系フレームワークの方が充実している印象ですが、Spring AIはリリースされたばかりなので今後の機能追加に期待したいと思います！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Building a Multi-Agent System With LangGraph Supervisor]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-28-building-Multi-Agent-system-by-using-langgraph-supervisor-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-28-building-Multi-Agent-system-by-using-langgraph-supervisor-en/</guid>
            <pubDate>Tue, 10 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Introducing a Practical Use Case with the Latest LangGraph Update]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Hello!<br>I&#39;m Alex, a Generative AI Engineer on the Generative AI Development Project team at KINTO Technologies.</p>
<p>Lately, there&#39;s been a growing interest in building multi-agent systems using large language models (LLMs). So in this article, I&#39;d like to share a case where I built a Supervisor-style multi-agent system in less than 30 minutes using the latest LangGraph update, langgraph_supervisor. This system allows a central supervisor agent to coordinate and manage multiple specialized agents. It can be applied to a wide range of use cases, from automating workflows to generating customizable proposal scenarios.</p>
<h2>What is LangGraph?</h2>
<p><strong>LangGraph</strong> is a Python library designed to simplify the development of AI agents and Retrieval-Augmented Generation (RAG) systems. Combined with LangChain, complex workflows and tasks can be designed and implemented efficiently. It supports state persistence, tool invocation, and features like human-in-the-loop and post-task validation through a centralized persistence layer which served as the foundation for this Supervisor-style system.</p>
<h2>What is langgraph-supervisor?</h2>
<p><strong>langgraph-supervisor</strong> is a Python library for building hierarchical multi-agent systems using LangGraph, which was recently released by LangChain. A central supervisory agent coordinates task assignments and facilitates communication among specialized agents. This enables the development of adaptable systems that can tackle complex tasks with ease and efficiency.</p>
<h3>Key Features</h3>
<ol>
<li><p><strong>Supervisor agent creation:</strong> Manages multiple specialized agents and orchestrates the overall workflow.</p>
</li>
<li><p><strong>Tool-based agent handoff mechanism:</strong> Provides a mechanism for achieving smooth communication between agents.</p>
</li>
<li><p><strong>Flexible message history management:</strong> Enables easy control over conversation flow by managing message history dynamically.</p>
</li>
</ol>
<p>@<a href="https://github.com/langchain-ai/langgraph-supervisor/">card</a></p>
<h2>What is a Supervisor-style Multi Agent System?</h2>
<p><img src="/assets/blog/authors/alex.q/supervisor.png" alt="Diagram of Supervisor-style Multi Agent System"> <em>Source: <a href="https://langchain-ai.github.io/langgraph/tutorials/multi_agent/agent_supervisor/">https://langchain-ai.github.io/langgraph/tutorials/multi_agent/agent_supervisor/</a></em></p>
<p>A Supervisor-style multi-agent system is a structure where a central controlling agent called the Supervisor coordinates with tool-enabled LLM agents, deciding which agent to call, when to do so, and what arguments to pass along.</p>
<h2>Building a Multi-Agent System with langgraph-supervisor</h2>
<p>The following steps outline how to build a multi-agent system using langgraph-supervisor. In this example, we’ll create a system that recommends a car and engine type based on simple, anonymized customer information. Additionally, the agent will retrieve car and engine information from the locally stored &quot;Vehicle_Information.csv&quot; file.</p>
<h3>Environment Setup</h3>
<p><strong>Prepare Azure OpenAI API keys</strong></p>
<p>This time, we&#39;ll be using GPT-4o via Azure OpenAI. Depending on your situation, you can also use the OpenAI API, or Anthropic API, etc.</p>
<p>And set Azure OpenAI API keys and endpoint as environment variables.</p>
<pre><code class="language-python">os.environ[&quot;AZURE_OPENAI_API_KEY&quot;] = &quot;YOUR API KEY&quot;
os.environ[&quot;AZURE_OPENAI_ENDPOINT&quot;] = &quot;YOUR ENDPOINT&quot;
os.environ[&quot;AZURE_OPENAI_API_VERSION&quot;] = &quot;YOUR API VERSION&quot;
os.environ[&quot;AZURE_OPENAI_DEPLOYMENT&quot;] = &quot;YOUR DEPLOYMENT NAME&quot;
</code></pre>
<p><strong>Install LangGraph and LangChain</strong></p>
<pre><code class="language-terminal">pip install langgraph
pip install langchain
pip install langchain_openai
</code></pre>
<p><strong>Install langgraph-supervisor</strong></p>
<pre><code class="language-terminal">pip install langgraph-supervisor
</code></pre>
<h3>Setup of tool functions used by LLM and each Agent</h3>
<p>Define the model using Azure OpenAI&#39;s GPT-4o. Next, define the tool functions for agents.</p>
<pre><code class="language-python">from langchain_openai import AzureChatOpenAI
import pandas as pd

#  LLMの初期化
llm = AzureChatOpenAI(
    azure_deployment=os.getenv(&quot;AZURE_OPENAI_DEPLOYMENT&quot;),
    api_version=os.getenv(&quot;AZURE_OPENAI_API_VERSION&quot;),
)

car_information_path = &quot;/xxx/xxx/車両情報.csv&quot;

# ツール関数の定義例

# CSVファイルから車両情報を読み込み、候補を生成する例
def get_car_features():
    &quot;&quot;&quot;The python code to get car information.&quot;&quot;&quot;
    path = car_information_path  # CSVファイルのパスを指定
    df = pd.read_csv(car_information_path)
    car_features = df[[&quot;車種名&quot;, &quot;ボディタイプ&quot;, &quot;説明&quot;]].drop_duplicates()
    return car_features.to_dict(orient=&quot;records&quot;)

# 選択された車種に対してエンジンタイプを抽出する例
def get_engine_type(selected_car):
    &quot;&quot;&quot;The python code to get engine type and engine information.&quot;&quot;&quot;
    path = car_information_path
    df = pd.read_csv(car_information_path)
    engine_types = list(df[df[&quot;車種名&quot;] == selected_car][&quot;エンジンタイプ&quot;].unique())
    return engine_types, &quot;エンジンに関する補足情報&quot;
</code></pre>
<h3>Defining Each Agent</h3>
<p>We use LangGraph&#39;s &quot;create_react_agent&quot; to define each specialized agent (car model recommendation, engine type selection).</p>
<pre><code class="language-python">from langgraph.prebuilt import create_react_agent

# 車種推薦エージェント
car_agent = create_react_agent(
    model=llm,
    tools=[get_car_features],
    name=&quot;car_agent&quot;,
    prompt=&quot;&quot;&quot;
    # Instructions
    Based on the recommended pattern, choose a car model to recommend and explain the reasoning in about 200 characters.
    &quot;&quot;&quot;
)

# Engine type selection agent
engine_agent = create_react_agent(
    model=llm,
    tools=[get_engine_type],
    name=&quot;engine_agent&quot;,
    prompt=&quot;&quot;&quot;
    # Instructions
    Choose the most suitable engine type for the recommended car and explain the reasoning in about 200 characters.
    &quot;&quot;&quot;
)
</code></pre>
<h3>Defining the Supervisor</h3>
<p>Create a Supervisor to oversee each agent and generate a final recommendation based on customer information. One improvement we made here was modularizing the prompt input to the Supervisor, allowing for greater versatility. By changing the contents of the role and task variables and the associated agent, the system can easily be used for other tasks.</p>
<pre><code class="language-python">from langgraph_supervisor import create_supervisor

# Create the contents of each module in the prompt
role = &quot;車のセールスマン&quot;
task = &quot;顧客情報をもとに、最適な車とエンジンタイプを提案してください。&quot;
guideline = &quot;&quot;&quot;
    - 出力は必ず上記JSON形式で行ってください。
    - 各根拠は200文字程度の豊かな文章で記述してください。&quot;
    &quot;&quot;&quot;
output_format = &quot;&quot;&quot;
    {
        &quot;提案内容&quot;: {
            &quot;提案する車種&quot;,
            &quot;車種の根拠&quot;,
            &quot;選択したエンジンタイプ&quot;,
            &quot;エンジン選定理由&quot;
        }
    }
    &quot;&quot;&quot;

# Create a prompt
system_prompt = f&quot;&quot;&quot;
    ## Role
    あなたは優れた{role}です。

    ## Task 
    {task}

    ## Guidelines
    {guideline}
    
    ## 最終生成物のフォーマット
    {output_format}
    &quot;&quot;&quot;

# Create the Supervisor
workflow = create_supervisor(
    # 先ほど作成したエージェントと紐づく
    [car_agent, engine_agent],
    model=llm,
    prompt=system_prompt
)

# Compile the graph
app = workflow.compile()
</code></pre>
<h3>Example Run</h3>
<pre><code class="language-python">from langchain.schema import HumanMessage
import time

# Example of customer information (add more details as needed)
customer_info = &quot;顧客情報: Age 35, prioritizes fuel efficiency, currently drives a compact car.

# Example run
start_time = time.time()
result = app.invoke({
    &quot;messages&quot;: [
        {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: customer_info}
    ]
})
end_time = time.time()

# Display the final output
print(&quot;最終提案:&quot;)
print(result[&quot;messages&quot;][-1].content)
print(&quot;実行時間: {:.3f}秒&quot;.format(end_time - start_time))
</code></pre>
<h3>Response from the Supervisor</h3>
<pre><code class="language-markdown">最終提案:
{
    &quot;提案内容&quot;: {
        &quot;提案する車種&quot;: &quot;ハイブリッド技術を採用した最新コンパクトカー&quot;,
        &quot;車種の根拠&quot;: &quot;顧客は現在コンパクトカーに乗車中であり、燃費性能の良さを重視しています。 ハイブリッド技術を採用した車は、燃料使用の効率が高く、経済的負担や環境への配慮が優れています。 また、ハイブリッドシステムは短距離や都市部での走行にも最適です。 そのため、最新のハイブリッドコンパクトカーが最適な選択肢となります。&quot;,
        &quot;選択したエンジンタイプ&quot;: &quot;ハイブリッドエンジン&quot;,
        &quot;エンジン選定理由&quot;: &quot;ハイブリッドエンジンは燃費性能が非常に優れており、顧客のニーズである燃料効率を最優先に考慮しています。 さらに、コンパクトカーとの相性も良く、都市部での利用や日常の移動において高いパフォーマンスを発揮します。 このエンジンの選択は、快適性、経済性、環境性能を全て満たします。&quot;
    }
}
Run Time: 21.938 seconds
</code></pre>
<h2>Impressions After Building the System</h2>
<p><strong>Rapid Prototyping</strong>
The complex inter-agent coordination and state management typically required in LangGraph were implemented with simple code using langgraph_supervisor, and a prototype was successfully built in under 30 minutes. A pretty impressive result.</p>
<p><strong>Flexibility and Scalability</strong>
Each agent can be implemented with its own prompt and tool functions, allowing for easy customization to meet specific business needs. In addition, since state management is handled centrally, future improvements and feature additions are expected to be carried out smoothly.</p>
<hr>
<h2>We Are Hiring!</h2>
<p>KINTO Technologies is looking for passionate colleagues to help drive AI adoption in our businesses. We&#39;re happy to start with a casual interview. If you’re even slightly interested, feel free to reach out via the link below or through <a href="https://twitter.com/cognac_n">X DMs</a>. We look forward to hearing from you!!</p>
<p>@<a href="https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303115">card</a></p>
<p>Thanks for reading all the way through!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[I Paid for Both GitHub Copilot and Cursor: Pros and Cons]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-25-cursor-vs-copilot-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-25-cursor-vs-copilot-en/</guid>
            <pubDate>Mon, 09 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I paid for both Copilot and Cursor Editor, used them extensively, and will share the results of my comparison.]]></description>
            <content:encoded><![CDATA[<p>My name is Yuya Sakamaki from KINTO Technologies.<br>I am usually involved in anything from data analysis to proposing strategies, and developing features using machine learning. Previously, I was in charge of AI functional development at <a href="https://ppap.kinto-jp.com/">Prism Japan</a>.</p>
<p>I conducted an internal comparison test of Cursor and GitHub Copilot, and I would like to share the results with you.</p>
<hr>
<h2>Premise</h2>
<ul>
<li>At KINTO Technologies, there&#39;s a policy that allows GitHub Copilot to be used as a standard tool.</li>
<li>I will test whether using Cursor Editor can further improve productivity.</li>
</ul>
<h3>Cursor</h3>
<p>An AI-powered code editor, created as a fork of Visual Studio Code.</p>
<h3>Github Copilot</h3>
<p>It’s a tool co-developed by GitHub and OpenAI, available as a plug-in for various IDEs such as Visual Studio Code, JetBrains, Eclipse, and more.</p>
<h2>Disclaimer</h2>
<ul>
<li>This article contains many of my own opinions. This is not meant to undermine the usefulness of Cursor.</li>
</ul>
<h2>Tested Plans</h2>
<ul>
<li><strong>Copilot Enterprise</strong> ... $39 / month</li>
<li><strong>Cursor Business</strong> ... $40 / month</li>
</ul>
<h2>Conclusion</h2>
<ul>
<li><strong>As of February 2025</strong>, I feel that there is almost no difference in functionality between GitHub Copilot and Cursor.</li>
<li><strong>As of December 2024</strong>, I would have liked to recommend Cursor, but with the incredible speed at which Copilot has progressed, I no longer feel the need to choose Cursor Editor again.</li>
</ul>
<h2>Evaluation Period</h2>
<ul>
<li><strong>December 2024 to February 2025</strong></li>
</ul>
<hr>
<h2>Comparison Table (Copilot vs. Cursor)</h2>
<p>I have put together a table of some of the most common points that came up in the discussions using Copilot and Cursor together. The details of each item will be provided later.</p>
<table>
<thead>
<tr>
<th>Item</th>
<th>GitHub Copilot</th>
<th>Cursor</th>
</tr>
</thead>
<tbody><tr>
<td><strong>UI/Operability</strong></td>
<td>- Inline suggestions and chat ability in VSCode<br>- Many operations by right-clicking</td>
<td>- AI-powered shortcuts are displayed instantly<br>- Inline menus are easy to understand</td>
</tr>
<tr>
<td><strong>QuickFix (completion of import, etc.)</strong></td>
<td>- VSCode standard quick fixes are powerful</td>
<td>- Sometimes it is unstable and doesn&#39;t work<br>- <code>Fix in Composer</code> etc., is a little troublesome</td>
</tr>
<tr>
<td><strong>Automatic reflection of similar corrections</strong></td>
<td>- Need to start writing something first</td>
<td>- Tabs predict and suggest the next correction to make (useful for making corrections to multiple places)</td>
</tr>
<tr>
<td><strong>Model selection</strong></td>
<td>- Can be changed, but the variety is limited</td>
<td>- Abundant additions and selections (multiple AI models can be used)</td>
</tr>
<tr>
<td><strong>Rule file</strong></td>
<td>- Can be done by changing the settings in <code>.github/copilot-instructions.md</code></td>
<td>- The <code>.cursorrules</code> file enables you to apply rules per project</td>
</tr>
<tr>
<td><strong>Loading documents (such as @Docs)</strong></td>
<td>- Supported by plug-ins, etc.</td>
<td>- The @Docs feature enables you to load documents for frameworks and libraries</td>
</tr>
<tr>
<td><strong>Agent functions</strong></td>
<td>- Similar functions available in Copilot Chat / Copilot CLI</td>
<td>- Automatic execution in agent mode, try &amp; error</td>
</tr>
<tr>
<td><strong>Automatic test code generation</strong></td>
<td>- Generation is possible (although accuracy varies)</td>
<td>- Can be generated in the same way, but the accuracy is questionable. Corrections are needed.</td>
</tr>
<tr>
<td><strong>Price (Business/Enterprise)</strong></td>
<td>$39</td>
<td>$40</td>
</tr>
</tbody></table>
<blockquote>
<p>*The content of the evaluation is subjective as of February 2025.</p>
</blockquote>
<hr>
<h2>Pros of Cursor (when compared to GitHub Copilot)</h2>
<ol>
<li><p><strong>AI-powered shortcuts are displayed immediately</strong><br>Since AI-powered shortcuts are displayed automatically when you select a code, the need to remember special operations can be eliminated. <img src="/assets/blog/authors/yuya_sakamaki/cursor/shortcut.png" alt="alt text"></p>
<ul>
<li><em>Is there something similar in Copilot?</em><ul>
<li>Inline chat is possible. However, there is no COMPOSE mode to reference other files, and you need to call it up from the right-click menu, so it requires a little bit of effort.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Suggests similar corrections together</strong><br>When you want to apply similar corrections to multiple places, Cursor will predict &quot;where and how to fix next&quot; in order on the tab. <img src="/assets/blog/authors/yuya_sakamaki/cursor/similar_fix.png" alt="similar"></p>
<ul>
<li><em>Is there something similar in Copilot?</em><ul>
<li><del>It doesn&#39;t make predictions until you start writing something first, so it is difficult for it to provide bulk suggestions as smoothly as with Cursor.</del></li>
<li><a href="https://code.visualstudio.com/updates/v1_97#_copilot-next-edit-suggestions-preview">A similar function is now available in Copilot</a> *There were some changes as I was writing this article.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Flexibility with selecting and adding models</strong><br>Cursor is designed to make it easy to add and select any model you want.</p>
<ul>
<li><em>Is there something similar in Copilot?</em><ul>
<li>Copilot Chat also enables model switching, but the scope of what can be done is currently limited.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>The @Docs feature enables you to load documents for frameworks and libraries</strong><br>Having it read the documents in advance will improve the accuracy of its answers.</p>
<ul>
<li><em>Is there something similar in Copilot?</em><ul>
<li>It is possible to achieve something similar using plug-ins, but the standard features are not that extensive.</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>Automatic execution of try &amp; error in agent mode</strong><br>In agent mode within Cursor, the AI will automatically execute commands under a certain level of authority and perform trial and error.</p>
<ul>
<li><em>Is there something similar in Copilot?</em><ul>
<li><del>Similar features are starting to appear in Copilot CLI and Copilot Chat, but they still require some fine tuning.</del></li>
<li>While still just a preview version, <a href="https://github.blog/news-insights/product-news/github-copilot-the-agent-awakens/">agent mode is now available in Copilot</a>.</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h2>Cons of Cursor (when compared to GitHub Copilot)</h2>
<ol>
<li><p><strong>Import completion is a bit troublesome</strong><br>VSCode + Copilot will automatically complete imports, but Cursor will not import unless you hover the mouse cursor over it and select a quick fix. <img src="/assets/blog/authors/yuya_sakamaki/cursor/quick_fix_cursor.png" alt="similar"></p>
<ul>
<li><em>Copilot offers greater stability and overall superiority</em></li>
</ul>
</li>
<li><p><strong>Automatic test code generation is not very accurate</strong><br>Automatically generated tests often don’t pass on the first run and usually require some adjustments.</p>
<ul>
<li>The same is true for Copilot, but I didn&#39;t get the impression that Cursor was much of an improvement.</li>
</ul>
</li>
<li><p><strong>There are still many parts that are not suitable for operation</strong><br>Depending on the app being developed, Cursor contained more AI functions than needed, which confused some team members.</p>
</li>
</ol>
<hr>
<h2>The Difference between Cursor&#39;s &quot;Composer&quot; and &quot;Chat&quot;</h2>
<p>Cursor is divided into two main modes: <strong>Composer</strong> and <strong>Chat</strong>. Since there are differences in usage and the handling of context, I have put together a quick summary of the results of the internal comparison.</p>
<table>
<thead>
<tr>
<th>Functional Items</th>
<th>Composer</th>
<th>Chat</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Context understanding</strong></td>
<td>Automatically links to the current file.<br>Auto-suggests related files.</td>
<td>Initially the context is empty.<br>Must be shared manually as needed</td>
</tr>
<tr>
<td><strong>Main uses</strong></td>
<td>Mainly code generation and editing.<br>Easy to correct suggested content immediately using inline</td>
<td>Mainly used for receiving general questions and explanations.<br>Suitable for longer exchanges</td>
</tr>
<tr>
<td><strong>History management</strong></td>
<td>Auto-saves in the control panel</td>
<td>Chat history is selectable</td>
</tr>
<tr>
<td><strong>UI</strong></td>
<td>Also usable in inline menu.<br>Can also be operated from the side menu</td>
<td>Side menu only</td>
</tr>
<tr>
<td><strong>Code block execution</strong></td>
<td>Not possible</td>
<td>Possible (execute commands via Chat in the side menu)</td>
</tr>
</tbody></table>
<p><strong>When to use Composer</strong></p>
<ul>
<li>Code corrections with simple context</li>
<li>When you want to generate and edit code quickly</li>
</ul>
<p><strong>When to use Chat</strong></p>
<ul>
<li>Code corrections with larger context</li>
<li>Analyzing error messages, general programming questions</li>
<li>When you want to work while maintaining context for a long period of time</li>
</ul>
<hr>
<h2>Conclusion Again</h2>
<ul>
<li><p><strong>As of February 2025, there are no huge functional differences between Copilot and Cursor</strong><br>In fact, Copilot&#39;s progress has been faster than expected, and it seems that within a few months, Copilot filled the gap that I thought Cursor was going to win as of December 2024.</p>
</li>
<li><p><strong>There are certainly pros to using Cursor</strong><br>As an AI-powered IDE, Cursor still has its strengths, such as dedicated shortcuts and agent mode. However, since Copilot has also been making progress in supporting these features, the pros of newly introducing it may be somewhat diminishing.</p>
</li>
<li><p><strong>Which one will you use in the end?</strong></p>
<ul>
<li>If GitHub Copilot is already available as standard equipment within the company, you will likely be able to get by with just Copilot for the time being.</li>
<li>That said, if you&#39;re wanting to embrace large-scale code corrections and chat-driven development, it&#39;s worth giving Cursor&#39;s Composer / Chat features a try, even though the Github Copilot AI agent is currently available to VSCode Insiders.</li>
<li>Since they are almost the same in terms of price, it is good idea to choose based on the UI and ease of use.</li>
</ul>
</li>
</ul>
<hr>
<h2>Development App</h2>
<ul>
<li><a href="https://apps.apple.com/jp/app/prism-japan-%E8%A6%B3%E5%85%89-%E3%81%8A%E5%87%BA%E3%81%8B%E3%81%91%E5%85%88%E6%A4%9C%E7%B4%A2%E3%82%A2%E3%83%97%E3%83%AA/id1604282082">Prism Japan - iOS</a></li>
<li><a href="https://play.google.com/store/apps/details?id=com.kinto.ppap&hl=ja">Prism Japan - Android</a></li>
</ul>
<hr>
<h2>References</h2>
<ul>
<li><a href="https://www.cursor.com/ja">Cursor - The AI Code Editor</a></li>
<li><a href="https://docs.github.com/ja/copilot/using-github-copilot/getting-code-suggestions-in-your-ide-with-github-copilot">Getting code suggestions in your IDE with GitHub Copilot</a></li>
</ul>
<p>The above are the results of my extensive use of Cursor and GitHub Copilot.<br>The speed of product updates is accelerating, so I would like to continue to test them regularly.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Use Social Media to Attract Customers! A Look Back on 4 Months of Social Media Marketing]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-01-Prism_SNS-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-01-Prism_SNS-en/</guid>
            <pubDate>Fri, 06 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Use Social Media to Attract Customers! A Look Back on 4 Months of Social Media Marketing]]></description>
            <content:encoded><![CDATA[<p>Hello! My name is Kita and I work on a travel media platform called <a href="https://ppap.kinto-jp.com/">Prism Japan</a> (for Web, iOS, and Android) at KINTO Technologies. Since November 2024, I&#39;ve been working on social media management for Prism Japan. Going beyond traditional advertising, the goal is to actively use social media as a way to grow brand awareness and create a channel to attract new customers. So here&#39;s a quick look back at how things went over the past four months!</p>
<h2>What We Achieved</h2>
<p>Let’s begin by looking at what we’ve accomplished while managing our social media accounts.</p>
<ol>
<li><p><strong>Creating short-form videos</strong>
 Short-form content, especially on TikTok and Instagram, is a key element in today&#39;s social networking environment. The algorithms are designed to show short videos to users beyond your followers, and depending on how viewers react, your reach can grow quickly. Increasing awareness using social media was a required subject, so we started by imitating popular videos that were going viral in similar categories. Based on those ideas, I picked up editing skills by experimenting with different editing tools. Here are the main tools I used:</p>
<table>
<thead>
<tr>
<th>Tool Name</th>
<th>Features</th>
</tr>
</thead>
<tbody><tr>
<td>Canva</td>
<td>Has a free plan available at no cost Images and videos can be freely edited and easily shared with co-editors, making management simple. There was only one time the server crashed during editing, and I had to start over.</td>
</tr>
<tr>
<td>Adobe Express</td>
<td>Has a free plan available at no cost It&#39;s not much different from Canva, but I personally prefer this one because the server is stable that I never experienced any crashes while working.</td>
</tr>
<tr>
<td>Capcut</td>
<td>Has a free plan available at no cost Its main feature is video editing. It offers tons of social media-friendly features, such as text-to-speech, 3D image conversion, and the ability to use fonts that are commonly seen on social media.<br>Note: The watermark in the free version doesn’t go over well on Instagram.</td>
</tr>
</tbody></table>
<p> Depending on your goals and preferences, you might favor one tool over another, but generally, all of them can get the job done—even with their free versions.</p>
</li>
<li><p><strong>Gaining impressions</strong> 
 Across all platforms, we were able to gain a lot of impressions. Let&#39;s take a look at the period from November 1, 2024, when the full-scale operations started, to March 17, 2025 (the time of writing). 
 <strong>X</strong> 
 Impressions increased 293% to 62,500. 
 <img src="/assets/blog/authors/kita/20250401/x_imp.png" alt="x_imp">
 <strong>Tiktok</strong>
 Since we only started using TikTok during this period, there’s no earlier data to compare, but our videos have been viewed 440,000 times so far. Compared to other platforms, it appeared to steadily accumulate views over time. 
 <img src="/assets/blog/authors/kita/20250401/tiktok_view.png" alt="tiktok_view">
 <strong>Instagram</strong>
 Reach increased by 90%, reaching 50,000.
 *Note: Accurate comparisons for views and interactions couldn’t be made due to the absence of data prior to August 2024. 
 <img src="/assets/blog/authors/kita/20250401/instagram_view.png" alt="instagram_view"></p>
<p> This is especially noticeable on TikTok and Instagram, with peaks occurring around the New Year period. We think this growth was driven by a combination of people being on holiday and a well-timed post introducing shrines that were perfect for the New Year&#39;s visits. When comparing the period before and after full-scale social media operations, factors like posting more frequently and simply being active on the platforms definitely played a role. However, what really helped boost views was tapping into trends like video format, seasonality, and timely topics. I&#39;d say these efforts helped &quot;raise awareness&quot; to some extent.</p>
</li>
<li><p><strong>Communication with users</strong> 
 On X in particular, we were able to discover and connect with new audiences. Previously, we hadn&#39;t taken any active steps like following users or replying to posts. But since X prioritizes engagement in its algorithm, we began directly interacting with users interested in tourism. As a result, one user not only installed our app, but also posted a review after actually visiting one of the spots we featured! Even better, they gave us some great content ideas. If you have a product or service, I definitely recommend reaching out actively. It can lead to both new users and valuable feedback.</p>
</li>
</ol>
<h2>What We Couldn&#39;t Achieve</h2>
<p>Next, let&#39;s take a look at what didn&#39;t go so well.</p>
<ol>
<li><p><strong>Did not result in app installations or website traffic</strong> 
 As mentioned earlier, we saw a noticeable increase in views and impressions. However, those numbers didn&#39;t translate into app installations or visits to the website. Below, I&#39;ll break down the results by platform. The evaluation period is set from November 1, 2024, when the full-scale operations started, to March 17, 2025. The following data reflects the number of app installs measured by AppsFlyer during the period. Note: We&#39;ve decided not to disclose the specific number of installations. <img src="/assets/blog/authors/kita/20250401/dl_daily.png" alt="dl_dailey"></p>
<p> <strong>Tiktok &amp; Number of app installs</strong> 
 First, we investigated whether there was any correlation between TikTok metrics and app install numbers.</p>
<p> <strong>Number of views</strong> 
 This chart was shown earlier, but here we compared the number of TikTok views with the app installs. <img src="/assets/blog/authors/kita/20250401/tiktok_view.png" alt="tiktok_view"> As you can see from a quick comparison of the graphs, there doesn&#39;t appear to be a clear correlation. For example, even though views peaked on January 2, 2025, there was no noticeable increase in installs. On the other hand, January 29, 2025 saw a spike in installs when there was no change in views. This also leads us to conclude that there is no correlation between TikTok views and app installs.</p>
<p> <strong>Profile views</strong>
 <img src="/assets/blog/authors/kita/20250401/tiktok_profile_view.png" alt="tiktok_profile_view">
 Next is the number of profile views. We assumed that users who visited our profile showed some level of interest. Thankfully, profile views have been trending upward, but for now, it doesn&#39;t seem like they&#39;re having much of an impact on app installs.</p>
<p> <strong>Likes</strong>
 <img src="/assets/blog/authors/kita/20250401/tiktok_like.png" alt="tiktok_like">
 What about the number of likes? At a glance, the trend in likes seemed to follow a similar pattern to views. Similarly, on January 2, 2025, when we got the highest number of likes, app installs didn&#39;t change. And on January 29, 2025, when installs spiked, likes didn&#39;t seem to contribute to that increase.</p>
<p> <strong>Comments</strong>
 <img src="/assets/blog/authors/kita/20250401/tiktok_comment.png" alt="tiktok_comment"> 
 The total number of comments was 81, which is relatively low considering the number of views. There were even days with zero comments. The number of comments on February 17, 2025 was slightly higher at 4, but there was no change in installs.</p>
<p> <strong>Shares</strong>
 <img src="/assets/blog/authors/kita/20250401/tiktok_share.png" alt="tiktok_share">
 The trend in shares was also similar to that of views and likes. We noticed firsthand how TikTok&#39;s algorithm tends to promote videos that get a lot of likes and shares, placing them on the For You feed, which in turn helps boost views.</p>
<p> <strong>Instagram &amp; Number of app installs</strong>
 Next up is Instagram.</p>
<p> <strong>Views</strong>
 <img src="/assets/blog/authors/kita/20250401/instagram_view.png" alt="instagram_view">
 Similarly, large peaks around the New Year holiday. As mentioned earlier, that timing didn&#39;t lead to any noticeable change in app installs. On the other hand, there was some correlation over multiple days. Overall, the correlation wasn&#39;t strong, but compared to TikTok, there might be slightly more of a relationship between views and installs. Also, the reach (i.e. number of unique users) showed a similar trend as above, so I&#39;ll skip the details here.</p>
<p> <strong>Interactions</strong>
 <img src="/assets/blog/authors/kita/20250401/instagram_interaction.png" alt="instagram_interaction">
 Lastly, interaction. On Instagram, this includes likes, saves, comments, and shares. Again, no correlation was found with app installs.</p>
<p> <strong>Summary</strong> Overall, there was no correlation between social media activity and app installs. So for now, it&#39;s hard to say that our social media efforts drove user acquisition. However, this analysis is based on correlation only, so it&#39;s possible that some viewers went on to install the app later. But to be honest, it&#39;s challenging to measure that accurately.</p>
<p> <strong>Traffic to the website</strong><img src="/assets/blog/authors/kita/20250401/web_referral.png" alt=" web_referral">
 We expected that social media would also contribute to attracting customers as a route other than SEO. In reality, we were able to drive some visits through Instagram stories and posts on X, but the total number was only around 300, which is quite small. We believe this is mainly due to the low follower count on X which limited our reach, and the lack of Instagram story viewers.</p>
</li>
<li><p><strong>Impressions increased, but follower growth was limited</strong></p>
<p> <strong>X: 425 followers</strong>
 On X, we were still lacking in follows and actions such as likes. Part of the reason is that we took a slower, more cautious approach while juggling other platforms over a long period of time. I think it&#39;s best to be more proactive with follows and likes early on to gain traction faster. But be aware that excessive following can lead to shadowbans or account suspensions.</p>
<p> <strong>Tiktok: 155 followers</strong>
 Despite the high number of views, we barely gained any followers. I think there are two main reasons for this: One is that the content was too generic. I think it lacked originality and creativity. There wasn&#39;t anything that made users feel they had to follow the account or risk missing out. Second, the target was unclear due to the nature of Prism Japan targeting a wide range of people who are interested in travel. But on TikTok, we didn&#39;t narrow down the demographic (age, gender), so information ended up reaching a wide range of people.</p>
<p> <strong>Instagram: 6,667 followers</strong>
 At first glance, this number may seem high, but since we stopped running ads, follower growth has been on the decline. It&#39;s often said that followers gained through ads tend to be less engaged. On top of that, our post style shifted significantly after we paused the ads, which may have further accelerated the drop-off in engagement.</p>
</li>
</ol>
<h1><strong>Key Characteristics &amp; Improvement Points by Platform</strong></h1>
<p>Here, I&#39;ll break down the strengths of each platform and post performances we observed during actual operations.</p>
<ol>
<li><p><strong>TikTok: Content can go viral even without a large number of followers</strong>
 Top-performing post: <a href="https://www.tiktok.com/@prism_japan/video/7468578530721385736?is_from_webapp=1&sender_device=pc&web_id=7436024728442635793"> 3 hidden spots in Tokyo perfect for a girls&#39; trip! </a> 
 <img src="/assets/blog/authors/kita/20250401/tiktok_best_post.png" alt="tiktok_best_post"> 
 We intentionally set a very specific target audience for this post: &quot;Girls traveling around Tokyo.&quot; This content guides users through the actual process of using the app to discover hidden gems. The spots featured are hidden gems with themes like &quot;overseas-inpired,&quot; &quot;anime-related,&quot; and &quot;en-musubi (shrines dedicated to love and relationships).&quot; As a result, 94% of traffic came from search, with queries like &quot;Tokyo places to hang out for two girls&quot; or &quot;Tokyo sightseeing.&quot; We believe narrowing the target and showcasing true hidden spots played a key role in its success.</p>
<p> Lowest-performing post: <a href="https://www.tiktok.com/@prism_japan/video/7446682449301490961?is_from_webapp=1&sender_device=pc&web_id=7436024728442635793"> The Hibiya illuminations were so beautiful! </a> 
 <img src="/assets/blog/authors/kita/20250401/tiktok_worst_post.png" alt="tiktok_worst_post"> 
 The video that didn&#39;t perform well was the one about the Hibiya illuminations. We actually visited the location, captured the lights and ambiance, and tried to create an engaging video by syncing with the background music. Despite these efforts, the video barely got any views. The likely reason is we entered a highly competitive area filled with high-quality content under popular keywords like &quot;Christmas&quot; and &quot;illuminations,&quot; making it hard to stand out.</p>
<p> <strong>Summary</strong>
 Comparing the two, we found that the successful post had a clear target audience and offered useful content to that audience. As a result, the number of views increased significantly, along with key metrics like average watch time and completion rate.</p>
</li>
<li><p><strong>X: Communication with users is its strength</strong></p>
<p> Top-performing post: <a href="https://x.com/PrismJapan/status/1857725299581944025">4 recommended shrines and temples for autumn foliage</a>
 <img src="/assets/blog/authors/kita/20250401/x_best_post.png" alt="x_best_post"> 
 This post highlighted a selection of spots where you can enjoy the beautiful combination of autumn leaves and traditional shrines or temples. It includes location details for each spot, highlights, and accompanying images.</p>
<p> Lowest-performing post: <a href="https://x.com/PrismJapan/status/1871714799392083996">Today is Christmas!</a> 
 <img src="/assets/blog/authors/kita/20250401/x_worst_post.png" alt="x_worst_post"> 
 This post featured a video created using generative AI, accompanied by the trending hashtag #MerryChristmas. The goal was to bring a more &quot;human&quot; feel to the account and to gain some impressions with the hashtag but the post didn&#39;t generate much engagement.</p>
<p> <strong>Summary</strong>
 Looking across other popular posts on X, content that ties into seasonal themes or current trends tends to perform better. I also found that useful content, whether it&#39;s well-organized or clearly valuable to the viewer, plays a key role in performance.</p>
</li>
<li><p><strong>Instagram: Short videos make it easier to reach non-followers</strong></p>
<p> Top-performing post: <a href="https://www.instagram.com/p/DDzDc7IiFLr/?utm_source=ig_web_copy_link&igsh=MzRlODBiNWFlZA==">[2024-2025 Season] 4 recommended ski resorts near Tokyo</a> 
 <img src="/assets/blog/authors/kita/20250401/instagram_best_post.png" alt="intagram_best_post">
 With the start of the winter ski season, we introduced ski resorts around the Kanto area using an image post. We listed the opening periods, hours, and pricing for each resort. As a result, the post received 10 saves, which was noticeably higher than other posts, and it reached 55.4% non-followers. In my opinion, Reels tend to reach non-followers even without many saves, while image posts usually stay within your followers, around 90 percent of the time. Looking back, I realized that creating posts people want to &quot;save&quot; is the most important.</p>
<p> Lowest-performing post: <a href="https://www.instagram.com/reel/DFsE4igtOpy/?utm_source=ig_web_copy_link&igsh=MzRlODBiNWFlZA==">AI diagnoses you! </a> 
 <img src="/assets/blog/authors/kita/20250401/instagram_worst_post.png" alt="instagram_worst_post"> 
 In contrast, this video introduced our app&#39;s &quot;Search by mood&quot; feature using a Reel. The intention was to have users recall how they would use the app by recording the actual app screen and walked through the steps, but it ended up being the least played video. I think the target audience wasn&#39;t clearly defined, and the value to the user wasn&#39;t communicated well.</p>
<p> <strong>Summary</strong>
 While Reels and image posts differ in how easily they reach non-followers, I got the impression that a post&#39;s performance often depends on whether it gets saved. More than just offering useful content, it&#39;s important to actively encourage saves and review it regularly.</p>
</li>
</ol>
<h2>Overall Summary &amp; Future Direction</h2>
<p>Overall, we found that social media didn&#39;t make a major contribution to attracting customers. That said, we can&#39;t say no effect either. So, we&#39;ll continue to reflect and refine our approach moving forward. This article highlights two particularly important points:</p>
<ol>
<li><strong>Clarify your target</strong></li>
<li><strong>Information that is useful to viewers</strong> 
These may sound obvious, but I realized that these two points are essential for videos to driving performance regardless of the platform. We also used the same content from TikTok on Instagram, but because the user demographics differ between platforms, it didn&#39;t get much of a response. While it&#39;s ideal to tailor posts for each platform, our limited resources made it difficult to do so effectively. Each platform has its own unique characteristics and user demographics, so it’s important to identify what works best based on the specific purpose.</li>
</ol>
<h2>References</h2>
<p><a href="https://www.amazon.co.jp/SNS%E3%83%9E%E3%83%BC%E3%82%B1%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B07%E3%81%A4%E3%81%AE%E9%89%84%E5%89%87-%E9%A3%AF%E9%AB%99%E6%82%A0%E5%A4%AA/dp/4296113704">The 7 Golden Rules of Social Media Marketing</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Google Sheets × MCPで業務を自動化してみた – 面倒な作業を一行で解決！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-06_Experience_the_MCP/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-06_Experience_the_MCP/</guid>
            <pubDate>Fri, 06 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[繰り返しの面倒な作業、そろそろAIに任せてみませんか？Google SheetsとAIをつなげて、自然言語の一行で手間を解決してみました。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>はじめまして。KINTOテクノロジーズでAndroidアプリ開発を担当しているJongSeokです。</p>
<p>ある日、資料を整理していて、Google Sheets に内容を移しながら整えていると、ふとこう思いました。</p>
<p>「これ、めっちゃ面倒くさくない……？」</p>
<p>同じような内容を何度も入力して、セルのフォーマットも整えて……<br>難しくはないけれど、これって本当に人間がやるべきことなんだろうか？と感じました。</p>
<p>そこで、最近よく目にするAIツールで自動化できないか調べてみたところ、<br>出会ったのが <strong>MCP（Model Context Protocol）</strong> でした。</p>
<p>いくつかのツールを試してみた結果、<br>なんと自然言語の「一行だけ」で Google Sheets を操作できました。
この体験は、正直かなり衝撃的でした。</p>
<h2>MCPとは？</h2>
<p>MCPは「Model Context Protocol」の略です。
名前は少し堅く聞こえるかもしれませんが、実際はとても直感的な役割を持っています。</p>
<p>簡単に言うと、
AIが実際のツールやサービスを自由に操作できるようにする「通訳」のような存在です。
この記事ではその中でも、Googleサービスと連携する事例にフォーカスして紹介していきます。</p>
<p>もっとわかりやすく言うと
普段使っているGmailやGoogle Drive、Google Sheetsなどのサービスは、人がクリックして操作します。
MCPは、そういったサービスを AIが自動で操作できるようにするものです。</p>
<p>たとえば、こんなことができます。</p>
<p>Google Sheetsに何かを記録したい時、通常はこんな感じかと思います。</p>
<ol>
<li>シートを開いて</li>
<li>セルをクリックして</li>
<li>データを入力する</li>
</ol>
<p>MCPは、そういったサービスをAIが自動で操作できるようにするものです。</p>
<p>それでは、実際のデモを見てみましょう。</p>
<blockquote>
<p><a href="https://www.kinto-technologies.com/products/">https://www.kinto-technologies.com/products/</a>
 次のWebページを参考にして、新しいGoogleスプレッドシートを作成し、紹介されている製品・サービスを整理してください。</p>
</blockquote>
<p>上記のように指示した場合の実際の操作イメージが、以下のデモ動画です。</p>
<p>![demo](/assets/blog/authors/jongseok/mcp/demo.gif =1080x)</p>
<p>このように一文で指示するだけで、AIがGoogle Sheets APIを呼び出し、シートに自動でデータを入力してくれます。
本当に簡単にデータ整理ができてしまいました。</p>
<h2>MCP最近流行ってるの？</h2>
<p>技術的に可能ということと、実際に人々が関心を持っているというのは別の話。
そこで、Google Trendsで「MCP」というキーワードの検索トレンドをチェックしてみました。</p>
<p><img src="/assets/blog/authors/jongseok/mcp/mcp_graph.png" alt="MCP Graph"></p>
<p>2025年3月から5月までのデータを見ると、
特に4月中旬から検索数が急激に増加しているのが分かります。</p>
<p>これは単なる興味を超えて、
開発者だけでなく一般ユーザーまでがMCPを実際に「使い始めている」ことを示しているとも言えます。
自分もその波に乗った、という感じです。</p>
<p>では、実際にMCPを試してみたい場合、どのような方法があるのでしょうか？</p>
<p>MCPを活用するには、自然言語の命令を受け取り、それを実行できる環境が必要です。<br>実際、Claude（Anthropic）など、MCPと連携できるAIエージェントはいくつか存在しますが、今回私が選んだのは、AI機能が組み込まれたコードエディタ「Cursor」でした。</p>
<h2>Cursorとは？</h2>
<p>Cursorは一言で言えば、AI機能が組み込まれたコードエディタです。
自然言語でコードを依頼すれば、AIが自動で提案・修正・説明などをしてくれます。</p>
<p>MCP の命令を書く際にも、Cursorを使えばよりスムーズに記述できます。</p>
<blockquote>
<p>Cursorについてもっと詳しく知りたい方は、公式サイトやブログを参考にするのがおすすめです。
<a href="https://www.cursor.sh">https://www.cursor.sh</a></p>
</blockquote>
<h2>それでは、一緒に始めてみましょう</h2>
<p>MCPとGoogle Sheetsを連携して実際に自動化を体験するには、まずいくつかの準備が必要です。<br>難しい知識は不要なので、安心してください。一つずつゆっくり進めていきましょう。</p>
<h3>事前準備</h3>
<p>自動化を始める前に、以下の項目を準備しておきましょう。</p>
<ul>
<li>MCPをサポートするエディタのインストール（今回はCursorを使用）</li>
<li>Googleアカウント</li>
<li>自動化したいGoogleサービス（DriveやSheetsなど）</li>
<li>MCP Server(<a href="https://github.com/xing5/mcp-google-sheets">https://github.com/xing5/mcp-google-sheets</a>) ←本記事ではこれを利用します。</li>
<li>簡単な自然言語での命令文を考える準備</li>
</ul>
<h2>Google Cloud Platform (GCP)の設定</h2>
<p>まず、Google Sheets APIを利用するためにGCP側の設定を行います。</p>
<h3>1-1. プロジェクトを作成</h3>
<p><a href="https://console.cloud.google.com">GCP</a> コンソールにアクセスし、新しいプロジェクトを作成します。</p>
<p>![Create Project1](/assets/blog/authors/jongseok/mcp/mcp_01_01.png =1080x)</p>
<p>![Create Project2](/assets/blog/authors/jongseok/mcp/mcp_01_02.png =360x)</p>
<h3>1-2-1. サービスアカウントを作成</h3>
<p>AIがGoogle Sheetsにアクセスするための「サービスアカウント」を作成します。</p>
<p>![Service Account1](/assets/blog/authors/jongseok/mcp/mcp_02_01.png =1080x)</p>
<p>Service Accountsの＋Create service accountを押します。</p>
<p>![Service Account2](/assets/blog/authors/jongseok/mcp/mcp_02_02.png =360x)</p>
<p>Service account nameを入力してDoneを押します。</p>
<h3>1-2-2. Permission追加</h3>
<p>![Service Account3](/assets/blog/authors/jongseok/mcp/mcp_02_03.png =1080x)</p>
<p>Permission→Manage access→RoleをEditorにしてSaveを押します。</p>
<h3>1-2-3. Keys追加</h3>
<p>![Service Account４](/assets/blog/authors/jongseok/mcp/mcp_02_04.png =1080x)</p>
<p>Keys→Add Keyに押してJSONを選択してCreateします。
そうしたら認証ファイルがDownloadされます。</p>
<p>![Service Account5](/assets/blog/authors/jongseok/mcp/mcp_02_05.png =1080x)
ファイルの中身のこんな感じになります。
これでサービスアカウント発行は終わりです。
このファイル<strong>client_email</strong>は後で使います。</p>
<h3>1-3. Googleサービスを設定</h3>
<p>![Service Account5](/assets/blog/authors/jongseok/mcp/mcp_03_01.png =1080x)</p>
<p>APIS &amp; ServicesのLibraryを押します。</p>
<p>![Service Account5](/assets/blog/authors/jongseok/mcp/mcp_03_02.png =1080x)
今回利用したいServiceが見えますね。</p>
<table>
<thead>
<tr>
<th align="center">Drive</th>
<th align="center">Sheets</th>
</tr>
</thead>
<tbody><tr>
<td align="center"><img src="/assets/blog/authors/jongseok/mcp/mcp_03_03.png" alt="Service Drive"></td>
<td align="center"><img src="/assets/blog/authors/jongseok/mcp/mcp_03_04.png" alt="Service Sheets"></td>
</tr>
</tbody></table>
<p>二つのServiceをEnableを押します。</p>
<h3>2-1. GoogleDriveの設定</h3>
<p>![Google Drive1](/assets/blog/authors/jongseok/mcp/mcp_04_01.png =1080x)
Google Driveに入ってAIを利用するFolderを作ります。
例）Sheets For MCPで作りました → その後ShareのShareを押します。</p>
<p>![Google Drive2](/assets/blog/authors/jongseok/mcp/mcp_04_02.png =360x)
上の欄には
<a href="#1-2-3.-keys%E8%BF%BD%E5%8A%A0">1-2. サービスアカウントを作成</a>したJSONファイルに中にある
<strong>client_email</strong>をcopyして入力します。</p>
<p>![Google Drive3](/assets/blog/authors/jongseok/mcp/mcp_04_03.png =360x)</p>
<p>追加されたか確認してDoneを押します。</p>
<h3>2-2. Folder IDを取得</h3>
<p>![Google Drive4](/assets/blog/authors/jongseok/mcp/mcp_04_04.png =1080x)
設定が終わったら<strong>Folder ID</strong>を取得します。</p>
<p>これで設定に必要な物は全部準備できました。
実際に設定して使ってみましょう！</p>
<h2>MCP設定(Cursor&amp;Terminal)</h2>
<p>Windowsでも可能ですが、ここではMacを基準に説明します。
MCPのセットアップには簡単なCloudServerを利用する方法もありますが、今回はLocalServerでやってみます。</p>
<p>まずはuv,uvxが必要なのでTerminalでインストールしましょう。
<code>uvx</code>は、高速なPythonパッケージインストーラとレゾルバである<code>uv</code>の一部らしいです。</p>
<h3>1. uv,uvxインストール &amp; 環境変数セット</h3>
<pre><code class="language-bash">curl -LsSf https://astral.sh/uv/install.sh | sh
</code></pre>
<p>![Terminal1](/assets/blog/authors/jongseok/mcp/mcp_05_01.png =1080x)
上手くインストールできました。</p>
<p>ご自身の環境に合わせて値を入力してください。</p>
<pre><code class="language-bash">export SERVICE_ACCOUNT_PATH=&quot;/path/to/your/service-account-key.json&quot;
export DRIVE_FOLDER_ID=&quot;YOUR_DRIVE_FOLDER_ID&quot;
</code></pre>
<p>準備した二つ<strong>SERVICE_ACCOUNT_PATH, DRIVER_FOLDER_ID</strong>を環境変数でセットします。</p>
<h3>2. Project設定</h3>
<pre><code class="language-bash">git clone https://github.com/yourusername/mcp-google-sheets.git
cd mcp-google-sheets
</code></pre>
<p>ProjectをCloneし、Cloneしたディレクトリに移動します。</p>
<p>どこに置いても問題ありませんが、管理しやすくするため、発行された<strong>service_account.json</strong>ファイルはProjectの下に移動しました。</p>
<p>![Terminal２](/assets/blog/authors/jongseok/mcp/mcp_05_02.png =360x)</p>
<p>サーバーを起動してみましょう。</p>
<pre><code class="language-bash">uv run mcp-google-sheets
</code></pre>
<p>![Terminal３](/assets/blog/authors/jongseok/mcp/mcp_05_03.png =1080x)
これでLocalのサーバーは準備できました。</p>
<p>最後に使うためにMCPを設定しましょう。</p>
<h3>3. MCP設定</h3>
<p>![Cursor1](/assets/blog/authors/jongseok/mcp/mcp_06_01.png =1080x)
MCP設定するためには右上にある⚙️を押します。
![Cursor2](/assets/blog/authors/jongseok/mcp/mcp_06_02.png =1080x)
MCP項目中にある+Add new global MCP Serverを押します。
![Cursor3](/assets/blog/authors/jongseok/mcp/mcp_06_03.png =1080x)
入力画面が表示されます。</p>
<pre><code class="language-bash">{
   &quot;mcpServers&quot;: {
     &quot;mcp-google-sheets-local&quot;: {
       &quot;command&quot;: &quot;uv&quot;,
       &quot;args&quot;: [
         &quot;run&quot;,
         &quot;--directory&quot;,
         &quot;/Users/jongseok.bae/MCP/GoogleServices/mcp-google-sheets&quot;,
         &quot;mcp-google-sheets&quot;
       ],
       &quot;env&quot;: {
         &quot;SERVICE_ACCOUNT_PATH&quot;: &quot;/Users/jongseok.bae/MCP/GoogleServices/mcp-google-sheets/service_account.json&quot;,
         &quot;DRIVE_FOLDER_ID&quot;: &quot;1n4HUOiglwjiTKHcw8jSATYcPtCRqNn6O&quot;
      }
    }
  }
}
</code></pre>
<ul>
<li><code>args</code>にはご自身のProjectがあるPathを入力</li>
<li><code>env</code>にはSERVICE_ACCOUNT_PATH,DRIVE_FOLDER_IDを入力
上記の値を入力して保存します。</li>
</ul>
<p>![Cursor4](/assets/blog/authors/jongseok/mcp/mcp_06_04.png =1080x)
🟢が点灯していれば、正しくセットアップされています。
このMCPで利用可能な機能はTools:list_spreadsheets,create_spreadsheet,get_sheet_data...など表示されます。</p>
<p>これでセットアップは完了です。お疲れさまでした！</p>
<p>それでは実際に、どのように自然言語で命令を出して、Google Sheets に自動入力を行うのかを試してみましょう。</p>
<h2>実際に使ってみる</h2>
<p>準備が整ったところで、いよいよ本番です。</p>
<p>例えば、以下のように自然言語で指示を出すだけで…</p>
<blockquote>
<p><a href="https://news.google.com/home?hl=en-US&gl=US&ceid=US:en">https://news.google.com/home?hl=en-US&amp;gl=US&amp;ceid=US:en</a> 「本日のGoogleニュース」 という名前の新しいGoogleスプレッドシートを作成してください。<br>掲載されているトップニュースを、タイトル、概要、URLの3列で整理してシートに追加してください。</p>
</blockquote>
<p>実際には裏側で、次のような流れが走っています：</p>
<ol>
<li>Cursorで命令を入力</li>
<li>MCP Serverが命令を受信</li>
<li>自然言語を解析 → Google Sheets APIに変換</li>
<li>自動でスプレッドシート作成 + データ挿入</li>
</ol>
<p>![Cursor4](/assets/blog/authors/jongseok/mcp/mcp_06_05.png =720x)
MCP実行する中身を見るとこんな感じになってます。(自然言語を解析して処理を実行)
![demo2](/assets/blog/authors/jongseok/mcp/demo2.gif =1080x)</p>
<p>結果として、上で紹介したようなスプレッドシートが生成されました！</p>
<h2>まとめ</h2>
<p>今回紹介したMCPとGoogle Sheetsの連携は、単なる技術紹介にとどまるものでがありません。</p>
<p>「自然言語一文で業務を動かす」という体験は、<br>もはや未来の話ではなく、<strong>今この場で実現可能なこと</strong>です。</p>
<p>もちろん、<strong>セキュリティやアクセス制御など、まだ検討すべき課題もあります。</strong><br>しかし、小さな自動化から始めていくことで、業務効率化の可能性は十分に感じられました。</p>
<p>もし業務の自動化を考えている方は、今回の事例を参考に、ぜひ一度試してみてください。<br>想像よりも早く、変化を実践できるかもしれません。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/jongseok/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Kotlin Multiplatform (KMP)でのテストのすべて]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-24-tests-in-kmp-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-24-tests-in-kmp-ja/</guid>
            <pubDate>Thu, 05 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Kotlin Multiplatformでの高度なクロスプラットフォームテスト複雑なテストシナリオの掘り下げ]]></description>
            <content:encoded><![CDATA[<p>本記事は<a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2024</a>の24日目の記事です🎅🎄</p>
<h1>はじめに</h1>
<p>こんにちは、<strong>Rasel</strong>です。現在、KINTOテクノロジーズ株式会社でAndroidエンジニアとして働いています。今日は、Kotlin Multiplatform (KMP) でのテストへのアプローチについて簡単に紹介します。</p>
<p>KMPでのクロスプラットフォームテストにより、AndroidとiOS間で共有されるコードの信頼性を確保できます。シンプルなテストは素晴らしい開始点ですが、多くのアプリケーションでは、より複雑なセットアップを行って、ビジネスロジックの検証、非同期関数の処理、コンポーネント間の相互依存性のテストなどを行う必要があります。</p>
<p>この投稿では、非同期コードの処理、依存関係のテスト、パラメーター化されたテストの使用、複雑なセットアップの構築など、高度なテストシナリオについて説明します。詳細を掘り下げてみましょう！</p>
<h1>高度なクロスプラットフォームテストの設定</h1>
<h3>共通のテスト依存関係を構成する</h3>
<p>こちらは、アサーション、コルーチン、モック用のテストライブラリを使用した強化されたセットアップです。</p>
<pre><code class="language-kotlin">// In shared module’s build.gradle.kts
plugins {
    id(&quot;dev.mokkery&quot;) version &quot;2.5.1&quot; // Mocking library for multiplatform
}

sourceSets {
    val androidInstrumentedTest by getting {
        dependencies {
            implementation(libs.core.ktx.test)
            implementation(libs.androidx.test.junit)
            implementation(libs.androidx.espresso.core)
            implementation(libs.kotlin.test)
        }
    }
    commonTest.dependencies {
        implementation(libs.kotlin.test)
        implementation(libs.kotlinx.coroutines.test)
        implementation(libs.kotlin.test.annotations.common)
    }
    iosTest.dependencies {
        implementation(libs.kotlin.test)
    }
}
</code></pre>
<p>このセットアップにより、非同期テストや、テストに必要な依存関係のモック化を処理できるようになります。</p>
<h1>KMPにおける基本的なテスト</h1>
<p>KMPを使用すると、<code>commonTest</code> で共有テストを簡単に記述できると同時に、<code>androidTest</code> と <code>iosTest</code> でプラットフォーム固有のテストが可能になります。開始方法の概要は次のとおりです。</p>
<pre><code class="language-kotlin">class GrepTest {
    companion object {
        val sampleData = listOf(
            &quot;123 abc&quot;,
            &quot;abc 123&quot;,
            &quot;123 ABC&quot;,
            &quot;ABC 123&quot;,
        )
    }

    @Test
    fun shouldFindMatches() {
        val results = mutableListOf&lt;String&gt;()
        // Let&#39;s get the matching results with our global function grep which is defined in commonMain module
        grep(sampleData, &quot;[a-z]+&quot;) {
            results.add(it)
        }

        // Check results with expectations
        assertEquals(2, results.size)
        for (result in results) {
            assertContains(result, &quot;abc&quot;)
        }
    }
}
</code></pre>
<p><code>commonTest</code>におけるこの簡単なテストを使用すると、プラットフォーム間で <code>commonMain</code> モジュールの共有ロジックを検証できます。</p>
<h1>高度なテストシナリオ</h1>
<h2>1.コルーチンを使用した非同期関数のテスト</h2>
<p>多くの共有KMPプロジェクトでは、非同期作業にコルーチンを使用します。コルーチンを効果的にテストするには、テストでコルーチンの実行を制御および進行を制御できるツールである<a href="https://kotlinlang.org/api/kotlinx.coroutines/kotlinx-coroutines-test/">kotlinx-coroutines-test</a>を使用する必要があります。</p>
<p>サンプルシナリオは次のとおりです。ユーザーデータを非同期的に取得するUserRepositoryを持っているとします。データ検索をシミュレートするためにコルーチンフローを制御するテストを記述します。</p>
<pre><code class="language-kotlin">@OptIn(ExperimentalCoroutinesApi::class)
class UserRepositoryTest {

    private val testDispatcher = StandardTestDispatcher()
    private val userRepository = UserRepository(testDispatcher)

    @BeforeTest
    fun setUp() {
        Dispatchers.setMain(testDispatcher) // Set test dispatcher as main
    }

    @AfterTest
    fun tearDown() {
        Dispatchers.resetMain() // Reset main dispatcher
    }

    @Test
    fun `fetch user successfully`() = runTest {
        val user = userRepository.fetchUser(&quot;123&quot;)
        assertEquals(&quot;John Doe&quot;, user.name)
    }
}
</code></pre>
<p>この例では、</p>
<ul>
<li><code>Dispatchers.setMain(testDispatcher)</code> がデフォルトのディスパッチャを置き換えることで、コルーチンの実行を制御できるようにします。</li>
<li><code>runTest {}</code>は、コルーチンのセットアップとクリーンアップを自動的に処理し、サスペンド関数の管理を容易にします。</li>
<li><code>runTest</code> 内のフローを制御して、遅延やその他の非同期動作をシミュレートできます。</li>
</ul>
<h2>2.モックの使用と相互関係の検証</h2>
<p>マルチプラットフォームセットアップで依存関係をモックすると、クラス間の複雑な相互作用のテストを効率化します。ここでは、APIクライアントをモックしてネットワーク呼び出しをシミュレートし、リポジトリとのやり取りを確認します。</p>
<pre><code class="language-kotlin">class NewsRepositoryTest {

    private val testDispatcher = StandardTestDispatcher()
    private val apiService = mock&lt;ApiService&gt; {
        everySuspend { getNews(defaultSectionName) } returns TopStoryResponse(mockNewsArticles)
    }
    private val repository = NewsRepository(apiService, testDispatcher)

    @BeforeTest
    fun setUp() {
        Dispatchers.setMain(testDispatcher) // Set test dispatcher as main
    }

    @AfterTest
    fun tearDown() {
        Dispatchers.resetMain() // Reset main dispatcher
    }

    @Test
    fun `fetch news list for home section successfully`() = runTest {

        val articleList:List&lt;Article&gt; = repository.fetchNews()
        assertEquals(mockNewsArticles.size, articleList.size)
        assertEquals(mockNewsArticles.first(), articleList.first())
        assertEquals(mockNewsArticles.last(), articleList.last())

        verifySuspend {
            apiService.getNews(defaultSectionName)
        }
    }
}

const val defaultSectionName = &quot;home&quot;
</code></pre>
<p>この例では、</p>
<ul>
<li><code>mock&lt;ApiService&gt;()</code> を使用すると、明示的な動作定義がなくてもモックがデフォルト値を返すことができます。</li>
<li><code>verifySuspend</code> は、<code>getNews()</code> が適切に呼び出されたかどうかを確認し、リポジトリ内の正しいフローを確保します。</li>
<li><code>everySuspend</code> は、<code>getNews()</code> の呼び出しごとに戻されるモックデータを設定します。</li>
<li><code>NewsRepository</code> の <code>getNews()</code> メソッドは、<code>ApiService</code> から <code>TopStoryResponse</code> としてデータを取得し、結果を <code>List&lt;Article&gt;</code> として返します。</li>
</ul>
<h2>3.さまざまな入力シナリオに関するパラメーター化されたテスト</h2>
<p>KMPは現在、JUnit5などのライブラリと同様に、パラメーター化されたテストにネイティブに対応していません。ただし、Kotlinの機能を使用してパラメーター化されたようなテスト動作を実現する回避策があり、さまざまな入力を使用して関数を簡潔かつ再利用可能な方法でテストできます。以下は、DateUtils関数のさまざまな日付フォーマットをテストする例です。</p>
<pre><code class="language-kotlin">class DateUtilsTest {

    private val testCases = listOf(
        Triple(&quot;2024-11-18T00:00:00&quot;, &quot;dd MMM yyyy&quot;, &quot;18 Nov 2024&quot;),
        Triple(&quot;2024-11-18T15:34:00&quot;, &quot;hh:mm&quot;, &quot;03:34&quot;),
        Triple(&quot;2024-11-18T15:34:00&quot;, &quot;dd MMM yyyy hh:mma&quot;, &quot;18 Nov 2024 03:34PM&quot;),
    )

    @Test
    fun `check date format`() {
        testCases.forEach { (dateString, outputFormat, expectedOutput) -&gt;
            val actualOutput = formatDatetime(dateString = dateString, inputFormat = &quot;yyyy-MM-dd&#39;T&#39;HH:mm:ss&quot;, outputFormat = outputFormat)
            assertEquals(expectedOutput, actualOutput, &quot;Failed for date $dateString and output format $outputFormat&quot;)
        }
    }
}
</code></pre>
<blockquote>
<p>[!注記] ここで、<code>formatDatetime()</code> は、日付文字列をフォーマットするために <code>DateUtils</code> 内で定義されたグローバル関数です。この例では、</p>
</blockquote>
<ul>
<li>入力ごとに予想される結果を定義して、コードを重複させることなくテスト範囲を簡単に拡張できます。</li>
<li>プラットフォーム固有のテストライブラリに依存せずに、<code>androidTest</code> と <code>iosTest</code> の両方で動作します。</li>
</ul>
<h1>複雑なセットアップによるプラットフォーム固有のテスト</h1>
<p>一部のテストケースでは、特にAndroidの <code>SharedPreferences</code> またはiOSの <code>NSUserDefaults</code> を使用する場合、プラットフォーム固有のAPIまたは動作を使用する必要があります。両方のプラットフォームの内訳は次のとおりです。</p>
<h3>Android固有のテスト：SharedPreferencesのテスト</h3>
<p>Android では、<code>SharedPreferences</code> を使用してデータストレージをテストする必要がある場合があります。AndroidXの <code>ApplicationProvider</code> を使用してテストコンテキストにアクセスするセットアップを次に示します。</p>
<pre><code class="language-kotlin">@RunWith(AndroidJUnit4::class)
class SharedPreferencesTest {

    private lateinit var sharedPreferences:SharedPreferences
    private val expectedAppStartCount = 10

    @BeforeTest
    fun setUp() {
        val context = ApplicationProvider.getApplicationContext&lt;Context&gt;()
        sharedPreferences = context.getSharedPreferences(SHARED_PREF_NAME, Context.MODE_PRIVATE)
    }

    @AfterTest
    fun tearDown() {
        sharedPreferences.edit().clear().apply()
    }

    @Test
    fun checkAppStartCountStoresCorrectly() {
        sharedPreferences.edit().putInt(KEY_APP_START_COUNT, expectedAppStartCount).apply()

        val actualAppStartCount = sharedPreferences.getInt(KEY_APP_START_COUNT, -1)
        assertEquals(expectedAppStartCount, actualAppStartCount)
    }

    companion object {
        private const val SHARED_PREF_NAME = &quot;test_prefs&quot;
        private const val KEY_APP_START_COUNT = &quot;app_start_count&quot;
    }
}
</code></pre>
<p>このプラットフォーム依存的なテストは、<code>androidInstrumentedTest</code>内に配置する必要があり、実行するにはエミュレーターまたは実際のAndroidデバイスが必要になります。</p>
<h3>iOS固有のテスト：NSUserDefaultsのテスト</h3>
<p>iOSでは、同様のロジックを <code>NSUserDefaults</code> でテストできます。通常、このテストは、以下に示すように <code>iosTest</code> において記述します。</p>
<pre><code class="language-kotlin">class NSUserDefaultsTest {

    private lateinit var userDefaults:NSUserDefaults
    private val expectedAppStartCount = 10

    @BeforeTest
    fun setUp() {
        userDefaults = NSUserDefaults.standardUserDefaults()
    }

    @AfterTest
    fun tearDown() {
        userDefaults.removeObjectForKey(KEY_APP_START_COUNT) // Clean up after test
    }

    @Test
    fun `test app start count stores correctly in NSUserDefaults`() {
        userDefaults.setObject(expectedAppStartCount, forKey = KEY_APP_START_COUNT)
        val actualAppStartCount = userDefaults.integerForKey(KEY_APP_START_COUNT)
        assertEquals(expectedAppStartCount, actualAppStartCount.toInt())
    }

    companion object {
        private const val KEY_APP_START_COUNT = &quot;app_start_count&quot;
    }
}
</code></pre>
<h1>複雑なテストセットアップ：相互依存関係のテスト</h1>
<p>高度なテストでは、相互に作用する複数の依存関係をシミュレートする必要がある場合があります。以下は、API クライアントとローカルキャッシュの両方に依存するリポジトリが関与する例です。</p>
<pre><code class="language-kotlin">class ComplexRepositoryTest {

    private val apiService = mock&lt;ApiService&gt;()
    private val localCache = mock&lt;LocalCache&gt;()
    private val repository = ComplexRepository(apiService, localCache)

    @Test
    fun `fetch data from local cache`() = runTest {
        everySuspend { localCache.getArticles() } returns mockNewsArticles

        val data = repository.getNewsArticles()
        assertEquals(mockNewsArticles.size, data.size)

        verifySuspend {
            localCache.getArticles()
        }
    }

    @Test
    fun `fetch data from remote source`() = runTest {
        everySuspend { localCache.getArticles() } returns emptyList()
        everySuspend { apiService.getNews(defaultSectionName) } returns TopStoryResponse(mockNewsArticles)
        everySuspend { localCache.insertAllArticles(mockNewsArticles) } returns Unit

        val data = repository.getNewsArticles()
        assertEquals(mockNewsArticles.size, data.size)

        verifySuspend {
            localCache.getArticles()
            apiService.getNews(defaultSectionName)
            localCache.insertAllArticles(mockNewsArticles)
        }
    }
}
</code></pre>
<p>この例では、</p>
<ul>
<li>リポジトリは最初にキャッシュをチェックし、キャッシュが空の場合にのみAPIを呼び出します。</li>
<li>私たちは相互作用を検証することで、リポジトリが意図したフローに従い、不必要にAPIを呼び出さないようにしています。</li>
</ul>
<p>上記のすべてにより、共通、Android、およびiOS実装のテストの記述が完了しました。これらのテストを個別に実行することも、<code>allTests</code> という名前のGradleタスクを使用してすべてのテストを一度に実行することもできます。これにより、プロジェクト内のすべてのテストが、対応するテストランナーで実行されます。![Gradle task allTests](/assets/blog/authors/ahsan_rasel/2024-12-24-testing-kmp/gradle-all-test-task.png =800x)
テストを実行するたびに、<code>shared/build/reports/</code> ディレクトリ内にHTMLレポートが生成されます。 ![Test report](/assets/blog/authors/ahsan_rasel/2024-12-24-testing-kmp/test-report.png =550x)</p>
<p>このようにして、当社のKMPの取り組みを進めながら、適切に調整されたテストでアプリの品質を確保することができます。</p>
<h1>複雑なKMPテストのベストプラクティス</h1>
<ol>
<li><strong>テストセットアップを分離する</strong>：ヘルパー関数やクラスを作成してモックの設定を行い、繰り返しコードを削減します。</li>
<li><strong>非同期動作を制御する</strong>：kotlinx-coroutines-testを使用して、コルーチンのタイミングを制御し、遅延やネットワーク応答をシミュレートします。</li>
<li><strong>クロスプラットフォームテストとプラットフォーム固有のテストをミックスする</strong>：ネイティブ動作にはプラットフォーム固有のテストを使用し、共有ロジックにはクロスプラットフォームテストを使用します。</li>
<li><strong>テスト範囲をモニターする</strong>：特に、依存関係のある複雑なフローについては、包括的なカバレッジを確保します。</li>
</ol>
<h1>結論</h1>
<p>Kotlin Multiplatformにおけるクロスプラットフォームテストでは、単純なテストシナリオと複雑なテストシナリオを処理でき、AndroidとiOS全体でコードの信頼性を確保するための強力なツールが得られます。再利用可能なテストをセットアップし、非同期動作を制御し、依存関係を分離することで、重要なビジネスロジックを検証して全体的なコード品質を向上させる高度なテストを記述することができます。</p>
<p>これらの手法により、KMPプロジェクトはプラットフォーム間で一貫性のある信頼性の高いパフォーマンスを発揮できるようになり、あらゆる場所のユーザーにシームレスな体験を提供できるようになります。KMPを楽しんでください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ahsan_rasel/2024-12-24-testing-kmp/coverimage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Kotlin Multiplatform ハイブリッドモード：Compose Multiplatform が SwiftUI や Jetpack Compose と融合]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-15-Kotlin_Multiplatform_Hybrid_Mode-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-15-Kotlin_Multiplatform_Hybrid_Mode-ja/</guid>
            <pubDate>Thu, 05 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[優れたアプリの開発を、予算、スケジュール、コード品質を考慮しながら実現。Kotlin Multiplatform のハイブリッド構成（共有ビジネスロジックと Compose Multiplatform の UI、重要な画面ではネイティブの SwiftUI を組み合わせた構成）で、小規模なチームでも MVP（Minimum Viable Product）を迅速にリリースでき、大規模なチームであればネイティブらしい細部の仕上げが可能になります。1つの Kotlin コードベースでどんな領域にも対応できる、スムーズなスケーリングと長期的なメンテナンスがしやすい構成です。]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>はじめに</h2>
<p>こんにちは！Yao Xie です。 KINTOテクノロジーズのモバイルアプリ開発グループで、 Android の<a href="https://kinto-jp.com/entry_app/">KINTO かんたん申込みアプリ</a> を開発しています。本記事では、Compose Multiplatform とネイティブの UI フレームワークの両方を活用したハイブリッドモデルを実装する方法について、私の考えをいくつか共有したいと思います。</p>
<p>モバイルアプリの開発では、限られた予算や多様なチーム規模、厳しい納期などに直面しつつ、長期的な保守性と一貫したユーザー体験の確保が求められる場合が多くあります。Kotlin Multiplatform (KMP) 上に構築されたハイブリッド アーキテクチャには、コア UI フレームワークとして Compose Multiplatform が組み込まれ、iOS の SwiftUI や Android の Jetpack Compose といったネイティブフレームワークと組み合わせることで、そういった課題に柔軟に対応することができます。少ない予算で MVP を素早く立ち上げる場合でも、完成度の高いアプリへとスケールアップする場合でも、このアプローチなら制約に適応でき、コード品質も長期的に維持できます。</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_kmp_structure.svg" alt="Kotlin Multiplatform の構成"></p>
<h2>さまざまな制約に対応できる、柔軟で保守しやすいアプローチ</h2>
<h3>一貫性を保ちながらの迅速な開発</h3>
<p>限られた予算、小規模なチーム、タイトなスケジュールといった制約のあるプロジェクトにおいて、私が感じている主なメリットは次のとおりです。</p>
<ul>
<li>共有ビジネスロジック： ネットワーク処理、データモデル、ビジネスルールといったコア機能を KMP のモジュールに一度記述することで、Android と iOS の両方で一貫した動作が保証され、コードの重複を避けつつ将来的なメンテナンスが楽になります。</li>
<li>Compose Multiplatform による共有 UI：Compose Multiplatform は Kotlin Multiplatform エコシステムの一部で、共有 Kotlin コードを基盤にして自然なかたちで成り立ち、UI コンポーネントを構築します。UI の大部分を、共用のコンポーザブルなコンポーネントとして開発します。Android では Jetpack Compose を通じてそのまま活用でき、iOS でも ComposeUIViewController などのヘルパーを使ってこれらのコンポーネントを組み込むことが可能です。このように統一されたアプローチを取ることで、両プラットフォームにおいて共通の設計・動作ガイドラインに従うことができ、長期的なサポートも容易になります。</li>
</ul>
<p>この戦略で、一貫性を維持しながら開発時間を最小限に抑えることができます。高品質なユーザーエクスペリエンスの提供と将来を見据えたコード設計にとって、この要素は重要なポイントとなります。</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_limited_res.svg" alt="限られたリソースで実現する Kotlin Multiplatform ハイブリッドモード"></p>
<blockquote>
<ul>
<li>Android ロボットは、Google が作成および提供している作品から複製または変更したものであり、クリエイティブ・コモンズ表示 3.0 ライセンスに記載された条件に従って使用しています。*</li>
</ul>
</blockquote>
<h3>ネイティブ拡張によるスケールアップ</h3>
<p>プロジェクトに多くの資金、人員、追加の時間が確保できるようになると、次のようなことが可能になります。</p>
<ul>
<li>段階的なネイティブ統合：強固な共有基盤があれば、ネイティブの UI 要素を使用して主要な画面を徐々に置き換えたり、改善したりすることができますたとえば、SwiftUI で iOS の重要な画面を強化し、プラットフォーム固有のデザイン標準により適した形に仕上げられます。</li>
<li>expect/actual メカニズム：Kotlin の expect/actual パターンを使用することで、プラットフォーム固有のボタンなどといった汎用的な共有コンポーネントを定義し、それぞれのネイティブ実装を提供できます。こうすると、まずは共有 UI で開発を始め、洗練されたネイティブの改良を後から一番重要な箇所に限定して加えることができるようになります。</li>
</ul>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_abundant_res.svg" alt="豊富なリソースを活かした Kotlin Multiplatform ハイブリッドモード"></p>
<h3>多様なチーム規模とプロジェクトのタイムラインに対応</h3>
<p>ハイブリッドアプローチは、プロジェクトの成長に応じて臨機応変に対応できるように設計されています。</p>
<ul>
<li>小規模なチームやタイトなスケジュールの場合：コードの再利用を最大限に活用することが焦点になります。少人数のチームでも共有のビジネスロジックや UI を構築・維持することができ、一貫性を保ちつつ、市場投入までのスピードを加速できます。</li>
<li>大規模なチームや開発期間に余裕がある場合：チームが拡大したりスケジュールに余裕ができたら、ネイティブコンポーネントの開発に追加のリソースを投入して、ユーザーエクスペリエンスを向上させます。こういった段階的な戦略を取ることでリスクを最小限に抑え、コアロジックの安定性と保守性を長期にわたって確保できます。</li>
</ul>
<p>共有ロジックと UI の一貫性をプラットフォーム間で保つと、複雑さと技術的な負債が軽減され、プロジェクトの保守や拡張がもっと楽になります。</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_standard_res.svg" alt="標準的なリソースで実現する Kotlin Multiplatform ハイブリッドモード"></p>
<h2>Kotlin Multiplatform ハイブリッドモードを示すコードスニペット</h2>
<h3>共有ビジネスロジック</h3>
<p>この共有モジュールでは、Android アプリと iOS アプリの両方で同じコアロジックを利用することができます。このため、一貫性が保たれてメンテナンスにかかるオーバーヘッドが軽減されます。</p>
<pre><code class="language-kotlin:Api.kt">// Api
interface Api {
    suspend fun getUserProfile():User
}

class ApiImpl(private val client:HttpClient) :Api {
    override suspend fun getUserProfile():User {
        return client.get {
            url {
                protocol = URLProtocol.HTTPS
                host = &quot;yourdomain.com&quot;
                path(&quot;yourpath&quot;)
            }
        }.safeBody()
    }
}
</code></pre>
<pre><code class="language-kotlin:Repository.kt">// Repository 
interface UserRepository {
    suspend fun getUserProfile():Flow&lt;User&gt;
}

class UserRepositoryImpl(private val api:Api) :UserRepository {
    override suspend fun getUserProfile():Flow&lt;User&gt; {
        return flowOf(api.getUserProfile())
    }
}

// ViewModel
class ProfileViewModel(
    private val repository:UserRepository,
) :ViewModel() {

    private val _uiState = MutableStateFlow(ProfileUiState())
    val uiState:StateFlow&lt;ProfileUiState&gt; = _uiState.asStateFlow()

    @OptIn(ExperimentalCoroutinesApi::class)
    fun onAction(action:ProfileScreenAction) {
        when (action) {
            is ProfileScreenAction.OnInit -&gt; {
                viewModelScope.launch {
                    // Set loading state to true
                    _uiState.update { it.copy(isLoading = true) }
                    
                    // Retrieve authorization info and then fetch user profile data
                    repository.getAuthorizationInfo()
                        .flatMapLatest { authInfo -&gt;
                            // If authInfo exists, call getUserProfile() to get user data; otherwise, emit null
                            if (authInfo != null) {
                                repository.getUserProfile()
                            } else {
                                flowOf(null)
                            }
                        }
                        // Catch any exceptions in the Flow chain
                        .catch { e -&gt;
                            e.printStackTrace()
                        }
                        // Collect the user profile data and update the UI state
                        .collect { userProfile -&gt;
                            _uiState.update { state -&gt;
                                state.copy(
                                    isLoading = false,
                                    data = userProfile,
                                    errorMessage = if (userProfile == null) &quot;User profile data is null&quot; else null
                                )
                            }
                        }
                }
            }
        }
    }
}
</code></pre>
<h3>Compose Multiplatform による共有 UI</h3>
<p>Kotlin Multiplatform に統合されている Compose Multiplatform は、共有の Kotlin コード上で UI コンポーネントの作成が可能です。</p>
<pre><code class="language-kotlin:ProfileScreen.kt">@Composable
fun ProfileScreen(
    viewModel:ProfileViewModel = koinViewModel()
) {
    val uiState = viewModel.uiState.collectAsState().value

    LaunchedEffect(Unit) { viewModel.onAction(ProfileScreenAction.OnInit) }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .background(White)
            .padding(16.dp)
    ) {
        // Profile header with image and user details
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .background(White, shape = RoundedCornerShape(16.dp))
                .padding(16.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            AsyncImage(
                model = uiState.data.profileImage,
                contentDescription = &quot;Profile Image&quot;,
                modifier = Modifier
                    .size(64.dp)
                    .clip(CircleShape)
            )
            Spacer(modifier = Modifier.width(16.dp))
            Column {
                Text(
                    text = uiState.data.userName,
                    fontSize = 16.sp,
                    color = Color.Black
                )
                Text(
                    text = uiState.data.email,
                    fontSize = 14.sp,
                    color = Color.Blue.copy(alpha = 0.6f)
                )
            }
        }
        Spacer(modifier = Modifier.height(16.dp))
        MenuLabel(label = &quot;Account Settings&quot;)
        ....
    }
}
</code></pre>
<p>リーンアプローチ：このコンポーザブルを両プラットフォームで直接使用すると、外観と動作の一貫性が維持できて便利だと思います。</p>
<p>スケールアプローチ：基盤となるビジネスロジックには手を加えず、必要に応じてネイティブコンポーネントを使用して UI を強化します。</p>
<h3>expect/actual 経由のネイティブ UI</h3>
<p>expect コンポーネントを定義します。</p>
<pre><code class="language-kotlin:PlatformButton.kt">@Composable
expect fun PlatformButton(
    modifier:Modifier = Modifier,
    label:String,
    onClick: () -&gt; Unit
)
</code></pre>
<p>Android 向けの実装 (androidMain)</p>
<pre><code class="language-kotlin:PlatformButton.android.kt">@Composable
actual fun PlatformButton(
    modifier:Modifier,
    label:String,
    onClick: () -&gt; Unit
) {
    Button(
        onClick = onClick,
        modifier = modifier
    ) {
        Text(text = label)
    }
}
</code></pre>
<p>iOS 向けの実装 (iosMain)</p>
<pre><code class="language-kotlin:PlatformButton.ios.kt">@Composable
actual fun PlatformButton(
    modifier:Modifier,
    label:String,
    onClick: () -&gt; Unit
) {
    UIKitView(
        modifier = modifier,
        factory = {
            // Create a native UIButton instance.
            val button = UIButton.buttonWithType(buttonType = 0)
            button.setTitle(label, forState = UIControlStateNormal)
            // Style the button
            ...
            
            // Create a target object to handle button clicks.
            val target = object :NSObject() {
                @ObjCAction
                fun onButtonClicked() {
                    onClick()
                }
            }
            // Add the target with an action selector.
            button.addTarget(
                target = target,
                action = NSSelectorFromString(&quot;onButtonClicked&quot;),
                forControlEvents = UIControlEventTouchUpInside
            )
            button
        }
    )
}
</code></pre>
<h3>ブレンド UI：Flutter に対する大きな強み</h3>
<p>Flutter と異なり、Compose Multiplatform は1 つの画面上で共有 UI とネイティブコンポーネントをシームレスに組み合わせることをサポートしてくれます。</p>
<p>共有 UI からネイティブ UI へ完全に切り替える必要のない場合が多く、1つの画面上で両者を組み合わせることができます。このアプローチで、画面全体をどちらか一方のフレームワークに完全に任せることなくCompose Multiplatform とネイティブフレームワークそれぞれの強みを活かすことができます。たとえば、iOS で SwiftUI のネイティブリストと並べて共有の コンポーザブルヘッダーを表示したいとします。</p>
<p>ブレンド UI は、次のように構築できます。</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_embedded.png" alt="ネイティブ UI を組み込んだ Compose Multiplatform"></p>
<h4>ネイティブレイアウトに共有コンポーザブルを埋め込む</h4>
<p>Compose Multiplatform の強みのひとつは、共有 Compose UI コンポーネントをネイティブ SwiftUI レイアウト内にシームレスに埋め込めることです。開発者は画面全体を書き直さなくとも、共有コンポーネントを段階的に導入することができます。この柔軟性は、Flutter のオーバーレイ方式では実現が難しいものです。</p>
<p>たとえば、SwiftUI をコンテナとして使用して、その中に UIViewController でラップした Compose Multiplatform の共有 UI を埋め込むことができます。</p>
<pre><code class="language-swift:ContentView.swift">import SwiftUI
import shared  // Import the shared KMP framework

struct ComposeContainerView:UIViewControllerRepresentable {
    let user:User

    func makeUIViewController(context:Context) -&gt; UIViewController {
        // Use the Compose wrapper to create the UIViewController
        return AppComposeUI().createProfileVC(user)
    }

    func updateUIViewController(_ uiViewController:UIViewController, context:Context) { }
}

struct ContentView:View {
    let user:User
    
    var body: some View {
        VStack {
            Text(&quot;Native SwiftUI Header&quot;)
                .font(.headline)
                .padding()
            // Embed the Compose UI inside SwiftUI.
            ComposeContainerView(user: user)
                .frame(height:250)
            List {
                Text(&quot;Native SwiftUI Item 1&quot;)
                Text(&quot;Native SwiftUI Item 2&quot;)
            }
        }
    }
}
</code></pre>
<h4>Compose Multiplatform 画面にネイティブ iOS UI を 埋め込む</h4>
<p>Compose Multiplatform では、共有 Compose 画面内に SwiftUI や UIKit などのネイティブ iOS ビューを埋め込むことも可能です。これも、Flutter では簡単に実現できない機能のひとつです。</p>
<p>埋め込む SwiftUI ビューを定義します。</p>
<pre><code class="language-swift:MySwiftUIView.swift">import SwiftUI
struct MySwiftUIView:View {
    let user:User
    var body: some View {
        VStack {
            Text(&quot;Native SwiftUI Content&quot;)
                .font(.body)
            Text(&quot;Welcome, \(user.firstName)&quot;)
        }
        .padding()
        .background(Color.gray.opacity(0.2))
        .cornerRadius(8)
    }
}
</code></pre>
<p>次に、UIKitView を使用してこのネイティブ ビューを共有 Compose UI に統合します。</p>
<pre><code class="language-kotlin:ComposeWithNativeUI.kt">@Composable
fun ComposeWithNativeUI(user:User) {
    Column {
        // Shared Compose UI header.
        ProfileHeader(user = user)
        Spacer(modifier = Modifier.height(16.dp))
        // Embed a native iOS view using UIKitView.
        UIKitView(factory = {
            // Create a UIHostingController with a SwiftUI view.
            val hostingController = UIHostingController(rootView = MySwiftUIView(user = user))
            hostingController.view
        }, modifier = Modifier.fillMaxWidth().height(100.dp))
    }
}
</code></pre>
<p>この双方向のインライン統合により、Compose Multiplatform が Flutter と明確に差別化されます。これでネイティブのルックアンドフィールを保ちながら、共有 UI コンポーネントを段階的に導入できるようになります。共有コードの効率性とネイティブ UI の豊かさ・品質を兼ね備えているので、開発者は柔軟に対応でき、大規模な書き換えリスクも抑えられます。</p>
<p>Compose Multiplatform は、共有 UI とネイティブプラットフォーム UI をより柔軟できめ細かく統合できます。この点で、Flutter よりも大きな強みがあります。Flutter は通常、オーバーレイ方式で共有 UI コンポーネントをレンダリングします。それとは異なり、Compose Multiplatform はインラインレンダリングを採用していて、共有 UI とネイティブ UIの要素を自然に組み合わせることができます。</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_vs.png" alt="ネイティブ UI に埋め込まれた、Compose Multiplatform と Flutter の比較"></p>
<h2>長所</h2>
<p>柔軟性、保守性、拡張性のベストプラクティス</p>
<h4>ラピッドプロトタイピングを取り入れる</h4>
<p>ユーザーがまだアプリに触れていない段階で、ピクセル単位の細部を完璧に仕上げようと何週間も費やすのは、リスクが高いことだと痛感しました。それを痛感しました。最近では、私たちのチームは 1〜2 週間で共有 UI のプロトタイプを作成し、関係者に配布して、早期にフィードバックを収集するようにしています。アイデアに手応えを感じたら、それまでに作ったものは捨てずに、ネイティブの装飾（アニメーション、プラットフォーム固有のジェスチャーなど）を重ねていきます。</p>
<h4>段階的な開発を計画する</h4>
<p>私たちの経験則は、「まずは小さくリリース、後からリファクタリング」です。最初は、コアロジック用の Kotlin Multiplatform モジュールと単独の共有 Compose 画面からスタートします。その構成で上手く動くことが確認できたら、次の画面の実装に進んだり、共有ウィジェットを SwiftUI や Jetpack Compose のバージョンに置き換えたりと、作業を１つずつ進めます。徐々に進めるやり方で、開発スピードを保ち、書き直しを避けることができます。</p>
<h4>リソース、品質、スケーラビリティのバランスをとる</h4>
<p>チームがまだ2人だけだった頃は、UI の9割を共有コードで作っていました。それが、限られた体制でできる精一杯の方法だったからです。メンバーが増えてデザインへのこだわりも強くなるにつれて、重要なフローはネイティブ実装に切り替え、その他の部分は共有コードのままにしました。このように段階的に移行したことで、バックログを現実的な状態に保ち、リリースに支障をきたすことなく品質を高めることができました。</p>
<h4>一貫性を保つ</h4>
<p>ビジネスロジックを1つの Kotlin モジュールに集約することで、マージに伴う悩みの種が数え切れないほど軽減されました。1回のバグ修正で、すべてのプラットフォームに反映されます。「iOS は直ったけど、Android はまだバグってる」のように取り残されることは、もうありません。派手さはありません。ですが、この共通コアがあるからこそ、チームは締め切り直前の夜でも安眠することができます。</p>
<h4>変化への適応力を確保する</h4>
<p>複数のプラットフォームにまたがるプロジェクトを率いた経験から言えるのは、成功するチームはアーキテクチャを静的な設計図ではなく、生き物のような存在として扱っています。予算が厳しくなったり、スケジュールが変わったり、突然新しいスキルを持つメンバーが加わっても対応できるよう、すべてのレイヤーを柔軟に設計しています。Compose Multiplatform を使用することで共有 UI とネイティブ UI をオンデマンドで組み合わせられるので、「MVP モード」から「ネイティブ仕上げモード」へ、数ヶ月とかからずに数日で切り替えることができます。製品戦略や市場が一夜にして変わったときも、この臨機応変さには何度も助けられてきました。</p>
<h2>結論</h2>
<p>Kotlin Multiplatform と Compose Multiplatform を基盤に、ネイティブ UI フレームワークと併用して構築されたこの適応力の高いハイブリッドアーキテクチャは、他に類を見ない柔軟性を備えています。このハイブリッドアーキテクチャがあれば、一貫性と長期的な保守性を維持しつつ、予算、チーム規模、スケジュールといった制約に応じて開発戦略を柔軟に調整できます。小規模なプロジェクトでは、コードの再利用を最大限に活用してスピーディに立ち上げましょう。そしてリソースが増えてきたら、コアロジックはそのままに、重要な画面をネイティブ UI で徐々に強化します。</p>
<p>重複の削減、メンテナンスの効率化に、柔軟で段階的なこのモデルを取り入れてみてください。そしてプロジェクトの成長に応じて拡張していける、一貫性のある高品質なユーザーエクスペリエンスを実現してください。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yao.xie/yao_kmp_hybrid_thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How an Infra Engineer Built a Full-Stack App with an AI Agent]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-02-VSCodeAgentMode-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-02-VSCodeAgentMode-en/</guid>
            <pubDate>Wed, 04 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Using generative AI agent features for system development]]></description>
            <content:encoded><![CDATA[<h1>Self-introduction</h1>
<p>Nice to meet you! I&#39;m Tsuji, a manager in the Cloud Infrastructure Group at KINTO Technologies.</p>
<p>I joined the company back in May 2020, so I&#39;ve been around for a while. Since graduating, I&#39;ve been working as an infrastructure engineer, handling both on-prem and cloud systems.</p>
<h1>Overview</h1>
<p>GitHub Copilot Agent Mode for VSCode officially launched recently (as of April 16, 2025), I&#39;ll refer to it as &quot;Agent&quot; from here. Seems like the perfect excuse to try building something useful with it.</p>
<h1>Premise</h1>
<ul>
<li>I&#39;m an infra engineer through and through.<ul>
<li>Frontend? Backend? Not really my thing.</li>
</ul>
</li>
<li>But when it comes to infra—especially AWS—I can hold my own.</li>
<li>This article won&#39;t dive deep into technical stuff.</li>
</ul>
<h1>What I Want to Do</h1>
<ul>
<li>See how far infra-only engineers can go using Agents.</li>
<li>Develop a practical system we can actually <strong>use in our daily work, as an MVP (Minimum Viable Product).</strong></li>
<li><strong>Absolutely no writing code by hand.</strong></li>
</ul>
<h1>So... What Should We Build?</h1>
<p>Basically, all of KINTO Technologies&#39; infrastructure is created and managed with IaC (Terraform).
If you have used Terraform before, you know the plan output can be hard to read unless you&#39;re used to it, right?  <strong>So this time I&#39;m aiming to create something that makes it easier to view Terraform plans.</strong></p>
<h1>What I Want to Make</h1>
<ul>
<li>Make Terraform plans easier to read:<ul>
<li>I might as well throw in some generative AI for summaries.</li>
<li>Since the Agent&#39;s building it, why not have it create a slick frontend too?</li>
</ul>
</li>
</ul>
<h1>Development Environment</h1>
<p>*Here&#39;s the development environment used this time just for reference:</p>
<table>
<thead>
<tr>
<th align="center">Items</th>
<th align="center">What I used</th>
</tr>
</thead>
<tbody><tr>
<td align="center">OS</td>
<td align="center">Linux(WSL2)</td>
</tr>
<tr>
<td align="center">IDE</td>
<td align="center">VSCode(1.99.3)</td>
</tr>
<tr>
<td align="center">Agent</td>
<td align="center">GitHub Copilot Agent</td>
</tr>
<tr>
<td align="center">Generative AI Model</td>
<td align="center">Claude 3.7 Sonnet</td>
</tr>
<tr>
<td align="center">Infra</td>
<td align="center">AWS</td>
</tr>
</tbody></table>
<h1>Let&#39;s Give It a Go</h1>
<p>First things first: we need a prompt. Yep, that&#39;s where it all starts. This time, I gave the Agent a super rough idea of what I wanted using the following prompt:</p>
<pre><code>I&#39;d like to implement a mechanism to view the result of Terraform plan in the browser.

AWS configuration: CloudFront + S3 + Lambda + Bedrock
Planned workflow:
1. Run Terraform plan -out foo.plan locally and output the plan contents to a file
2. Upload foo.plan to S3.
3. A lambda is triggered, runs Terraform show foo.plan, converts it into a human-readable format, and stores it to S3
4. Bedrock analyzes the result of Terraform show and generates a summary of the plan
5. Users can view both the summary and full plan content via a browser.

I&#39;m planning to proceed with this approach.
If you have a better suggestion, I&#39;m happy to hear it. If not, please help me build the above.
If any code needs to be generated, please create a directory under /home/mizuki/ and save the files there.
</code></pre>
<p> Got a better prompt in mind? No need to get too deep into the details. I&#39;m just an infra engineer here.</p>
<p>But then the brilliant Agent took my idea, leveled it up by 300% and made it happen.</p>
<pre><code>You&#39;re looking to implement a system to view the result of a Terraform plan in a browser. The proposed idea is feasible and well thought out. First, I will evaluate the plan, suggest a few improvements, and then help with the implementation.

# Evaluation of the current plan

1. Run Terraform plan -out foo.plan locally and save the output to a file
2. Upload foo.plan to S3
3. An S3 event triggers a Lambda function, which runs Terraform show foo.plan and outputs the result in a human-readable version to S3.
4. Bedrock analyzes the result of Terraform show and generates a summary of the plan
5. Users access the summary and full plan details via a browser

# Possible improvements

1. **Security enhancement**: Restrict access to S3 and configure IAM permissions appropriately
2. **Visual representation of differences**: Visualize the plan result using graphs or colored text
3. **History management**: Save and compare past plan results
4. **Notifications**: Notify via Slack or email when important changes are detected
5. **Interactive UI**: Expand/collapse resource sections for easier browsing.

Let’s move forward with these points in mind. First, the directory structure will be created.  Next, we&#39;ll implement each component of this project.

After creating a Terraform configuration file and define the necessary AWS resources. The directory structure has been created. Now, let&#39;s start implementing each component.

Then, create `main.tf` to define the infrastructure using Terraform. This file will define the required resources such as the S3 bucket, CloudFront, Lambda, and IAM roles.
:
:
(omitted)
</code></pre>
<p><strong>... Wow, you&#39;re killing it.</strong></p>
<p>The Agent&#39;s killer feature is its ability to work locally. It even sets up a working directory and fills it with the Terraform files. <img src="/assets/blog/authors/m.tsuji/20250424/agent1.png" alt="Input Prompt"></p>
<p>After that, the Agent explains its actions and checks in with &quot;Is it okay if I do this?&quot; at each step. <img src="/assets/blog/authors/m.tsuji/20250424/agent2.png" alt="Input Prompt"></p>
<p>I have zero clue about Python, so I just kept mashing the &quot;Accept&quot; button every time it asked.</p>
<p>In the end, it didn&#39;t just generate JavaScript, CSS, Python, and Terraform files, but also gave me a shell for building the Lambda. And on top of that? It included usage instructions, deployment steps, and suggestions for improvements. <img src="/assets/blog/authors/m.tsuji/20250424/agent3.png" alt="Input Prompt"></p>
<h1>Time to Deploy</h1>
<p>It&#39;s awesome that the Agent made it for me—but… does it actually work??? No idea, but I&#39;ll go ahead and follow the deployment steps exactly as provided.</p>
<ol>
<li>Apply Terraform and create S3, CloudFront, etc. on AWS.</li>
<li>Build it and create a zip for Lambda.</li>
<li>Deploy the Lambda.</li>
<li>Upload the JS and CSS files to S3.</li>
</ol>
<p>The deployment was completed without any errors. <img src="/assets/blog/authors/m.tsuji/20250424/aws_lambda.png" alt="Input Prompt"></p>
<h1>As Expected… Error.</h1>
<p>When I accessed the environment the Agent set up, got hit with a Lambda error. Not surprising, to be honest. I don&#39;t know anything about Python, there&#39;s no way to debug it myself. So... I&#39;ll just hand the Lambda logs over to the Agent and let it figure things out.</p>
<p><img src="/assets/blog/authors/m.tsuji/20250424/agent4.png" alt="Input Prompt"></p>
<p>Yes—just as I thought. It analyzed the logs, found the problem, and even fixed it. <img src="/assets/blog/authors/m.tsuji/20250424/agent5.png" alt="Input Prompt"></p>
<p>I redeployed the updated files, and this time, it actually worked.</p>
<h1>What Kind of System Did We End Up With?</h1>
<p>Here&#39;s what the final system architecture looks like! <img src="/assets/blog/authors/m.tsuji/20250424/aws1.png" alt="Input Prompt"></p>
<p>Here&#39;s how the whole thing works:</p>
<ol>
<li>The user sends Terraform plan results to S3.</li>
<li>That triggers a Lambda function via an event.</li>
<li>Lambda sends the plan results to Bedrock for analysis and summarization.</li>
<li>Place the generated plan summary and the summary page&#39;s HTML file to S3.</li>
<li>Step Functions deletes CloudFront cache.</li>
</ol>
<p>The user just drops the plan file into S3, and everything else happens automatically.</p>
<h1>Let&#39;s Try Accessing It</h1>
<p><img src="/assets/blog/authors/m.tsuji/20250424/console1.png" alt="Input Prompt"></p>
<p><strong>Looks super cool!</strong> But hey, did it actually summarize the Terraform plan results like we originally set out to do?</p>
<p><img src="/assets/blog/authors/m.tsuji/20250424/console2.png" alt="Input Prompt"> <img src="/assets/blog/authors/m.tsuji/20250424/console3.png" alt="Input Prompt"></p>
<p><strong>Perfect...</strong> The indentation&#39;s a little wonky here and there, but, let&#39;s not get hung up on the details.</p>
<p>What&#39;s Good About It?</p>
<ul>
<li>The number of created, updated, and deleted resources is clearly displayed at the top for easy viewing.</li>
<li>It explains the specific changes being made to each resource.</li>
<li>A risk summary is provided to help you grasp what could go wrong with this plan.</li>
<li>It even points out things you might want to improve going forward.</li>
</ul>
<p>This is exactly what I was hoping for.</p>
<h1>Add Detailed Requirements</h1>
<p>After that, I had the Agent make a few minor tweaks here and there:</p>
<ul>
<li>Timestamps are in UTC, change to JST.</li>
<li>Fix indentations that are off.<ul>
<li>Attach a screenshot.</li>
</ul>
</li>
<li>Once the plan is generated, clear the CloudFront cache.</li>
</ul>
<p>Stuff like that.</p>
<p>Even with all those detailed requests, the Agent handled everything <strong>without breaking a sweat</strong>.</p>
<h1>My Impressions After Trying It</h1>
<p>Building this system was way easier than I expected. I put it together in a single day, and honestly, most of that time was just waiting for the Agent to do its thing. The actual hands-on time was just about an hour. Even someone like me, who&#39;s only ever touched infrastructure, managed to build out both the frontend and backend solo.</p>
<p>Now, to be fair—when I looked at the Python code, even I could tell there were some parts that didn&#39;t need to be there. Definitely room for improvement. But for something at the Minimum Viable Product (MVP) stage, this is more than good enough. From here, it&#39;ll be easy to optimize the code or expand the features as needed. The important thing is that we were able to create a functioning system in a short period of time.  This allows us to quickly shape our ideas and find areas for improvement through actual operation.</p>
<p>The key point is this: <strong>I was able to build a working system by myself in a short amount of time.</strong></p>
<p>I think Agent is the best tool for building an MVP (Minimum Viable Product)</p>
<h1>Summary</h1>
<p>It might still take some time before we can build full-scale enterprise systems using only the Agent, but even as a beginner in writing prompts, I was able to develop a working system with ease.
What surprised me the most was how smoothly the Agent handled everything. All I had to do was keep clicking the &quot;accept&quot; button, and it built the system for me. Up until now, I was used to copying and pasting suggestions from generative AI and running commands manually. But this time, I barely touched the terminal. I just kept pressing &quot;accept.&quot; I was able to create a system that can reduce workloads and prevent mistakes just by pressing a button. Also, the system I built is already in use, and I plan to fully integrate it into our operational workflow.</p>
<h1>Finally</h1>
<p>The Cloud Infrastructure Group I belong to is looking for people to join us. Whether you&#39;re confident in your cloud skills or just getting started and eager to learn, we&#39;d love to hear from you. Please feel free to contact us.</p>
<p>For more details, please check <a href="https://hrmos.co/pages/kinto-technologies/jobs/1811937538128224257">here</a> and <a href="https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303147">here</a> (in Japanese).</p>
<p>We’ve also posted a group introduction on Wantedly! You can read more about the Cloud Infrastructure Group <a href="https://www.wantedly.com/companies/company_7864825/post_articles/968522">here</a>! (in Japanese)</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/m.tsuji/20250424/top.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Language Skill-Up Project]]></title>
            <link>https://blog.kinto-technologies.com/posts/2022-12-17-Language-Skill-Up-Project-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2022-12-17-Language-Skill-Up-Project-en/</guid>
            <pubDate>Tue, 03 Jun 2025 11:00:00 GMT</pubDate>
            <description><![CDATA[Language Skill-Up Project]]></description>
            <content:encoded><![CDATA[<p>Hello, I am Ito, an Administrative Assistant in the Global Development Group at KINTO Technologies. My role is to support team members with various tasks including answering their inquiries and translating documents.</p>
<p>I love cats and reading novels. Lying down and reading with my cat used to feel like heaven. But ever since my cat passed on to another heaven some time ago, I’ve been spending my days reading alone. I have read 5 or 6 English novels this year. Reading in English used to be nothing but a struggle, but lately, I’ve started to enjoy experiencing them in their original language. However, I think the most enjoyable part of speaking foreign languages is having conversations. If you speak in another language, not just English, you definitely open yourself up to more opportunities and choices. Of course, there are challenges along the way.</p>
<p>What should you do to be able to speak foreign languages? Unlike children, who can absorb things effortlessly, most adults need to put in a significant amount of effort. Effort to create many opportunities to speak, based on understanding basic grammar and vocabulary. I think this is essential to mastering a language.</p>
<p>In this article, I would like to introduce the Language Skill Up Project, organized by the Business Enhance Team of the Global Development Group, which aims to help improve the language skills of working adults.</p>
<h1>Introduction</h1>
<p>Currently, the Global Development Group has almost sixty members from diverse backgrounds, including various nationalities, cultures, and languages. Among them, some aren’t very proficient in Japanese, while others aren’t used to speaking English due to limited use in their previous roles. Although the members share strong development skills, it would be a loss for the company if language barriers prevented effective communication. To help address this issue, the Language Skill-Up Project was launched, led primarily by the Business Enhance Team, whose mission is to maximize the members&#39; potential.</p>
<p>The Ministry of Internal Affairs and Communications defines global human resources as individuals who possess language proficiency, communication skills, and cultural awareness. The Global Development Group offers an environment that fosters the development of these qualities. One of the goals of this project is to cultivate global human resources by making use of this environment. People from outside of the Group can also join our study sessions.  </p>
<p>The key to language learning is not telling yourself that you can’t do it. Our goal is to help learners build confidence by first becoming comfortable speaking in English or Japanese. To support this, we organize and run the following events.</p>
<h1>What Shall We Do?</h1>
<p>What shall we do to help improve people&#39;s language proficiency? Everyone has different starting points.   </p>
<p>What needs to be done to learn languages?</p>
<ol>
<li><p>Focus on mastering basic vocabulary and grammar, such as junior high school-level English.</p>
</li>
<li><p>Read (also read aloud)</p>
</li>
<li><p>Write</p>
</li>
<li><p>Listen</p>
</li>
<li><p>Practice shadowing to improve your pronunciation and intonation</p>
</li>
<li><p>Speak with others as much as possible</p>
</li>
</ol>
<p>I think you can do steps 1 through 5 on your own.  However, number 6 requires you to take the initiative and create opportunities on your own, unless you&#39;re willing to spend money on language schools or online conversation lessons. Probably what many of our members are lacking is opportunities to speak the language. Since we could add steps 1 to 5 later if needed, we decided to start by providing speaking opportunities first.</p>
<h2>Japanese Cafe</h2>
<p>It is a casual meeting to enjoy conversations held in lunch time, fortnightly,  also known as the pizza party. It&#39;s been nine months since we&#39;ve started in April 2022. Since the event is conducted entirely in Japanese, it mainly attracts participants with intermediate to advanced Japanese proficiency. Having fun is key, and our goal is to help people communicate more smoothly in Japanese.</p>
<p><img src="/assets/blog/authors/y.ito/nihongo-cafe.jpg" alt="Japanese Cafe"> <em>Japanese cafe</em></p>
<h2>Japanese Study Sessions</h2>
<p>While providing opportunities to speak is essential, the Japanese Cafe alone cannot meet everyone&#39;s diverse needs. That&#39;s why we launched the Japanese study sessions in August 2022: 30-minute weekly sessions held in small groups. </p>
<ul>
<li><p>Beginner class - Learn greetings, Hiragana, Katakana, useful phrases for everyday life.  </p>
</li>
<li><p>Conversation Class (Beginner to Intermediate) - Practice role-playing situations like shopping or making phone calls. Use newly learned phrases in conversation and expand vocabulary with related words.</p>
</li>
<li><p>Kanji class (Beginner to Intermediate) - Learn Kanji while practicing new phrases. </p>
</li>
<li><p>Business Class (Intermediate to Advanced) - Learn business Japanese vocabulary and expressions using real examples from Slack communications.</p>
</li>
</ul>
<h2>English Cafe</h2>
<p>It started in July 2022. Each session lasts 30 to 60 minutes and is held weekly in the late afternoon. The event is held at the Jimbocho office, but participants also join from outside the Global Development Team, including some who attend remotely from the Muromachi office. (Unfortunately, no pizza is provided.) So far, we&#39;ve organized various activities such as English games, presentation practice, and group discussions. Similar to the Japanese Cafe, most participants are at an intermediate level or higher and are looking to practice conversation. However, since the sessions are typically held in small groups, we can adjust the level based on the participants each time. We also welcome beginner-level participants and those who want to discuss their concerns about learning English.</p>
<p><img src="/assets/blog/authors/y.ito/english-cafe.jpg" alt="英語カフェ">
<em>English cafe (It&#39;s packed. People gathered for the photo shooting.)</em></p>
<h2>Individual English Sessions</h2>
<p>These 30-minute weekly sessions began in April 2022, designed for those who aren’t very familiar with English. For the first six months, the sessions were held twice a week. As participants grew more comfortable speaking English, the frequency was reduced to once a week.Participants have become noticeably more proactive in speaking English compared to when the sessions first began. They said, &quot;I feel more comfortable speaking English,&quot; and &quot;I have more confidence now.&quot; Having something to say is a key factor in learning a foreign language. I hope they will continue improving their skills.</p>
<h1>Reflections and Future Plans</h1>
<p>We started with the goal of enhancing communication among team members. Both the Japanese and English sessions have a friendly, relaxed atmosphere and have been very effective for team building. Even within the same group, some people rarely have the chance to talk to those they don’t work with directly. By attending the Japanese cafe or the English cafe, you can communicate with people you usually don’t have a chance to talk to. Participants come from diverse backgrounds, and some are native speakers of neither Japanese nor English. Through these conversations, you get the chance to explore a variety of topics and gain insights into different cultures.</p>
<p>The Language Skill Up Project is still in its infancy and we are exploring more effective ways. I hope those who haven’t joined yet will give it a try. We also welcome your ideas and suggestions. I hope this can be a place that helps people stay motivated and inspires them to create more opportunities to speak outside of the sessions as well.</p>
<p>There’s no magic potion to improve your foreign language skills. It all comes down to your own effort. In other words, you will be able to speak the language as long as you keep on doing it without giving up. If you don&#39;t try to create opportunities to speak the language after reaching a certain level, unfortunately it is easy to get rusty. There will always be something new to learn, so continuous study is essential. Once studying becomes a habit, it turns into a natural part of your life rather than just a task.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/y.ito/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Making Code Reviews a Little More Manageable (Hopefully)]]></title>
            <link>https://blog.kinto-technologies.com/posts/2022-12-16-codeReview-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2022-12-16-codeReview-en/</guid>
            <pubDate>Mon, 02 Jun 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>Hello</h1>
<p>Hello! My name is Nakagawara from the KINTO ONE Development Group. I work as a front-end engineer on a project, using Next.js and TypeScript for development.</p>
<p>This time, I will talk about code reviews, which are essential for team-based development. I would like to introduce the points I pay attention to when performing code reviews, both as a reviewee or a reviewer.</p>
<h1>When Requesting a Code Review as a Reviewee</h1>
<p>Let&#39;s get straight to the point. First, I would like to share two things I pay attention to when creating a pull request (I&#39;ll call them PR from now) and requesting a code review.</p>
<h2>1. Write clear commit messages</h2>
<p>I believe that by making an effort to write concise and clear commit messages, the granularity of commits will naturally improve. If necessary, I include supplemental explanations or reference links from the second line onward. I also add emoji prefixes to my commit messages. <strong>It makes it visually easier to understand the purpose of each commit</strong>, and since the emojis carry meaning, <strong>they naturally help discourage cramming too many changes into a single commit</strong>🌈</p>
<p>Below is a commit template that the FE team actually uses.</p>
<pre><code class="language-INI:"># ==== Format ====
# :emoji: PBI_id Subject
#
# Commit body...

# ==== Emojis ====
# 🐛  :bug: Bug fixes
# 👍  :+1: Functionality improvement
# ✨  :sparkles: Partial feature addition
# 🎉  :tada: A major feature addition worth celebrating
# 🎨  :art: Visual additions and tweaks
# 🔧  :wrench: Feature fix
# ♻   :recycle: Refactoring
# 🚿  :shower: Clean-up of deprecated or unused features
# 📝  :pencil: Documentation and comment updates
# 🚚  :truck: File relocation
# 👕  :shirt: Lint fixes and style adjustments
# 🤖  :robot: Test additions and fixes
# 🚀  :rocket: Performance improvements
# 🆙  :up: Updating dependencies and related packages
# 👮  :cop: Security improvements and resolving warnings
</code></pre>
<p>When creating the template, the following articles served as reference:</p>
<ul>
<li><a href="https://github.com/atom/atom/blob/master/CONTRIBUTING.md#git-commit-messages">atom</a></li>
<li><a href="https://goodpatch.com/blog/beautiful-commits-with-emojis">Getting Fun and Beautiful Commits Using Emojis</a></li>
</ul>
<p>And here is an actual commit👇</p>
<p><img src="/assets/blog/authors/RinoNakagawara/commit_comment.png" alt="commit comment 01"></p>
<p>I consciously make sure the purpose of the commit is clear <strong>when viewed in isolation.</strong></p>
<h2>2. Using a template to enrich PRs</h2>
<p>The FE team has a template available for PRs. Since it clarifies the perspectives from which the reviewer should check during a review, it is expected to improve review efficiency.</p>
<p>Below is a template we actually use. Since we use <a href="https://www.atlassian.com/ja/software/jira">JIRA</a> for the ticket management of tasks, we include a link to the <strong>ticket</strong>, specify <strong>what this PR will and will not address</strong>, and outline <strong>what will become possible</strong>.</p>
<pre><code class="language-MarkDown:">## Link to ticket

* https://example.com

## What was done

* What was accomplished in this pull request?

## What was not done

* What is explicitly not included in this pull request? (If nothing, write &quot;None&quot;. If excluded, indicate when it will be addressed.)

## What this enables

* What new functionality or behavior does this pull request enable? (If none, write &quot;None&quot;).

## What this disables

* What functionality or behavior is no longer possible due to this pull request? (If none, write &quot;None&quot;).

## Other notes

* Additional context for reviewers (e.g., implementation concerns, areas to pay special attention to, etc.)
</code></pre>
<p>When creating the template, the following articles served as reference:</p>
<ul>
<li><a href="https://dev.classmethod.jp/articles/pull-request-template/">Let’s Create a Pull Request Template to Review Efficiently!</a></li>
</ul>
<p>In addition to the above, I consciously add information as needed. For example, <strong>in the case of a PR addressing a bug, I also include the cause</strong>, and <strong>if there is a UI change, I attach screenshots or videos showing the states before and after the fix</strong>. This is because I believe this will facilitate a smoother understanding of the intent of the PR and the code.</p>
<h1>When Reviewing Code as a Reviewer</h1>
<p>Next, I&#39;d like to talk about three points I pay attention to when reviewing my team members&#39; PRs.</p>
<h2>1. Understanding the overview of a PR</h2>
<p>In the previous section, I mentioned using a template to write a PR carefully. So first, I make sure to read the overview carefully. I believe that understanding the background, intent, and <strong>what is not being addressed here</strong>, and then reviewing the code improves review efficiency and helps avoid off-target comments.</p>
<h2>2. Check locally</h2>
<p>The code can be checked from a PR change file. However, for the following reasons, when there is a fix that affects behavior, I always pull the branch into my local environment to check it.</p>
<ul>
<li>To check the behavior and appearance</li>
<li>To review the entire code</li>
</ul>
<h3>To check the behavior and appearance</h3>
<p>By running the code myself, I can check whether the expected behavior and appearance have been achieved, as well as understand the purpose and intent of the change. If you only look at the code without understanding why the change was made, you may not be able to perform an appropriate review, and there is a risk of regression when you make future changes to that part. So, I try to follow the processing of the code with my own eyes as much as possible to better understand it. By doing this, I can catch overlooked issues or unintended side effects caused by fixing such issues, which helps <strong>prevent unexpected bugs from occurring</strong>.🐛</p>
<h3>To review the entire code</h3>
<p>In addition to understanding the processing, you may also notice areas that could be optimized, such as &quot;this and this perform similar processing, so it seems like they could be combined.&quot;</p>
<h2>3. Labeling review comments</h2>
<p>I often add labels such as <code>[imo]</code> or <code>[nits]</code> at the beginning of comments to clarify their tone or nature. (though I still tend to forget this at times, so I want to make it a habit).</p>
<p><img src="/assets/blog/authors/RinoNakagawara/review_comment.png" alt="review comment"></p>
<h1>Conclusion</h1>
<p>I&#39;ve written a lot, but to sum it up: when doing code reviews, I try to stay mindful of <strong>what would make the review process easier, both from the reviewer’s and the reviewee’s perspective.</strong> My future challenges include not just being thorough but also improving the speed of my reviews and developing my own consistent set of review criteria to avoid variations in quality. It might also be valuable to align on these review perspectives as a team.</p>
<p>I hope this article offered you some helpful insights into code review. Thank you for reading to the end!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[KMP Project with Tuist - KMPのiOSビルドシステム構築]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-06-01-KMP-with-Tuist/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-06-01-KMP-with-Tuist/</guid>
            <pubDate>Sun, 01 Jun 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[TuistでKMPアプリプロジェクトのiOSモジュールのビルドシステムを構築する方法]]></description>
            <content:encoded><![CDATA[<h2>0. はじめに</h2>
<p>Androidアプリ「<a href="https://play.google.com/store/apps/details?id=com.kinto.one.app">KINTO かんたん申し込み</a>」の開発を担当している<a href="https://blog.kinto-technologies.com/authors/8ff703cc-0401-5075-b94e-ff4b91ead205">Choi Garamoi</a>です。</p>
<p>このアプリでは<a href="https://kotlinlang.org/docs/multiplatform.html">KMP</a>を導入し、一部のビジネスロジックを<a href="https://apps.apple.com/jp/app/kinto-%E3%81%8B%E3%82%93%E3%81%9F%E3%82%93%E7%94%B3%E3%81%97%E8%BE%BC%E3%81%BF/id6450170362">iOSアプリ</a>と共有しています。</p>
<p>今回はよりスムーズなKMPプロジェクtの開発のため<a href="https://tuist.dev">Tuist</a>を試した結果をまとめました。</p>
<h2>1. 概要</h2>
<p>Android Studioでは、KMP Applicationプロジェクトは新規プロジェクトウィザードを使って作成します。作成後は以下のような<a href="https://monorepo.tools">Monorepo</a>になります：</p>
<pre><code>KmpProject/
├── androidApp/          # Android専用モジュール(Kotlin/Android)
├── iosApp/              # iOS専用モジュール(Swift)
└── shared/              # 共通ビジネスロジック(Kotlin/Multiplatform)
</code></pre>
<p>Androidの<a href="https://gradle.org">Gradle</a>と同様に、iOSでもモジュール化とチーム開発に適したビルド環境が重要です。
本記事では、それを<a href="https://tuist.dev">Tuist</a>を使って構築する方法を紹介します。</p>
<h2>2. Xcodeプロジェクトの問題</h2>
<p>Xcodeはアプリのビルドに必要な情報を<code>*.xcodeproj</code>で管理していますが、いくつかの課題があります。</p>
<ol>
<li><strong>マージコンフリクトが頻発する</strong> : Xcodeは設定を変更すると、<a href="https://beromkoh.medium.com/what-is-project-pbxproj-in-xcode-d99e831eda99"><code>project.pbxproj</code></a>ファイルを自動的に更新します。このファイルは構造化されていないテキスト形式のため、複数人が同時に編集するとGit上でコンフリクトが頻繁に発生します。</li>
<li><strong>実質的に意味のない差分が生成される</strong> : XcodeのGUI操作によって、実質的な変更がなくても多くの差分が生じ、履歴が煩雑になります。</li>
<li><strong>自動化が困難</strong> : 多くの設定がGUI依存であるため、CI/CDやスクリプトによるビルド自動化が困難です。</li>
<li><strong>レビューし難い</strong> : <code>project.pbxproj</code>は可読性が低く、変更内容のレビューがしづらくなります。</li>
<li><strong>拡張性に限界がある</strong> : チーム規模が大きくなると、複数ターゲットやビルド設定の管理が煩雑になります。</li>
</ol>
<p><code>*.xcodeproj</code>ディレクトリは、Androidプロジェクトで言うところの<a href="https://gradle.org">Gradle</a>と<a href="https://www.jetbrains.com/help/idea/configure-project-settings.html"><code>.idea</code>ディレクトリ</a>を合わせたようなもので、ローカルのXcode設定とiOSアプリのビルド設定を分離できません。</p>
<p><a href="https://trends.google.com/trends/explore?date=all&q=xcode%20conflict,xcode%20merge,xcode%20dev">Googleトレンド</a>でも「xcode conflict」の検索数が「xcode dev」に比べて多く、開発時の衝突が多いことがうかがえます。</p>
<h2>3. <a href="https://tuist.dev">Tuist</a>とは</h2>
<p><a href="https://www.swift.org">Swift</a>言語で<a href="https://developer.apple.com/documentation/xcode/projects-and-workspaces">XcodeのProjectsとworkspaces</a>を生成と<a href="https://developer.apple.com/xcode">Xcode</a>のターミナルツールを組み合わせてビルドも出来るツールです。</p>
<p>主な機能は</p>
<ol>
<li>モジュール化サポート</li>
<li>環境独立的ビルド(チーム開発指向)</li>
<li>自動化サポート</li>
<li><a href="https://www.swift.org/documentation/package-manager">Swift Package Manager</a>サポート</li>
</ol>
<p>です。</p>
<p>他のXcodeのビルドツールとしては<a href="https://www.swift.org/documentation/package-manager">Swift Package Manager</a>、<a href="https://github.com/yonaskolb/XcodeGen">XcodeGen</a>、<a href="https://bazel.build">Bazel</a>があります。</p>
<ul>
<li><a href="https://www.swift.org/documentation/package-manager">Swift Package Manager</a> : Gradleの依存性管理機能(<code>dependencies</code>ブロック)だけを提供します。</li>
<li><a href="https://github.com/yonaskolb/XcodeGen">XcodeGen</a> : ツールの設定値チェックが足りないため人的ミスが発生しやすいです。</li>
<li><a href="https://bazel.build">Bazel</a> : 大規模プロジェクトを対象にするため使い方が複雑で、中小規模のプロジェクトにはオーバースペックです。</li>
</ul>
<h2>4. <a href="https://tuist.dev">Tuist</a>を導入する</h2>
<p>以下は<a href="https://docs.tuist.dev/en/guides/develop/projects/adoption/migrate/xcode-project">Migrate an Xcode project</a>を基本に、最新情報を反映した手順です。</p>
<h3>4-1. Tuistをインストールする</h3>
<pre><code class="language-shell">brew update
brew upgrade tuist
brew install tuist
</code></pre>
<p><a href="https://brew.sh">Homebrew</a>以外のインストール方法は<a href="https://docs.tuist.dev/en/guides/quick-start/install-tuist">マニュアル</a>をご参照ください。</p>
<h3>4-2. Tuist設定ファイルを追加する</h3>
<p><code>Tuist.swift</code>、<code>Project.swift</code>、<code>Tuist/Package.swift</code>の3つのファイル（Manifestファイル）を追加します。</p>
<pre><code>KmpWithSwift/
├── Tuist.swift
├── Tuist/
│   └── Package.swift
├── Project.swift
├── androidApp/
│   └── ...
├── iosApp/
│   └── ...
└── shared/
    └── ...
</code></pre>
<h3>4-3. 設定にモジュール（<code>Target</code>）を追加する</h3>
<p><code>Project.swift</code>が主な設定ファイルで、他のファイルは<a href="https://docs.tuist.dev/en/guides/develop/projects/adoption/migrate/xcode-project">Migrate an Xcode project</a>のサンプルをそのまま利用しても問題ありません。</p>
<h4>4-3-1. <code>Tuist.swift</code></h4>
<pre><code class="language-Swift">import ProjectDescription

let tuist = Tuist(project: .tuist())
</code></pre>
<h4>4-3-2. <code>Project.swift</code></h4>
<p>ターゲットの設定とKMP共通モジュールのビルドスクリプトを追加する。</p>
<ul>
<li><code>infoPlist</code> : 全体画面の設定。</li>
<li><code>scripts</code> : <strong>KMP共通モジュールをビルドするコマンド。</strong></li>
</ul>
<pre><code class="language-swift">import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        .target(
            name: &quot;App&quot;,
            destinations: .iOS,
            product: .app,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.App&quot;,
            infoPlist: .extendingDefault(
                with: [
                    &quot;UILaunchScreen&quot;: [
                        &quot;UIColorName&quot;: &quot;&quot;,
                        &quot;UIImageName&quot;: &quot;&quot;,
                    ],
                ]
            ),
            sources: [&quot;iosApp/iosApp/**&quot;],
            resources: [&quot;iosApp/iosApp/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        )
    ]
)
</code></pre>
<h4>4-3-3. <code>Tuist/Package.swift</code></h4>
<pre><code class="language-swift">// swift-tools-version: 6.0
import PackageDescription

#if TUIST
    import struct ProjectDescription.PackageSettings

    let packageSettings = PackageSettings(
        productTypes: [:]
    )
#endif

let package = Package(
    name: &quot;App&quot;,
    dependencies: [
    ]
)
</code></pre>
<h4>4-4. 古いXcodeプロジェクトを削除する</h4>
<ol>
<li><code>./iosApp/iosApp.xcodeproj</code>を削除します。</li>
<li><code>./.gitignore</code>に<code>*.xcodeproj</code>を追加します。</li>
</ol>
<h4>4-5. 確認</h4>
<p>Tuistの設定が正しくできたか確認します。</p>
<ol>
<li>TuistのManifestファイルをXcodeで開けることを確認します。<pre><code class="language-shell"># KmpWithSwiftディレクトリで
tuist edit
</code></pre>
</li>
<li>TuistでXcodeプロジェクトを生成します。<pre><code class="language-shell"># KmpWithSwiftディレクトリで
tuist generate
</code></pre>
</li>
<li>Xcodeが開いたらアプリを実行します。</li>
<li>アプリが正常に起動すれば完了です。</li>
</ol>
<h2>5. 共通機能からiOS設定を分離する</h2>
<p>共通モジュールの<code>./shared/build.gradle.kts</code>は、共通のビジネスロジックのビルド設定とiOS専用の<a href="https://developer.apple.com/documentation/xcode/creating-a-static-framework">XCFramework</a>のビルドの責任範囲が適切に分離されていません。</p>
<pre><code class="language-kotlin">kotlin {
    // ...

    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = &quot;shared&quot;
            isStatic = true
        }
    }

    // ...
}
</code></pre>
<h3>5-1. 対応</h3>
<p>下記のiOSの<a href="https://developer.apple.com/documentation/xcode/creating-a-static-framework">XCFramework</a>設定を<code>:shared</code>から分離して<code>ios</code>へ移動すると、より自然な形で設定でき、マルチモジュール化も楽になります。</p>
<pre><code class="language-kotlin">it.binaries.framework {
    baseName = &quot;shared&quot;
    isStatic = true
}
</code></pre>
<h3>5-2. 手順</h3>
<ol>
<li><code>./iosApp/shared</code>モジュールからXCFrameworkをビルドする。</li>
<li><code>App</code>ターゲット(<code>./iosApp/iosApp</code>ディレクトリー)のスクリプトを更新する。</li>
</ol>
<h4>5-2-1. <code>:iosApp:shared</code>モジュール追加</h4>
<p><code>./iosApp/shared/build.gradle.kts</code>ファイルを追加し、<code>./settings.gradle.kts</code>にモジュールを追加します。
<strong>Androidの設定は不要です。</strong></p>
<pre><code class="language-kotlin">// ./iosApp/shared/build.gradle.kts
plugins {
    alias(libs.plugins.kotlin.multiplatform)
}

kotlin {
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = &quot;shared&quot;
            isStatic = true

            export(projects.shared)
        }
    }

    sourceSets {
        commonMain.dependencies {
            api(projects.shared)
        }
    }
}
</code></pre>
<pre><code class="language-kotlin">// ./settings.gradle.kts
// ... 省略 ...
rootProject.name = &quot;KmpProject&quot;
include(
    &quot;:androidApp&quot;,
    &quot;:iosApp:shared&quot;,
    &quot;:shared&quot;
)
</code></pre>
<p><code>./shared/build.gradle.kts</code>からXCFramework設定を削除します。</p>
<pre><code class="language-kotlin">// ./shared/build.gradle.kts
plugins {
    alias(libs.plugins.kotlinMultiplatform)
    alias(libs.plugins.androidLibrary)
}

kotlin {
    // ... 省略 ...

    iosX64()
    iosArm64()
    iosSimulatorArm64()

    // ... 省略 ...
}
// ... 省略 ...
</code></pre>
<h4>5-2-2. Tuistターゲット更新</h4>
<p><code>Project.swift</code>の<code>scripts</code>のGradleコマンドを更新します。</p>
<p><code>scripts</code>のモジュールを<code>:shared</code>から追加した<code>:iosApp:shared</code>へ変更します(<code>./gradlew :shared:embedAndSignAppleFrameworkForXcode</code> ➡️ <code>./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode</code>)。</p>
<pre><code class="language-swift">// ./Project.swift
import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        .target(
            name: &quot;App&quot;,
            destinations: .iOS,
            product: .app,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.App&quot;,
            infoPlist: .extendingDefault(
                with: [
                    &quot;UILaunchScreen&quot;: [
                        &quot;UIColorName&quot;: &quot;&quot;,
                        &quot;UIImageName&quot;: &quot;&quot;,
                    ],
                ]
            ),
            sources: [&quot;iosApp/iosApp/**&quot;],
            resources: [&quot;iosApp/iosApp/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        )
    ]
)
</code></pre>
<h2>6. マルチモジュール化</h2>
<p>アプリが成長するとフィーチャーモジュール化が必要になります。</p>
<pre><code class="language-mermaid">%%{
  init: {
    &#39;theme&#39;: &#39;neutral&#39;
  }
}%%

graph TB
    App ==&gt; FeatureA
    App ==&gt; FeatureB
    FeatureA --&gt; :iosApp:shared
    FeatureB --&gt; :iosApp:shared
</code></pre>
<p>しかし各フィーチャーが<code>:iosApp:shared</code>が必要な場合、Tuist設定は下記のようになります。</p>
<pre><code class="language-swift">// ./Project.swift
import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        .target(
            name: &quot;App&quot;,
            destinations: .iOS,
            product: .app,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.App&quot;,
            infoPlist: .extendingDefault(
                with: [
                    &quot;UILaunchScreen&quot;: [
                        &quot;UIColorName&quot;: &quot;&quot;,
                        &quot;UIImageName&quot;: &quot;&quot;,
                    ],
                ]
            ),
            sources: [&quot;iosApp/iosApp/**&quot;],
            resources: [&quot;iosApp/iosApp/**&quot;],
            dependencies: [
                .target(&quot;FeatureA&quot;),
                .target(&quot;FeatureB&quot;)
            ]
        ),
        .target(
            name: &quot;FeatureA&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.FeatureA&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/FeatureA/**&quot;],
            resources: [&quot;iosApp/FeatureA/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        ),
        .target(
            name: &quot;FeatureB&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.FeatureB&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/FeatureB/**&quot;],
            resources: [&quot;iosApp/FeatureB/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        )
    ]
)
</code></pre>
<p>この設定では下記の2つの大きな課題があります。</p>
<ol>
<li>共通のXCFrameworkがフィーチャーモジュール（<code>:iosApp:shared</code>）の数の分ビルドされてしまいます。</li>
<li>フィーチャーモジュールのターゲット設定やビルドオプションによっては、アプリが複数バージョンの<code>:iosApp:shared</code>を利用してしまう恐れが有ります。</li>
</ol>
<p>この問題を解決するために、<code>:iosApp:shared</code>をTuistターゲットでラップします。</p>
<h3>6-1. Wrappingターゲット追加</h3>
<p>フィーチャーモジュールが直接にGradleの<code>:shared</code>モジュールを使わず、Xcodeのターゲットを共有するように<code>KmpCore</code>ターゲットを追加します。</p>
<pre><code class="language-mermaid">%%{
  init: {
    &#39;theme&#39;: &#39;neutral&#39;
  }
}%%

graph TB
    App ==&gt; FeatureA
    App ==&gt; FeatureB
    FeatureA ==&gt; KmpCore
    FeatureB ==&gt; KmpCore
    KmpCore --&gt; :iosApp:shared
</code></pre>
<p>ソースコードは<code>iosApp/shared/**</code>で<code>:iosApp:shared</code>と同じですが、KMPで生成するネームスペースとカプセル化してWrappingターゲットを使うように<code>KmpCore</code>にします。
この対応により、KMP共通コードの情報を持っているターゲットは<code>KmpCore</code>のみになります。</p>
<pre><code class="language-swift">// ./Project.swift
import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        .target(
            name: &quot;App&quot;,
            destinations: .iOS,
            product: .app,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.App&quot;,
            infoPlist: .extendingDefault(
                with: [
                    &quot;UILaunchScreen&quot;: [
                        &quot;UIColorName&quot;: &quot;&quot;,
                        &quot;UIImageName&quot;: &quot;&quot;,
                    ],
                ]
            ),
            sources: [&quot;iosApp/iosApp/**&quot;],
            resources: [&quot;iosApp/iosApp/**&quot;],
            dependencies: [
                .target(&quot;FeatureA&quot;),
                .target(&quot;FeatureB&quot;)
            ]
        ),
        .target(
            name: &quot;FeatureA&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.FeatureA&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/FeatureA/**&quot;],
            resources: [&quot;iosApp/FeatureA/**&quot;],
            dependencies: [.target(name: &quot;KmpCore&quot;)]
        ),
        .target(
            name: &quot;FeatureB&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.FeatureB&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/FeatureB/**&quot;],
            resources: [&quot;iosApp/FeatureB/**&quot;],
            dependencies: [.target(name: &quot;KmpCore&quot;)]
        ),
        .target(
            name: &quot;KmpCore&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.KmpCore&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/shared/**&quot;],
            resources: [&quot;iosApp/shared/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ]
        )
    ]
)
</code></pre>
<h3>6-2. <code>KmpCore</code>で<code>shared</code>を露出する</h3>
<p>単純に<code>KmpCore</code>ターゲットが<code>shared</code>へ依存性を持つだけでは<code>FeatureA</code>と<code>FeatureB</code>から<code>:shared</code>のコードへアクセスができません。</p>
<p><code>FeatureA</code>と<code>FeatureB</code>から<code>KmpCore</code>を経由してKMP共通コード(Gradleの<code>:shared</code>モジュール)へアクセスできるように追加設定が必要です。</p>
<p>まずは<code>KmpCore</code>ターゲットに<code>settings</code>設定を追加します。</p>
<pre><code class="language-swift">// ./Project.swift
import ProjectDescription

let project = Project(
    name: &quot;KmpWithSwift&quot;,
    targets: [
        // ... 省略 ...
        .target(
            name: &quot;KmpCore&quot;,
            destinations: .iOS,
            product: .framework,
            bundleId: &quot;ktc.garamoi.choi.kmp.with.tuist.KmpCore&quot;,
            infoPlist: .default,
            sources: [&quot;iosApp/shared/**&quot;],
            resources: [&quot;iosApp/shared/**&quot;],
            scripts: [
                .pre(
                    script: &quot;&quot;&quot;
                    cd &quot;$SRCROOT&quot;
                    ./gradlew :iosApp:shared:embedAndSignAppleFrameworkForXcode
                    &quot;&quot;&quot;,
                    name: &quot;Build KMP&quot;
                )
            ],
            settings: .settings(base: [
                &quot;FRAMEWORK_SEARCH_PATHS&quot;: &quot;iosApp/shared/build/xcode-frameworks/**&quot;,
                &quot;OTHER_LDFLAGS&quot;: &quot;-framework shared&quot;
            ])
        )
    ]
)
</code></pre>
<p><code>KmpCore</code>ターゲットが<code>shared</code>ネームスペースを<code>KmpCore</code>ネームスペースで露出するするように下記のSwiftファイルを追加します。</p>
<pre><code class="language-swift">// ./iosApp/shared/KmpCore.swift
@_exported import shared
</code></pre>
<h3>6-3. 確認</h3>
<p>Xcodeでプロジェクトをビルドしたら<code>FeatureA</code>と<code>FeatureB</code>が<code>import KmpCore</code>したら<code>:shared</code>へアクセス出来ます。</p>
<p>例えば<code>:shared</code>モジュールに<code>SomeModel</code>(<code>shared/src/commonMain/kotlin/ktc/garamoi/choi/kmp/with/tuist/SomeModel.kt</code>)のクラスがある場合<code>FeatureA</code>から下記のようにアクセスできます。</p>
<pre><code class="language-Swift">import Foundation
import KmpCore

public class SomeFeatureAClass {
    let model: SomeModel

    // ...
}
</code></pre>
<p>もしコンパイルエラーが発生する場合は、ビルド順やキャッシュの影響で一度目のビルドが不安定になることがあります。その場合はクリーンビルド又は複数回のビルドを試すことで解決できます。</p>
<h2>7. 結論</h2>
<ol>
<li>Xcodeの<code>*.xcodeproj</code>は自動化とチーム開発に適していません。</li>
<li>Xcodeプロジェクトの<code>*.xcodeproj</code>の代替として<a href="https://tuist.dev">Tuist</a>の使用を推奨します。</li>
<li>KMP共通モジュールのXCFrameworkの生成をXcodeプロジェクトのターゲットでラップすることで、フィーチャーモジュール化が容易になります。</li>
</ol>
<h2>8. 参考</h2>
<ol>
<li><a href="https://kotlinlang.org/docs/multiplatform.html">Kotlin Multiplatform</a> : Kotlin言語でクロスプラットフォーム開発ができる色んなツールを提供するKotlin公式プロジェクト。</li>
<li><a href="https://gradle.org">Gradle</a> : Android、Javaプロジェクトのde-factoビルドツール。</li>
<li><a href="https://tuist.dev">Tuist</a> : Xcodeプロジェクトのビルドツール。</li>
<li><a href="https://www.swift.org">Swift</a> : AppleがObjective-Cの代わりに開発したOOP言語。</li>
<li><a href="https://developer.apple.com/xcode">Xcode</a> : Appleプラットフォーム用のIDE。</li>
<li><a href="https://developer.apple.com/documentation/xcode/projects-and-workspaces">Xcode / Projects and workspaces</a></li>
<li><a href="https://www.swift.org/documentation/package-manager">Swift Package Manager</a> : <a href="https://www.swift.org">Swift</a>言語の公式依存性管理ツール。</li>
<li><a href="https://github.com/yonaskolb/XcodeGen">XcodeGen</a> : YAMLとJSONで<a href="https://developer.apple.com/documentation/xcode/projects-and-workspaces">Xcode Project</a>を生成するツール。</li>
<li><a href="https://bazel.build">Bazel</a> : Googleが開発したビルドツール。大規模<a href="https://monorepo.tools">Monorepo</a>を対象にする。</li>
<li><a href="https://monorepo.tools">Monorepo Explained</a> : 複数のSWを1つのレポジトリーで管理する仕組み。</li>
<li><a href="https://trends.google.com/trends/explore?date=all&q=xcode%20conflict,xcode%20merge,xcode%20dev">Google Trends : <code>xcode conflict</code>, <code>xcode merge</code>, <code>xcode dev</code></a> : Xcodeの開発全般的な検索に比べてXcodeのコンフリクトの検索の割合が高い。</li>
<li><a href="https://beromkoh.medium.com/what-is-project-pbxproj-in-xcode-d99e831eda99">What is <code>project.pbxproj</code> in Xcode</a></li>
<li><a href="https://www.jetbrains.com/help/idea/configure-project-settings.html">Project configuration / Projects / Project settings</a> : Android Studio, IntelliJ IDEAの<code>.idea</code>ディレクトリーの説明。</li>
<li><a href="https://docs.tuist.dev/en/guides/develop/projects/adoption/migrate/xcode-project">Migrate an Xcode project</a> : マニュアルで既存のXcodeプロジェクトをTuistプロジェクト化する手順。</li>
<li><a href="https://brew.sh">Homebrew</a> : macOSのシステムパッケージ管理ツール。</li>
<li><a href="https://docs.tuist.dev/en/guides/quick-start/install-tuist">Install Tuist</a></li>
<li><a href="https://developer.apple.com/documentation/xcode/creating-a-static-framework">Xcode / Bundles and frameworks / Creating a static framework</a></li>
<li><a href="https://developer.apple.com/swift/resources">Swift logo</a> : 下段から公式ロゴをダウンロード出来ます。</li>
<li><a href="https://kotlinlang.org/assets/images/favicon.svg?v2">Kotlin logo</a></li>
<li><a href="https://gradle.org/icon/safari-pinned-tab.svg">Gradle logo</a></li>
<li><a href="https://docs.tuist.dev/logo.png">Tuist logo</a></li>
<li><a href="https://play.google.com/store/apps/details?id=com.kinto.one.app">KINTO かんたん申し込み</a> : Androidアプリ</li>
<li><a href="https://apps.apple.com/jp/app/kinto-%E3%81%8B%E3%82%93%E3%81%9F%E3%82%93%E7%94%B3%E3%81%97%E8%BE%BC%E3%81%BF/id6450170362">KINTO かんたん申し込み</a> : iOSアプリ</li>
<li><a href="https://blog.kinto-technologies.com/authors/8ff703cc-0401-5075-b94e-ff4b91ead205">Choi Garamoi</a></li>
</ol>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ChoiGaramoi/KMP-with-Tuist/header.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Introduction to Quality Assurance]]></title>
            <link>https://blog.kinto-technologies.com/posts/2023-03-28-QA-work-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2023-03-28-QA-work-en/</guid>
            <pubDate>Fri, 30 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Introduction to our daily QA work]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>My name is Endo, and I am the leader of the QA group at KINTO Technologies. In this article, I&#39;d like to give you a look into our daily QA work especially through our daily efforts on subjects that anyone involved in QA may have encountered in the course of their daily tasks. There are already a couple of articles about QA work:<br></p>
<ul>
<li>Manager Zume&#39;s <a href="https://blog.kinto-technologies.com/posts/2022-12-22-QAgroup-en/">Quality Assurance Group Introduction</a></li>
<li>Team Member Okapi&#39;s <a href="https://blog.kinto-technologies.com/posts/2022-12-22-IncreasedAwarenessOfQA-en/">Increased Awareness of QA</a></li>
</ul>
<p>So I hope you will read them as well.</p>
<p>I hope this article sparks conversations like, &#39;Here’s how we do it at our workplace,&#39; or &#39;How do you usually handle this kind of task? I&#39;d love to hear your thoughts!</p>
<h1>QA Workflow and What We Focus On</h1>
<p>Our QA work mainly involves:</p>
<ul>
<li>QA for development projects</li>
<li>QA for maintenance and updates</li>
<li>Test Automation</li>
<li>Internal QA improvements<ul>
<li>QA work improvement activities</li>
<li>Study sessions</li>
</ul>
</li>
</ul>
<p>　 In this article, I&#39;ll walk you through the QA workflow for projects and share the key perspectives we keep in mind.<br>
<img src="/assets/blog/authors/endo/2023-03-28/QAwork.jpg" alt="QA Workflow"></p>
<h2>Different Views on QA</h2>
<p>The outline of QA work is explained during the orientation when joining the company and at the kickoff of each project. However, since all project members are experienced professionals, they have their own ideas and expectations of QA based on their past experience. Because of that, we sometimes run into misunderstandings like, &quot;Isn&#39;t QA supposed to check things down to this level?&quot; Such gaps in understanding can even pop up among team members. However, despite differences in what people expect from QA, I think we&#39;re mostly aligned on one key point:</p>
<p>　&quot;We want to identify and resolve any lingering quality concerns during the QA phase.&quot;</p>
<p> I think the key is to figure out how to address those concerns, ease that uncertainty, and make sure the release goes smoothly.</p>
<p>To start, we go over the following three main points during the project kickoff, so everyone involved has a clear understanding of QA.<br>
(1) Let&#39;s build quality together!
(2) If you have any concerns about quality, don&#39;t hesitate to reach out.
(3) QA&#39;s feedback isn&#39;t absolute, so let&#39;s consider whether it can be addressed as a project.<br></p>
<h3>Quality is something we build together!</h3>
<p>Point (1) might go without saying, but quality can&#39;t be improved by QA alone. As a result of the aforementioned misunderstandings, we sometimes get requests that lean more toward white-box testing, like:</p>
<ul>
<li>Checking the details at the unit level</li>
<li>Checking with a focus on source and data flow</li>
</ul>
<p>And more. QA work mainly focuses on black-box testing, so when it comes to white-box testing, we usually explain that it&#39;s more suited to be performed by the development side.</p>
<p>These requests often come from past experiences where QA helped before, or from a hope that QA will &quot;definitely&quot; catch issues they might have missed.</p>
<p>While there&#39;s generally a shared understanding that QA handles things like system tests, acceptance tests, and checks at each phase, people&#39;s expectations can still vary depending on their past experience. Some may expect QA to dig into system-level checks like source and data flow, while others expect a more user-centered checks. In general, though, I think QA is widely seen as a team that supports and checks quality control using its own indicators, including the standardization of all development processes.</p>
<p>While we in QA want to meet all those expectations, there are limits to what we can catch, and the testing time is not endless. So, it is necessary for everyone to approach quality as something we build &quot;together,&quot; especially when working under tight timelines to achieve our quality goals. However, it&#39;s not just about QA verifying requirements, pointing out issues, and having them fixed as a routine task. We believe better results come when we&#39;re aligned on QA specifications, test plans, and test timing by matching requirements <strong>together</strong>, aligning test perspectives <strong>together</strong>, and considering improvements <strong>together</strong>. And through those thorough discussions, we can apply those insights to test design.</p>
<h3>Developer Concerns Are a Goldmine of Tips for Improving Quality</h3>
<p>In reviews from a testing perspective, we can usually identify specific concerns that the development team has. But sometimes, the feedback is more vague, like simply feeling risky about a certain process. Even if those concerns aren&#39;t clearly articulated, they&#39;re incredibly valuable for QA when defining test perspectives and designing tests. That vague sense of unease often comes from things like complex code, unclear requirements, or uncertainty about whether all the finer details were fully nailed down. Even when everything seems to be working fine, so it&#39;s hard to put into words, but developers still have that <strong>gut feeling</strong> that something might be off. Surprisingly, testing these areas can sometimes reveal unexpected issues, so these instincts shouldn&#39;t be taken lightly.</p>
<p>As QA, we review the requirements and plan and design tests to address specific concerns. These instincts offer valuable hints that help us strengthen our testing and apply deeper coverage than originally planned. In the QA phase, our goal isn&#39;t really to catch unit- or integration-level bugs. Instead, it&#39;s about executing hundreds of test cases based on requirements and catching that one critical or fatal bug. That&#39;s where QA really shows its value. So, rather than worrying about whether a developer&#39;s gut feeling might lead to wasted effort, we try to create an environment where those instincts and hints can be freely shared. By encouraging open conversation, we can uncover areas to strengthen beyond the test perspectives which helps us design better tests and ultimately improve quality.</p>
<p>For example, instead of ending a test perspective meeting like, &quot;Here&#39;s how we plan to proceed. Please let us know if anything comes up.&quot; We try to add a simple question like, &quot;Is there anything in this spec that makes you feel uneasy?&quot; By adding this, the conversation can turn into something like, &quot;Now that you mention it...&quot; Even if you have concerns but can&#39;t put them into words, you may feel there&#39;s nothing worth sharing. But let&#39;s talk to QA anyway! By taking the aforementioned together approach, we can strengthen our test coverage and often uncover a goldmine of valuable insights in the process.</p>
<h3>Don&#39;t Get Distracted by QA Feedback, Stay Focused on the Original Goal</h3>
<p>If QA is seen as only working within a limited test scope, it can sometimes lead to misunderstandings such as<br>
thinking QA merely follows the requirements without understanding the code, focuses on overly detailed issues, and causes delays to the release schedule.<br>
 However, by providing the above-mentioned explanation in advance, people start to see QA in a completely different light, seeing it as a team that builds quality &quot;together.&quot;</p>
<p>On the other hand, with the change in perspective, some people start to worry that everything QA &quot;points out&quot; must be addressed before the release can go live.</p>
<p>What QA &quot;points out&quot; with testing fall into the following three categories:
　</p>
<ul>
<li>Bugs: QA points out behavior (display) that differs from the specifications as understood by QA</li>
<li>Improvement requests: There is no problem with the specifications, but QA suggests a change to improve the functionality</li>
<li>Questions: Ask about unclear points in the specifications</li>
</ul>
<p>For bugs, QA points them out when we notice behavior that doesn&#39;t match the expected specifications. The development team reviews the issue, makes any necessary fixes, and then QA rechecks the fix. Improvement requests are suggestions where there is no issue with the specifications, but it might be better to improve them. For example, if most screens have a button in the top right, but one screen has it in the top left, there&#39;s no problem with the button&#39;s functionality, but QA might propose moving it for consistency.  Questions arise when QA finds unclear parts of the specifications while testing. We ask for clarification first, before labeling anything as a bug.</p>
<p>It doesn&#39;t mean that all of these points need to be addressed before release. Ideally, every issue should be addressed, and every question answered, but depending on the project&#39;s circumstances, it is not realistic to tackle everything before release. If it&#39;s difficult to address all of the issues, it&#39;s the project, not QA, that decides which ones to tackle before release, based on their importance and priority.</p>
<p>QA&#39;s work is to conduct testing based on the idea of <strong>how things should be ideally</strong> according to the requirements. Naturally, even if the requirements are well-defined, there may be some parts of the specifications that are unclear. Ideally, everything would be addressed, but within the limited time available, it is important not to lose sight of the original goal by checking and organizing the final specifications through QA&#39;s feedback.</p>
<p>This leads to point (3), as a project, it&#39;s crucial to align on what kind of service will be delivered, and when. With that shared understanding, we can work toward building quality to meet that goal. This is where it&#39;s important for QA to provide solid support in terms of quality. Now, this might sound like we&#39;re suggesting that, given the time constraints, it&#39;s okay to just meet the bare minimum requirements and compromise on quality to release the service, but that&#39;s not the case at all.</p>
<p>As mentioned earlier, QA&#39;s work is to help the team move toward the project&#39;s defined goals by verifying whether the specified requirements are fully met. At the same time, if there are issues that could negatively impact the user experience, we will persistently discuss issues with stakeholders to address them. It&#39;s not about making compromises, nor is it about QA forcing our feedback onto the team. Instead, we focus on the project goals and the end users receiving the service, and we work <em>together</em> to decide how the project should respond and take action accordingly.</p>
<p>If a critical issue were to occur after release, the impact could be significant. So, QA stays fully engaged to help ensure quality is upheld. However, in larger projects, potential concerns are typically addressed early through the interviews mentioned in point (2). And given the nature of the development process, it&#39;s rare for a critical issue to suddenly appear at the very end. As a result, it’s extremely uncommon for the release date to be delayed due to QA work. Ultimately, what matters most is working <em>together</em> to achieve the target quality by the target release date. So, to repeat the important point: QA doesn&#39;t just point out issues and ask for fixes. It&#39;s about staying focused on the project&#39;s original goal to build quality <strong>together</strong>. For example, delivering the intended service to customers within the planned timeframe while making sure users aren&#39;t affected by any inconvenience or negative impact.</p>
<h1>Conclusion</h1>
<p>In this article, I focused on our mindset as QA and the communication practices we value, but from a more technical perspective, QA is also the only team that can take a cross-functional, bird&#39;s-eye view across all projects and products. If the opportunity arises, I&#39;d like to share how QA provides support along with our approaches to test planning, defining test perspectives, and test design.</p>
<p>Sometimes, people approach us with QA requests a bit hesitantly. But the point is that we&#39;re in this <em>together</em>. We want to build a relationship where everyone feels comfortable collaborating without hesitation. After release, we often hear, &quot;It was a huge help!&quot; &quot;Thanks so much!&quot;  At those times I always say, &quot;Not at all! Thank you for taking QA&#39;s feedback seriously and responding with such care.&quot;I&#39;m deeply grateful to everyone involved in the project and have great respect for their commitment. We strive every day to build trust by fully supporting the quality of the services we deliver. And when a service we&#39;ve built <em>together</em> ends up being truly useful to users, I believe that&#39;s the true reward of working in QA. I hope this article has sparked some interest in working in QA. Also, if you&#39;re working in QA, I&#39;d love to hear your thoughts and exchange ideas. Feel free to share your feedback on Twitter!</p>
<p><a href="https://twitter.com/KintoTech_Dev/status/1619979941856280577?s=20">https://twitter.com/KintoTech_Dev/status/1619979941856280577?s=20</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[生成AIがサポート詐欺サイトへ誘導？]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-30-Gen-AI-Lead-to-a-Support-Scam-Site/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-30-Gen-AI-Lead-to-a-Support-Scam-Site/</guid>
            <pubDate>Fri, 30 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[生成AIがサポート詐欺サイトへ誘導？]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは！セキュリティ・プライバシーG所属の<a href="https://blog.kinto-technologies.com/posts/2025-02-26-newcomers-introduction-oct-nov/#%E3%81%9F%E3%81%AA%E3%81%A1%E3%82%85%E3%83%BC">たなちゅー</a>です。</p>
<p>本記事では、弊社で最近発生した生成AIチャットツールに関連するセキュリティ事案についてお話しします。技術的に特別新しいものではありませんが、発生した状況が少し珍しいケースだったため、紹介させていただきます。</p>
<h1>事案の概要</h1>
<p>弊社では、生成AIチャットツールを全社員が気軽に利用できる環境を整えています。ある日、そのツールを使用していた社員が生成AIへ質問した際に、回答として提示されたリンクへアクセスしたところ、「サポート詐欺サイト」が表示される事案が発生しました。</p>
<p><img src="/assets/blog/authors/tanachu/2025-05-30_Gen-AI-Lead-to-a-Support-Scam-Site/Gen-AI-Chat-Demo.png" alt="Gen-AI-Chat-Demo.png"> <em>生成AIチャット イメージ図</em></p>
<p><img src="/assets/blog/authors/tanachu/2025-05-30_Gen-AI-Lead-to-a-Support-Scam-Site/Support-Scam-Site-Demo.png" alt="Support-Scam-Site-Demo.png"> <em>サポート詐欺サイト イメージ図</em></p>
<p>セキュリティ製品でもブロックできず結果としてサポート詐欺サイトへ誘導されましたが、社員の冷静な判断により、大きな被害を防ぐことができました。</p>
<p>このことから、ブラウザによるインターネット検索と同様ですが、生成AIが提示するリンクが常に安全であるとは限らないことを実感する事案となりました。</p>
<h1>事案の発生要因</h1>
<p>生成AIがサポート詐欺サイトを提示した要因調査を目的に、インターネットアーカイブサービス「<a href="https://web.archive.org/">WAYBACK MACHINE</a>」で生成AIが提示したWebサイトを検索したところ、このWebサイトが過去に正規と思われるコンテンツを提供していることが確認されました。</p>
<p>また、このWebサイトを参考情報として紹介しているサイトが数件、存在することも確認されました。</p>
<p>このことから、問題となったWebサイトの過去のコンテンツ情報や紹介しているWebサイトの情報をもとに生成AIが学習した結果、生成AIが誤った情報を提示するハルシネーションに類する現象が発生し、サポート詐欺サイトのリンクを提示した可能性が考えられます。</p>
<p>以下は調査結果をもとに問題となったWebサイトのコンテンツ変遷を整理した内容です。</p>
<ul>
<li><p><strong>2012年～2018年前半</strong>
ドメイン名に合致したコンテンツ履歴あり。この時点では信頼性のあるサイトと判断されていたと推測される。</p>
</li>
<li><p><strong>2018年後半～2019年前半</strong>
ドメイン管理サービスの販売画面が表示され、運営者がドメインを手放した可能性がある。</p>
</li>
<li><p><strong>2019年後半以降</strong>
ドメイン名に関連性のないコンテンツ（病気、オンラインカジノ、偽警告画面、ドメインパーキングなど）の履歴あり。</p>
</li>
</ul>
<p>セキュリティ製品で検知できなかった要因調査については、「<a href="https://blog.kinto-technologies.com/posts/2025-05-30-Gen-AI-Lead-to-a-Support-Scam-Site/#%E4%BB%98%E9%8C%B2%EF%BC%9A%E8%AA%BF%E6%9F%BB%E3%83%A1%E3%83%A2">付録：調査メモ</a>」をご覧ください。</p>
<h1>類似事案への対策</h1>
<p>以下の観点から、このような事案に対して現時点では根本的な対策を講じることは非常に難しいと考えられます。</p>
<h4><strong>生成AIの学習の課題</strong></h4>
<p>この事案が発生した背景として、問題となったWebサイトの過去のコンテンツ情報や紹介しているWebサイトの情報をもとに生成AIが学習した可能性があります。一度学習されたWebサイトのコンテンツが変更されても、そのセキュリティリスクが生成AIの回答に反映されることは難しいと考えられます。</p>
<h4><strong>最大の対策は「知ること」</strong></h4>
<p>この事案から得られる教訓は、「生成AIが提示するリンクが常に安全であるとは限らない」ということを知ることです。事例を学び、実際に遭遇したときにどのように対処すべきかを理解することが大切です。</p>
<h1>まとめ</h1>
<p>今回の事案は、生成AIが提示するリンクが必ずしも安全ではないことを示す、少し珍しいケースでした。不正サイトの特性によっては、セキュリティ製品でも検知が難しい場合があります。また、生成AIが学習した後にサイトコンテンツが変更されると、そのリスクを反映できない可能性もあります。</p>
<p>現時点では、根本的な対策は難しいと考えられますが、こうした事例を知ることで、生成AIを利用する際のセキュリティ意識を高めるきっかけになればと思います。</p>
<h1>付録：調査メモ</h1>
<p>セキュリティ製品で検知できなかった要因調査のメモです。あくまでも簡単な調査となりますので、参考程度にご覧ください。</p>
<h4><strong>1. セキュリティベンダーの検知状況</strong></h4>
<p>弊社で利用しているセキュリティ製品や「<a href="https://www.virustotal.com/gui/home/search">VirusTotal</a>」で問題となったWebサイトのドメインを検索したところ、ほぼ全てのベンダーが「安全」判定となっていました。</p>
<h4><strong>2. Webサイトのソースコード</strong></h4>
<p>問題となったWebサイトのソースコードを確認したところ、「domaincntrol[.]com（c の後ろに o 無し）」へリクエストを送信後、レスポンス情報をもとに、訪問者の誘導先を動的に決定する仕組みが実装されていると考えられます。</p>
<p>実際に安全な環境で数回アクセスを試みたところ、サポート詐欺サイトやドメインパーキングなど、異なるWebサイトへ遷移することが確認されました。これにより、セキュリティベンダーによる悪性判定を回避した可能性があります。</p>
<h4><strong>3. サポート詐欺サイトのホスティング環境</strong></h4>
<p>最終的に表示されるサポート詐欺サイトは「web.core.windows[.]net」ドメインにホストされており、Microsoft Azure環境が利用されていると推測されます。Microsoft Azureに限らず、クラウドサービスを利用した不正サイトは、環境構築の容易さや業務影響の観点からクラウドサービスのドメインをブロックすることが事実上不可能であることから、セキュリティ製品でブロックすることが難しいと考えられます。</p>
<p>※本記事公開時点で今回のサポート詐欺サイトが、Microsoft Azureから削除されていることを確認しています。</p>
<h4><strong>4. PublicWWWでの調査結果</strong></h4>
<p>Webサイトのソースコードを検索できるツール「<a href="https://publicwww.com/">PublicWWW</a>」を用いて、問題となったWebサイトの特徴的な文字列「domaincntrol[.]com/?orighost=」を検索したところ、2万件以上のサイトでこの文字列を含むコードが使用されていることが確認されました。また、その中からいくつかのサイトを調査した結果、同様にサポート詐欺サイトへ誘導される挙動が確認されました。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[UUIDとは何でしょう? どのバージョンを使用すればよいでしょうか?]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-12-WhatAreUUIDsAndWhichVersionShouldYouUse-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-12-WhatAreUUIDsAndWhichVersionShouldYouUse-ja/</guid>
            <pubDate>Thu, 29 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[UUID 、それらの生成方法、および使用可能なさまざまなバージョンに関する概要。]]></description>
            <content:encoded><![CDATA[<h1>UUIDとは何でしょう? どのバージョンを使用すればよいでしょうか?</h1>
<p>最近、データベース内のキーが重複していたためにサービスがダウンするというインシデントに対応しなければなりませんでした。私のチームと私は、これらがUUIDだったため頭を悩ませていました。ご存知のとおり、これらは「一意」の識別子です。どうして重複が生じるのでしょうか?結局、この問題は、同じUUIDが2回生成されたことではなく、サービスが同じイベントを2回追加しようとしたことが原因であることがわかりました。この出来事がきっかけで、UUIDについて考えるようになりました。UUIDとは何でしょうか?UUIDはどのように生成されるのでしょうか?UUIDの使用例は?そして最も重要なのは、どのバージョンを使用すべきかということです。</p>
<h2>UUIDとは何でしょう?</h2>
<p>UUIDは通常、リソースのIDを提供するために使用されます。UUIDは「Universally Unique IDentifier（汎用一意識別子）」の略称です。その名称を見ると、生成される値の一意性が強く期待されているようです。これには十分な理由があります。例えば、数千兆のUUIDなど、膨大な量のUUIDを生成したとしても、それらが一意である確率は99.999%です。これらの確率の背後にある数学に興味がある方は、<a href="https://towardsdatascience.com/are-uuids-really-unique-57eb80fc2a87">この非常に優れた記事</a>を読むことをお勧めします。</p>
<p>UUIDは「保証された一意性」ではなく「実質的な一意性」です。衝突の可能性は非常に小さいため、ほとんどのアプリケーションでは、UUIDの衝突が発生する可能性よりも、ハードウェアが故障するか、<a href="https://www.bbc.com/future/article/20221011-how-space-weather-causes-computer-errors">宇宙線が原因でビットがマシンのメモリ内で反転する</a>可能性の方が高くなります。</p>
<p>ただし、これらの確率は適切な乱数発生を前提としていることに注意してください。乱数発生器に欠陥があったり予測可能だったりすると、衝突の実際の確率ははるかに高くなる可能性があります。この記事の後半でもう少し詳しく説明します。</p>
<p>ソフトウェアに携わっている方であれば、UUIDがどのようなものかすでにご存知かと思いますが、念のため：UUIDは128ビット幅で、ハイフンで区切られた5つの部分で構成されます。それらは通常、16進数で表され、次のようになります。</p>
<ul>
<li>ccba8c00-cbed-11ef-ad79-1da827afd7cd</li>
<li>74febad9-d652-4f6b-901a-0246562e13a8</li>
<li>1efcbedf-13bf-61e0-8fb8-fe3899c4f6f1</li>
<li>01943a0e-dd73-72fd-81ad-0af7ce19104b</li>
</ul>
<p>でも待ってください！これらのUUIDは実際には異なるバージョンのUUIDを使用して生成されました。上記のUUIDのリストにおいて、それらはバージョン1、バージョン4、バージョン6、バージョン7の順で使用して生成されます。UUIDにおいてバージョンがどこに示されているかを調べてみてください。</p>
<p><em>ヒント：中間あたりにあります。</em></p>
<p>UUIDのバージョンは、UUIDの真ん中にある、UUIDの3番目の部分の最初の文字に示されていることにお気づきだと思います。4番目の部分の最初の文字にもバリアントが示されています。バージョンはUUIDがどのように生成されたかを示すために使用され、バリアントはそのUUIDのレイアウトを示すために使用されますが、おそらくバリアントについて心配する必要はなく、バージョンが最も重要です。</p>
<p><img src="/assets/blog/authors/mrtnhwtt/uuid/uuid_representation.png" alt="UUID structure diagram"></p>
<p>先ほど説明したように、UUID には複数のバージョンがあります。先ほど発見したバージョンインジケーターの他に、各バージョンの違いは何でしょうか?それらはすべて、一意のUUIDを同様に生成できるでしょうか?また、あるバージョンを他のバージョンよりも優先して使用する理由は何でしょうか?もちろん、最新かつ最良のUUIDバージョンを使用すべきですよね？とても良い質問です！UUIDの異なるバージョンを見てみましょう。</p>
<h3>バージョン1とバージョン6</h3>
<p>バージョン1および6のUUIDは、UUIDを生成したコンピューターの現在の時刻と<a href="https://en.wikipedia.org/wiki/MAC_address">MACアドレス</a>を使用して生成されます。タイムスタンプ部分は UUIDの先頭にあり、コンピューターのCPUに応じてランダムビットまたは増分カウンターを含む場合があります。MACアドレス部分は最後にあるため、同じコンピューターを使用していれば、その部分が変化することはありません。興味深いことに、MACアドレスはUUIDから取得できるため、UUIDバージョン1または6を生成するとプライバシーのリスクが生じます。しかし、これはこのバージョンのUUIDの利点の1つでもあり、2台のコンピュータが同じUUIDを生成することはありません。そのため、これらのバージョンは、グローバルな一意性が求められる分散システムで役立ちます。</p>
<p>バージョン1と6の違いは、UUIDでタイムスタンプの各部分が使用される順序です。バージョン1とは異なり、バージョン6のUUIDは時系列で並べ替えることができるため、データベース内での順序付けに役立ちます。</p>
<p><img src="/assets/blog/authors/mrtnhwtt/uuid/uuid_version_1_6_representation.png" alt="UUID version 1 and 6 structure diagram"></p>
<p>バージョン1および6では予測可能な要素（生成時刻とMACアドレス）を使用するため、UUIDを推測可能であり、UUIDを秘密にしておく必要がある用途には適していません。</p>
<h3>バージョン2</h3>
<p>バージョン2は、タイムスタンプと、UUIDを生成するコンピューターのMACアドレスを使用する点でバージョン1と似ています。ただし、バージョン2では、POSIX UIDまたはGIDという追加の識別子データも使用します。これにより、バージョン2はバージョン1および6よりもランダム性が低くなり、タイムスタンプの使用が少なくなります。その結果、特定の時点で生成できるUUID v2の数は限られており、ほとんどの用途においてあまり望ましくありません。これが使用されることは稀であり、通常、ほとんどのライブラリでサポートされていません。これは、UUID仕様にも記載されていません。</p>
<h3>バージョン3と5</h3>
<p>バージョン3と5は他のUUIDとはまったく異なります。他のバージョンはランダム性を目指していますが、バージョン3とバージョン5は決定論的であることを目指しています。これはどういう意味でしょう？いずれもハッシュアルゴリズムを使用してUUIDを生成するため、UUIDを再現可能にします。UUIDを生成するためにランダム性やタイムスタンプは使用されず、与えられた入力は常に同じUUIDを生成する必要があります。バージョン3ではMD5ハッシュアルゴリズムを使用し、バージョン5ではSHA1を使用します。</p>
<p>これらのバージョンは、同じ入力データから同じUUIDを繰り返し生成する必要がある場合に特に便利です。例えば、ユーザーの電子メールアドレスに基づいてユーザーのUUIDを作成するとします。異なるサーバーや時間にわたっても、同じ電子メールで常に同じUUIDが生成されるようにしたいとします。もう1つの良い例としては、重複を避けるために何らかのデータに基づいて主キーを生成する必要があるが、データ自体を主キーとして使用するのは良い選択肢ではない場合が挙げられます。</p>
<p>バージョン3とバージョン5のいずれかを選択する場合、SHA1の方が少し安全ですが、計算負荷も大きくなることに留意してください。それがユースケースで懸念事項である場合は、バージョン3を使用してコンピューティングリソースの使用量を削減することをお勧めしますが、ほとんどの場合、より安全なバージョン5を選択すべきです。また、SHA1よりもMD5と衝突が発生する可能性が高くなりますが、その確率は依然として非常に低いです。</p>
<h3>バージョン4</h3>
<p>バージョン4は、UUIDの最も広く使用されているバージョンです。バージョン4はランダムビットを使用してUUIDを生成するため、UUIDは一意で予測不可能になります。これは乱数発生に大きく依存していますが、すべての乱数発生器が実際に真の乱数を生成できるわけではありません。衝撃的ですよね。</p>
<p>多くのプログラミング言語では、疑似乱数発生器(PRNG)と呼ばれるものを使用しています。ほとんどの場合はこれで問題ありませんが、UUID生成の場合は、システムが暗号論的にセキュアな疑似乱数生成器(CSPRNG)を使用していることを確認する必要があります。</p>
<p>なぜかって?通常のPRNGは、出力を十分に分析すれば予測可能になる場合があります。一方、CSPRNGは、攻撃者が以前に生成されたすべての値を知っている場合でも、その出力を予測することが事実上不可能になるように特別に設計されています。最近のUUIDライブラリのほとんどがデフォルトでCSPRNGを使用していますが、念のため確認してみる価値はあります。</p>
<p>他のバージョンと同様に、予測可能な部分はバージョンインジケーターのみなので、その部分を推測して友達を感心させてみましょう。</p>
<p>これらは、大量のUUIDを生成する必要があり、後で並べ替えたり再現したりする必要がない場合など、ほとんどの用途に最適です。これらは、データベースにおいてキーとしてよく使用されます。</p>
<h3>バージョン7</h3>
<p>バージョン7は、バージョン4を時系列順に並べ替えられるバージョンとして設計されています。バージョン4と同様に、バージョン7はランダムビットを使用しますが、タイムスタンプを含むため、UUIDはソート（並び替え）可能かつ一意になります。一意性を保ちつつ、作成時間によって並べ替えたい場合に、バージョン7はバージョン4の優れた代替手段となります。</p>
<p>バージョン7でもタイムスタンプにエポックタイムを使用しますが、バージョン1と6では1582年10月15日以降、100ナノ秒間隔の数値を使用しています。これにより、バージョン7での作業が少し簡単になっています。</p>
<h3>バージョン8</h3>
<p>バージョン8はカスタムなので少し特殊です。ベンダーは希望どおりに実装できます。バージョン8を自分で実装することもできますが、UUIDの3番目の部分にあるUUIDバージョンを尊重する必要があります。おそらくそれを使用する必要はないでしょう。</p>
<h2>では、何を使用すればよいでしょうか?</h2>
<p>ほとんどの人にとって、バージョン4になります。バージョン4は、一意性の保証が最も大きく、比較的安全です(乱数発生器が予測不可能な限り)。UUIDを作成時間によって並べ替えられるようにしたい場合は、MACアドレスの漏洩によるプライバシーの懸念がない限り、バージョン7またはバージョン6を使用できます。場合によってはバージョン3と5が便利ですが、ほとんどのアプリケーションではそれらの使用は制限されます。</p>
<h3>データベースキー?</h3>
<p>データベースキーにUUIDを使用することに関する議論を見たことがあるかもしれませんが、データベースキーにUUIDを使用することを検討している場合は、覚えておくべき事実がいくつかあります。</p>
<ul>
<li>UUIDは大きく、128ビットを占めます。大量のデータを保存する予定がない場合は、UUID用に追加で占有されるスペースが大きくなる可能性があります。あるいは、32ビットの自動増分整数（オートインクリメント整数）では約2147483647行が得られますが、それでも足りない場合は、64ビットのBIGINTで最大18446744073709551615になります。ほとんどのユースケースの場合、これで十分でしょう。</li>
<li>一部のデータベースでは、キーにUUIDを使用すると、挿入性能が低下する可能性があります。挿入性能が懸念される場合は、自動増分整数の使用を検討するか、少なくともUUIDを使用してデータベースの性能をテストすることをお勧めします。</li>
<li>UUIDは、データの移行を容易にします。自動増分整数を使用すると衝突が発生しますが、UUIDではその問題は発生しないでしょう。</li>
<li>一部のUUIDはソート（並び替え）可能ですが、読みやすくはありません。2つのUUIDに着目すると、どちらが先に来たのかを知るのは非常に困難です。これは非常に些細なことですが、留意すべきです。</li>
</ul>
<p>ほとんどのデータベースには、UUIDを生成するための何らかのモジュールまたは関数があるため、データベースのドキュメントをチェックしてUUIDの生成方法を確認できます。UUIDを使用する際に性能上の問題や考慮すべき特別な事項がある場合は、おそらくそこで判明するでしょう。</p>
<h2>結論</h2>
<p>この記事を読む前よりも、UUIDとそれらのさまざまなバージョンについて少し理解が深まったと思います。</p>
<p>バージョン4 UUIDは、ほとんどのアプリケーションで依然として定番です。バージョン4 UUIDは、強力な一意性保証と予測不可能性を備えており、おそらくこれがUUIDに求められるものです。バージョン4 UUIDは主に、データベースキー、分散システム、および調整なしでグローバルに一意な識別子が必要なシナリオで使用されます。</p>
<p>バージョン7は、ランダム性とソート（並び替え）可能性とのバランスが取れているため、時系列的ソートが望ましい場合に適した代替手段です。</p>
<p>バージョン1と6は、グローバルな一意性が必要な分散システムでは役立ちますが、MACアドレスが含まれるためプライバシーに関する懸念を伴います。</p>
<p>バージョン3と5は、特定の入力からUUIDを再現する必要がある場合に便利ですが、MD5はSHA1ほど安全ではないことに注意してください。</p>
<p>自分のシステムでUUIDを使用する予定の場合は、UUIDバージョンを選択する際に次の要素を考慮してください。</p>
<ul>
<li>一意性に関する自身の要件</li>
<li>時系列的ソートが必要か否か</li>
<li>プライバシーに関する懸念 (特に MACアドレスを含むバージョンを使用する場合)</li>
<li>ストレージ スペースの制約 (あなたのキーに128ビットは必要ないかもしれません)</li>
</ul>
<p>UUIDの衝突は理論的には起こり得ますが、暗号的に安全な乱数発生器を使用した適切な実装をしている限り、その可能性は非常に低いため、システム設計において大きな懸念事項にはならないはずです。UUIDの衝突が発生した場合(天文学的な確率を覆した場合!)、実際のUUID生成衝突ではなく、重複したイベント処理などのアプリケーションロジック問題が原因である可能性が高くなります。そのような場合は、UUID生成自体に疑問を抱くよりも、アプリケーションの一意性制約の処理を調べることに重点を置いてください。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/mrtnhwtt/uuid/uuid_cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ARSCNViewビデオフィードのビジョンフレームワークによって検出されたオブジェクトの3D座標の抽出]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-21_ar-coordinates-extraction-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-21_ar-coordinates-extraction-ja/</guid>
            <pubDate>Wed, 28 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Core MLオブジェクト検出モデルによって検出されたオブジェクトの3D座標を取得する基本例。アドベントカレンダー2024。]]></description>
            <content:encoded><![CDATA[<p>この記事は <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2024 の21日目の記事です</a>🎅🎄</p>
<h1>はじめに</h1>
<p>こんにちは！iOSエンジニアのViacheslavです。</p>
<p>今年、私は当社の<a href="https://apps.apple.com/jp/app/kinto-unlimited/id1663164737">Unlimitedアプリ</a>の新機能である<a href="https://www.youtube.com/watch?v=E8zfNzuHr7g">「これなにガイド」</a>に取り組む機会を得ました。これなにガイドは、車のダッシュボードをスマホでスキャンすることで、車のボタンやスイッチの上に仮想マーカーを表示できる拡張現実(AR)マニュアルです。特定のボタンに対応するマーカーを選択すると、機能を確認できるマニュアルページにアクセスできます。</p>
<p>今日は、この機能の開発中に遭遇した課題の1つである、「画面上でVisionフレームワークによって認識された物理オブジェクトの座標を正確にキャプチャし、それらの座標をARシーン内の3D座標に変換する」という課題に対する、短くてシンプルな解決策を共有したいと思います。 </p>
<p>最初は些細な作業に思えたものが、予想以上に複雑であることが判明しました。いくつかのアプローチを模索し、多くの手動計算を実行した後、ようやく、単純かつ驚くほど簡潔な解決策にたどり着きました。ARKit と CoreML の統合に関する情報は比較的少ないので、始めたときに知っていればよかったと思います。では、知識ベースに追加していきましょう!</p>
<h1>いくつかの前提条件</h1>
<p>実際のコードに進む前に、作業する環境を明確に定義してみましょう。</p>
<ul>
<li><p><strong><a href="https://developer.apple.com/documentation/arkit/arscnview">ARSCNView</a></strong><br>これは、デバイスのカメラからのビデオフィードを表示し、現実世界をキャプチャして3Dオブジェクトを「ブレンド」させてARエクスペリエンスを実現するビューです。<code>ARSCNView</code>はAppleの<a href="https://developer.apple.com/documentation/arkit">ARKit</a>の一部で、ARシーンでの3Dオブジェクトのレンダリングを処理する<a href="https://developer.apple.com/documentation/scenekit/">SceneKit</a>上に構築されます。</p>
</li>
<li><p><strong><a href="https://developer.apple.com/documentation/coreml/">Core ML</a> オブジェクト検出モデル</strong><br>オブジェクトの座標を決定する前に、まずデバイスのカメラによって提供されるビデオ フィード フレーム内でオブジェクトを認識する必要があります。Visionフレームワークは、この目的のためにCore MLオブジェクト検出モデルを利用します。本記事では、読者がすでに使用できるモデルを持っているものと想定します。そうでない場合は、<strong>YOLOv3-Tiny</strong> など、ダウンロード可能な事前訓練済みモデルが多数あります。<a href="https://developer.apple.com/machine-learning/models/">こちら</a>から入手できます。</p>
</li>
</ul>
<p>最低限のソリューションに必要なのはこれだけです。<code>ARSCNView</code> からビデオフレームをキャプチャし、Core MLモデルを使用して<code>ARSCNView</code>ビューポート内のオブジェクトの位置を検出し、「ヒットテスト」と呼ばれる手法を適用して3D AR空間におけるオブジェクトの座標を決定します。</p>
<h1>ARSCNViewにおいて認識されたオブジェクトの座標をキャプチャする</h1>
<p>Vision を使用して認識要求を実行する場合の一般的な設定は以下の通りです。</p>
<p>Core MLモデルと <code>VNCoreMLRequest</code> を初期化して、そのモデルを使用して認識を処理します。</p>
<pre><code class="language-swift">let vnModel = try!VNCoreMLModel(for: myCoreMLModel)
let vnRequest = VNCoreMLRequest(model: vnModel) { [weak self] request, _ in
    guard let observations = request.results else { return }
    // Observations handling
}
request.imageCropAndScaleOption = .centerCrop
</code></pre>
<p>次に、<code>vnRequest</code> への参照を適切な場所に保存し、次の引数セットで実行できるように準備します。引数のタイプは、ビデオフィードフレームをどこからキャプチャするかによって異なります。</p>
<p>私たちのシナリオでは、<code>ARSCNView</code> からフレームを渡し、<code>ARSessionDelegate</code> の <code>session(_:didUpdate:)</code> メソッドにおいてフレームをキャプチャする必要があります。このデリゲートメソッドは、<code>ARSCNView</code> で表示できる新しいフレームが利用可能になるたびに呼び出されます。</p>
<pre><code class="language-swift">func session(_ session:ARSession, didUpdate frame:ARFrame) {
    guard let vnRequest else { return } // 1
    let options: [VNImageOption:Any] = [.cameraIntrinsics: frame.camera.intrinsics] // 2
    let requestHandler = VNImageRequestHandler(
        cvPixelBuffer: frame.capturedImage, // 3
        orientation: .downMirrored, // 4
        options: options
    )
    try? requestHandler.perform([vnRequest]) // 5
}
</code></pre>
<h3>コードを分解する：</h3>
<ol>
<li><strong><code>VNCoreMLRequest</code>の参照</strong>：新しいフレームを受信すると、先ほど初期化したリクエストを実行する準備が整います。</li>
<li><strong>カメラ内部パラメータ</strong>：<code>frame.camera.intrinsics</code> は、Visionがシーンの幾何学的特性を解釈するのに役立つカメラ較正データを提供します。</li>
<li><strong>画像入力</strong>：<code>VNImageRequestHandler</code> は、ARフレームから取得された生画像データを <code>CVPixelBuffer</code> として受け入れます。</li>
<li><strong>画像の向き</strong>：<code>.downMirrored</code> 方向は、Visionのデフォルト方向と比較して、カメラフィードの座標系の反転を考慮します。</li>
<li><strong>リクエストの実行</strong>：準備されたリクエストは、リクエストハンドラーを使用して実行されます。</li>
</ol>
<p>Vision にフレームを渡し始めると、オブジェクト検出の結果が <code>VNCoreMLRequest</code> 完了ハンドラー内の<code>VNRecognizedObjectObservation</code> オブジェクトの配列として返されます。これらの結果を信頼度レベルでフィルタリングしたり、その他の処理を実行したりすることもできますが、今日は認識された特定のオブジェクトの座標を抽出することに焦点を当てます。</p>
<h3>境界ボックス座標の抽出</h3>
<p>最初は、<code>VNRecognizedObjectObservation</code> に <code>boundingBox</code> プロパティ（認識されたオブジェクトを囲む <code>CGRect</code>）があるため、これは簡単に思えるかもしれません。ただし、いくつかの複雑な問題があります。</p>
<ul>
<li><code>boundingBox</code>は、物体認識モデルの入力画像に対する正規化された座標系（座標値は0から1の間）であり、それもY軸が反転しています。</li>
<li>カメラフィード、Core MLモデル入力、そして<code>ARSCNView</code>ビューポートのサイズとアスペクト比はそれぞれ異なります。</li>
</ul>
<p>つまり、<code>boundingBox</code> を <code>ARSCNView</code> ビューポートの座標系に変換するには、一連の座標変換と再スケーリングの手順が必要になります。これらの変換を手動で行うのは面倒で、間違いが起こりやすくなります。幸いなことに、<code>CGAffineTransform</code> を使用すると、これを処理できる非常に簡単な方法があります。方法は次のとおりです。</p>
<pre><code class="language-swift">let sceneView:ARSCNView

func getDetectionCenter(from observation:VNRecognizedObjectObservation) -&gt; CGRect? {
    guard let currentFrame = sceneView.session.currentFrame else { return nil }
    let viewportSize = sceneView.frame.size

    // 1
    let fromCameraImageToViewTransform = currentFrame.displayTransform(for: .portrait, viewportSize: viewportSize)
    let viewNormalizedBoundingBox = observation.boundingBox.applying(fromCameraImageToViewTransform)

    // 2
    let scaleTransform = CGAffineTransform(scaleX: viewportSize.width, y: viewportSize.height)
    let viewBoundingBox = viewNormalizedBoundingBox.applying(scaleTransform)

    return viewBoundingBox
}
</code></pre>
<h3>説明：</h3>
<ol>
<li><strong>ビュー座標への変換</strong>：<code>displayTransform(for:viewportSize:)</code> を使用して、検出された境界ボックスは、入力画像の正規化座標系から <code>ARSCNView</code> の正規化座標系に変換されます。</li>
<li><strong>ピクセル寸法へのスケーリング</strong>：正規化された境界ボックスは、<code>ARSCNView</code> ビューポートのサイズに合わせてスケーリングされ、画面のピクセル寸法での境界ボックスが生成されます。</li>
</ol>
<p>以上です！これで、<code>ARSCNView</code> ビューポートの座標系に境界ボックスが作成されました。</p>
<h1>3番目の座標を取得する</h1>
<p>私は、ARシーンの3D座標空間内で認識されたオブジェクトの座標を取得すると約束しました。<br>そのためには、「ヒットテスト」と呼ばれる手法を利用します。これにより、ビューポート内の任意のポイントで最も近い物理オブジェクトまでの距離を測定できます。この手法は、デバイスからビューポート内の選択したポイントにある物理オブジェクトとの最初の交点まで直線の光線を投影し、その光線の長さを測定するものと考えることができます。この機能は ARKit の一部であり、非常に使いやすいです。<br>先ほど検出したオブジェクトの知覚可能な中心の3D座標を見つける方法は以下の通りです。</p>
<pre><code class="language-swift">func performHitTestInCenter(of boundingBox:CGRect) -&gt; SCNVector3? {
    let center = CGPoint(x: boundingBox.midX, y: boundingBox.midY) // 1
    return performHitTest(at: center)
}

func performHitTest(at location:CGPoint) -&gt; SCNVector3? {
    guard let query = sceneView.raycastQuery( // 2
        from: location,
        allowing: .estimatedPlane, // 3
        alignment: .any // 4
    ) else {
        return nil
    }
    guard let result = sceneView.session.raycast(query).first else { return nil } // 5
    let translation = result.worldTransform.columns.3 // 6
    return .init(x: translation.x, y: translation.y, z: translation.z)
}
</code></pre>
<h3>説明：</h3>
<ol>
<li>ここでは、ヒットテストを実行するために1つのポイントが必要なため、前の手順から境界ボックスの中心を計算します。</li>
<li>指定された2Dポイントから始まるレイキャストクエリを作成します。</li>
<li>ヒットテストで、ARKitが推定することしかできない非平面の表面や平面を考慮できるようにします。</li>
<li>水平面と垂直面の両方のヒットテストを有効にします（既定値は水平面のみです）。</li>
<li>ARセッションを使用してレイキャストクエリを実行します。交差がない場合は <code>nil</code> を返します。</li>
<li>各 <code>ARRaycastResult</code> には、ワールド空間で検出されたポイントの3D変換を表す4x4行列である <code>worldTransform</code> が含まれます。<code>columns.3</code> には、交点の3D位置を指定する並進ベクトルが含まれています。この並進ベクトルは <code>SCNVector3</code> として返され、ARKit/SceneKitはこれを使用して3D位置を表します。</li>
</ol>
<p>完了しました！これで、Visionによって検出されたオブジェクトの 3D座標を取得できました。目的に合わせてご利用ください。:)</p>
<h1>最後に</h1>
<p>Unlimitedアプリでは、これらの3D座標を使用して、車内にARマニュアルマーカーを表示します。もちろん、ユーザーエクスペリエンスをよりスムーズにし、マーカーの位置をより安定させるために私たちが採用している追加テクニックは数多くありますが、これはコアテクニックの1つです。<br>とはいえ、この方法は、考えられる他のあらゆる目的にも使用できます。お役に立てたら幸いです。</p>
<p>最後に、私たちのテストプロセスと、オブジェクト検出後にARマニュアルマーカーがどのように表示されるかについて少し紹介します。<br><img src="/assets/blog/authors/vorona/ar_coordinates/demo.gif" alt="demo"></p>
<p>今日はここまでです。読んでいただきありがとうございました!<br>楽しいクリスマスをお過ごしください。そして、幸せな新年をお迎えください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/vorona/ar_coordinates/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Agent Store v1.0リリース：社内エージェント開発を効率化する社内プラットフォーム]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-30-Agent-Store/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-30-Agent-Store/</guid>
            <pubDate>Wed, 28 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Agent Store v1.0についての簡単な紹介]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>はじめに</h2>
<p>こんにちは、AIファーストグループのAlexです。
AI技術の急速な発展に伴い、エージェント開発の需要も高まっています。しかし、エージェントに関する最新技術の知見の共有不足や、開発リソースの分散により、効率的なエージェント開発を始めることが難しい状況にあります。そこで私たちは、社内各所で開発したAIエージェントを社内で共有し、技術・ノウハウを集約するためのプラットフォーム「Agent Store v1.0」をリリースしました。</p>
<h2>Agent Storeの目的</h2>
<p>Agent Storeは以下の2つの主要な目的を持っています：</p>
<ol>
<li><strong>社内Agent開発の効率向上</strong><ul>
<li>既存Agentの再利用により開発サイクルを短縮</li>
<li>テンプレートを活用した迅速な開発を実現</li>
</ul>
</li>
<li><strong>技術・ノウハウの蓄積</strong><ul>
<li>社内のAgent関連技術を一元管理</li>
<li>ベストプラクティスの横展開を容易に</li>
</ul>
</li>
</ol>
<h2>利用形態と対象ユーザー</h2>
<p>Agent Store は、社員が AI エージェントを自由に開発、共有、ダウンロードして再利用するプラットフォームです。
なお再利用に関しては、App Storeのように、ユーザーがエージェントをダウンロードし、自身の環境にデプロイすることを想定しています。</p>
<p>Agent Storeは以下の要素で構成しています。</p>
<ul>
<li>社内で開発したエージェントを共有するGithubリポジトリ</li>
<li>エージェント開発のCI/CDの仕組み</li>
</ul>
<p><img src="/assets/blog/authors/alex.q/agent-store/repository.png" alt="Agent StoreのGithubリポジトリ">
<em>Agent StoreのGithubリポジトリ</em></p>
<h3><strong>利用形態</strong></h3>
<p>Agent Store v1.0では主にAWS Bedrockエージェントをサポートしています。</p>
<p>エージェントのCI/CDプロセスに関しては、IaC (Infrastructure as Code)を基づいて設計しました。
Agent Storeで共有されたエージェントは、CloudformationでデプロイするためのSAMテンプレートの形でAgent StoreのGithubリポジトリ上で格納されています。</p>
<p>エージェントを再利用したいユーザーは、該当するエージェントのSAMテンプレートをダウンロードし、Cloudformation経由で自分の環境にデプロイする形で利用します。
なお、デプロイに関してはGithub Actionsで自動化しています。</p>
<p>エージェントを新規開発したいユーザーに関しても、Agent Storeが提供している空のSAMテンプレートを利用し、素早くエージェント開発することが可能です。</p>
<h3><strong>対象ユーザー（v1.0）</strong></h3>
<ul>
<li>AWS Bedrockを利用してエージェント開発を始めたいエンジニア</li>
<li>Bedrockで開発したエージェントを社内で共有したいエンジニア</li>
<li>既存のエージェントを探して活用したいエンジニア</li>
</ul>
<h3><strong>想定するユースケース</strong></h3>
<p>Agent Storeは以下のような利用シーンを想定しています：</p>
<ol>
<li><strong>AIエージェントの新規作成</strong><ul>
<li>Agent StoreのCI/CDフローを利用することで開発の高速化を実現</li>
</ul>
</li>
<li><strong>AIエージェントの共有</strong><ul>
<li>自作エージェントを社内で共有</li>
</ul>
</li>
<li><strong>共有されたAIエージェントの再利用</strong><ul>
<li>既存プロダクトにAIエージェントを搭載</li>
<li>社内業務効率化アプリの開発</li>
<li>一からの開発を避け、開発効率の向上を実現</li>
</ul>
</li>
<li><strong>AIエージェントのPoC</strong><ul>
<li>既存エージェントをデプロイして迅速にPoC実施</li>
<li>効果検証の時間短縮</li>
</ul>
</li>
<li><strong>AIエージェント開発ノウハウの取得</strong><ul>
<li>類似事例の参照によるベストプラクティスの学習</li>
<li>技術的な障壁の低減</li>
</ul>
</li>
</ol>
<h2>Agent Storeを活用したエージェント開発、共有と再利用のフロー</h2>
<ol>
<li><p><strong>エージェントを新規開発するフロー</strong>
Agent Storeリポジトリから空のSAMテンプレートを取得し、記入した上でデプロイを行う流れとなります。  </p>
<ul>
<li>SAMについては<a href="https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/AWS_Bedrock.html">こちら</a>を参照してください。</li>
<li>なお、エージェントのAction Groupを利用する場合、①Lambda、②エージェントの順番でテンプレート記入・デプロイを行う必要があります。
<img src="/assets/blog/authors/alex.q/agent-store/cicd_1.png" alt="エージェントを新たに開発するフロー"></li>
</ul>
</li>
<li><p><strong>エージェントを共有するフロー</strong>
開発したエージェントのSAMテンプレートを用意し、Agent Storeのリポにプルリクを実行します。エージェントのレビュー担当者にて内容を確認後、問題なければマージを行います。
<img src="/assets/blog/authors/alex.q/agent-store/cicd_2.png" alt="エージェントを共有するフロー"></p>
</li>
<li><p><strong>共有されたエージェントを新たに再利用するフロー</strong>
基本の流れはエージェントを新規開発するフローと共通していますが、Agent Storeで共有されているエージェントのSAMテンプレートを取得し、必要に応じて修正・加筆を行った上でデプロイを行う流れになります。
<img src="/assets/blog/authors/alex.q/agent-store/cicd_3.png" alt="エージェントを再利用するフロー"></p>
</li>
</ol>
<h2>エージェントのアーキテクチャ</h2>
<p>エージェントのアーキテクチャは以下です。
デプロイしたBedrockエージェントは、必要に応じでAction Groupとして設定されたLambdaを呼び出し実行することができる。
また、スタック管理はCloudFormationで行っている。
<img src="/assets/blog/authors/alex.q/agent-store/architecture_1.png" alt="エージェントのアーキテクチャ"></p>
<p>また、複数のエージェントが協働するマルチエージェントコラボレーションも構築することが可能です。
<img src="/assets/blog/authors/alex.q/agent-store/architecture_2.png" alt="マルチエージェントのアーキテクチャ"></p>
<h2>今後の展開計画</h2>
<p>今後は、Agent Storeの利用促進のために、社内勉強会、ワークショップ、ハッカソンなどを多数企画しています。</p>
<p>また、現在のv1.0は「AWS Bedrockの開発経験があるエンジニア（タイプa）」を対象としていますが、今後は段階的に対象ユーザーを拡大していく予定です：</p>
<table>
<thead>
<tr>
<th>ユーザーの種類</th>
<th>詳細</th>
<th>サポート状況</th>
</tr>
</thead>
<tbody><tr>
<td>エンジニア タイプa</td>
<td>AWS Bedrockの開発経験があるエンジニア</td>
<td>v1.0でサポート中</td>
</tr>
<tr>
<td>エンジニア タイプb</td>
<td>AWS Bedrock以外のエージェント開発環境を利用したいエンジニア</td>
<td>検討中</td>
</tr>
<tr>
<td>非エンジニア、エージェント開発初心者</td>
<td>開発・コーディング経験がない、非エンジニアの社員</td>
<td>検討中</td>
</tr>
</tbody></table>
<h2>まとめ</h2>
<p>Agent Store v1.0は、エージェント開発の効率化と知見共有を実現するプラットフォームです。現時点ではAWS Bedrockユーザー向けに機能を提供していますが、今後はより幅広いユーザー層へのサポートを拡大し、様々なエージェントフレームワークにも対応していく予定です。社内のAI開発リソースを最大限に活かし、イノベーションのペースを加速させるため、Agent Storeの機能拡充と進化に積極的に取り組んでいきたいと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/alex.q/agent-store/agent-store-cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[世界の橋渡し:ビジネスアナリスト（BA）の力]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-21-Bridging-Worlds-as-BA-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-21-Bridging-Worlds-as-BA-ja/</guid>
            <pubDate>Tue, 27 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[世界の橋渡し:ビジネスアナリスト（BA）の力]]></description>
            <content:encoded><![CDATA[<p>本記事は<a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2024</a>の21日目の記事です🎅🎄</p>
<h1><strong>はじめに</strong></h1>
<p>皆さん、こんにちは！DLです。KINTOテクノロジーズ（KTC）のグループコアシステム部で、ビジネスディベロップメントチームに所属しています。今は、保険などの付加サービスを備えた自動車リース事業 KINTO ONE の社内システムを導入するため、ラテンアメリカのKINTO事業部と連携しています。ビジネスアナリスト（BA）として私が注力しているのは、 KINTOの業務分析と、ITソリューションの作成でワークフローを合理化して、効率性を向上させることです。</p>
<p>ビジネス分析がグローバルチームの一部となる前は、システム設計がKINTOグローバルビジネスの期待に十分応えられていないことがよくありました。日常業務の複雑さに対応できる、エンタープライズレベルのソリューションが求められていたのです。ここで活躍するのがBAです。日々の業務プロセスを把握し、懸念材料となる課題に対処する上で、BAは欠かせない存在です。ビジネス分析は、グローバルプロジェクトに価値を付加する戦略的な柱と言えるでしょう。</p>
<p>今日は、なにかと誤解されがちなBAの世界へ、皆さんをご案内します。「ただ数字ばかり気にしてる人じゃないの？」とか、「他の人の言うことをメモしてるだけなのでは？」と思うかもしれません。でも、BAの仕事って、それ以上に奥が深いんです。問題解決や戦略、コミュニケーションも欠かせません。</p>
<p>BAの本質は、技術とビジネスの世界を橋渡しすることです。効率アップ、業務の合理化、テクノロジーの導入など、どんな場面でもBAは耳を傾け、分析し、実用的で効果のあるソリューションを形にしていきます。経営幹部からエンドユーザーまで、関係者を巻き込むことでBAは重要な言葉の架け橋となります。そして、ビジネスニーズを効果的なテクニカルソリューションへと落とし込んでいきます。</p>
<h3><strong>ストーリー 1 - 聞く力：ビジネスアナリスト（BA）の役割</strong></h3>
<p>BAにとって欠かせないスキルのひとつが、聞く力です。ただ言葉を聞くだけでなく、その裏にある本当のニーズに耳を傾けることが大切です。あるプロジェクトで、とある金融会社の非効率的なレポート作成プロセスに取り組んだことがあります。世界中の事業部門から四半期ごとのレポートがメールで届き、その数は1日あたり100通以上。それが障害になっていました。届いたファイルは手作業でダウンロード、統合、エラーチェックされていました。時間がかかり、ミスも起こりやすい状態だったのです。</p>
<p>実際にその現場を見て、関係者と一緒に課題を洗い出していきました。IT部門と連携し、レポートを直接提出できる安全なLANディレクトリ構造を開発しました。バッチ処理やVBAスクリプトの自動化によって、データ確認、コピー、集計の作業がぐっと効率化されました。その結果、手作業の負担を約70%も削減することができ、より付加価値の高い分析に時間を使えるようになりました。この取り組みがうまくいったことで、部門全体でこの方法が採用されることになりました。</p>
<h3><strong>ストーリー 2 - 関係者を見落とした教訓</strong></h3>
<p>その後、とあるSaaS製品が、BAやユーザーの十分なレビューを経ることなく導入されました。コスト削減やダッシュボード機能が評価された結果の選定でしたが、既存の自動化ワークフローには対応できず、チームは手作業に逆戻りすることに…。</p>
<p>不満の声が上がりましたが、決定は覆らず、ユーザーの間には落胆と不満が残ってしまいました。この出来事からはっきりしたのは、ユーザーの本当のニーズに合ったソリューションを提供するには、初期段階からBAの関与が欠かせないということです。</p>
<h3><strong>物語は続きます…</strong></h3>
<p>ストーリー2は少し残念な展開に見えるかもしれませんが、とても大事な教訓を教えてくれます。それは、プロセスを変えるにせよ、新しい製品をつくるにせよ、<strong>成功のカギはエンドユーザーの声に耳を傾け</strong>、真のニーズを理解することにある、ということです。ここで、KINTOテクノロジーズのグローバルプロジェクトにおいて、BAが果たす重要な役割についてご紹介します。前にも少し触れましたが、グローバルチームは現在、ラテンアメリカのKINTO各部門と連携し、保険などの付加サービスを備えた自動車リース事業 KINTO ONE に取り組んでいます。</p>
<p>グローバルな製品やシステムを開発するということは、国や言語を超えて多様な仕様に対応することです。課題となるのは、各国の異なるニーズに柔軟に対応できるシステムを、どう構築するかという点です。では、どうアプローチすればよいのでしょうか?
各国のビジネスユーザーは、自分たちのプロセスについてはプロですが、そのプロセスは国によって変わります。一方エンジニアは、それぞれの違いを踏まえたうえで、柔軟性のあるシステムを構築するという難題に向き合っています。そこで登場するのが、BAです。BAはビジネスユーザーとエンジニアの間に立ち、両者の懸け橋となるのです。</p>
<p>BAとして私たちは、以下のような体系的なアプローチを取っています。</p>
<ol>
<li>ビジネスプロセスの収集：各国特有の業務プロセスを丁寧に文書化していきます。<ul>
<li>BAとして、このステップは非常に重要です。というのも、BAは基本的にビジネスプロセスの専門家として、すべての工程を詳細まで完全に理解し、どんなステップも見逃さないようにする必要があるからです。このステップを達成するために、KINTOテクノロジーズのBAたちは実際にラテンアメリカの現地企業を訪問し、現場に赴きました。こうした現場の観察は、各プロセスのニュアンスを掴むために欠かせません。</li>
<li>さらに、このステップで重要なのは、企業が現在どんな懸念材料を抱えているのかを理解することです。なぜでしょう？それは、KINTOテクノロジーズが開発するシステムは、こうした懸念材料を解決し、ビジネスユーザーの日常業務に具体的な価値を付加することが求められているからです。この懸念材料にうまく対処できれば、ビジネスユーザーの手作業が減り、生産性と業務効率を上げられます。その効果は、作業時間の短縮、リードタイムの短縮、ユーザーの働きやすさの向上といった形で評価することができます。</li>
</ul>
</li>
<li>ギャップ分析の実施：この分析では、各国におけるプロセスの違いや共通点が浮き彫りになります。
 BAとして、このステップでは特に重要なポイントが2つあります。<ul>
<li>作業手順の順序：各国で作業手順の順序が似ていれば、それはかなりの朗報です！でも、もし一方の国だけ順序が逆だった場合、両方に対応できるシステムを設計するのは、はるかに複雑になります。たとえば、車両の修理を行う場合：ケース1では、まず修理がKINTOサービスとして承認されて、その後実際の修理作業が始まります。一方ケース2では、先に修理作業が行われます。そしてその後で、そのサービスがKINTOサービスとして含まれているかどうかが確認されます。このように手順に違いがあると、システム設計の難易度が上がるのです。</li>
<li>外部システムとの統合：このステップで欠かせない、もう1つの重要なポイントがあります。それは、現在さまざまな国で使われている外部システムやプラットフォームに目を向けることです。たとえば、ある国では会計システムにSAPを使用していて、別の国では異なるタイプの会計システムを使用しているかもしれません。こういった違いが、グローバル製品に必要な統合を複雑にしているのです。</li>
</ul>
</li>
<li>ソリューションの共同作業：ギャップ分析を使用して、BAはエンジニアとタッグを組みながら、関連のある国すべての要件に対応できる柔軟な設計を進めていきます。<ul>
<li>このステップでは、エンジニアの力を借りて複雑なパズルを解いてくような感じです。BAにとってこのステップはかなり重要です。なぜかというと、BAは基本的に両国のビジネスユーザーを代表する立場なので、どちらのニーズにも応えられるシステムを設計する必要があるからです。例え話ですが、ある国のニーズはセダンで、もう一方のニーズはバンだとします。ここでBAはエンジニアと協力して「じゃあSUVを開発しようか？」となるような感じです（笑）。このステップでは、プロセスの一つひとつを細かく検討し、すべての工程が両国のニーズにちゃんと対応できるかを確認していきます。そうして、すべてのステップがうまくつながり、まとまりのある柔軟なシステムとして作成できることを確認します。こういった複雑な課題を解決していくのって、とても楽しいんですよ！</li>
</ul>
</li>
</ol>
<p>結果として共同努力が生まれ、グローバル製品はビジネスニーズに応えつつ、システムの柔軟性とのバランスもしっかり取れるようになります。グローバルプロジェクトが成功する上で、BAの役割がいかに重要であるかを物語っていますね。</p>
<h4><strong>ビジネスアナリスト（BA）の力とは？</strong></h4>
<p>BA になるとは、どういうことでしょうか?それは、問題を解決する人にも、コミュニケーターにも、戦略家にもなるということです。つまり、疑問を投げかける好奇心、耳を傾ける忍耐力、そして粘り強く答えを探る力を持つということです。最終的には、組織の運営方法、人の働き方、意思決定の方法に、本当の変化をもたらすことがBAの役割です。</p>
<p>BAは目立つ存在ではないかもしれません。でも、私たちの仕事は成果に影響を与え、リソースの無駄を減らし、誰かの仕事をちょっとラクにしているんです。アイデアを現実に変え、ニーズを形に変える。その架け橋となっているのが、BAです。</p>
<p>ひとつ、覚えておいていただきたいことがあります。それは、「聞く力」をあなどってはいけない、ということです。BAでも、ステークホルダーでも、何かを変えたいと尽力している人でも。本当の前進は、聞くこと、言葉の橋渡し、そしてつなぐことから生まれます。</p>
<p>読んでくださって、ありがとうございました！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/DL/2024-12-21_cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Android Composeオブジェクト指向ナビゲーション]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-16-Android-Compose-Object-Oriented-Navigation-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-16-Android-Compose-Object-Oriented-Navigation-ja/</guid>
            <pubDate>Mon, 26 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[目的地とナビゲーションコードを処理する方法]]></description>
            <content:encoded><![CDATA[<h1>前提条件と範囲</h1>
<p>本記事では、Android Composeのナビゲーションを、オブジェクト指向の観点から処理する方法について説明します。基盤となる技術をカプセル化し、開発の利便性を高め、特定の技術へのロックインを防止する機能を開発する方法を網羅します。</p>
<p>これにより、Android Composeアプリ向けの高品質なナビゲーションコードが書けるだけでなく、オブジェクト指向開発の考え方も身につけられます。</p>
<ol>
<li>本記事では、Dagger/Hiltを使用したMVVMアーキテクチャを前提としています。</li>
<li>ここでは、ナビゲーションルート→ViewModel→UIのパスのみを通じてデータを提供し、ナビゲーションルート→UIの直接的なデータ提供は行いません。 </li>
<li>Android Navigationバックスタックに関する事前知識が必要となります。 </li>
<li>ナビゲーションボタンの素早い連続クリックによるUXの問題などは考慮していません。</li>
<li>ディープリンクには対応していません。</li>
<li>Android OSレベルでサポートされているバックジェスチャの取り扱いについては説明していません。</li>
<li>厳密な引数の検証やパフォーマンスの最適化については触れていません。</li>
<li>本記事で使用している用語や名称は便宜的なものであり、一般的な技術用語や学術的な表現とは異なる場合があります。</li>
</ol>
<p>この記事は、<a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTOテクノロジーズ・アドベントカレンダー2024</a>の16日目の記事です🎅🎄</p>
<h1>ナビゲーションタイプ</h1>
<h2>詳細</h2>
<p>詳細情報を提供する画面へ移動する際は、一般的にリスト画面から特定の項目画面へ遷移します。例えば、ニュースフィードからニュース詳細、メニューからメニュー項目への移動などが該当します。各ナビゲーションアクションによって、バックスタックが1つ増えます。前の画面に戻るには、バックスタックの一番上(<code>pop</code>)を削除します。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/navigation-detail.png" alt="navigation-detail"></p>
<p><code>navController.navigate(&quot;sub3_1&quot;)</code>のように、route stringを指定して <code>NavController.navigate </code>functionを呼び出せます。</p>
<pre><code class="language-kotlin">@Composable
fun NavigationGraph() {
    val navController = rememberNavController()
    NavHost(navController, &quot;splash&quot;) {
        composable(&quot;main3&quot;) {
            val viewModel: Main3ViewModel = hiltViewModel()
            Main3Screen(
                id = viewModel.id,
                navMain1 = { /* ... */ },
                navMain2 = { /* ... */ },
                navMain3 = { /* ... */ },
                navSub31 = { navController.navigate(&quot;sub3_1&quot;) },
                navSub32 = { navController.navigate(&quot;sub3_2&quot;) },
                navSub33 = { navController.navigate(&quot;sub3_3&quot;) }
            )
        }
    }
}
</code></pre>
<h2>切り替え</h2>
<p>このタイプのナビゲーションでは、ユーザーは別の画面に移動するのではなく、同じ画面内でコンテンツが変化したと認識します。通常、タブや下部のナビゲーションバーで使用されます。</p>
<p>下部ナビゲーションバーの場合、バックスタックの高さは変わりません。バックスタックの一番下は、 必ず<code>Main#1</code>、<code>Main#2</code>、または<code>Main#3</code>のいずれかである必要があります。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/navigation-switching.png" alt="navigation-switching"></p>
<p>バックスタックから自身を削除するには、<code>popUpTo</code> の呼び出しが必要で、必要に応じて UI 状態の保存と復元が求められることがあります。</p>
<pre><code class="language-kotlin">@Composable
fun NavigationGraph() {
    val navController = rememberNavController()
    NavHost(navController, &quot;splash&quot;) {
        composable(&quot;main3&quot;) {
            val viewModel: Main3ViewModel = hiltViewModel()
            Main3Screen(
                id = viewModel.id,
                navMain1 = {
                    navController.navigate(&quot;main1&quot;) {
                        popUpTo(&quot;main1&quot;) {
                            inclusive = true
                            saveState = true
                        }
                        launchSingleTop = true
                        restoreState = true
                    }
                },
                navMain2 = {
                    navController.navigate(&quot;main2&quot;) {
                        popUpTo(&quot;main2&quot;) {
                            inclusive = true
                            saveState = true
                        }
                        launchSingleTop = true
                        restoreState = true
                    }
                },
                navMain3 = {
                    navController.navigate(&quot;main3&quot;) {
                        popUpTo(&quot;main3&quot;) {
                            inclusive = true
                            saveState = true
                        }
                        launchSingleTop = true
                        restoreState = true
                    }
                },
                navSub31 = { /* ... */ },
                navSub32 = { /* ... */ },
                navSub33 = { /* ... */ }
            )
        }
    }
}
</code></pre>
<h2>一方向</h2>
<p>このタイプのナビゲーションでは、前の画面に戻れない画面へ遷移します。バックスタックから自身を削除し(<code>pop</code>)、遷移先の画面を追加します (<code>push</code>)。例えば、フォームを送信した後やスプラッシュ画面から移動した後に、元の画面へ戻れないケースなどが該当します。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/navigation-oneway.png" alt="navigation-oneway"></p>
<p>戻れないようにするだけなら<code>popBackStack</code> で対応できますが、 必要に応じて<code>popUpTo</code> を使用し、バックスタックから複数の画面を削除することもできます。</p>
<pre><code class="language-kotlin">@Composable
fun NavigationGraph() {
    val navController = rememberNavController()
    NavHost(navController, &quot;splash&quot;) {
        composable(&quot;splash&quot;) {
            val viewModel: SplashViewModel = hiltViewModel()
            SplashScreen(
                timeout = viewModel.timeout,
                navMain1 = {
                    navController.popBackStack()
                    navController.navigate(&quot;main1&quot;)
                }
            )
        }

        composable(&quot;transactional3&quot;) {
            val viewModel: Transactional3ViewModel = hiltViewModel()
            Transactional3Screen(
                onClickSave = { /* ... */ },
                onClickSubmit = {
                    viewModel.onClickSubmit {
                        navController.navigate(&quot;transactional1&quot;) {
                            popUpTo(&quot;sub1&quot;) {
                                inclusive = true
                            }
                        }
                    }
                }
            )
        }
    }
}
</code></pre>
<h2>トランザクション（分割）</h2>
<p>このタイプのナビゲーションでは、非常に複雑または長い単一の画面を、複数のステップに分割します。伝える情報が多い場合や、ユーザーにアクションを求める場合に、ストレスを軽減してUXを向上させるために使用されます。ユーザーは途中でフローを終了できますが、再び入るときは最初から開始する必要があります。タスクを完了したり、フローから離れたりすると、フロー全体がバックスタックから削除されます。このアプローチは、フォームを含む複雑なUIの途中放棄を防ぐために、一方向ナビゲーションと組み合わせることができます。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/navigation-transactional.png" alt="navigation-transactional"></p>
<p>フロー内では前後に自由に移動できますが、フローを終了するときは<code>popUpTo</code> を使ってバックスタックからフロー内のすべての画面を削除する必要があります。</p>
<pre><code class="language-kotlin">@Composable
fun NavigationGraph() {
    val navController = rememberNavController()
    NavHost(navController, &quot;splash&quot;) {
        composable(
            route = &quot;transactional1&quot;,
            arguments = listOf(
                navArgument(&quot;draft&quot;) {
                    type = NavType.IntType
                }
            )
        ) {
            val viewModel: Transactional1ViewModel = hiltViewModel()
            Transactional1Screen(
                id = viewModel.id,
                onClickBack = {
                    viewModel.onClickBack {
                        navController.popBackStack()
                    }
                },
                onClickSave = {
                    viewModel.onClickSave {
                        navController.navigate(&quot;sub2&quot;) {
                            popUpTo(&quot;transactional1&quot;) {
                                inclusive = true
                            }
                        }
                    }
                },
                onClickNext = {
                    navController.navigate(&quot;transactional2&quot;)
                }
            )
        }
    }
}
</code></pre>
<h1>ナビゲーション管理</h1>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/navigation-oo-navigation-sample.png" alt="navigation-oo-navigation-sample.png"></p>
<p><code>Sub#1</code> 画面と、その関連する遷移（赤で強調）を使用して、オブジェクト指向の方法でナビゲーションを管理する方法を説明します。オブジェクト指向の要素を段階的に導入していきます。</p>
<p><code>Sub#1</code>画面には、次の条件があります。</p>
<ol>
<li><code>Sub#1</code>画面を開くには、<code>draft</code>パラメータ、または<code>param1</code>、<code>param2</code>、<code>param3</code>、<code>Param4</code>パラメータの組み合わせが必要です。</li>
<li><code>Sub #1</code>画面から<code>Sub #2</code>画面に移動するには、長時間実行される<code>save</code> 関数の完了を待つ必要があります。</li>
<li><code>Sub #1</code>画面から<code>Transactional #1</code>画面に移動するには、長時間実行される<code>start</code> 関数の完了を待つ必要があります。</li>
</ol>
<p><code>ViewModel </code>はナビゲーショングラフやUIから独立している必要があります。ナビゲーションルートから直接受け取った引数を使用するか、それを元にデータを取得し、UI に必要なプロパティを設定する必要があります。</p>
<pre><code class="language-Kotlin">@HiltViewModel
class Sub1ViewModel @Inject constructor(
    handle: SavedStateHandle,
    private val draftModel:DraftModel
) :ViewModel() {
    val id:UUID = UUID.randomUUID()

    val draft: Draft? = handle.get&lt;Int?&gt;(&quot;draft&quot;)?.let {
        runBlocking {
            if (null != draft &amp;&amp; 0 &lt; draft) {
                draftModel.get(draft)!!.also {
                    param1 = it.param1
                    param2 = it.param2
                    param3 = it.param3
                    param4 = it.param4
                }
            }
        }
    }

    var param1: String? = handle[&quot;param1&quot;]
        private set
    var param2: String? = handle[&quot;param2&quot;]
        private set
    var param3: String? = handle[&quot;param3&quot;]
        private set
    var param4: String? = handle[&quot;param4&quot;]
        private set

    fun onClickSave(callback: () -&gt; Unit) {
        viewModelScope.launch {
            // Long save task.
            delay(2_000)
            callback()
        }
    }

    fun onClickStart(callback: () -&gt; Unit) {
        viewModelScope.launch {
            // Long start task.
            delay(2_000)
            callback()
        }
    }
}
</code></pre>
<h2>ガイドドキュメントの基本機能のみを使用する</h2>
<p>「<a href="https://developer.android.com/guide/navigation/design#compose-minimal">ナビゲーショングラフの最小例</a>」ガイドを基に記述されたナビゲーションでは、 NavigationGraph に次の役割があります。</p>
<ol>
<li>画面の登録 </li>
<li>画面ルートのパラメータリストの宣言と登録 </li>
<li>画面ナビゲーション用の<code>route</code>stringsの作成 </li>
<li><code>NavController.Navigate</code>のカプセル化 </li>
<li>画面のUIを実行し、カプセル化された<code>NavController.Navigate</code>ロジックを渡す</li>
</ol>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-guide.png" alt="ガイドドキュメントの基本機能のみを使用する"></p>
<pre><code class="language-Kotlin">/**
 * ガイドドキュメント形式のナビゲーショングラフ
 */
@Composable
fun NavigationGraph() {
    val navController = rememberNavController()
    NavHost(navController, &quot;splash&quot;) {
        composable(
            route = &quot;sub1?${
                listOf(
                    &quot;draft={draft}&quot;,
                    &quot;param1={param1}&quot;,
                    &quot;param2={param2}&quot;,
                    &quot;param3={param3}&quot;,
                    &quot;param4={param4}&quot;
                ).joinToString(&quot;&amp;&quot;)
            }&quot;,
            arguments = listOf(
                navArgument(&quot;draft&quot;) {
                    type = NavType.IntType
                    defaultValue = 0
                },
                navArgument(&quot;param1&quot;) {
                    type = NavType.StringType
                    nullable = true
                },
                navArgument(&quot;param2&quot;) {
                    type = NavType.StringType
                    nullable = true
                },
                navArgument(&quot;param3&quot;) {
                    type = NavType.StringType
                    nullable = true
                },
                navArgument(&quot;param4&quot;) {
                    type = NavType.StringType
                    nullable = true
                }
            )
        ) {
            Sub1Screen(
                navBack = navController::popBackStack,
                navSub2 = {
                    navController.navigate(&quot;sub2&quot;) {
                        popUpTo(&quot;sub1&quot;) {
                            inclusive = true
                        }
                    }
                },
                navTransactional1 = { draft -&gt;
                    if (null == draft) {
                        navController.navigate(&quot;transactional1&quot;)
                    } else {
                        navController.navigate(&quot;transactional1?draft=${draft.id}&quot;)
                    }
                }
            )
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">/**
 * ナビゲーション（カプセル化）、`ViewModel`（状態ホイスティング）、UI をつなぐ
 */
@Composable
fun Sub1Screen(
    viewModel: Sub1ViewModel = hiltViewModel(),
    navBack: () -&gt; Unit = {},
    navSub2: () -&gt; Unit = {},
    navTransactional1: (Draft?) -&gt; Unit = {}
) {
    Sub1Content(
        id = viewModel.id,
        param1 = viewModel.param1!!,
        param2 = viewModel.param2!!,
        param3 = viewModel.param3!!,
        param4 = viewModel.param4!!,
        onClickBack = navBack,
        onClickSave = {
            viewModel.onClickSave(callback = navSub2)
        },
        onClickStart = {
            viewModel.onClickStart(callback = { navTransactional1(viewModel.draft) })
        }
    )
}

/**
 * 引数の状態を表示するだけ
 */
@Composable
private fun Sub1Content(
    id: UUID,
    param1: String,
    param2: String,
    param3: String,
    param4: String,
    onClickBack: () -&gt; Unit = {},
    onClickSave: () -&gt; Unit = {},
    onClickStart: () -&gt; Unit = {}
) {
    IconButton(onClick = onClickBack) {
        Icon(Icons.AutoMirrored.Filled.ArrowBack, &quot;back&quot;)
    }

    // ...

    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        OutlinedButton(
            onClick = onClickSave,
            modifier = Modifier.padding(16.dp)
        ) {
            Text(&quot;SAVE&quot;, style = MaterialTheme.typography.labelLarge)
        }

        Button(
            onClick = onClickStart,
            modifier = Modifier.padding(16.dp)
        ) {
            Text(&quot;START&quot;, style = MaterialTheme.typography.labelLarge)
        }
    }
}
</code></pre>
<h3>進行中の課題</h3>
<ol>
<li><strong>長いナビゲーショングラフコード</strong>：<code>NavigationGraph</code>のコードは、画面数、各画面の引数、接続された画面、バックスタック操作が増えるにつれて長くなります。</li>
<li><strong>コード順と実行順</strong>の不一致:コードが記述される順序は、そのまま読み込まれる順序になりますが、実行順とは一致しません。この不一致により、開発者は読み取っているコードが現在のコンテキストに関連しているかどうかを常に判断する必要があり、認知的オーバーヘッドが増え、メンテナンスが難しくなります。</li>
<li><strong>UI関数のパラメータが多すぎる</strong>：UI関数(<code>Sub1Screen</code>)のパラメータ数は、接続される画面が増えるほど増加します。例えば、詳細な設定項目が多い設定画面では、それに応じてパラメータの数も増えてしまいます。</li>
</ol>
<h2><code>Navigator</code>の導入</h2>
<p>ナビゲーターを導入する目的は、UI 関数のパラメータ数を減らすことです。UI 関数のナビゲーション関連パラメータをナビゲーターオブジェクトにまとめます。</p>
<p>ナビゲータの役割：</p>
<ol>
<li><strong><code>Sub#1</code>から移動可能な画面をオブジェクトにグループ化</strong>:ナビゲーターオブジェクトは、<code>Sub#1</code>画面から遷移できる画面のナビゲーションロジックをカプセル化します。</li>
<li><strong>ナビゲーション<code>route </code> の作成</strong>：ナビゲーターオブジェクトが、ナビゲーションに必要な <code>route</code>stringsを生成します。</li>
<li><strong>バックスタックの操作</strong> :ナビゲーターオブジェクトは、画面の pop やpush などのバックスタック操作を管理し、適切なナビゲーションフローを維持します。</li>
</ol>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-introducing-navigator.png" alt="Introducing `Navigator`"></p>
<pre><code class="language-Kotlin">@Immutable
class Sub1Navigator(
    private val navController: NavController
) {
    fun back() {
        navController.popBackStack()
    }

    fun sub2() {
        navController.navigate(&quot;sub2&quot;) {
            popUpTo(&quot;sub1&quot;) {
                inclusive = true
            }
        }
    }

    fun transactional1(draft: Drift? = null) {
        if (null == draft) {
            navController.navigate(&quot;transactional1&quot;)
        } else {
            navController.navigate(&quot;transactional1?draft=${draft.id}&quot;)
        }
    }
}
</code></pre>
<p><code>Navigator</code>を導入し、ナビゲーションルートの作成とバックスタック操作コードを分離することで、 <code>NavigationGraph</code> がよりシンプルになりました。</p>
<pre><code class="language-Kotlin">composable(
    route = &quot;sub1?${
        listOf(
            &quot;draft={draft}&quot;,
            &quot;param1={param1}&quot;,
            &quot;param2={param2}&quot;,
            &quot;param3={param3}&quot;,
            &quot;param4={param4}&quot;
        ).joinToString(&quot;&amp;&quot;)
    }&quot;,
    arguments = listOf(
        navArgument(&quot;draft&quot;) {
            type = NavType.IntType
            defaultValue = 0
        },
        navArgument(&quot;param1&quot;) {
            type = NavType.StringType
            nullable = true
        },
        navArgument(&quot;param2&quot;) {
            type = NavType.StringType
            nullable = true
        },
        navArgument(&quot;param3&quot;) {
            type = NavType.StringType
            nullable = true
        },
        navArgument(&quot;param4&quot;) {
            type = NavType.StringType
            nullable = true
        }
    )
) {
    Sub1Screen(navigator = remember(navController) { Sub1Navigator(navController) })
}
</code></pre>
<p><code>Sub1Screen</code>では、ナビゲーション関連のパラメータを 1 つのオブジェクトにまとめることでパラメータ数が減り、オブジェクトメンバーとしてカプセル化することでコールコードがより明確になります。</p>
<pre><code class="language-Kotlin">@Composable
fun Sub1Screen(
    navigator:Sub1Navigator,
    viewModel:Sub1ViewModel = hiltViewModel()
) {
    Sub1Content(
        id = viewModel.id,
        param1 = viewModel.param1!!,
        param2 = viewModel.param2!!,
        param3 = viewModel.param3!!,
        param4 = viewModel.param4!!,
        onClickBack = navigator::back,
        onClickSave = {
            viewModel.onClickSave(callback = navigator::sub2)
        },
        onClickStart = {
            viewModel.onClickStart(callback = { navigator.transactional1(viewModel.draft) })
        }
    )
}
</code></pre>
<h3>進行中の課題</h3>
<p>UI 関数は簡素化されましたが、ナビゲーショングラフの複雑さは変わらず、開発者は次の役割のために継続的なコンテキスト切り替えを求められます。</p>
<ol>
<li>画面の登録</li>
<li>画面ルートのパラメータリストの宣言と登録</li>
</ol>
<h2>Navigator <code>コンパニオンオブジェクト</code></h2>
<p><code> NavigationGraph</code>の機能を簡素化し、ナビゲーション情報を一元化するために、Navigatorクラス内で<code>コンパニオンオブジェクト</code>を使用できます。このアプローチにより、routes と argumentsを1か所で定義できます。方法は次のとおりです。</p>
<ol>
<li>Navigator クラスの <code>コンパニオンオブジェクト内</code>でroutes と argumentsを定義する。</li>
<li>これらの定義を、<code>NavigationGraph</code>関数で使用する。</li>
</ol>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-navigator-companion-object.png" alt="Navigator + `companion object`"></p>
<pre><code class="language-Kotlin">@Immutable
class Sub1Navigator(
    private val navController: NavController
) {
    @Suppress(&quot;MemberVisibilityCanBePrivate&quot;)
    companion object {
        const val ARG_DRAFT = &quot;draft&quot;
        const val ARG_PARAM1 = &quot;param1&quot;
        const val ARG_PARAM2 = &quot;param2&quot;
        const val ARG_PARAM3 = &quot;param3&quot;
        const val ARG_PARAM4 = &quot;param4&quot;

        const val ROUTE = &quot;sub1?&quot; +
                &quot;$ARG_DRAFT={draft}&amp;&quot; +
                &quot;$ARG_PARAM1={param1}&amp;&quot; +
                &quot;$ARG_PARAM2={param2}&amp;&quot; +
                &quot;$ARG_PARAM3={param3}&amp;&quot; +
                &quot;$ARG_PARAM4={param4}&quot;

        val ARGUMENTS = listOf(
            navArgument(ARG_DRAFT) {
                type = NavType.LongType
                defaultValue = 0
            },
            navArgument(ARG_PARAM1) {
                type = NavType.StringType
                nullable = true
            },
            navArgument(ARG_PARAM2) {
                type = NavType.StringType
                nullable = true
            },
            navArgument(ARG_PARAM3) {
                type = NavType.StringType
                nullable = true
            },
            navArgument(ARG_PARAM4) {
                type = NavType.StringType
                nullable = true
            }
        )
    }
    // ...
}
</code></pre>
<p>ナビゲータークラスで <code> コンパニオンオブジェクト</code>を使用することで、ナビゲーショングラフを単純化し、画面登録と UI 関数の呼び出しに集中できるようになります。UI 関数自体は変更されません。</p>
<pre><code class="language-Kotlin">composable(
    route = Sub1Navigator.ROUTE,
    arguments = Sub1Navigator.ARGUMENTS
) {
    Sub1Screen(navigator = remember(navController) { Sub1Navigator(navController) })
}
</code></pre>
<h3>進行中の課題</h3>
<p><code>Sub#1</code>画面のみを考慮する場合、これで十分です。しかし、<code>Sub #1</code>画面に移動する必要がある<code>Main #1</code> や <code>Sub #2</code> のナビゲーターも含める場合、次のような対応が必要になります。</p>
<pre><code class="language-Kotlin">@Immutable
class Main1Navigator(
    private val navController: NavController
) {
    // ...

    fun sub1(item:Main1Item) {
        navController.navigate(&quot;sub1?param1=${item.param1}&amp;param2=${item.param2}&amp;param3=${item.param3}&amp;param4=${item.param4}&quot;)
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Immutable
class Sub2Navigator(
    private val navController: NavController
) {
    // ...

    fun sub1(draft: Draft) {
        navController.navigate(&quot;sub1?draft=${draft.id}&quot;) {
            popUpTo(Main2Navigator.ROUTE) {
                inclusive = true
            }
        }
    }
}
</code></pre>
<p><code> Sub1Navigator</code>は有効な<code>route</code> フォーマットを管理しますが、実際の<code>route</code> の作成は <code>Main1Navigato</code>と<code>Sub2Navigator</code> が処理するため、 <code>Sub#1</code> のroute管理が <code>Sub#1</code> に集中せず、ユーザー側に分散してしまうという一貫性のない状態になります。</p>
<h2>共有宛先の処理</h2>
<p>有効な<code>route</code>フォーマットと、有効な<code>route</code>値を構成するロジックを、一緒に管理するのが妥当です。各 <code>companion object</code>に <code>route</code> 作成ロジックを移動させることで、標準化できます。</p>
<ol>
<li><code>route</code> 自体とその組み立てをカプセル化する。</li>
<li>オブジェクトベースのナビゲーションを実装する。</li>
</ol>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-shared-destination-handling.png" alt="共有宛先の処理"></p>
<pre><code class="language-Kotlin">@Immutable
class Sub1Navigator(
    private val navController: NavController
) {
    companion object {
        // ...

        const val ARG_DRAFT = &quot;draft&quot;
        const val ARG_PARAM1 = &quot;param1&quot;
        const val ARG_PARAM2 = &quot;param2&quot;
        const val ARG_PARAM3 = &quot;param3&quot;
        const val ARG_PARAM4 = &quot;param4&quot;

        fun route(item:Main1Item) = &quot;sub1?$ARG_PARAM1=${item.param1}&amp;$ARG_PARAM2=${item.param2}&amp;$ARG_PARAM3=${item.param3}&amp;$ARG_PARAM4=${item.param4}&quot;

        fun route(draft:Draft) = &quot;sub1?draft=${draft.id}&quot;
    }

    // ...
}
</code></pre>
<pre><code class="language-Kotlin">@Immutable
class Main1Navigator(
    private val navController: NavController
) {
    // ...

    fun sub1(item: Main1Item) {
        navController.navigate(Sub1Navigator.route(item))
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Immutable
class Sub2Navigator(
    private val navController: NavController
) {
    // ...

    fun sub1(draft: Draft) {
        navController.navigate(Sub1Navigator.route(draft)) {
            popUpTo(Main2Navigator.ROUTE) {
                inclusive = true
            }
        }
    }
}
</code></pre>
<h3>進行中の課題</h3>
<p>画面を登録する際は、<code>route</code>、<code>arguments</code>、そしてナビゲーターインスタンスの作成を同じクラス内でまとめて管理することが重要です。もし誤りがあると、次のような論理エラーが発生する可能性があります。</p>
<ol>
<li><strong>不整合なルート定義</strong>：routeとargumentsが一貫して定義されていない場合、ナビゲーションが失敗したり、予期しない動作をする可能性があります。</li>
<li><strong>不正なナビゲーターインスタンス</strong>：異なる ナビゲーターインスタンスを使用すると、ナビゲーションロジックのエラーが発生し、アプリが誤った画面に遷移したり、ナビゲーションが機能しなくなったりする可能性があります。</li>
</ol>
<pre><code class="language-Kotlin">composable(
    route = Main1Navigator.ROUTE,
    arguments = Transactional1Navigator.ARGUMENTS
) {
    Sub1Screen(navigator = remember(navController) { Sub1Navigator(navController) })
}
</code></pre>
<h2>Navigator、 <code>コンパニオンオブジェクト</code>の抽象化、および宛先処理の標準化</h2>
<p>ナビゲーターとその <code>コンパニオンオブジェクト</code>を抽象化し、次のプロパティを定義できます</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-abstracting-navigator-companion-object-standardizing-destination-handling.png" alt="Navigator, `companion object` の抽象化、および宛先処理の標準化"></p>
<pre><code class="language-Kotlin">interface Navigator {
    val destination: Destination
}

interface Destination {
    val routePattern: String
    
    val arguments: List&lt;NamedNavArgument&gt;
    
    fun route(varargs arguments: Any?): String
}
</code></pre>
<p>ナビゲーターと <code>コンパニオンオブジェクト</code>がそれぞれ<code>Navigator</code>と<code>Destination</code>を実装している場合、ナビゲーショングラフの設定は次のように標準化されます。</p>
<pre><code class="language-Kotlin">@Immutable
class Sub1Navigator(
    private val navController: NavController
):Navigator {
  companion object:Destination {
      const val ARG_DRAFT = &quot;draft&quot;
      const val ARG_PARAM1 = &quot;param1&quot;
      const val ARG_PARAM2 = &quot;param2&quot;
      const val ARG_PARAM3 = &quot;param3&quot;
      const val ARG_PARAM4 = &quot;param4&quot;
      
      override val routePattern = &quot;sub1?$ARG_DRAFT={draft}&amp;$ARG_PARAM1={param1}&amp;$ARG_PARAM2={param2}&amp;$ARG_PARAM3={param3}&amp;$ARG_PARAM4={param4}&quot;
      
      override val arguments = listOf(
            navArgument(ARG_DRAFT) {
                type = NavType.LongType
                defaultValue = 0
            },
            navArgument(ARG_PARAM1) {
                type = NavType.StringType
                nullable = true
            },
            navArgument(ARG_PARAM2) {
                type = NavType.StringType
                nullable = true
            },
            navArgument(ARG_PARAM3) {
                type = NavType.StringType
                nullable = true
            },
            navArgument(ARG_PARAM4) {
                type = NavType.StringType
                nullable = true
            }
        )

      override fun route(varargs arguments: Any?): String = when {
          1 == arguments.size &amp;&amp; arguments[0] is Main1Item -&gt; route(arguments[0] as Main1Item)
          1 == arguments.size &amp;&amp; arguments[0] is Draft -&gt; route(arguments[0] as Draft)
          else -&gt; throw IllegalArgumentException(&quot;Invalid arguments : arguments=$arguments&quot;)
      }

      fun route(item: Main1Item) = &quot;sub1?$ARG_PARAM1=${item.param1}&amp;$ARG_PARAM2=${item.param2}&amp;$ARG_PARAM3=${item.param3}&amp;$ARG_PARAM4=${item.param4}&quot;

      fun route(draft: Draft) = &quot;sub1?draft=${draft.id}&quot;
  }

  override val destination = Companion
}
</code></pre>
<p><code> NavigationGraph</code>の役割は次のとおりです。　</p>
<ol>
<li>ナビゲーターインスタンスの作成</li>
<li>抽象化されたナビゲーションオブジェクトをUI関数と接続する。</li>
</ol>
<pre><code class="language-Kotlin">Main1Navigator(navController).let { navigator -&gt;
    composable(navigator.destination.routePattern, navigator.destination.arguments) {
        Main1Screen(navigator)
    }
}

Sub1Navigator(navController).let { navigator -&gt;
    composable(navigator.destination.routePattern, navigator.destination.arguments) {
        Sub1Screen(navigator)
    }
}

Sub2Navigator(navController).let { navigator -&gt;
    composable(navigator.destination.routePattern, navigator.destination.arguments) {
        Sub2Screen(navigator)
    }
}
</code></pre>
<h1>開発の生産性向上</h1>
<h2>グローバルナビゲーション</h2>
<p>Web ブラウザの起動、電話の発信、アプリ設定を開く、アプリの再起動、UI の再読み込みなどは、画面に関係なく必要になることがあります。これらの 共通機能 を、各画面ごとに個別実装するよりも、単一の実装を共有する方が効率的です。</p>
<pre><code class="language-Kotlin">@Immutable
class Sub1Navigator(
    private val navController: NavController
): Navigator {
    fun web(uri: Uri) { /* Indivisual impl */ }

    fun call(phoneNumber: String) { /* Indivisual impl */ }
}
</code></pre>
<pre><code class="language-Kotlin">@Immutable
class Sub31Navigator(
    private val navController: NavController
): Navigator {
    fun web(uri: Uri) { /* Indivisual impl */ }

    fun settings() { /* Indivisual impl */ }
}
</code></pre>
<h3>ソリューション</h3>
<p><code>Navigator</code> インターフェースで共通関数を宣言し、ユニバーサル関数を実装した上で、各スクリーンのナビゲーターに委任することで共通化を実現できます。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-global-navigation.png" alt="Global Navigation"></p>
<pre><code class="language-Kotlin">/**
 * 共通のナビゲーション関数を定義するインターフェース
 */
interface Navigator {
    val destination: Destination

    fun web(uri: Uri)

    fun call(phoneNumber: String)

    fun settings()

    fun reopen()

    fun restart()
}
</code></pre>
<pre><code class="language-Kotlin">/**
 * 共通のナビゲーション関数を実装する基底クラス
 */
open class BaseNavigator(
    private val activity: Activity,
    val navController: NavController
): Navigator {
    override fun web(uri: Uri) {
        activity.startActivity(Intent(ACTION_VIEW, uri))
    }

    // ...

    override fun reopen(){
        activity.finish()
        activity.startActivity(Intent(activity, activity::class.java))
    }

    override fun restart() {
        activity.startActivity(Intent(activity, activity::class.java))
        exitProcess(0)
    }
}
</code></pre>
<pre><code class="language-Kotlin">/**
 * 各画面のナビゲーターが共通のナビゲーション機能を委譲
 */
@Immutable
class Sub1Navigator(
    private val baseNavigator: BaseNavigator
): Navigator by baseNavigagor {
    fun sub2() {
        baseNavigator.navController.navigate(Sub2Navigator.route()) {
            popUpTo(routePattern) {
                inclusive = true
            }
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge()
        setContent {
            NavigationGraph(this@MainActivity)
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">/**
 * `NavController` を `BaseNavigator` インスタンスに置き換える
 *
 * @param activity ナビゲーショングラフを所有するアクティビティ
 */
@Composable
fun NavigationGraph(activity: Activity) {
    val baseNavigator = BaseNavigator(activity, rememberNavController())

    NavHost(baseNavigator.navController, SplahNavigator.routePattern) {
        Sub1Navigator(baseNavigator).let { navigator -&gt;
            // ...
        }
    }
}
</code></pre>
<h2>ナビゲーショングラフ設定ユーティリティ</h2>
<p>以下のような論理的エラーのリスクがあります。</p>
<pre><code class="language-Kotlin">Sub1Navigator(baseNavigator).let { navigator -&gt;
    composable(Main1Navigator.routePattern, Transactional1Navigator.arguments) {
        Sub1Screen(navigator)
    }
}
</code></pre>
<h3>ソリューション</h3>
<p>ナビゲーターインスタンスと画面登録を処理するユーティリティ関数を追加することで、</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-navigation-graph-configuration-utility.png" alt="ナビゲーショングラフ設定ユーティリティ"></p>
<pre><code class="language-kotlin">fun &lt;N :Navigator&gt; NavGraphBuilder.composable(
    navigator: N,
    content: @Composable AnimatedContentScope.(NavBackStackEntry, N) -&gt; Unit
) {
    composable(
        route = navigator.destination.routePattern,
        arguments = navigator.destination.arguments
    ) { backStackEntry -&gt;
        content(backStackEntry, navigator)
    }
}
</code></pre>
<p>ナビゲーショングラフの構成を単純化し、論理エラーの可能性を排除することができます。</p>
<pre><code class="language-kotlin">composable(Main1Navigator(baseNavigator)) { _, navigator -&gt;
    Main1Screen(navigator)
}

composable(Sub1Navigator(baseNavigator)) { backStackEntry, navigator -&gt;
    Sub1Screen(navigator)
}

composable(Sub2Navigator(baseNavigator)) { _, navigator -&gt;
    Sub2Screen(navigator)
}
</code></pre>
<h2><code>@Preview </code>のサポート</h2>
<p>静的リソースをバンドルで表示する画面では、引数として別のイベントハンドラを渡すのではなく、ナビゲーターのみを渡すことで ナビゲーションコードを記述できます。
例：</p>
<pre><code class="language-Kotlin">@Composable
fun StaticResourceListScreen(navigator: StaticResourceListNavigator) {
    Column {
        Button(onClick = navigator::static1) {
            Text(&quot;Static#1&quot;)
        }
        Button(onClick = navigator::static2) {
            Text(&quot;Static#2&quot;)
        }
        Button(onClick = navigator::static2) {
            Text(&quot;Static#2&quot;)
        }
    }
}
</code></pre>
<p><code>@Preview</code> コードでは、 Navigatorインスタンスごとに<code>BaseNavigator(PreviewActivity(), rememberNavController())</code> を繰り返し記述することになります。　プレビューの種類や数が増えると、プレビューの記述が面倒になります。そして、このオーバーヘッドが原因で開発者がプレビューをスキップしてしまうこともあります。</p>
<pre><code class="language-Kotlin">@Composable
@Preview(showSystemUi = true)
fun PreviewStaticResourceListScreen() {
    MaterialTheme {
        StaticResourceListScreen(StaticResourceListNavigator(BaseNavigator(PreviewActivity(), rememberNavController())))
    }
}

@Composable
@Preview(showSystemUi = true)
fun PreviewStatic1Screen() {
    MaterialTheme {
        Static1Screen(Static1Navigator(BaseNavigator(PreviewActivity(), rememberNavController())))
    }
}

@Composable
@Preview(showSystemUi = true)
fun PreviewStatic2Screen() {
    MaterialTheme {
        Static2Screen(Static2Navigator(BaseNavigator(PreviewActivity(), rememberNavController())))
    }
}

@Composable
@Preview(showSystemUi = true)
fun PreviewStatic3Screen() {
    MaterialTheme {
        Static3Screen(Static3Navigator(BaseNavigator(PreviewActivity(), rememberNavController())))
    }
}
</code></pre>
<h3>ソリューション</h3>
<p><code>BaseNavigator</code>をインスタンス化するユーティリティ関数を作成し、実際のアプリケーションとプレビューを個別に処理 できるように実装します。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-preview-support.png" alt="`@Preview` Support"></p>
<pre><code class="language-Kotlin">@Composable
fun baseNavigator(
    activity: Activity = if (LocalInspectionMode.current) {
        PreviewActivity()
    } else {
        LocalContext.current as Activity
    }
):BaseNavigator {
    val navHostController = rememberNavController()
    val base = remember(activity) {
        BaseNavigator(activity, navHostController)
    }
    return base
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
fun NavigationGraph(activity: Activity) {
    val baseNavigator = baseNavigator(activity)

    NavHost(navController, SplahNavigator.routePattern) {
        // ...
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
@Preview(showSystemUi = true)
fun PreviewStaticResourceListScreen() {
    MaterialTheme {
        StaticResourceListScreen(StaticResourceListNavigator(baseNavigator()))
    }
}

@Composable
@Preview(showSystemUi = true)
fun PreviewStatic1Screen() {
    MaterialTheme {
        Static1Screen(Static1Navigator(baseNavigator()))
    }
}

@Composable
@Preview(showSystemUi = true)
fun PreviewStatic2Screen() {
    MaterialTheme {
        Static2Screen(Static2Navigator(baseNavigator()))
    }
}

@Composable
@Preview(showSystemUi = true)
fun PreviewStatic3Screen() {
    MaterialTheme {
        Static3Screen(Static3Navigator(baseNavigator()))
    }
}
</code></pre>
<h2>カスタムスタート画面</h2>
<p>これまで使用していた <code>NavigationGraph</code>設定機能は、スタート画面の変更は可能ですが、使用できるナビゲーショングラフは 1 つのみ です。そのため、既存の機能の一部だけを活用した デモアプリケーションの開発はできません。</p>
<pre><code class="language-Kotlin">@AndroidEntryPoint
class DemoActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge()
        setContent {
            NavigationGraph(this@DemoActivity) // 固定されたスタート画面　
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge()
        setContent {
            NavigationGraph(this@MainActivity)
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
fun NavigationGraph(activity: Activity) {
    val baseNavigator = baseNavigator(activity)

    NavHost(navController, SplahNavigator.routePattern) {
        Sub1Navigator(baseNavigator).let { navigator -&gt;
            composable(navigator.destination.routePattern, navigator.destination.arguments) {
                Sub1Screen(navigator)
            }
        }
    }
}
</code></pre>
<h3>ソリューション</h3>
<p><code>BaseNavigator</code>に<code>destination </code>を実装し、アクティビティからホイストされた<code>BaseNavigator</code>インスタンスをナビゲーショングラフに渡します。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-custom-start-screen.png" alt="カスタムスタート画面"></p>
<pre><code class="language-Kotlin">open class BaseNavigator(
    private val activity: Activity,
    val navController: NavController,
    override val destination: Desination
):Navigator {
    // ...
}
</code></pre>
<pre><code class="language-Kotlin">/**
 * @param startDestination プレビュー時のみ使用可能なデフォルト値
 */
@Composable
fun baseNavigator(
    activity: Activity = if (LocalInspectionMode.current) {
        PreviewActivity()
    } else {
        LocalContext.current as Activity
    },
    startDestination: Destination = if(LocalInspectionMode.current || activity is PreviewActivity) {
	      object: Destination {
	          override val routePattern = &quot;preview&quot;
	          // ...
	      }
    } else {
        throw IllegalArgumentException (&quot;本番環境では `startDestination` を指定する必要があります。&quot;)
    }
): BaseNavigator {
    val navHostController = rememberNavController()
    val base = remember(activity) {
        BaseNavigator(activity, navHostController)
    }
    return base
}
</code></pre>
<pre><code class="language-Kotlin">@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge()
        setContent {
            NavigationGraph(baseNavigator(destination = SplashNavigator.Companion))
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
fun NavigationGraph(baseNavigator: BaseNavigator = baseNavigator()) {
    //追加の共通ナビゲーション機能を実装

    NavHost( // Encapsulate the `navigation-compose` dependency in activity(`MainActivity`) and UI(`UiRoot`).
        baseNavigator.navController,
        baseNavigator.destination.routePattern
    ) {
        // ...
    }
}
</code></pre>
<h2>DEMOアプリ開発サポート</h2>
<p>ナビゲーショングラフの設定機能(<code>NavigationGraph</code>)では、スタート画面の変更は可能ですが、使用できるナビゲーショングラフとスタート画面は 1 つのみです。そのため、既存の機能の一部だけを活用した デモアプリケーションの開発はできません。</p>
<pre><code class="language-Kotlin">@Composable
fun NavigationGraph(baseNavigator: BaseNavigator = rememberBaseNavigator()) {
    NavHost(
        navController = baeNavigator.navController,
        startDestination = baseNavigator.destination.routePattern
    ) {
        // ...
    }
}
</code></pre>
<h3>ソリューション</h3>
<p>ナビゲーショングラフ構成コードを、<code>navigation-compose</code>のコールから分離します。これにより、ナビゲーショングラフの構築、ナビゲーターと UI 機能の接続、共通ナビゲーション機能の分離 が可能になり、さらに 依存関係のカプセル化 も実現できます。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-demo-app-development-support.png" alt="DEMOアプリ開発サポート"></p>
<pre><code class="language-Kotlin">@Composable
fun NavigationGraph(baseNavigator: BaseNavigator = rememberBaseNavigator(), builder: NavGraphBuilder.() -&gt; Unit) {
    NavHost(
        navController = baeNavigator.navController,
        startDestination = baseNavigator.destination.routePattern,
        builder = builder
    )
}
</code></pre>
<h4>プロダクションアプリ</h4>
<pre><code class="language-Kotlin">@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge()
        setContent {
            UiRoot(baseNavigator(destination = SplashNavigator.Companion))
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
fun UiRoot(baseNavigator: BaseNavigator) {
    NavigationGraph(baseNavigator) {
        // ...
        composable(Sub1Navigator(baseNavigator)) { _, navigator -&gt;
	          Sub1Screen(navigator)
        }
        // ...
    }
}
</code></pre>
<h4>デモアプリ</h4>
<pre><code class="language-Kotlin">@AndroidEntryPoint
class DemoActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState:Bundle?) {
        super.onCreate(savedInstanceState)

        enableEdgeToEdge()
        setContent {
            DemoUiRoot(baseNavigator(destination = Transactional1Navigator.Companion))
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
fun DemoUiRoot(baseNavigator: BaseNavigator) {
    NavigationGraph(baseNavigator) {
        composable(Transactional1Navigator(baseNavigator)) { _, navigator -&gt;
	          Transactional1Screen(navigator)
        }
    }
}
</code></pre>
<h2>用語の統一</h2>
<p><a href="https://github.com/android/compose-samples/blob/60708b134fdc38038036beee7c6bd480e5ce937c/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt"> ガイドドキュメント</a>では、<code>screen</code> という用語を ナビゲーションターゲット を指すために使っています。しかし、多くの UI デザインシステム（例： <a href="https://atomicdesign.bradfrost.com">Atomic Design</a>、<a href="https://carbondesignsystem.com">Carbon Design System</a>、<a href="https://ant.design">Ant Design</a>、<a href="https://polaris.shopify.com">Shopify Polaris</a>など）では、<code>ページ</code>を使い、ナビゲーションターゲットに使用し、<code>screen</code> は物理デバイスのディスプレイに使用します。</p>
<p>さらに、ナビゲーショングラフに画面を登録するとき、関数名<code>composable </code>は<code>@Composable </code>関数が呼び出されたことを示すために使用されます。</p>
<p>開発者向けの専門用語を 開発以外の分野でも通じる表現に統一することで、混乱や意味の確認が不要になり、スムーズで素早いコミュニケーション ができるようになります。そのため、用語の統一が望ましいというわけです。</p>
<pre><code class="language-Kotlin">fun &lt;N : Navigator&gt; NavGraphBuilder.composable(
    navigator: N,
    content: @Composable AnimatedContentScope.(NavBackStackEntry, N) -&gt; Unit
) {
    composable(
        route = navigator.destination.routePattern,
        arguments = navigator.destination.arguments
    ) { entry -&gt;
        content(entry, navigator)
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
fun UiRoot(baseNavigator: BaseNavigator) {
    NavigationGraph(baseNavigator) {
        composable(Sub1Navigator(baseNavigator)) { _, navigator -&gt;
	          Sub1Screen(navigator)
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
fun Sub1Screen(navigator: Sub1Navigator, viewModel:Sub1ViewModel = hiltViewModel()) {
		// ...
}
</code></pre>
<h3>ソリューション</h3>
<p>設計システムとして<a href="https://atomicdesign.bradfrost.com">Atomic Design</a>を採用していると仮定し、ナビゲーションターゲットには<code>screen</code>ではなく<code>page</code>を使用する形で用語を統一します。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-shared-and-app.png" alt="用語の統一"></p>
<pre><code class="language-Kotlin">fun &lt;N : Navigator&gt; NavGraphBuilder.page(
    navigator: N,
    content: @Composable AnimatedContentScope.(NavBackStackEntry, N) -&gt; Unit
) {
    composable(
        route = navigator.destination.routePattern,
        arguments = navigator.destination.arguments
    ) { entry -&gt;
        content(entry, navigator)
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
fun UiRoot(baseNavigator: BaseNavigator) {
    NavigationGraph(baseNavigator) {
        page(Sub1Navigator(baseNavigator)) { _, navigator -&gt;
	          Sub1Page(navigator)
        }
    }
}
</code></pre>
<pre><code class="language-Kotlin">@Composable
fun Sub1Page(navigator: Sub1Navigator, viewModel: Sub1ViewModel = hiltViewModel()) {
		// ...
}
</code></pre>
<h1>結論</h1>
<p>最初は、ガイドドキュメントの基本機能だけを使っていたため、構造はシンプルでした。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-guide.png" alt="Object :Guide"></p>
<p>オブジェクト指向ナビゲーション設計を取り入れることで、特定のナビゲーション仕様に影響されることなく、それぞれが特殊機能を担当する複数のジェネリック型や機能を実現できるようになりました。</p>
<ul>
<li><code>Navigator</code>:UI に関連する グループナビゲーション を管理し、ナビゲーショングラフにどのように登録されるかを決めるプロパティを定義する。</li>
<li><code>Destination</code>:ナビゲーショングラフにページを登録するためのプロパティを定義する。</li>
<li><code>BaseNavigator</code>:<code>Application</code>、<code>Activity</code>、およびナビゲーショングラフの統合を定義し、共通・グローバルナビゲーションを実装する。</li>
<li><code>NavGraphBuilder.page</code>:ナビゲーショングラフと各ページを <code>Navigator</code>) を通じて接続する。</li>
<li><code>@Composable fun NavigationGraph</code>:<code>navigation-compose </code>をカプセル化し、共通のナビゲーション機能を実装することで、共通機能と具体的なナビゲーショングラフを分離する。</li>
<li><code>@Composable fun baseNavigator</code>:開発の利便性を向上させる。</li>
</ul>
<p>ジェネリックオブジェクトの構造は以下のとおりです。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-shared.png" alt="Shared Object"></p>
<p>汎用オブジェクトを使用してナビゲーショングラフを構築すると、どうしても複雑な構造になりがちです。しかし、すでに汎用的な機能が整備されている場合、アプリケーションレベルの開発では考慮すべき要素が減るため、開発期間を短縮できます。その結果、新しい画面を追加するときの平均的な開発コストが低下します。</p>
<p><img src="/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/object-shared-and-app.png" alt="Object :Shared &amp; App"></p>
<p>この設計アプローチが、 すべての分野、製品、開発環境、個人に必要というわけではありません。必要に応じて、優先度を下げたり、別の解決策を選択することが求められます。本記事で紹介しているオブジェクト指向設計あくまで一例です。ただ、ここで紹介した設計上の課題や方向性は、他のソフトウェア開発にも応用可能です。</p>
<h2>オブジェクト指向設計とは？</h2>
<p>オブジェクト指向設計は、必ずしもコードを短くし、シンプルな構造にするためのものではありません。コードは役割ごとに分割されている場合にのみ、見た目がシンプルになります。例えば、このガイドの手法に従っても、ナビゲーショングラフ全体を構築する機能と各画面の機能は別々に記述することになります。極端な話、すべての画面を<code>NavigationGraph</code>内に実装すること可能ですし、<code>NavigationGraph</code> をまったく使わずに、<code>MainActivity</code>では<code>setContent</code>を呼び出してすべての画面を構築することもできます。</p>
<p>オブジェクト指向設計は、コードの役割と、その機能を実現するために必要な責務を分析することから始まります。そこから何をグループ化し、どのようにタイプやファイルに分割すべきかを決めます。各コードは、<code>import</code> またはオブジェクト参照を通じて接続されています。最終的に、どの開発者がコードを管理し、それに関する調査や保守の責任を負うのかが決まります。</p>
<p>IT業界において、開発者は非常に重要な生産リソースです。そのため、コストと生産性は開発者がどのように時間を使うかに大きく左右されます。このことから、開発者の認知能力は貴重なリソースとなります。技術スキルが高く、製品への理解が深まり、作業履歴に関する知識が豊富であるほど、開発者の集中力が生み出す価値も高まります。現在見ているコードが今のタスクに必要かどうかを判断する認知オーバーヘッド は、最も価値の低い形で開発者の集中力（認知能力）を消費してしまいます。集中力が低下すると、生産性も落ちてしまいます。</p>
<p>オブジェクト指向設計を適用すると、必然的に構造が複雑になり、コードを正しく理解するために 深い技術的知識や過去の開発履歴に関する知識が必要になります。<strong>オブジェクト指向設計_は、表面レベルの機能を迅速かつ容易に開発できる環境を整えるために、事前に大規模な開発と学習を必要とする開発手法です。</strong></p>
<h2>オブジェクト指向設計のリスクとコスト</h2>
<p>オブジェクト指向設計は、大量の汎用機能と複雑な構造 を活用し、高い生産性を実現するための開発手法です。しかし、汎用的な機能や構造には限界があります。そして、これらの制限を超える変更は、いつ発生してもおかしくありません。開発者ではないプランナー、マネージャー、デザイナーは、暗黙の知識（ドメイン知識）を日常的に習得・活用しているわけではありません。そのため、一見シンプルに思える変更が、なぜ実際には難しく、時間がかかるのかを理解するのはほぼ不可能 です。開発者同士であっても、暗黙知を持っていない人は、それを理解するのが難しいと感じるでしょう。この問題は、個人が暗黙知を身につけることや、開発者が繰り返し説明して理解してもらうことで解決できるものではありません。代わりに、これは個人の問題ではなく、組織や企業全体での体系的かつ文化的な解決策が求められる課題です。</p>
<p>特に、企業や組織の生産性向上を目的としてオブジェクト指向設計を導入する場合、開発者同士や、開発者と他の職種との間で生じる暗黙知の違いによる衝突を管理することが重要になります。そして、そこにリスク（またはコスト）が発生します。もちろん、オブジェクト指向設計が適切に機能している場合は問題になりません。その前に、誰がオブジェクト指向設計を担当するのか、そして 組織や企業レベルで責任者をどのように決定するのかが大きな課題となります。もしこの判断を誤ると、組織や企業にとって最も貴重なリソースである「チームワーク」 が損なわれる可能性があります。</p>
<h1>参考</h1>
<ol>
<li><a href="https://developer.android.com/guide/navigation/design#compose-minimal">Design your navigation graph / Minimal example</a></li>
<li><a href="https://github.com/android/compose-samples/blob/60708b134fdc38038036beee7c6bd480e5ce937c/JetNews/app/src/main/java/com/example/jetnews/ui/home/HomeScreens.kt"><code>android/compose-samples</code> &gt; Jetnews &gt; <code>HomeScreens</code></a></li>
<li><a href="https://atomicdesign.bradfrost.com">Atomic Design</a></li>
<li><a href="https://carbondesignsystem.com">Carbon Design System</a></li>
<li><a href="https://ant.design">Ant Design</a></li>
<li><a href="https://polaris.shopify.com">Shopify Polaris</a></li>
</ol>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ChoiGaramoi/Android-Compose-Object-Oriented-Navigation/navigation_graph.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ハッカソンからビジネス資産へ:Toyota Community by KINTO]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-03-toyota-community-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-03-toyota-community-ja/</guid>
            <pubDate>Fri, 23 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[ハッカソンのアイデアからスタートした Toyota Community by KINTO プラットフォーム、どうやってビジネス資産へと成長していったのか。ユーザーにとっての価値とビジネスとしての目標、そのバランスをどう達成したのかを、見ていきましょう。]]></description>
            <content:encoded><![CDATA[<h1>👋 はじめに</h1>
<p>こんにちは！<a href="https://toyota-community.kinto-technologies.com/">Toyota Community by KINTO</a> プラットフォームのプロダクトデザイナーのMoji です。</p>
<p> 今回は、このプラットフォーム作成の裏話（コラボレーション、イノベーション、用途に合わせたデザインの道のり）についてご紹介できて嬉しいです。</p>
<p>私にとって、デザインは美しさを追求するだけのものではなく、課題を解決しながら、ユーザーのニーズとビジネスの目標をうまく調和させることだと思います。そういったわけで、Toyota Community by KINTOは私にとってとても刺激的なプロジェクトなのです。</p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/overview.png" alt="Toyota Community by KINTO"> <em>Toyota Community by KINTO のロゴと、プラットフォーム機能を示すモバイル インターフェースのモックアップ</em></p>
<h1>🚀 背景</h1>
<p>Toyota Community by KINTO は、<a href="https://blog.kinto-technologies.com/posts/Innovation-Days-03-ja/">ハッカソンで生まれたシンプルなアイデア</a>から始まり、今ではレスポンスに優れた Webアプリに進化しています。現在、 トヨタの顧客やトヨタ車愛好家たちをひとつのグローバルなプラットフォームでつなぐことを目指して、顧客エンゲージメントを高めるための拡張性のあるソリューションとして提案できるよう、準備を進めています。 </p>
<p>過去 2年間、このプロジェクトでは、単純に <strong>設計と開発</strong>に取り組んでいたのではありません。 <strong>社内のフィードバックに対応</strong>し、<strong>国や地域による市場の微妙な違い</strong>を理解し、<strong>事業目標とユーザーのニーズ</strong>の両方に沿った製品を継続的に改良する。このプロセスの連続でした。</p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/innovation-days-2022.png" alt=" 2022年 KINTO Global Innovation Days のハイライト"> <em>2022年 KINTO Global Innovation Days より。優勝したハッカソンチーム、チームコラボレーション、イノベーター・オブ・ザ・イヤー賞の盾の写真。</em></p>
<h1>💡 チャレンジ</h1>
<h3>分散構造とバリューチェーンのギャップ</h3>
<p>自動車業界では、分散型構造で運営されているブランドもあります。このアプローチでは、国や地域で異なる市場ニーズに柔軟かつ的確に対応できるという利点があります。一方で、<strong>グローバルなブランドとしての一貫性を保つことや、購入後の顧客とのつながりを効果的に維持する点</strong>では、課題が生じることもあります。 </p>
<p>ここで、集中型ハブを構築する絶好のチャンスが姿を現します。このハブを通じて、ブランドは貴重な顧客の本音をくみ取り、ポジティブかつ影響力のあるかたちでブランドイメージを強化することができます。</p>
<h2>競合分析</h2>
<p>オンラインコミュニティの現状をより深く理解するために、オンライン上にある従来のカーコミュニティや競合プラットフォームを、 <strong>包括的に分析</strong>しました。</p>
<h3>重要な気づき</h3>
<ul>
<li><p>🚗 トヨタ車愛好家の中でも、特に旧車、または復刻モデルの所有者たちは、 <strong>Toyota Nation</strong>（VerticalScope が運営）や <strong>Reddit</strong> といった外部プラットフォームにアクセスして、交流したり、フィードバックを共有したり、自分の愛車について語り合っています。 </p>
</li>
<li><p>🌐 多くのプラットフォームではすでにコミュニティスペース（フォーラム）が用意されていて、ユーザー同士がつながり、意見を交換し、互いに学び合っているのです。</p>
</li>
</ul>
<p>ですが、その中でひときわ目立ったあるギャップがありました。 </p>
<ul>
<li><p>車好きは、自分の愛車を披露することにとても強い情熱を持っています。</p>
<ul>
<li><p>にもかかわらず、車の所有者が自分専用のバーチャル・ガレージを作って、次のようなことを実現できる特別な場所がなかったのです。</p>
<ul>
<li>🛠️ 改造やカスタマイズの紹介</li>
<li>📸 車の写真の共有</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2>チャンスの特定</h2>
<p>次の3つの戦略的な統合で、私たちのプラットフォームは他のライバルと一線を画す存在になりました。単なる <strong>閉ざされたソーシャル空間</strong> ではなく、ユーザーが本物のコンテンツに触れたり、それについて語り合えるゲートウェイにもなりました。</p>
<h3>1.ミスター・ランクルとのつながり</h3>
<ul>
<li><p>TOYOTAの幅広いネットワークの一つとして、トヨタのランドクルーザーの元チーフエンジニアで、<strong>ミスター・ランクル</strong>としても知られる<a href="https://toyotatimes.jp/en/spotlights/landcruiser_70th/040.html">小鑓貞嘉氏</a>とつながる機会に恵まれました。</p>
<ul>
<li>このご縁を通じて、<strong>限定動画</strong>および専用の <strong>Ask Me Anything (AMA)</strong> チャンネルを特集することができました。ユーザーはこのチャンネルでミスター・ランクルと直接やり取りをしたり、質問を投げかけたりしながら、貴重な気づきを得ることができます。</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/mr-landcruiser.png" alt="ミスター・ランクルこと小鑓貞嘉さんとのコラボレーション"><em>ミスター・ランクルこと小鑓貞嘉さんとのコラボレーション。チームディスカッションや撮影風景など。</em></p>
<h3>2.トヨタイムズから信頼できる自動車ニュースを共有</h3>
<ul>
<li><p>さらに、信頼性の高い自動車ニュースにも価値があることから、トヨタのオウンドメディアとして知られている<a href="https://toyotatimes.jp/en/">トヨタイムズ</a>と交渉し、公式に掲載している記事を当社のWebアプリ上で共有することについて、許可をいただきました。</p>
<ul>
<li>これにより、ユーザーは <strong>信頼できるブランド 発のコンテンツ</strong> にアクセスできるだけでなく、自分自身のストーリーを共有したり、有意義なディスカッションに参加したりできるようになりました。</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/toyota-times-articles.png" alt=" Toyota Community by KINTO トヨタイムズの記事"> <em>Toyota Community by KINTO プラットフォーム、ランドクルーザーに関するトヨタイムズ記事スクリーンショット</em></p>
<h3>3.「ガレージ」機能のご紹介</h3>
<ul>
<li><p>車好きにとって、自分の愛車を披露したり、時間をかけて仕上げたカスタマイズや改造を共有したり、同じ情熱を持つ人たちとつながったりすることは、かけがえのない楽しみのひとつですよね。その気持ち、私たちもよく分かります。</p>
<ul>
<li><p>そこで、「ガレージ」機能を導入しました。車のオーナーが、より楽しく、自分らしく愛車を紹介できるようになっています。この機能では、次のようなことができます。</p>
<ul>
<li>愛車の写真を紹介</li>
<li>カスタマイズや改造の記録、印象的な出来事の記録</li>
<li>自分だけのストーリーを、魅力的なインターフェース上で共有</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/garage-example.png" alt="バーチャル ガレージ機能の例"> <em>Toyota Community by KINTOプラットフォーム。バーチャルガレージ内、ユーザーの1981年型ランドクルーザーFJ40ソフトトップのスクリーンショット。改造の詳細や思い出とともに紹介されています。</em></p>
<h1>🎨 ソリューションの設計</h1>
<h3>ディストリビューターのフィードバック</h3>
<p>実用最小限の製品 （MVP）の開発に入る前に、まずは主要な関係者から早期のフィードバックを得ることを目的として、モックアップとプロトタイプを設計しました。得られた意見をもとに何度も改良を重ねて、完成版のプロトタイプをいくつかのマーケットの地域ディストリビューターに提示し、さらなるフィードバックを募りました。 </p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/early-mockups.png" alt="Toyota Communityの企画案へのフィードバックの初期モックアップ"> <em>Toyota Community の初期モックアップ。上は、デザイン案に対するフィードバック募るために提案された機能。</em></p>
<p>受け取ったフィードバックを踏まえ、プラットフォームのデザインを新たなビジネス目標に沿う形に調整し、他と差別化できるポイントを強調しました。 </p>
<p>こうして、より多くの時間やリソースを投じる前に概念実証（PoC）を確保することを目標に、MVPの本格的な開発に着手することができたのです。</p>
<h1>🛠️ 第1段階 - 開発</h1>
<h3>実用最小限の製品（MVP）の作成</h3>
<p>限られたリソースの中で、ギャップを解消して既存のプラットフォームにはない価値を提供するMVPを設計・開発しました。このプロダクトは、2つの主要機能を軸に、2024年7月に誕生しました。</p>
<h4>💬 コミュニティセクション</h4>
<p>トヨタの文化について語り合えるディスカッションスペースです。ユーザーはスレッドを立てたり、絵文字でリアクションを送ったりしながら交流できます。</p>
<ul>
<li><p>ミスター・ランクル専用チャンネル：</p>
<ul>
<li>ミスター・ランクルのガレージ、これまでの体験、インタビューなどを特集した限定コンテンツです。</li>
<li>ミスター・ランクルに何でも質問できる専用チャンネルで、愛好家がミスターと直接交流することができます。</li>
</ul>
</li>
<li><p>トヨタニュースチャンネル：</p>
<ul>
<li>トヨタについて、信頼できる自動車ニュースを共有する専用チャンネル。</li>
</ul>
</li>
</ul>
<h4>🚘 バーチャルガレージ</h4>
<p>ユーザーが自分の愛車を写真やストーリーとともに紹介し、ほかのユーザーと共有できる専用スペースです。</p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/design-process.png" alt="デザインプロセスの概要"> <em>Figmaのデザイン画面、各デザイン段階とマイルストーンを示すタイムライン。</em></p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/mobile-view-screens.png" alt="モバイル ビュー画面"> <em>Toyota Community by KINTO プラットフォーム上のガレージ、ホーム、コミュニティの3セクションのモバイル画面</em></p>
<h2>ユーザーからのフィードバック収集</h2>
<p>製品にとって重要な転機となったのは、当社プロダクトオーナーが、 <a href="https://buschtaxi.org/buschtaxi-treffen/">ドイツで開催されたランドクルーザーのイベント </a>に参加したときでした。 </p>
<p>トヨタ車愛好家との直接の交流を通じて、非常に貴重な気づきを得ることができました。参加者からは、小鑓氏のようなトヨタの象徴的な人物と交流しながら、自分の車を披露できるスペースがあるというアイデアに対して、好評の声が寄せられました。</p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/event-photos.png" alt=" 2024年 Buschtaxi イベント ハイライト "> <em>ドイツで開催されたランドクルーザーイベント。改造車両、ランドクルーザーのオーナーや愛好家の集合写真。</em></p>
<p>このイベントの重要性を実感したことで、私たちはユーザーの記憶に残る、強い存在感というものを築こうと決めました。 </p>
<p>KINTOとトヨタ、両ブランドのアイデンティティに自然に調和するロゴとマーケティング資料を作成するため、関係者と緊密に連携しながら、デザインをリードしました。 </p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/TCbK_logo.png" alt="Toyota Community マーケティング資料"> <em>Toyota Community by KINTOのプロモーション資料のコレクション。バーチャルガレージとコミュニティのメリットを宣伝するチラシを掲載しています。</em></p>
<p>ユーザーからのフィードバックと、直前でのブランディング作業がうまくかみ合ったことで、 <strong>プロダクトの方向性</strong>と<strong>ビジュアル アイデンティティ</strong> の両方が洗練されて、道筋がぐっと明確になりました。</p>
<h1>🔄 第2段階- 改良</h1>
<h3>導入と拡張</h3>
<p>大規模なユーザーに向けてMVPをテストするだけのリソースは、まだ十分にありません。ですが、 <strong>ディストリビューターのフィードバックやランドクルーザーイベントで得られた気づきをもとに、プラットフォームの改善は今も着実に進んでいます</strong>。こうした改良が、次の開発段階への足がかりになっています。</p>
<p>Toyota Community by KINTO プラットフォームは、拡張性のあるソリューションとして位置付けられつつあり、断片化されたバリューチェーンに関する課題に応えられる可能性を示しながら、地域ごとの多様なマーケットにも柔軟に対応できる仕組みを備えています。</p>
<h3>この先にあるものは？</h3>
<p>私たちは今後も、プラットフォームのブラッシュアップを進めながら、さらなる検討に向けて価値をしっかりと伝えられる機会を探っていきます。</p>
<h1>✨ 結論</h1>
<h3>トヨタの取り組みが広がる未来へ</h3>
<p>Toyota Community by KINTO は、グローバルなエンゲージメントをひとつの集中型ハブに集約し、再びつなげていくチャンスを提供しています。</p>
<p>さらにリソースを得られれば、このプラットフォームは貴重な資産となり、顧客エンゲージメントを高めるだけでなく、より強力なブランドロイヤルティの機会を育んでいける可能性を秘めています。</p>
<p>このプロジェクトは、単に <strong>UX デザイン</strong> や <strong>機能の構築</strong> が目的ではありませんでした。ユーザー中心のデザインが持つ真の力。それは、ビジネスの目標にも、文化的なニーズにも<strong>適応</strong> していく力なのだということを実感しました。 </p>
<hr>
<p>この記事を楽しんでいただけましたら、ぜひ私の過去の記事もご覧ください。<a href="https://blog.kinto-technologies.com/posts/2023-12-21_Diversity-and-Inclusivity-in-UIUX-Design/">多様性とインクルーシブな視点の重要性</a>や、<a href="https://blog.kinto-technologies.com/posts/2023-08-15-Redesigning_TechBlog/">TechBlog の再設計プロセス</a> ついても詳しく書いています。 </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/moji/2024-12-toyota-community/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Appium Meetup Tokyo: First Event Report]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-20-Appium-Meetup-Tokyo-開催レポート-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-20-Appium-Meetup-Tokyo-開催レポート-en/</guid>
            <pubDate>Thu, 22 May 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>The very first Appium Meetup Tokyo was held on February 20, 2025, at Autify&#39;s Tokyo Office. Around 10 people attended in person on the day, with many more joining online.</p>
<h3>Opening and Icebreaker</h3>
<p>The event started with a lively and humorous opening talk by <a href="https://x.com/tsueeemura">tsueeemura</a>-san from Autify. To check the online connection and break the ice, he asked, &quot;What&#39;s everyone having for dinner tonight?&quot; An attendee responded, &quot;Hot pot tonight,&quot; which brought a warm and relaxed atmosphere.</p>
<h1>Autify&#39;s Appium Plugin Use Case</h1>
<p>The first speaker was <a href="https://x.com/rererorerero">rerorero</a>-san, who is in charge of mobile product development at Autify.</p>
<p>&quot;Autify NoCode Mobile&quot; provided by Autify is a cloud-based service that enables easy mobile app test automation without writing any code. Even without programming knowledge, you can record tests through an intuitive interface and have them automatically re-executed. The biggest advantage of this is that even developers and non-engineers can quickly set up a testing environment. Another key feature is the ability to use real devices and simulators in the cloud, eliminating the need for in-house equipment and significantly reducing capital investment.</p>
<p>However, there was a performance issue where the app would significantly slow down on screens with a large number of UI elements. Tap operations that normally took just a few seconds could take up to 40 seconds.</p>
<p>To tackle this problem, rerorero-san introduced &quot;<a href="https://github.com/facebook/idb">IDB (iOS Development Bridge)</a>&quot; developed by Facebook. IDB is an open-source CLI tool that allows fast interaction with iOS simulators and real devices by sending events directly to the Core Simulator Service, dramatically improving response speed. By integrating IDB as an Appium plugin and building a system that can be used directly without complex network settings between servers, Autify succeeded in reducing a 40-second operation to just 40 milliseconds, achieving roughly a 1,000x performance improvement.</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/autify-command.png" alt="Overview"></p>
<p><strong>Key Points from the presentation</strong></p>
<ul>
<li>How to adopt the Appium plugin and implementation examples using JavaScript</li>
<li>The technical mechanism behind IDB&#39;s fast tapping (sending events directly to the Core Simulator Service)</li>
<li>A practical demonstration of performance improvements</li>
</ul>
<p>The plugin can be implemented using the following code:</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/autify-code.png" alt="Appium Plugin"></p>
<h1>KINTO Technologies: Guidelines and Practices for Efficient App Test Automation</h1>
<p>Next, Oka-san and Pann Nu-san from KINTO Technologies presented their method for building an automated testing environment and the results they achieved.</p>
<p>At our company, the growing number of device and OS combinations had significantly increased the burden of manual testing. To address this, the QA and development teams began collaborating from the early stages of development and introduced a method for setting unified, dedicated test IDs. This greatly reduced the burden of modifying XPATH when changing layouts, and significantly improved test stability.</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/ktc-id.png" alt="ID"></p>
<p>In addition, we also built a system where test results are notified in real time via Slack, while managing detailed logs and test videos through Box. This creates an environment where all involved parties can easily check the test status.</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/ktc-report.png" alt="ID"></p>
<p><strong>Key Points from the presentation</strong></p>
<ul>
<li>Integrating automated testing awareness into the development process</li>
<li>Comparing maintenance load before and after introducing dedicated test IDs</li>
<li>How to efficiently manage test results using Slack and Box</li>
<li>Improving coding efficiency using Github Copilot</li>
</ul>
<h3>Presentation Materials</h3>
<p><a href="https://speakerdeck.com/kintotechdev/xiao-lu-de-naapurizi-dong-hua-notamenogaidoraintoshi-jian-fang-fa">https://speakerdeck.com/kintotechdev/xiao-lu-de-naapurizi-dong-hua-notamenogaidoraintoshi-jian-fang-fa</a></p>
<h1>📌 Current Status of E2E Testing and Topics of Interest from the Participant Survey</h1>
<p>This time, we conducted a survey of everyone who attended the Meetup. Here are some interesting trends that emerged from the results.</p>
<h3>(1) Participants by Occupation</h3>
<p>More than half of the participants (54.1%) were QA engineers, but we also had participants from a variety of professions, including SET/SDET, web and mobile application engineers.</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/appium-role.png" alt=" Occupation ratio"></p>
<h3>(2) Experience with Appium</h3>
<p>More than half (55.7%) of respondents said they had never used Appium, suggesting that many participants were either new to the tool or currently considering adoption. On the other hand, there were also participants with over a year of experience, showing a wide range of maturity in Appium usage.</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/appium-experience.png" alt="Experience with Appium"></p>
<h3>(3) Practical Experience with E2E Testing</h3>
<p>In terms of E2E testing in general, over half of the respondents were relatively experienced, between 1 and 3 years (27.9%) or more than 5 years (24.6%), confirming that E2E Testing is already being widely used in practice.</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/appium-e2e-experience.png" alt="Practical experience with E2E testing"></p>
<h3>(4) Topics of Greatest Interest</h3>
<p>Participants expressed strong interest in the following topics:</p>
<ul>
<li>Case studies and results from adopting Appium for testing</li>
<li>Appium usage scenarios, tips, and challenges</li>
<li>Integration with CI/CD pipelines and cross-platform (React Native, Flutter, etc.) support</li>
</ul>
<p>Based on these survey results, we&#39;d like to continue to share information that meets your interests and needs in future events.</p>
<h1>Networking and Future Outlook</h1>
<p>After the session, participants gathered around enjoyed some pizza for a lively networking session that sparked new ideas and collaborations. Appium Meetup Tokyo will be held regularly, and we&#39;re actively looking for speakers and new community members. We&#39;d love to have you join us again next time!</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/appium-meetup-onsite.jpg" alt="On-site"></p>
<h1>For Those Considering Participating</h1>
<ul>
<li><strong>Those who want to fully implement automated testing for mobile apps</strong>  </li>
<li><strong>Those who are interested in Appium and seeking specific examples or know-how</strong>  </li>
<li><strong>Engineers and QA personnel interested in CI/CD-based operations</strong>  </li>
<li><strong>Those who want to improve their testing culture by learning from other companies&#39; experiences</strong></li>
</ul>
<p>If any of the above applies to you, we&#39;d love to have you join us at Appium Meetup Tokyo to exchange the latest insights. Future announcements and detailed information will be provided by <a href="https://x.com/AutifyJapan">@AutifyJapan</a> and <a href="https://x.com/KintoTech_Dev">@KintoTech_Dev</a>. If you have any questions or suggestions, feel free to reach out.</p>
<p>We look forward to seeing you at the next &quot;Appium Meetup Tokyo #2&quot;!</p>
<h1>Archive Distribution</h1>
<p><a href="https://www.youtube.com/watch?v=zV4WbClGquE">https://www.youtube.com/watch?v=zV4WbClGquE</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: October and November 2024 Update]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-26-newcomers-introduction-oct-nov-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-26-newcomers-introduction-oct-nov-en/</guid>
            <pubDate>Wed, 21 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Introducing the new members who joined KINTO Technologies in October and November 2024.]]></description>
            <content:encoded><![CDATA[<h1>Hello</h1>
<p>Hello, I&#39;m Tanachu, a new member who joined the company in October 2024! In this article, we have summarized the impressions of those who joined the company in October and November 2024 immediately after joining the company. I hope this content will be useful for those who are interested in KINTO Technologies, and serve as a reflection for the members who participated in the interview!</p>
<h1>H.I</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-H_I.png" alt="H.I"></p>
<ul>
<li><strong>Self-introduction</strong>  <ul>
<li>Nice to meet you! I&#39;m H.I. and I&#39;ve recently been assigned to the Mobile App Development Group.</li>
<li>I&#39;ve mainly been involved in Android development, but I&#39;m looking forward to growing and contributing together with you all in this new environment.</li>
<li>I&#39;m particularly interested not only in development but also in data analysis and service growth, and I hope to contribute as a member of the team. There&#39;s still a lot for me to learn, but I&#39;ll do my best. I&#39;d really appreciate your continued support along the way!</li>
</ul>
</li>
<li><strong>How is your team structured?</strong>  <ul>
<li>Our team works in a Scrum framework, and we follow an agile development process. We plan, develop, and review using sprints, striving for continuous improvement in short cycles.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong>  <ul>
<li>Since KINTO Technologies is part of a large corporate group, I expected a more formal atmosphere. But once I joined, I was surprised by how flexible and open it actually is. There are also many study groups, so I&#39;m excited about what&#39;s ahead.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong>  <ul>
<li>Everyone on the team has strong technical skills and a high level of motivation. I think the atmosphere is one where you can easily ask questions or make suggestions.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong>  <ul>
<li>I&#39;d like to write as much as possible. I want to share what I have learned and my experiences through writing.</li>
</ul>
</li>
<li><strong>Questions from Horichan to H.I.</strong>  <ul>
<li><strong>Do you have any favorite events within the company (either official or unofficial)?</strong>  <ul>
<li>There are a lot of study sessions and opportunities to attend external events. People often share insights about things I didn&#39;t know or always wanted to learn, so I think they&#39;ll become a great source of my growth in the future.</li>
</ul>
</li>
<li><strong>Tell us about your hometown!</strong>  <ul>
<li>I&#39;m from an island near Seoul, South Korea. It is rich in nature and known for eel and ginseng production. There are many historical sites such as temples, so it attracts a lot of tourists.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Tanachu</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-tanachu.png" alt="tanachu"></p>
<ul>
<li><strong>Self-introduction</strong>  <ul>
<li>My name is Tanachu and I joined in October. I am a member of the Cybersecurity Defense Team of the Security and Privacy Group.</li>
<li>In my previous role at a security vendor, I was involved in incident response and log analysis related to cybersecurity.</li>
<li>In my current team, I am mainly involved in security log monitoring using SIEM and building monitoring systems.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong>  <ul>
<li>Our group consists of nine members from four different countries, including Japan.</li>
<li>We&#39;re split into four teams handling incident response related to security and privacy, security guideline assessments, internal cybersecurity frameworks, vulnerability assessments, and SOC.</li>
<li>My team has four members, mainly working on vulnerability assessments and SOC.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong>  <ul>
<li>I had heard that KINTO Technologies had people from various countries, but I was still a bit surprised to find that I was the only Japanese on my team.</li>
<li>The company is promoting the use of generative AI more strongly than I had expected, and being in an environment where we can easily use it in work was a positive surprise. Beyond just chatting with it, I&#39;m always looking for new ways to use it in my own work.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong>  <ul>
<li>We have a team lunch once a week with members who can join. It gives the impression that the team really values communication.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong>  <ul>
<li>It feels a bit strange to be featured in a blog that I used to read before joining the company.</li>
</ul>
</li>
<li><strong>Question from H.I to Tanachu</strong>  <ul>
<li><strong>Please tell us your own way of relaxing!</strong>  <ul>
<li>At the end of the day, I like to unwind by watching the TV Tokyo news program &quot;World Business Satellite.&quot;</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>lksx</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-lksx.png" alt="lksx"></p>
<ul>
<li><strong>Self-introduction</strong>  <ul>
<li>My name is lksx and I joined the company in November. I work as a backend engineer in the DX Development Group. In my previous job, I also worked as a backend engineer. Although I was nervous at first, I&#39;m learning a lot and enjoying my time here so far. Looking forward to working with you.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong>  <ul>
<li>Our development team currently has eight members. Seven of them work in the Muromachi office, while one joins remotely from the Osaka office. Since the team is spread across locations, we often collaborate online, which makes efficient communication especially important.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong>  <ul>
<li>KINTO Technologies gave me a strong impression of being a startup, and it was very refreshing to see multiple products being developed simultaneously. I was also impressed by several products in the POC stage, which made me feel that the company is willing to take on new challenges. So far, I haven&#39;t felt any major surprises, but it&#39;s definitely a fast-paced environment, and I&#39;m working on adjusting to it.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong>  <ul>
<li>Everyone on site is easy to talk to, and the atmosphere is relaxed. Even as a newcomer, I feel comfortable asking for help when I run into questions or issues.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong>  <ul>
<li>I haven&#39;t written a blog post before, so this being my first time, I&#39;m actually kind of excited.</li>
</ul>
</li>
<li><strong>Question from Tanachu to lksx</strong>  <ul>
<li><strong>What does your typical workday look like? (what do you do right after starting work, what kind of tasks do you mainly do in the afternoon?)</strong>  <ul>
<li>Since I&#39;ve just joined the company, there&#39;s still a lot to learn and study. So from 9:30 a.m., I spend about 30 minutes studying to build up my work skills. In the morning, I focus on tasks that require more concentration, and in the afternoon, I tend to work on tasks that are more physically engaging.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Kirby</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-kirby.png" alt="kirby"></p>
<ul>
<li><strong>Self-introduction</strong>  <ul>
<li>I am Kirby and I joined the company in October 2024.</li>
<li>After spending six years in Tokyo working at an IT company as a Director and Product Manager, I decided to return to my hometown of Osaka, hoping to make a living there while utilizing my skills. Following a role in planning and managing in-person events, I joined KINTO Technologies.</li>
<li>I&#39;m currently working at Osaka Tech Lab as part of the KINTO FACTORY development team in the New Service Development Group.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong>  <ul>
<li>The KINTO FACTORY development team at KINTO Technologies consists of one manager, two product managers, four frontend engineers, five backend engineers, and one QA member. We develop a website that provides car customization and upgrade services for car owners. At Osaka Tech Lab, development is being carried out by two members, including a backend engineer and myself.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong>  <ul>
<li>Since the teams are spread across Tokyo, Nagoya, and Osaka, I was a bit worried about how communication would work. However, KINTO and KINTO Technologies have a strong &quot;meet in person&quot; culture rooted in the Toyota Group, and we often have opportunities for face-to-face communication, even offline. So, that was a surprise in a good way. (I&#39;m actually writing this blog post from Nagoya!)</li>
<li>Osaka Tech Lab members actively engage in cross-project conversations, with strong horizontal connection that inspire many engineers to take on technical challenges.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong>  <ul>
<li>I am happy to be able to work with members who want to be involved in product and service development and who are passionate about making services better.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong>  <ul>
<li>I used to read this tech blog a lot before joining, so I&#39;m excited to finally be part of it!</li>
</ul>
</li>
<li><strong>Question from lksx to Kirby</strong>  <ul>
<li><strong>Is there anything you want to try at KINTO Technologies?</strong>  <ul>
<li>Of course, I want to contribute to the growth of the KINTO FACTORY service, but I&#39;d also love to take on the challenge of leading an entire project on my own at Osaka Tech Lab!</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Jun</h1>
<ul>
<li><strong>Self-introduction</strong>  <ul>
<li>My name is Jun and I joined the company in November 2024.</li>
<li>After gaining experience as a web engineer at a business company for nearly seven years, I decided to join KINTO Technologies in hopes of sharpening my skills in a more challenging environment.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong>  <ul>
<li>I belong to the backend team in the New Car Subscription Development Group, which has 13 members. There are always multiple projects ongoing in parallel within the team, and for each new project, a small group of about two to four members is selected to drive it forward.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong>  <ul>
<li>I was struck by how many engineers here are highly motivated to learn. In addition to study sessions within the team, company-wide study sessions were also held frequently, with many engineers take part enthusiastically. I used to think engineering was something you studied quietly on your own, so this was a surprise in a good way.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong>  <ul>
<li>Since everyone joined the company mid-career and brings diverse expertise, there&#39;s no rigid way of doing things. This makes our discussions around tech stacks and operational methods very dynamic. I&#39;m inspired every day by the people I work with.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong>  <ul>
<li>I’ve written tech blogs before, but it&#39;s been a while. Writing this made me want to start again.</li>
</ul>
</li>
<li><strong>Question from Kirby to Jun</strong>  <ul>
<li><strong>Do you have any go-to routines to refresh your mood?</strong>  <ul>
<li>I love coffee, so whenever I feel stuck, I take a break and brew a cup to refresh myself.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>H.I.</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-H_I_.png" alt="H.I."></p>
<ul>
<li><strong>Self-introduction</strong>  <ul>
<li>My name is HI and I joined the company in November.</li>
<li>I&#39;ve worked at several IT startups where I gained experience in marketing, development, and product management. I&#39;m currently working as PdM for KINTO Unlimited, at the Muromachi office in Tokyo.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong>  <ul>
<li>Our KINTO Unlimited team has three PdMs including myself and around 20 engineers.</li>
<li>We&#39;ve divided roles among the three PdMs. I focus on data analysis and marketing, one handles development, and the other is in charge of UI/UX. I think it&#39;s a great balance that allows each of us to play to our strengths.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong>  <ul>
<li>I was a little nervous because the average age here felt a bit higher compared to the companies I&#39;ve worked for in the past, but everyone is really kind so I can work with ease (really).</li>
<li>One thing that surprised me was how big the organization actually is, much bigger than I had expected. Coming from a startup venture company, it was definitely an adjustment to get used to the number of stakeholders and figuring out who does what.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong>  <ul>
<li>Compared to most companies with products, we have relatively abundant resources, so as long as we have ideas, there&#39;s a lot we can try.</li>
<li>I also get along well with people from other teams. Recently, near my desk we&#39;ve had Christmas cake together and even a mini pop-up coffee stand where members bring their beans and grinders.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong>  <ul>
<li>One day I&#39;d like to write something that isn&#39;t a self-introduction!</li>
</ul>
</li>
<li><strong>Question from Jun to H.I.</strong>  <ul>
<li><strong>Which of your past skills or experiences have been especially useful or valuable at KINTO Technologies?</strong>  <ul>
<li>The Python and statistics knowledge I studied as a hobby have been surprisingly useful.</li>
<li>My experience in both marketing and development has given me a broad perspective, which I&#39;ve found very helpful in my role as a PdM.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Fúyuán</h1>
<ul>
<li><strong>Self-introduction</strong>  <ul>
<li>My name is Fúyuán from the Planning and Management Group of the Development Support Division. I joined the company in November 2024. I work at Osaka Tech Lab.</li>
<li>I handle back-office operations related to KINTO Technologies&#39; contract-based development projects, including budget planning, invoicing, and payments.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong>  <ul>
<li>The Planning and Management Group consists of five members, each of whom is responsible for various tasks related to KINTO Technologies&#39; contract development work, such as budgeting, contracts, invoicing/payments, and intellectual property management.</li>
<li>Although our team members are based in Tokyo, Nagoya, and Osaka, we stay closely connected through Slack. It feels like we&#39;re working side by side despite the physical distance.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong>  <ul>
<li>I came from a more Japanese traditional company, so I was surprised by how fast decisions are made and how flat and open the communication is here.</li>
<li>I had a similar impression at my job interview, which attracted me and made me decide to join the company, so it didn&#39;t come as a surprise.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong>  <ul>
<li>There&#39;s a strong sense of balance: members are very focused and quiet when working, but when it&#39;s time to chat, the conversations are lively and fun. With such a diverse team in terms of nationality, age, and career background, I find myself constantly inspired by them.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong>  <ul>
<li>I&#39;m usually a Read-Only-Member and don&#39;t use social media or blogs in my private life, so I&#39;m a bit nervous...</li>
</ul>
</li>
<li><strong>Question from H.I. to Fúyuán</strong>  <ul>
<li><strong>What is your morning routine like when working from home?</strong>  <ul>
<li>I try to move my body before I start working to get my mind going, so I make a point of doing radio calisthenics.</li>
<li>My favorite thing to do is exercise on the balcony while soaking up the sunlight on a nice day! (I just hope allergy season ends soon!)</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>R.O</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-R_O.png" alt="R.O"></p>
<ul>
<li><strong>Self-introduction</strong>  <ul>
<li>I belong to the Project Promotion Group in the New Service Development Division. I am involved in the KINTO ONE development project as a project manager.</li>
<li>In my previous role at an offshore development company, I also worked as a project manager, collaborating with team members in Vietnam on various development projects in the fields of construction, entertainment, and finance.</li>
</ul>
</li>
<li><strong>How is your team structured?</strong>  <ul>
<li>Including our partner members, we&#39;re a team of about 10 people.</li>
<li>All of us are project managers, each responsible for a different development project related to KINTO ONE.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong>  <ul>
<li>I had heard that the age group would be a little older than at my previous job and that everyone was hired mid-career, so I was a bit nervous about what the internal atmosphere and relationships would be like. But I found that many people are very smart and open, making it easy to collaborate and ask questions.</li>
<li>There are more internal study sessions and participation in external ones than I expected.</li>
<li>Many of our members travel to other locations as needed to collaborate offline with members there. I expected most communication to be online, but it&#39;s relatively easy to meet in person when needed, which I think is a plus.</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong>  <ul>
<li>Our Project Promotion Group has more of a calm atmosphere rather than a lively one, and both work discussions and casual chats happen in a relaxed manner.</li>
<li>We usually communicate through messages using communication tools such as Slack, but we also have many opportunities to visit the development team and directly confirm specifications, so I felt that the atmosphere was conducive to cross-departmental conversations and consultations.</li>
<li>I feel that the atmosphere varies from team to team, as some teams with a lot of engineers tend to have more lively conversations around specs and implementation.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong>  <ul>
<li>I actually enjoy putting my thoughts into words, so I was looking forward to this.</li>
<li>I&#39;m happy to have the chance to contribute to the blog that I used to gather information before joining the company.</li>
</ul>
</li>
<li><strong>Question from Fúyuán to R.O.</strong>  <ul>
<li><strong>How do you keep up with new technologies, products, and services out there?</strong>  <ul>
<li>I created a separate account on X (formerly Twitter) to only follow accounts sharing those kinds of updates, so my timeline shows exactly the information I want.</li>
<li>I also catch up through casual conversations with friends and acquaintances from other industries (usually over drinks).</li>
<li>When I want to get a feel for the excitement surrounding a new service or technology, I attend offline events like conferences.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Horichan</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-horichan.png" alt="horichan"></p>
<ul>
<li><strong>Self-introduction</strong>  <ul>
<li>This is Horichan, who joined the company in October 2024! I&#39;m from Hiroshima, a big Carp baseball fan, and I love sweet potato shochu.</li>
<li>I was originally an Android engineer, then transitioned to a PdM role two years ago at my previous job. Now, I’m working as a director on systems designed to solve challenges faced by Toyota dealers, and I&#39;m giving it my all every day!</li>
</ul>
</li>
<li><strong>How is your team structured?</strong>  <ul>
<li>The DX Solution Group under the Mobility Product Development Division has seven members: one producer, three directors, and three designers, each working on their own assigned projects.</li>
</ul>
</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong>  <ul>
<li>My first impression was that it&#39;s a company that combines the best of both a large company and a startup company! Things like the rent subsidy and generous paid leave feel like a large company, but the employee-led study sessions (and BeerBash!) and the energy of the CHO All-Hands Meeting definitely bring a startup feel, which I love.</li>
<li>If I had to name a surprise, it would probably be the tools. In my previous job, I was in charge of internal system, so I created all of my documents using FigJam or Notion, but here PowerPoint is the main tool. But I&#39;m getting quite used to it! At this rate, I&#39;ll aim to become a PowerPoint master.💪</li>
</ul>
</li>
<li><strong>What is the atmosphere like on-site?</strong>  <ul>
<li>Everyone in the group is kind and I&#39;m learning in a relaxed environment! Lunch is also fun!🫶</li>
<li>Our project team is made up of professionals from various fields who all work together as one team to solve dealership challenges.</li>
</ul>
</li>
<li><strong>How did you feel about writing a blog post?</strong>  <ul>
<li>I had written a few blog posts at my previous job, so I was interested in! I&#39;m happy to finally take part!☺️</li>
</ul>
</li>
<li><strong>Question from R.O. to Horichan</strong>  <ul>
<li><strong>Is there any company culture or event at KINTO Technologies that you found interesting?</strong>  <ul>
<li>I love how many opportunities there are to hear directly from management! During onboarding, we had small group sessions with both President Kotera-san and Vice President Kageyama-san, which was already a surprise. But even after that, there are monthly all-hands meetings, CHO All-Hands Meeting, optional 1-on-1s, and U35 events for younger employees.😳</li>
<li>Kotera-san told us that we could even invite him out for drinks. So a few of us actually did, and we had a great time chatting.🍶 (R.O. and Tanachu were there too!🙌)</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Closing words</h1>
<p>Thank you everyone for sharing your thoughts on our company after joining it!</p>
<p>There are more and more new members at KINTO Technologies every day! We&#39;ll be posting more new-joiner stories from across divisions, so stay tuned!</p>
<p>And yes: we&#39;re still hiring! KINTO Technologies is looking for new teammates to join us across a variety of divisions and roles. For more details, check it out <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[New Community Launch: Appium Meetup Tokyo Expands the Possibilities of Mobile UI Testing]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-13-Appium-Meetup-Tokyo-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-13-Appium-Meetup-Tokyo-en/</guid>
            <pubDate>Tue, 20 May 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This is Nakanishi from the Developer Relations Group. I&#39;m excited to share that we&#39;re launching a new community!</p>
<p>The mobile app market continues to grow year after year, making fast and high-quality releases more crucial than ever to remain competitive. That&#39;s where <strong>Appium</strong>, a testing tool that works with iOS and Android and feels similar to Selenium, is drawing a lot of attention. That said, implementing and operating Appium comes with its own set of challenges, including OS version differences and the complexity of setting up the environment.  </p>
<p>To tackle these challenges and share solutions, we&#39;ve launched a new community: <strong>Appium Meetup Tokyo</strong>.</p>
<hr>
<h2>Reasons for Launching Appium Meetup Tokyo</h2>
<ul>
<li><p><strong>Growing Needs</strong><br>Release cycles for mobile apps are becoming increasingly shorter and manual testing alone is reaching its limits. Appium is a valuable tool for enhancing test automation efficiency, but knowledge about its setup and implementation is still not widely shared.<br />
At KINTO Technologies, we&#39;ve set <strong>technical capabilities</strong>, <strong>development productivity</strong>, and <strong>release speed</strong> as our <a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">Core Goals for 2025</a>, and test automation with Appium plays a central role in achieving all three.<br />
With the growing spotlight on <strong>development productivity</strong> across companies, and as our development speed continues to increase, automated testing is becoming more critical than ever.</p>
</li>
<li><p><strong>Lack of information for Japan</strong><br>While English documentation and overseas case studies are on the rise, there&#39;s still limited comprehensive Appium information available in Japanese. When putting it into practice, familiar examples and real stories of success and failure can be a huge help.<br />
I&#39;m sure many QA engineers and mobile app developers at other companies are facing the same challenges. That’s why we aim to energize the Japanese-speaking community and encourage information sharing to help create an even better automated testing environment.</p>
</li>
<li><p><strong>Collaboration with Autify</strong><br>While talking with Autify, a company that provides a mobile app automation testing platform, the idea came up that it would be great to have more opportunities to learn about Appium. As the conversation progressed, we decided to get the community started with small study sessions.<br />
Autify uses Appium behind the scenes in their services and has accumulated a wide range of knowledge. Their insights and expertise aren&#39;t usually shared publicly, things you wouldn&#39;t easily gain just from developing your own apps or in-house automated testing. Personally, I&#39;m really excited about this collaboration.<br />
Furthermore, by sharing comprehensive know-how, including not only Appium but also related tools, we aim to build a community that can solve a wider range of problems.<br />
We&#39;re currently looking for more members to help grow this initiative.</p>
</li>
</ul>
<hr>
<h2>Community Activities</h2>
<ul>
<li><p><strong>Regular Study Sessions</strong>  </p>
<ul>
<li>Introduction to Appium  </li>
<li>Hands-on sessions using real devices and simulators  </li>
<li>Explanation of CI/CD pipelines</li>
</ul>
</li>
<li><p><strong>Lightning Talks (LT) and Discussions</strong>  </p>
<ul>
<li>Case studies from companies that have adopted Appium  </li>
<li>Sharing issues and know-how unique to the field  </li>
<li>Sharing the latest updates and useful tips</li>
</ul>
</li>
<li><p><strong>Online Information Sharing</strong>  </p>
<ul>
<li>Archiving slides and resources  </li>
<li>Q&amp;As on the community Slack and social media  </li>
<li>Creating a collection of best practices</li>
</ul>
</li>
</ul>
<p>We&#39;re hoping to explore all of these initiatives together with you.</p>
<hr>
<h2>First Event Details</h2>
<ul>
<li><strong>Date</strong>: Thursday, February 20, 2025 19:00-21:30</li>
<li><strong>Location</strong>: Autify Tokyo Office (Hybrid event)  </li>
<li><strong>Main theme</strong>:  <ol>
<li>Use case of Appium plugin in Autify (15 min + 5 min Q&amp;A)</li>
<li>Guidelines and practices for efficient app test automation (15 min + 5 min Q&amp;A) </li>
<li>[Open Call] Lightning Talk 3 (15 min + 5 min Q&amp;A) </li>
<li>[Open Call] Lightning Talk 4 (15 min + 5 min Q&amp;A)</li>
</ol>
</li>
</ul>
<p>Check out for details on connpass.
<a href="https://autifyjapan.connpass.com/event/342867/">https://autifyjapan.connpass.com/event/342867/</a></p>
<p>If you&#39;re interested in presenting or attending, please sign up via the link above!</p>
<hr>
<h2>Future Plan</h2>
<ul>
<li><p><strong>Ongoing Events</strong><br>We plan to continue hosting study sessions and workshops to meet the needs of everyone, from beginners and advanced users.</p>
</li>
<li><p><strong>Information on Integration with Other Tools</strong><br>We&#39;ll also actively cover comparisons and integration examples with other tools like Selenium and Cypress, helping you expand your practical options.</p>
</li>
<li><p><strong>Community-Driven Knowledge Sharing</strong><br>As we hold more events, we aim to share a wealth of insights, including stories of success and failure relate to Appium, across the community, contributing to participants&#39; skill development.</p>
</li>
</ul>
<hr>
<h2>For Those Considering Participating</h2>
<ul>
<li><strong>Those who want to fully implement automated testing for mobile apps</strong>  </li>
<li><strong>Those who are interested in Appium and seeking specific examples or know-how</strong>  </li>
<li><strong>Engineers and QA personnel interested in CI/CD-based operations</strong>  </li>
<li><strong>Those who want to improve their testing culture by learning from other companies&#39; experiences</strong></li>
</ul>
<p>If any of the above applies to you, we&#39;d love to have you join us at Appium Meetup Tokyo to exchange the latest insights. Future announcements and detailed information will be provided by <a href="https://x.com/AutifyJapan">@AutifyJapan</a> and <a href="https://x.com/KintoTech_Dev">@KintoTech_Dev</a>. If you have any questions or suggestions, feel free to reach out.</p>
<p>I&#39;m looking forward to seeing you at Appium Meetup Tokyo!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-02-13-appium-meetup-tokyo/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Introducing the Engineering Office: Strengthening KINTO Technologies’ Engineering Organization]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-12-engineering-office-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-12-engineering-office-en/</guid>
            <pubDate>Mon, 19 May 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h2>About KTC&#39;s Engineering Office</h2>
<p>In January 2025, KINTO Technologies (KTC) established a new organization: the Engineering Office. This article introduces what the Engineering Office is working on, while also helping raise awareness of its purpose within the company.</p>
<h3>What We’re Aiming For</h3>
<p>Our primary goal is to &quot;strengthen KINTO Technologies&#39; capabilities as an engineering organization.&quot; To achieve this over the medium to long term, we’re actively working to foster new capabilities and shape a supportive, evolving engineering culture.</p>
<p>As a team reporting directly to the <a href="https://www.kinto-technologies.com/recruit/cio-message/">Vice President</a>, we stay closely aligned with key management issues. Together, we formulate hypotheses about what initiatives KTC truly needs, and <strong>each of us leverages our own experiences and expertise to lead cross-functional efforts across the company</strong>.</p>
<h3>Our Mindset</h3>
<p>It’s easy to look at other teams and wish for what they have. But if we want to unlock new possibilities, we also need to take a closer look at the strengths we’ve already built and make the most of them. Real progress begins when we start asking ourselves questions like, &quot;What if we added this, tweaked that, or took something away? Could we become even stronger?&quot; It is important to correctly understand the current situation and take action based on that context.</p>
<p>KTC already has a workforce of over 350 people. Because our existing operations and organizational structures are well established, bringing about change is not easy. Precisely because we are in an uncertain and ambiguous situation where no one knows the right answer for us, we must remain persistent. Instead of rushing to conclusions, we should focus on identifying what is truly necessary.</p>
<h3>What Not to Do</h3>
<ul>
<li>We don’t plan to directly handle the following areas: Planning, promotion and execution of business strategies and development projects</li>
<li>Project and program management for development work</li>
<li>Recruitment, PR, internal communications, and company culture initiatives</li>
</ul>
<p>While the Engineering Office works to strengthen KTC’s engineering capabilities through planning and promotion, we do not take on responsibilities such as hiring, training, evaluation, or public relations, which are roles that similar departments at other companies may often manage. At present, as we move projects forward, we’re collaborating with the respective dedicated teams at key points when needed.</p>
<h2>Activity Example 1: Planning to Accelerate Key Initiatives</h2>
<p><img src="/assets/blog/authors/ahomu/20250212/ponchi.png" alt="Diagram: Contribution of the Toyota Group to Business. At the center are two elements, &quot;User First&quot; and &quot;Release First&quot;, with arrows showing the relationship between them. On the left is &quot;AI First,&quot; which represents the group’s effort to promote AI utilization internally. At the bottom is a section labeled &quot;Organizational Intensity,&quot; describing the organizational culture behind the Toyota Group&#39;s in-house IT development."></p>
<p>While KTC is steadily bringing together talented individuals, we believe that achieving even greater outcomes as an in-house development team requires further organizational and team maturity.</p>
<p>Through ongoing discussions with the Vice President, I was involved in shaping and articulating <a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">the key focus themes for 2025.</a> Since AI First already has a <a href="https://blog.kinto-technologies.com/posts/2024-01-26-GenerativeAIDevelopProject/">dedicated project</a> team, the Engineering Office is focusing on the remaining three themes:</p>
<ul>
<li>Release First</li>
<li>User First</li>
<li>Organizational Intensity</li>
</ul>
<p>We’re planning initiatives to support internal volunteer efforts and cross-team projects, based on two key pillars: <strong>1. Improving IT literacy across the organization</strong> and <strong>2. Creating real examples and encouraging behavioral change</strong>.</p>
<h2>Activity Example 2: Supporting Development Team Process and Communication</h2>
<p><a href="https://blog.kinto-technologies.com/posts/2025-01-14-newcomers-introduction-aug-sep/#naito">Y.Naito-san</a> is leveraging her expertise and experience in SPI (<em>Software Process Improvement</em>) to support in-house development teams. Specifically, our efforts include facilitating team retrospectives, assisting in the operation of development tools such as JIRA and Findy Team+, organizing study sessions to share knowledge needed for process improvement, and providing mentoring.</p>
<p>The initiative aims to improve communication, tools, processes, and empowerment, enabling teams to achieve results that no individual member could accomplish alone.</p>
<p>We’re also working to uncover know-how and best practices developed by individuals or specific teams, and to share them across the organization to broaden and deepen collective knowledge, ultimately driving efforts to enhance the company’s overall technical capabilities.</p>
<h2>Activity Example 3: Creating an Environment Where Individual Growth and Contribution Go Hand in Hand</h2>
<p>Our Vice President made the following request, in line with our identity as a technology company: <strong>&quot;We want to ensure that engineers, designers, and other professionals working as specialists are able to contribute to the company and that their contributions are properly recognized.&quot;</strong> We’ve only just started the discussion, so nothing is set in stone yet. However, we’ve begun working with HR and are starting conversations with department heads to explore how we can turn this into reality.</p>
<p>To continue being a company that contributes through its software development and technical capabilities, we believe it’s essential to build an environment where members can continue growing. This includes initiatives like developing career paths and increasing opportunities for professional challenges.</p>
<h2>Let&#39;s Make Our Work More Interesting! (Hiring Info)</h2>
<p>We haven&#39;t formalized our operations just yet, so we&#39;re not officially advertising job openings. But if you’re the kind of person who’s excited to shape your own mission based on your own themes and beliefs. we think you’ll really enjoy being here. Let&#39;s make our work more exciting and meaningful, with our own hands! 💪</p>
<ul>
<li><strong>Requirements</strong><ul>
<li>Someone who can move independently and take initiative in a high-autonomy environment</li>
<li>Someone who can navigate uncertainty and keep moving forward through trial and error</li>
<li>Someone who can stay patient and persistent in the face of ambiguity, without rushing to conclusions</li>
<li>Someone who can inspire change and bring others along for the ride</li>
</ul>
</li>
</ul>
<p>If you are interested, please feel free to reach out via <a href="https://hrmos.co/pages/kinto-technologies/jobs/0000127">Open Positions</a> page. If you know someone on the team, you can also message us directly via DM! (・∋・)ﾉｼ</p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/0000127">https://hrmos.co/pages/kinto-technologies/jobs/0000127</a></p>
<p>KINTO Technologies is also looking for people to join us across a variety of teams and roles. For more information, please see the <a href="https://www.kinto-technologies.com/recruit/">recruitment information</a>.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ahomu/20250212/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Kotlin Multiplatform Hybrid Mode: Compose Multiplatform Meets SwiftUI and Jetpack Compose]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-15-Kotlin_Multiplatform_Hybrid_Mode/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-15-Kotlin_Multiplatform_Hybrid_Mode/</guid>
            <pubDate>Mon, 19 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Develop great apps without blowing budgets, timelines, or code quality. Kotlin Multiplatform’s hybrid stack—shared business logic and Compose Multiplatform UI, enriched with native SwiftUI on critical screens—lets lean teams ship MVPs fast and larger teams polish native detail. One Kotlin codebase adapts to any scope, scales smoothly, and stays maintainable for the long haul.]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>Introduction</h2>
<p>Hello! I&#39;m Yao Xie from the Mobile Application Development Group at KINTO Technologies, where I develop the Android app of <a href="https://kinto-jp.com/entry_app/">KINTO Kantan Moushikomi (KINTO Easy Application)</a>. In this article, I’ll be sharing some thoughts on how to implement a hybrid model that takes advantage of both Compose Multiplatform and native UI frameworks.</p>
<p>Developing mobile apps often means juggling limited budgets, varied team sizes, and tight timelines: all while ensuring long-term maintainability and a consistent user experience. A hybrid architecture built on Kotlin Multiplatform (KMP), which now includes Compose Multiplatform as a core UI framework, combined with native frameworks such as SwiftUI (iOS) and Jetpack Compose (Android), offers the flexibility to meet these challenges. Whether you’re launching an MVP on a shoestring budget or scaling up for a fully polished app, this approach adapts to your constraints and preserves code quality over time.</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_kmp_structure.svg" alt="Kotlin Multiplatform Structure"></p>
<h2>A Flexible and Maintainable Approach for Diverse Constraints</h2>
<h3>Rapid Development with Consistency</h3>
<p>For projects constrained by limited budget, small teams, or short deadlines, the benefits I perceive are the following:</p>
<ul>
<li>Shared Business Logic: by writing your core functionality (networking, data models, business rules) once in a KMP module, will ensure consistent behavior across Android and iOS while reducing duplication and ease future maintenance.</li>
<li>Shared UI with Compose Multiplatform: Compose Multiplatform is part of the Kotlin Multiplatform ecosystem, which means it naturally builds on shared Kotlin code to build UI components. Develop most of your UI as shared composable components. Android benefits directly via Jetpack Compose, and iOS can embed these components using helpers like ComposeUIViewController. This uniform approach guarantees that both platforms adhere to the same design and behavior guidelines, simplifying long-term support.</li>
</ul>
<p>This strategy minimizes development time while maintaining consistency—a key factor for high-quality user experiences and future-proof code.</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_limited_res.svg" alt="Kotlin Multiplatform Hybrid Mode with Limited Resources"></p>
<blockquote>
<p><em>&quot;The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.&quot;</em></p>
</blockquote>
<h3>Scaling Up with Native Enhancements</h3>
<p>As your project secures more funding, personnel, or additional time:</p>
<ul>
<li>Incremental Native Integration: With a solid shared foundation, you can gradually replace or enhance key screens using native UI elements. For example, you can enhance critical iOS screens with SwiftUI to better align with platform-specific design standards.</li>
<li>Expect/Actual Mechanism: Kotlin’s expect/actual pattern lets you define a generic shared component (like a platform-specific button) and then provide native implementations. This enables you to start with a shared UI and later invest in polished native refinements where they matter most.</li>
</ul>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_abundant_res.svg" alt="Kotlin Multiplatform Hybrid Mode with Abundant Resources"></p>
<h3>Adapting to Different Team Sizes and Project Timelines</h3>
<p>The hybrid approach is designed to flex as your project evolves:</p>
<ul>
<li>For Lean Teams and Tight Schedules: Focus on maximum code reuse. A small team can build and maintain the shared business logic and UI, ensuring consistency while speeding up time-to-market.</li>
<li>For Larger Teams or Extended Deadlines: As your team grows or deadlines relax, allocate additional resources toward developing native components to enhance user experience. This phased strategy minimizes risk and ensures that the core logic remains stable and maintainable over time.</li>
</ul>
<p>I find that by keeping the shared logic and UI consistent across platforms, you reduce complexity and technical debt, making projects easier to maintain and evolve.</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_standard_res.svg" alt="Kotlin Multiplatform Hybrid Mode with Standard Resources"></p>
<h2>Code Snippets Illustrating the Kotlin Multiplatform Hybrid Mode</h2>
<h3>Shared Business Logic</h3>
<p>This shared module guarantees that both Android and iOS apps use the same core logic, ensuring consistency and reducing maintenance overhead.</p>
<pre><code class="language-kotlin:Api.kt">// Api
interface Api {
    suspend fun getUserProfile(): User
}

class ApiImpl(private val client: HttpClient) : Api {
    override suspend fun getUserProfile(): User {
        return client.get {
            url {
                protocol = URLProtocol.HTTPS
                host = &quot;yourdomain.com&quot;
                path(&quot;yourpath&quot;)
            }
        }.safeBody()
    }
}
</code></pre>
<pre><code class="language-kotlin:Repository.kt">// Repository 
interface UserRepository {
    suspend fun getUserProfile(): Flow&lt;User&gt;
}

class UserRepositoryImpl(private val api: Api) : UserRepository {
    override suspend fun getUserProfile(): Flow&lt;User&gt; {
        return flowOf(api.getUserProfile())
    }
}

// ViewModel
class ProfileViewModel(
    private val repository: UserRepository,
) : ViewModel() {

    private val _uiState = MutableStateFlow(ProfileUiState())
    val uiState: StateFlow&lt;ProfileUiState&gt; = _uiState.asStateFlow()

    @OptIn(ExperimentalCoroutinesApi::class)
    fun onAction(action: ProfileScreenAction) {
        when (action) {
            is ProfileScreenAction.OnInit -&gt; {
                viewModelScope.launch {
                    // Set loading state to true
                    _uiState.update { it.copy(isLoading = true) }
                    
                    // Retrieve authorization info and then fetch user profile data
                    repository.getAuthorizationInfo()
                        .flatMapLatest { authInfo -&gt;
                            // If authInfo exists, call getUserProfile() to get user data; otherwise, emit null
                            if (authInfo != null) {
                                repository.getUserProfile()
                            } else {
                                flowOf(null)
                            }
                        }
                        // Catch any exceptions in the Flow chain
                        .catch { e -&gt;
                            e.printStackTrace()
                        }
                        // Collect the user profile data and update the UI state
                        .collect { userProfile -&gt;
                            _uiState.update { state -&gt;
                                state.copy(
                                    isLoading = false,
                                    data = userProfile,
                                    errorMessage = if (userProfile == null) &quot;User profile data is null&quot; else null
                                )
                            }
                        }
                }
            }
        }
    }
}
</code></pre>
<h3>Shared UI with Compose Multiplatform</h3>
<p>Compose Multiplatform—being an integrated part of Kotlin Multiplatform—allows you to create UI components in shared Kotlin code:</p>
<pre><code class="language-kotlin:ProfileScreen.kt">@Composable
fun ProfileScreen(
    viewModel: ProfileViewModel = koinViewModel()
) {
    val uiState = viewModel.uiState.collectAsState().value

    LaunchedEffect(Unit) { viewModel.onAction(ProfileScreenAction.OnInit) }

    Column(
        modifier = Modifier
            .fillMaxSize()
            .verticalScroll(rememberScrollState())
            .background(White)
            .padding(16.dp)
    ) {
        // Profile header with image and user details
        Row(
            modifier = Modifier
                .fillMaxWidth()
                .background(White, shape = RoundedCornerShape(16.dp))
                .padding(16.dp),
            verticalAlignment = Alignment.CenterVertically
        ) {
            AsyncImage(
                model = uiState.data.profileImage,
                contentDescription = &quot;Profile Image&quot;,
                modifier = Modifier
                    .size(64.dp)
                    .clip(CircleShape)
            )
            Spacer(modifier = Modifier.width(16.dp))
            Column {
                Text(
                    text = uiState.data.userName,
                    fontSize = 16.sp,
                    color = Color.Black
                )
                Text(
                    text = uiState.data.email,
                    fontSize = 14.sp,
                    color = Color.Blue.copy(alpha = 0.6f)
                )
            }
        }
        Spacer(modifier = Modifier.height(16.dp))
        MenuLabel(label = &quot;Account Settings&quot;)
        ....
    }
}
</code></pre>
<p>Lean Approach: I find using this composable directly on both platforms convenient for a consistent look and behavior.</p>
<p>Scaled Approach: Enhance the UI with native components as needed without altering the underlying business logic.</p>
<h3>Native UI via Expect/Actual</h3>
<p>Define an expected component:</p>
<pre><code class="language-kotlin:PlatformButton.kt">@Composable
expect fun PlatformButton(
    modifier: Modifier = Modifier,
    label: String,
    onClick: () -&gt; Unit
)
</code></pre>
<p>Android Implementation (androidMain):</p>
<pre><code class="language-kotlin:PlatformButton.android.kt">@Composable
actual fun PlatformButton(
    modifier: Modifier,
    label: String,
    onClick: () -&gt; Unit
) {
    Button(
        onClick = onClick,
        modifier = modifier
    ) {
        Text(text = label)
    }
}
</code></pre>
<p>iOS Implementation (iosMain):</p>
<pre><code class="language-kotlin:PlatformButton.ios.kt">@Composable
actual fun PlatformButton(
    modifier: Modifier,
    label: String,
    onClick: () -&gt; Unit
) {
    UIKitView(
        modifier = modifier,
        factory = {
            // Create a native UIButton instance.
            val button = UIButton.buttonWithType(buttonType = 0)
            button.setTitle(label, forState = UIControlStateNormal)
            // Style the button
            ...
            
            // Create a target object to handle button clicks.
            val target = object : NSObject() {
                @ObjCAction
                fun onButtonClicked() {
                    onClick()
                }
            }
            // Add the target with an action selector.
            button.addTarget(
                target = target,
                action = NSSelectorFromString(&quot;onButtonClicked&quot;),
                forControlEvents = UIControlEventTouchUpInside
            )
            button
        }
    )
}
</code></pre>
<h3>Blended UI: A Key Advantage Over Flutter</h3>
<p>Unlike Flutter, Compose Multiplatform supports blending shared UI and native components seamlessly on a single screen.</p>
<p>In many scenarios, you may not need a complete switch from shared UI to native UI—rather, you can blend them on a single screen. This approach allows you to tap into the strengths of both Compose Multiplatform and native frameworks without fully committing to one for an entire screen. For example, you might want to display a shared composable header alongside a native SwiftUI list on iOS.</p>
<p>Here’s how you can achieve a blended UI:</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_embedded.png" alt="Compose Multiplatfor embedded with Native UI"></p>
<h4>Embedding Shared Composables in a Native Layout:</h4>
<p>One of Compose Multiplatform’s strengths is the ability to seamlessly embed shared Compose UI components into native SwiftUI layouts. Developers can gradually adopt shared components without rewriting entire screens, a flexibility that&#39;s difficult to achieve with Flutter’s overlay approach.</p>
<p>For example, you can use SwiftUI as a container to host Compose Multiplatform’s shared UI wrapped in a UIViewController:</p>
<pre><code class="language-swift:ContentView.swift">import SwiftUI
import shared  // Import the shared KMP framework

struct ComposeContainerView: UIViewControllerRepresentable {
    let user: User

    func makeUIViewController(context: Context) -&gt; UIViewController {
        // Use the Compose wrapper to create the UIViewController
        return AppComposeUI().createProfileVC(user)
    }

    func updateUIViewController(_ uiViewController: UIViewController, context: Context) { }
}

struct ContentView: View {
    let user: User
    
    var body: some View {
        VStack {
            Text(&quot;Native SwiftUI Header&quot;)
                .font(.headline)
                .padding()
            // Embed the Compose UI inside SwiftUI.
            ComposeContainerView(user: user)
                .frame(height: 250)
            List {
                Text(&quot;Native SwiftUI Item 1&quot;)
                Text(&quot;Native SwiftUI Item 2&quot;)
            }
        }
    }
}
</code></pre>
<h4>Embedding Native iOS UI into Compose Multiplatform Screens</h4>
<p>Compose Multiplatform also enables embedding native iOS views (e.g., SwiftUI or UIKit) within shared Compose screens—another capability not easily achieved by Flutter.</p>
<p>Define a SwiftUI view to embed:</p>
<pre><code class="language-swift:MySwiftUIView.swift">import SwiftUI
struct MySwiftUIView: View {
    let user: User
    var body: some View {
        VStack {
            Text(&quot;Native SwiftUI Content&quot;)
                .font(.body)
            Text(&quot;Welcome, \(user.firstName)&quot;)
        }
        .padding()
        .background(Color.gray.opacity(0.2))
        .cornerRadius(8)
    }
}
</code></pre>
<p>Then integrate this native view into your shared Compose UI using UIKitView:</p>
<pre><code class="language-kotlin:ComposeWithNativeUI.kt">@Composable
fun ComposeWithNativeUI(user: User) {
    Column {
        // Shared Compose UI header.
        ProfileHeader(user = user)
        Spacer(modifier = Modifier.height(16.dp))
        // Embed a native iOS view using UIKitView.
        UIKitView(factory = {
            // Create a UIHostingController with a SwiftUI view.
            val hostingController = UIHostingController(rootView = MySwiftUIView(user = user))
            hostingController.view
        }, modifier = Modifier.fillMaxWidth().height(100.dp))
    }
}
</code></pre>
<p>This bidirectional inline integration sets Compose Multiplatform apart from Flutter, enabling teams to incrementally adopt shared UI components while maintaining native look and feel. It offers developers greater flexibility and reduces the risk of large-scale rewrites, combining the efficiency of shared code with the richness and quality of native UI.</p>
<p>Compose Multiplatform provides a significant advantage over Flutter by enabling more flexible, fine-grained integration between shared UI and native platform UI. Unlike Flutter, which typically renders shared UI components through an overlay mechanism, Compose Multiplatform uses an inline rendering approach, allowing shared and native UI elements to blend naturally together.</p>
<p><img src="/assets/blog/authors/yao.xie/yao_kmp_hybrid_vs.png" alt="Compose Multiplatfor embedded in Native UI vs Flutter embedded in Native UI"></p>
<h2>Pros</h2>
<p>Best Practices for Flexibility, Maintainability, and Scalability</p>
<h4>Embrace Rapid Prototyping:</h4>
<p>I’ve learned the hard way that spending weeks perfecting pixel details before users even touch the app is risky. These days our team spins up a shared‑UI prototype in a week or two, pushes it into stakeholders’ hands, and soaks up feedback early. When we’re confident the idea sticks, we layer on native flourishes—animations, platform‑specific gestures—without throwing away what we built.</p>
<h4>Plan Incremental Development:</h4>
<p>Our rule of thumb: launch small, refactor later. We start with a KMP module for core logic and a single shared Compose screen. Once that slice proves itself, we peel off the next screen—or replace a shared widget with a SwiftUI or Jetpack Compose version—one commit at a time. The gradual approach keeps velocity up and rewrites down.</p>
<h4>Balance Resources, Quality, and Scalability:</h4>
<p>When it was just two of us, 90 percent of the UI lived in shared code because that’s what we could afford. As head‑count and design ambitions grew, we shifted the critical flows to native, leaving the rest untouched. That staged migration kept the backlog realistic and let us dial quality up without derailing releases.</p>
<h4>Maintain Consistency:</h4>
<p>Centralizing business logic in one Kotlin module has saved us countless merge headaches. A single bug fix lands once and rolls out everywhere—no more “iOS fixed, Android still broken” stand‑ups. It’s not flashy, but this shared core is what lets the team sleep at night when deadlines loom.</p>
<h4>Ensure Elasticity:</h4>
<p>In my experience leading mixed‑platform projects, the teams that thrive are the ones that treat architecture like a living organism, not a static blueprint. We design every layer so it can flex when budgets tighten, timelines shift, or a surprise hire brings new skills. Because Compose Multiplatform lets us blend shared and native UI on demand, we can pivot from “MVP mode” to “native‑polish mode” in days, not months. That elasticity has repeatedly saved us when product strategy—or the market itself—changed overnight.</p>
<h2>Conclusion</h2>
<p>This elastic hybrid architecture, built on Kotlin Multiplatform and Compose Multiplatform alongside native UI frameworks, offers unparalleled flexibility. It allows you to adjust your development strategy based on budget, team size, and timeline constraints while maintaining consistency and long-term maintainability. For lean projects, maximize code reuse to launch quickly; as resources expand, incrementally enhance critical screens with native UI elements without rewriting your core logic.</p>
<p>Embrace this flexible, incremental model to reduce duplication, streamline maintenance, and deliver a consistent, high-quality user experience that scales as your project evolves.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yao.xie/yao_kmp_hybrid_thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: August and September 2024 Update (Part 2)]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-19-newcomers-introduction-aug-sep-2-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-19-newcomers-introduction-aug-sep-2-en/</guid>
            <pubDate>Fri, 16 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Meet the fresh faces at KINTO Technologies this August and September: our latest team additions.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I&#39;m Mizuki, and I joined the company in September! In this article, I interviewed the members who joined in August and September 2024 and gathered their first impressions after joining KINTO Technologies. I hope this article will be helpful for those interested in KINTO Technologies and serve as a nice reflection for the members featured here!</p>
<h1>K.W</h1>
<ul>
<li><strong>Self-Introduction</strong><br>I am a PdM (Product Manager) on the JP Membership Platform Team, which is part of the Common Service Development Group in the Group Core Systems Division. I started my career as a PdM at a major web services company straight out of university. KINTO Technologies (KTC) is my second company.</li>
<li><strong>How is your team structured?</strong><br>The JP Membership Platform team develops membership-related features for KINTO&#39;s web services and mobile apps in Japan. Our team consists of eight members: one team leader, one PdM, six software engineers (including team members from partner companies).</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><br>Since KTC is made up entirely of mid-career professionals and new members join almost every month, I immediately felt a welcoming atmosphere for newcomers. As for any surprises, this was my first time changing jobs, so I was honestly a bit nervous about whether I could contribute. But thanks to the support of my teammates, I feel like I’ve blended in better than I expected.</li>
<li><strong>What’s the atmosphere like on the team?</strong><br>KTC is still a relatively new company, so we’re in the process of establishing rules and structures. That said, I’ve found that each member is proactive and that there’s a culture of actively adopting new development methods and initiatives.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I haven’t had many chances to share things through blogs, so I took my time writing this one while looking at past onboarding posts for inspiration.</li>
<li><strong>Question from MK-san: Any recommended items you use in the office?</strong><br>It’s actually something I use at home rather than at the office, but I highly recommend a height-adjustable desk. Here’s the one I use. <a href="https://www.amazon.co.jp/dp/B08CXSV3RX">https://www.amazon.co.jp/dp/B08CXSV3RX</a></li>
</ul>
<h1>Shiori</h1>
<p>![](/assets/blog/authors/mizuki/20250123/suda.png =250x)</p>
<ul>
<li><strong>Self-Introduction</strong><br>I work as a Generative AI Engineer in the IT/IS Division on the Generative AI Development Project. My main responsibility is promoting the use of generative AI. This includes conducting internal training sessions, developing use cases, and providing technical support for generative AI.</li>
<li><strong>How is your team structured?</strong><br>Our team consists of five members, including myself: three based in Nagoya, one in Jimbocho, and me in Muromachi.</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><br>I was impressed by the well-established environment for keeping up with technology and sharing knowledge — there are monthly themed study sessions, regular Toyota Group tech meetups, internal training opportunities, and access to external seminars. The company is also proactive in sharing its work through the tech blog and presentations. Just four months after I joined, I had the opportunity to speak at an external seminar about how we’re using generative AI internally. I also learned the term &quot;menchaku&quot; after joining the company. This is a Toyota word that refers to &quot;communicating in person.&quot; I felt that this culture of balancing in-person and online communication helps reduce misunderstandings and promotes faster decision-making and value creation. As Toyota’s in-house development organization, we handle much more than just mobility products like KINTO. Our work also includes finance, MaaS, technical support for vehicle systems, dealership digital transformation, and generative AI. This broad scope gives us many opportunities to expand our knowledge across different fields. I also feel that decision-making here is quite democratic. For example, our office drink flavors are rotated seasonally — spring/summer and fall/winter— and each time, a “Flavor Preference Survey” is conducted. The current fall/winter flavors were chosen based on the survey results, which really impressed me!</li>
<li><strong>What’s the atmosphere like on the team?</strong><br>Whether it’s on Slack or in person, when someone runs into a problem, others always step in with thoughtful support. It’s a really warm and welcoming environment. It&#39;s also easy to invite people to lunch, and I’ve had the chance to connect with team members outside of my division, which has been a lot of fun.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>✌️😆✌️</li>
<li><strong>Question from K.W: Do you have a personal motto?</strong><br>I&#39;m not sure if I’d call them mottos, but I do have two personal themes I’m focusing on for 2024 and 2025: &quot;Pivot&quot; and &quot;Act now.&quot; In the field of generative AI, technology and information are updated incredibly fast. Major tech announcements happen frequently, and it’s not uncommon for a worldview that felt new yesterday to become the norm today. In such an environment, it’s important to quickly test ideas, measure their impact, adjust course, and put them into action — all in fast cycles, even when the outcome is uncertain. That&#39;s why I try to minimize the time between “having an idea” and “taking action,” whether it’s for work or daily life.</li>
</ul>
<h1>Yukki~</h1>
<p>![](/assets/blog/authors/mizuki/20250123/yukki-.jpeg =250x)</p>
<ul>
<li><strong>Self-Introduction</strong><br>Nice to meet you, I&#39;m Yukki. I belong to the Corporate IT Group in the IT/IS Division. My role involves identifying and resolving issues across a wide range of areas, including internal information systems, network infrastructure, and security.</li>
<li><strong>How is your team structured?</strong><br>I’m part of the Boost team, which currently has two members. Our team supports the Corporate IT Group by tackling challenges and providing behind-the-scenes support. Each of us focuses on problem-solving in our respective areas of expertise. Currently, I’m involved in infrastructure design related to the renewal of various KINTO systems. I’m working on improvement initiatives while keeping the concept of Shu-Ha-Ri in mind. *Shu-ha-ri is a process of learning: first following the established way (Shu), then breaking it (Ha), and finally creating a new approach (Ri).</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><br>Before joining, I assumed that, as a subsidiary of a large company, there would be some limitations — even though KTC moves at a startup-like pace. But after joining, I was surprised at how much freedom and autonomy the company offers. It feels fast-paced and venture-like. At the same time, the areas that require governance are well managed, and I think the balance between agility and stability is well maintained. Regarding remote work, the basic guideline is to come into the office twice a week, but I really appreciate the flexibility KTC provides in accommodating family needs and personal circumstances.</li>
<li><strong>What’s the atmosphere like on the team?</strong><br>There were more people working remotely than I expected. I was surprised to sometimes be the only one in my seating row. With so many people taking advantage of remote work, I found the environment to be very remote-friendly. That said, the office is well-equipped with video conferencing systems and communication tools like Slack, so it’s easy to talk to people, and I feel reassured by the setup. It’s also been a huge help that my manager is always available when I need to talk. Although it’s a group-wide initiative, we also have a 30-minute casual chat session once a week, which is great. It gives us a chance to connect with colleagues from Tokyo, Nagoya, and Osaka despite the physical distance.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>Even though I joined the company at the same time as others, the physical distance made it hard to talk or interact much. Writing this blog gave me a great chance to connect with other new joiners from different locations. Before joining KTC, my previous role didn’t involve much outward communication. I hope to gradually take more opportunities to share and communicate externally.</li>
<li><strong>Question from Shiori-san: I heard that photography is your hobby. Do you have any tips for improving photography skills?</strong><br>I’m still learning myself, but lately I’ve been paying more attention to the balance between the background, middle ground, and foreground, as well as how elements are arranged within the frame. I also try to go out and find new subjects each season to increase my shooting opportunities.</li>
</ul>
<h1>Mizuki</h1>
<ul>
<li><strong>Self-Introduction</strong><br>I’m part of the Talent Acquisition Team in the Human Resources Group, within the Development Support Division. We handle engineer recruitment and also organize hiring events in Osaka!</li>
<li><strong>How is your team structured?</strong><br>There are six members on the Talent Acquisition team.</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><br>There wasn&#39;t really any gap. Since I had previously worked as a recruitment agent supporting KTC, I already had a good understanding of the company’s atmosphere and culture! However, I was pleasantly surprised by how much trust and autonomy each employee is given.</li>
<li><strong>What’s the atmosphere like on the team?</strong><br>I feel that our team truly lives out the value of &quot;respect for all,&quot; which is something we hold dear as an HR team. Until December, I was working at Osaka Tech Lab, and I was honestly amazed at how warm and homey the atmosphere was. I really hope that Osaka Tech Lab can continue to keep that great vibe.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>Since I’m based in a different location, I only had a brief chance to meet most of the other new joiners during orientation. So writing this blog felt like a great opportunity to get to know everyone&#39;s hobbies and roles.</li>
<li><strong>Question from Yukki~-san: What’s your most recommended tsukemen spot?</strong><br>I listed my favorites by location! Please check them out if you get the chance!<br>Tokyo: Dogenzaka Mammoth (Shibuya) <a href="https://tabelog.com/tokyo/A1303/A130301/13122700/">https://tabelog.com/tokyo/A1303/A130301/13122700/</a><br>Osaka: Miyata Menji (Shinsaibashi) <a href="https://miyata-menji.com">https://miyata-menji.com</a><br>Nagoya: Hanzo (Fujigaoka) <a href="https://www.menya-hanzo.com/hanzo.html">https://www.menya-hanzo.com/hanzo.html</a></li>
</ul>
<h1>R.K</h1>
<ul>
<li><strong>Self-Introduction</strong><br>I belong to the Marketing Product Group within the Mobility Product Development Division. I’m currently the PdM for &quot;Prism Japan,&quot; a travel app and website. My first company was in the human resources industry, where I worked on app and website growth. KTC is my second company.</li>
<li><strong>How is your team structured?</strong><br>Our team consists of one team leader/engineer, two engineers, one marketer, one writer, and one PdM.</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><br>I had heard that the team was very friendly, and when I actually joined, it turned out to be exactly that. Since this was my first job change, I was honestly a bit anxious about whether I could be of any help. But thanks to the strong support from my teammates, I’ve been able to keep going and do my best!</li>
<li><strong>What’s the atmosphere like on the team?</strong><br>Since we&#39;re working on a product in its growth phase, everyone naturally goes beyond their job descriptions, contributing in various ways within a cheerful and collaborative atmosphere.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I haven&#39;t had many chances to write blog posts, so I referred to everyone else&#39;s comments for inspiration!</li>
<li><strong>Question from Mizuki-san: Where is the best place you’ve ever visited overseas?</strong><br>Aside from the more common destinations, I really enjoyed New Zealand! I took a road trip around the South Island, visiting lots of different spots. The nature was rich and breathtaking, the food was great, the towns were clean and beautiful, and time just felt slower in the best way. It was a trip that exceeded all my expectations, and I’d love to go back someday!</li>
</ul>
<h1>Chen</h1>
<ul>
<li><strong>Self-Introduction</strong><br>I’m originally from Xiamen, China, and worked as an engineer in the Internet industry there for about 10 years. KTC is the first Japanese company I’ve worked for. I am currently part of the Platform Development Division, in the DBRE team, where I develop database-related operational tools. My MBTI type is INFP. My hobby is trying out new things. In just three months, I&#39;ve been to almost every restaurant in the Jimbocho (half of them are curry spots, though). I enjoy visiting art museums in Tokyo, especially the contemporary art ones — part of the fun is not always understanding them. I have a weakness for new product releases.</li>
<li><strong>How is your team structured?</strong><br>There are five of us working in the Jimbocho office, and we recently merged with the SRE team, so we now have a total of seven members.</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><br>My first impression was that the office wasn&#39;t flashy, but it didn&#39;t feel like a traditional Japanese company either — it actually had a bit of a global/foreign-company vibe. During orientation, there was a lot of information about the internal tools and initial setup, and even with my IT background, it took some time to get used to everything. Since there aren’t many people in the Jimbocho office, it’s a quiet space where I can really focus. I was glad to hear that many people work from home — something that’s still relatively rare in China.</li>
<li><strong>What’s the atmosphere like on the team?</strong><br>Our team members are very kind and hardworking. It’s rare to see such detailed support for internal clients (like product teams) and fellow teammates — but that’s exactly what I’ve found at DBRE. During onboarding, I was never ignored for not knowing something — I was always encouraged to ask questions anytime. Also, no one ever put me through the usual “let’s see what you can do” period as a new hire. We always discuss tasks together before starting development. At first, I was surprised by this team-based approach, since I was used to solving things on my own. But once I got used to the pace, I found it really comfortable. There is a lot to learn from how others approach problem-solving, and when everyone’s aligned, it really boosts your confidence in taking on challenges.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I want to improve how I express and share what I know, so I’m going to keep challenging myself to write more. After reading the tech blogs written by my teammates, I realized that what I’ve been missing is the ability to organize and present information clearly.</li>
<li><strong>Question from R.K-san: You&#39;ve been to almost every restaurant in the Jimbocho — what&#39;s your favorite lunch spot?</strong><br>Thank you for the question! For reference, I personally love Southeast Asian food — sour, sweet, and full of variety.<br>[Thai food] The best spot is Bangkok Cosmo Shokudo. Even at night, the prices are reasonable and the portions are generous. You can&#39;t go wrong with any item on the menu.<br>[Ramen] To be honest, I don&#39;t have the patience to wait three hours at Ramen Jiro...😅 So instead, I recommend Yojinbo. I personally like it better than Butayama. Also, &quot;Ebimaru Ramen&quot; is a must-try. The flavor of the soup is really amazing.<br>[Korean food] I went to Keijoen without realizing it had been featured on the TV show &quot;Solitary Gourmet.&quot; Their kontan soup set meal is delicious and well-priced.<br>[Indian food] Namaste India (on the first floor) is plenty good in my opinion — though to be honest, I don’t really notice much difference between most Indian places...😂 That said, I recently found a place called Gurgaon in Ginza, and their cheese kulcha was so good, it’s worth the trip.<br>[Curry] Here are my top 3 curry picks:
Panch Mahal — their lassi is excellent<br>Takeuchi Jimbocho Main Branch — be warned: some dishes are quite spicy
Chantoya Coconut Curry — the coconut-based soup curry leaves a lasting impression</li>
</ul>
<h1>T.A.</h1>
<p>![](/assets/blog/authors/mizuki/20250123/ki2.jpeg =250x)</p>
<ul>
<li><strong>Self-Introduction</strong><br>I’m in Information Security and belong to the Security and Privacy Group in the IT &amp; Information Security Division. My hobby is doing solo activities.</li>
<li><strong>How is your team structured?</strong><br>There are three of us, including the team leader and a contractor. It&#39;s currently our busiest season, so I&#39;m feeling the pressure!</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><br>I felt that KTC was a flexible and fast-paced company. There are many people from different nationalities and backgrounds, so I learn a lot just from casual conversations.</li>
<li><strong>What’s the atmosphere like on the team?</strong><br>There’s a strong sense of continuous improvement. It really feels like something gets upgraded every week, which keeps my curiosity sparked. We often focus on how to do things more efficiently, so I feel like I&#39;m constantly sharpening my own skills as well.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I just hope I don’t make any typos...</li>
<li><strong>Question from Chen-san: What exactly do you mean by &quot;solo activities”?</strong><br>I often go drinking alone, watch movies alone, or go solo camping. Recently, I even went to a Christmas market by myself. The illuminations were beautiful.</li>
</ul>
<h1>MK</h1>
<ul>
<li><strong>Self-Introduction</strong><br>I’m a front-end engineer in the Digital Transformation Development Group of the Mobility Product Development Division. I develop products that support the digital transformation of dealership operations. My career path so far has been: sales → admin → front-end engineer. Also... I love prosciutto.</li>
<li><strong>How is your team structured?</strong><br>We’re a team of nine, working on multiple products. All of our engineers are skilled in a wide range of technologies, so we switch between coding and planning depending on the project, without being limited to any specific domain.</li>
<li><strong>What was your first impression of KTC when you joined it? Were there any surprises?</strong><br>To be honest, I thought it might be a bit strict before I joined... But once I got here, I found that it’s actually a very flexible company. It feels like the best of both worlds — the stability of Toyota with the agility of a young company.</li>
<li><strong>What’s the atmosphere like on the team?</strong><br>There’s a real passion for technology. There’s a real passion for the product, too. Communication is lively, and there’s great energy all around.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I thought it would be a great way for people outside the company to hear directly from those of us actually working on development. I&#39;m also excited to read what everyone else thinks about the company!</li>
<li><strong>Question from T.A.-san: Were there any superstitions or lies you believed (or were fooled by) as a child?</strong><br>I used to believe the purple part of a pigeon&#39;s neck was poisonous.</li>
</ul>
<h1>Finally</h1>
<p>Thank you to everyone for sharing your impressions after joining the company!</p>
<p>At KINTO Technologies, new members are joining the company almost every day! We&#39;ll be posting more new-joiner stories from across divisions, so stay tuned!</p>
<p>And yes — we&#39;re still hiring! KINTO Technologies is looking for new teammates to join us across a variety of divisions and roles. For more details, check it out <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-09-newcomers-introduction/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-09-newcomers-introduction/</guid>
            <pubDate>Fri, 16 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズに2025年2月に入社されたみなさまを紹介します。]]></description>
            <content:encoded><![CDATA[<h1>こんにちは</h1>
<p>こんにちは、2025年2月入社の hayashi-d1125 です！
本記事では、2025年2月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。
KINTOテクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>佐竹 康晴</h1>
<p><img src="/assets/blog/authors/hayashi-d1125/newcomers/member_s.jpeg" alt="Satake"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>新サービス開発部 プロジェクト推進Gの佐竹です。
 社内で新たに企画するプロダクトやプロジェクトのPdM/PMを担当しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>プロジェクト推進G全体で15名、私が所属するPMのチームは6名の体制になります。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>トヨタグループの内製会社として、社内組織や体制が高度に組み上げられており、想像以上の内製組織でびっくりしました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>不明なことに対して情報やアドバイスをもらえるなど、チーム内での相互フォロー関係ができており、出社時にはランチ、業務後や週末には飲み会や一緒に出かけるなど、部署の垣根を越えた交流も盛んで、活気があります。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>入社前に読んでいたテックブログにこんな早々にブログ投稿することになるとは思ってもみませんでした。
 大変光栄ですし、色々なことを発信していくことは好きですので、今後も機会があれば積極的に投稿させていただきたいと思います。</li>
</ul>
</li>
<li><strong>工藤 啓さん → 佐竹 康晴さんへの質問</strong><ul>
<li><strong>他部署のメンバーとの関わりなどどのようにされていますか？</strong><ul>
<li>Bear BashやKTC内の部活など他部署メンバーと交流できる機会に積極参加して繋がりを広げています。
 特にBear Bashでは会場BGMとしてDJを披露したおかげで、多数の方と交流することができました！</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>脇坂 友里絵</h1>
<ul>
<li><strong>自己紹介</strong><ul>
<li>開発支援部　企画管理Gで請求や予算など、KTCのお金に携わるバックオフィス業務を主に行っています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>6名体制で分担しながら業務を行っています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>思っていたよりも意思決定から行動までのスピードが早く驚きました！</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>現場の雰囲気としてはとても協力的だと思います。
 各拠点にチームメンバーがいるのでリモートでのやり取りが多いのですが、定期ミーティングを行うなど業務がスムーズに進むようにコミュニケーションを増やしています。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>文章を書くことに対して苦手意識はあるのですが、いい機会だな！と前向きに捉えています。</li>
</ul>
</li>
<li><strong>佐竹 康晴さん → 脇坂 友里絵さんへの質問</strong><ul>
<li><strong>入社してみて、今まで勤務されていた会社とは異なっていたのはどんなところでしょうか？</strong><ul>
<li>会社として勉強会やセミナーなど、学ぶことに対しての投資は惜しみなくされているのかなと思っています。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>楊 小龍</h1>
<ul>
<li><strong>自己紹介</strong><ul>
<li>業務システム開発部Salesforce開発Gの楊です。
 Kinto Factory保守開発を担当しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>自分を含めて全 6 名のグループです。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>「自由」だと感じます。
 服装も、出社時間も</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>チーム全員が優しく、わからないことがあったら聞きやすいです。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>感想類の文書を書くことが苦手と思います。。。</li>
</ul>
</li>
<li><strong>脇坂 友里絵さん → 楊 小龍さんへの質問</strong><ul>
<li><strong>入社後、一番困ったことを教えてください！</strong><ul>
<li>MTG、しゃべりで、時々わからない言葉があります。
今も日本語を頑張って勉強しています！</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>林田 洋平</h1>
<ul>
<li><strong>自己紹介</strong><ul>
<li>プラットフォームグループ／プラットフォームエンジニアリングチームの林田です。
 KTC の開発チームのために各種ツールの開発・提供・メンテナンス等を行っております。
 普段は Osaka Tech Lab で働いております。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>Osaka Tech Lab に3名。
 神保町オフィスに6名の体制で仕事をしております。
 拠点が離れているのでやりとりは Slack や Teams 等のコミュニケーションツールを使って行っております。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>創業して僅か約4年の会社とは思えないほどに色んな制度やフローが整っている会社だと思いました。
 一方で自チームに関しては、まだ未整備な部分も多くこれからチームビルディングに関われる機会も多そうで楽しみに思いました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>私が勤務している Osaka Tech Lab はもともと1名だったのですが、私が入社した時点でやっと3名になりました。
 雰囲気としては拠点が離れているぶん、まだ神保町オフィスのトレンドに付いて行けていない印象があるので、コミュニケーション面で課題を感じました。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>前職でも書いていたので特に苦手意識や違和感はありませんでした。
 しかしネタ探しは会社が変わっても変わらず大変なので（笑）日頃から新しい技術に積極的に触れるようにしてネタに困らないようにしなければと思っております。</li>
</ul>
</li>
<li><strong>楊 小龍さん → 林田 洋平さんへの質問</strong><ul>
<li><strong>休みの日どう過ごしますか？</strong><ul>
<li>妻と子の5人家族なのでもっぱら家族サービスに精を出しております。
先週は家族5人で車で愛知県の中部国際空港セントレアに遊びに行って来ました。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>兒玉 桜</h1>
<ul>
<li><strong>自己紹介</strong><ul>
<li>データ分析部 分析プロデュースG でデータ分析をしています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>上長＋先輩4名＋自分</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>入社オリエンテーションが手厚くて驚きました。
 それ以外は事前にお聞きしていた通り適度(？) に自由な印象を受けているので、ギャップはありませんでした。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>みんな穏やかで優しい。
 しかし仕事はプロフェッショナルという謎のギャップにやられています。
 (プロフェッショナル＝怖いという先入観あり)</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>話をお聞きしたときは、正直「めんどうだな」と思いました。
 が、実際に記事を書いていると自分の現在地の再確認にもなりやって良かったなと感じました。
 何事も挑戦する事が大事だなと思いました。</li>
</ul>
</li>
<li><strong>林田 洋平さん → 兒玉 桜さんへの質問</strong><ul>
<li><strong>データ分析のお仕事をしようと思ったきっかけは？</strong><ul>
<li>アウトソーシング業界にいたある時、急遽欠員が出たクライアント先の穴埋めとしてたまたま配属されたのがアクセス解析チームでした。
そんな仕事がある事すら知らなかった未経験状態で始まったのですが、業務にあたるうちに深めたくなってしまい現在に至ります。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>小川 就也</h1>
<ul>
<li><strong>自己紹介</strong><ul>
<li>業務システム開発部 Salesforce開発G 小川です。
 Factory BOシステムの保守を担当しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>マネージャー1名メンバー5名でSalesforceの運用保守、データ連携、Salesforceが関連するプロジェクト対応を行っています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>技術者集団=ある程度の技術力や知識が無いと質問や相談しにくいのでは？と思っていましたが、気軽に質問/相談できる雰囲気でした。
 レスポンスもとても早く、親身に対応してくださる方が多いです。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>入社したときの第一印象、ギャップにも書きましたが、とても気軽に質問/相談ができる雰囲気です。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>今までブログを書くことをやったことが無かったので、機会をいただけたことに感謝しています。</li>
</ul>
</li>
<li><strong>兒玉 桜さん → 小川 就也さんへの質問</strong><ul>
<li><strong>仕事の合間や休憩時間、どのようにリフレッシュしていますか？</strong><ul>
<li>在宅勤務時は昼休憩に30分程ランニングしてます。
 ランニング中は考えている余裕が無くなるので強制的に無心になれます。
 出社時はまだ模索中です。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>安田 早希</h1>
<p><img src="/assets/blog/authors/hayashi-d1125/newcomers/member_y.jpeg" alt="Yasuda"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>プラットフォーム開発部 Cloud InfrastructureGに所属となりました、やっさんです。
 部署名の通り、サービス基盤のクラウドインフラの部分を担当しております。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>クラウドインフラGとしては9名体制ですが、さらに細かくチームが分かれております。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>ルールとか非常に硬いイメージを持って入社したのですが、実際は上下関係なく接しやすく、チャットでもスタンプが飛び交うような和気あいあいとしていたことが、ギャップでしたね。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>前職では、「静かに、音を立てない」といった雰囲気だったこともあり、なかなか業務のことを相談しずらかったのですが、今はすぐに相談できる状況ですし、チーム仲がよいので出社メンバーで必ず、ランチを食べに行っています♪</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>入社前に見ていたので、自分も書くことになったのか～と、感慨深いです</li>
</ul>
</li>
<li><strong>小川 就也さん → 安田 早希さんへの質問</strong><ul>
<li><strong>神保町オフィス周辺でおすすめの飲食店はありますか？</strong><ul>
<li>先日行った、「森のブッチャーズ」というお店がおすすめです。
 ランチメニューで、ビーフやポークのステーキが食べ応えあってよかったです。
 11時半ごろに行って30分待ちましたが、並ぶ価値がありました！</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>工藤 啓</h1>
<p><img src="/assets/blog/authors/hayashi-d1125/newcomers/member_k.png" alt="Kudo"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>モビリティプロダクト開発部エンゲージG配属になりました工藤です。
 販売店様内の業務DX化支援を行います。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>チームはMgr含め3名ですが、部内の開発担当チームやKINTO営業部とも協業しながら販売店様に入り込みDXニーズを聞き出していきます。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>エンジニアが周りにたくさんいるなーという印象。
 これまでエンジニアとがっつり仕事をしたことがなかったので皆さんのモニター画面がコードばかりなのが新鮮でした。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>現場に訪問することが多く、社内にいないことが多いのですが、全員販売店様ファーストで提案を考えていることに刺激を受けます。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>会社のブログに携わることがこれまでなかったのでこの内容が発信されると思うと、本当に大丈夫かなと心配になります。</li>
</ul>
</li>
<li><strong>安田 早希さん → 工藤 啓さんへの質問</strong><ul>
<li><strong>エンゲージメントを高めるために生成AIを使うとしたら、どのようなものがあると思いますか？</strong><ul>
<li>お客様への代替車種提案業務など既に、生成AIを活用したプロダクトがあります。
 他にも社内問合せ業務の効率化に活用するなど、販売店様への業務DXにおいて活用のシーンはかなり多いと思います。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTO テクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Meet Our New Team Members: August and September 2024 Update (Part 1)]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-01-14-newcomers-introduction-aug-sep-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-01-14-newcomers-introduction-aug-sep-en/</guid>
            <pubDate>Thu, 15 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Meet the fresh faces at KINTO Technologies this August and September: our latest team additions.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I&#39;m kh, and I came on board this September! In this article, we asked those who joined the company in August and September 2024 about their impressions immediately after joining the company, and summarized them. I hope this content will be useful for those who are interested in KINTO Technologies, and serve as a reflection for the members who participated in the interview.</p>
<h1>Naito</h1>
<ul>
<li><strong>Self-introduction</strong><br>I work in the Engineering Office. I&#39;m involved in improving processes across different product teams.</li>
<li><strong>What is the structure of your team?</strong><br>The Engineering Office was just established in January and consists of two people.</li>
<li><strong>What was your first impression of KTC (KINTO Technologies) when you joined? Were there any surprises?</strong><br>Thanks to the interviews and chats I had before joining, there weren&#39;t any major surprises. The company has offices in Tokyo, Nagoya, and Osaka, with people frequently coming and going between them. On my very first day, I met several colleagues from the Nagoya office and thought, &#39;Wow, Nagoya’s actually pretty close! As I started chatting with more people in the company, I realized how embedded &quot;kaizen&quot; is in everyday work. Everyone knows about the Toyota Production System (TPS), which impressed me.</li>
<li><strong>What is the atmosphere like on-site?</strong><br>I&#39;ve been reaching out to various development teams for chats and advice, and everyone has been genuinely kind. I really appreciate how they loop me in and make me feel part of things!</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I had been reading tech blogs before I joined the company. So being on the writing side now feels pretty exciting.</li>
<li><strong>Question from Mari: Why did you choose this company?</strong><br>Cars are a familiar part of my daily life, so the domain naturally caught my interest. On top of that, I felt like KTC is a company that&#39;s going to keep evolving—and I wanted to be part of that journey and enjoy the changes firsthand.</li>
</ul>
<h1>R.Y.</h1>
<ul>
<li><strong>Self-introduction</strong><br>I&#39;m originally from Hong Kong and moved to Japan in 2023. After graduating, I joined KTC this August, and now I&#39;m part of the Security &amp; Privacy Group.</li>
<li><strong>What is the structure of your team?</strong><br>In the Security &amp; Privacy Group, we&#39;re divided into teams based on our responsibilities. I&#39;m on the Defense Team, where I mainly handle vulnerability assessments and respond to security consultation tickets.</li>
<li><strong>What was your first impression of KTC (KINTO Technologies) when you joined? Were there any surprises?</strong><br>The office is really spacious, with lots of open areas that everyone can use freely. There are also plenty of study sessions and social events, so you get the chance to pick up knowledge beyond your daily work.</li>
<li><strong>What is the atmosphere like on-site?</strong><br>Everyone is friendly and easy to talk to. In addition to work, there are all kinds of Slack channels for hobbies and local topics, so it&#39;s easy to connect with others who share your interests.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>Looking back on everything I&#39;ve experienced over the past three months—and getting to share it with future team members—makes me genuinely happy.</li>
<li><strong>Question from Naito: I&#39;d love to travel to your hometown, Hong Kong. Please let me know if you have any recommendations for sightseeing or food!</strong><br>If you&#39;re a Disney fan, Hong Kong Disneyland is a must. The park is compact, so it&#39;s easy to explore the whole park in one day. When it comes to food, Hong Kong has tons of delicious local comfort food. Some examples of must-tries: chewy and bouncy curry fish balls, fish-meat shumai, and rich, bold-flavored Hong Kong-style milk tea. Eating on the go is common in Hong Kong, so dive in and enjoy the experience of this culture. (By the way, you can try some popular Hong Kong eats right here in Japan too—check out the <a href="https://www.tjsamgor.jp/">Tam Jai Sam Goh Official Website</a> if you&#39;re curious.)</li>
</ul>
<h1>tHyt</h1>
<ul>
<li><strong>Self-introduction</strong><br>Hi, I&#39;m tHyt from the Mobility Product Development Division. While I take on some engineering lead responsibilities, I mainly work as a front-end engineer.</li>
<li><strong>What is the structure of your team?</strong><br>Our team has nine members, with specialists across front-end, back-end, and infrastructure. It&#39;s a well-rounded crew.</li>
<li><strong>What was your first impression of KTC (KINTO Technologies) when you joined? Were there any surprises?</strong><br>Since the company is still relatively young, I expected some areas might be underdeveloped in terms of systems and structure—but to my surprise, everything was impressively well organized.</li>
<li><strong>What is the atmosphere like on-site?</strong><br>I&#39;m based at the Osaka Tech Lab, and since the members aren&#39;t many, there&#39;s always chatter happening across teams. It keeps things interesting. All of my team members except for myself work in the Muromachi office, but we enjoy working together through daily meetings and business trips.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I knew about these new joiner blog posts even before I joined the company, and the day has finally come for me to write one...! That&#39;s how I feel. I&#39;m looking forward to using the tech blog to share more than just intros!</li>
<li><strong>Question from R.Y.: After joining the company, was there anything you struggled to get used to? How did you overcome it?</strong><br>Hands down, business trips. Since I&#39;m the only one in my team based in Osaka, I travel to Tokyo about once a month to help keep communication flowing. (Huge thanks to the company for supporting that!) Before joining KTC, I had only ever been on one business trip, and I rarely took the Shinkansen—so at first, I was pretty anxious about it all. But now, I&#39;ve gotten used to it and have no trouble traveling. Remote communication works well, but face-to-face interaction is different. I&#39;d definitely like to keep that going.</li>
</ul>
<h1>kh</h1>
<p>![](/assets/blog/authors/kh/kh.png =250x)</p>
<ul>
<li><strong>Self-introduction</strong><br>I&#39;m part of the Analysis Group. My work mainly involves building the data infrastructure and collecting the data needed for analysis.</li>
<li><strong>What is the structure of your team?</strong><br>There are 10 people including the leader, myself, and folks from partner companies and temporary staff.</li>
<li><strong>What was your first impression of KTC (KINTO Technologies) when you joined? Were there any surprises?</strong><br>Since KTC is still a relatively new company, I was expecting a fast-paced, startup-like vibe. But it actually felt more relaxed—in a good way.</li>
<li><strong>What is the atmosphere like on-site?</strong><br>We are a group of people with a variety of backgrounds, and everyone brings something different to the table in a friendly vibe.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I’d known KTC was active with tech blog posts, so writing this felt like taking that first step into being part of that culture.</li>
<li><strong>Question from tHyt: Got any tips for data analysis?</strong><br>Well, I actually focus more on building the data infrastructure rather than doing the analysis itself, so this might not be the exact answer you were expecting. But no matter how great your data analysis is, it won&#39;t be helpful if the data quality is poor. Ensuring data quality at the platform level is, in my view, the essential first step in any successful data analysis.</li>
</ul>
<h1>woshahua</h1>
<ul>
<li><strong>Self-introduction</strong><br>Hi, my name is Gao. I work in the Woven Payment Solution Development Group! I&#39;m a Chinese-born engineer with a solid Kansai dialect—it&#39;s stuck with me completely. My hobbies are basketball, sauna and poker. Recently, I&#39;ve been working on a personal app that analyzes poker win rates—just for fun!</li>
<li><strong>What is the structure of your team?</strong><br>Our team is primarily made up of engineers, and we also work together with engineers from Woven. What I really like is that, no matter which organization we’re part of, everyone&#39;s aligned and moving together toward the same goals.</li>
<li><strong>What was your first impression of KTC (KINTO Technologies) when you joined? Were there any surprises?</strong><br>Even though KTC&#39;s attempt is still relatively new in the automotive field, it struck me as a company actively taking on a wide range of services and development projects. No big surprises, but I was honestly impressed by how much the company encourages technical sharing—and how there are study sessions going on nearly every week.</li>
<li><strong>What is the atmosphere like on-site?</strong><br>It&#39;s an environment where you&#39;re encouraged to explore all sorts of technologies, regardless of your past experience. Whenever I run into a roadblock, someone on the team is always quick to jump in and help. I really appreciate how supportive everyone is.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>Both the company and the team are big on promoting tech knowledge, so I&#39;m excited to contribute and share more going forward!</li>
<li><strong>Question from kh: I heard that you have a pet. Does living with a pet affect your daily life or work?</strong><br>Yes! I have a guinea pig at home, and just watching it after work is incredibly soothing. Being able to work remotely means I can spend more time with my pet, which I love. I truly think having emotional breathing room at work makes a difference in performance. My guinea pig helps ease the stress and gives me a nice mental reset!</li>
</ul>
<h1>Mari</h1>
<ul>
<li><strong>Self-introduction</strong><br>I&#39;m a team member of the Development Support Division, Human Resources Group, Talent Acquisition Team. We are recruiting mainly engineers.</li>
<li><strong>What is the structure of your team?</strong><br>There are six of us, including myself—and starting in January, we&#39;ll all be working from the Muromachi office!</li>
<li><strong>What was your first impression of KTC (KINTO Technologies) when you joined? Were there any surprises?</strong><br>The office is lovely! All the engineers are easy to talk to—cheerful, approachable, and just fun to be around.</li>
<li><strong>What is the atmosphere like on-site?</strong><br>The atmosphere is joyful and open—everyone&#39;s kind, and honest. It&#39;s full of people who are not only hardworking but also genuinely considerate of those around them.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I wasn&#39;t sure what to write at first, but I ended up going with an honest take on how I’m feeling right now! I also got inspiration from reading everyone&#39;s blog posts!</li>
<li><strong>Question from woshahua: How do you refresh yourself during work or on your days off?</strong><br>During work, I like to go for short walks to clear my head. On my days off, I try to stay active—going out or hitting the gym whenever I can.</li>
</ul>
<h1>C4</h1>
<p>![](/assets/blog/authors/kh/20250123/c4.jpg =250x)</p>
<ul>
<li><strong>Self-introduction</strong><br>I&#39;m am part of KTC, but currently seconded to the KINTO Administration Department, where I mainly handle general office operations. My MBTI type is ISFJ-T, so I think I&#39;m naturally suited for administrative work, though that might just be wishful thinking.</li>
<li><strong>What is the structure of your team?</strong><br>There are eight of us working at the Nagoya office, including myself.</li>
<li><strong>What was your first impression of KTC (KINTO Technologies) when you joined? Were there any surprises?</strong><br>My first thought was, &quot;Oh, this office is really stylish.&quot; It was my first time working in an office, and I imagined it to be more formal, but the atmosphere in the team is relaxed and friendly, and we often go out to eat outside of work, so I think there is a lot of communication.</li>
<li><strong>What is the atmosphere like on-site?</strong><br>Not just my team, but everyone around me has been incredibly kind. Even when I ask questions during busy times, no one has ever reacted negatively or made me feel like a bother. We have weekly 1-on-1s and team meetings, which help me stay in the loop about what others are working on—even if I&#39;m not directly involved—and I&#39;m really grateful to have regular chances to talk through any challenges in my own work.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>To be honest, I wasn&#39;t sure if a story from a non-engineer like me—doing admin and general affairs—would even be useful on a tech blog (laughs). But here I am, giving it a shot.</li>
<li><strong>Question from Mari: My MBTI is ESFP! The results were eerily accurate, especially the part about struggling with details. What about you? Was ISFJ-T accurate for you? What parts hit home?</strong><br>Oh, yes. Some parts hit a little too close to home, like &quot;resistant to change,&quot; &quot;perfectionist,&quot; and &quot;self-critical&quot; (ouch, right? lol). And that mindset of &quot;If it makes someone else happy, I&#39;ll put myself second&quot;—yep, that one definitely checks out. So yeah, I&#39;d say the result was pretty spot-on. MBTI is great for self-reflection. I definitely recommend giving it a try!!</li>
</ul>
<h1>Mari</h1>
<ul>
<li><strong>Self-introduction</strong><br>I&#39;m a UI/UX designer in the Retailer Digital Transformation Planning Team, part of the Mobility Product Development Division&#39;s DX Planning Promotion Group. I design products that support the digital transformation of dealership operations.</li>
<li><strong>What is the structure of your team?</strong><br>We&#39;ve got one team lead, three directors, and four designers (including me).</li>
<li><strong>What was your first impression of KTC (KINTO Technologies) when you joined? Were there any surprises?</strong><br>Since the parent company is big, I expected a lot of structure—and that&#39;s definitely true. But at the same time, the atmosphere is surprisingly free and relaxed. No real surprises after joining.</li>
<li><strong>What is the atmosphere like on-site?</strong><br>It&#39;s busy and we&#39;ve got a lot on our plates. But that just makes the work more rewarding. The team&#39;s full of energy, and everyone gets along great. Very positive and easygoing environment.</li>
<li><strong>How did you feel about writing a blog post?</strong><br>I wasn&#39;t sure what to write at first, but ended up just sharing how I genuinely feel! I&#39;m also looking forward to reading everyone else&#39;s posts!</li>
<li><strong>Question from C4: I heard you have a cat at home! I live with five myself. What&#39;s one product your cat absolutely loved? (Snacks, toys—anything!)</strong><br>Five!! Amazing! 🥰 My cat&#39;s a bit of a diva—most things get the “how dare you present me this peasant toy” treatment. That said, Churu treats is her one true weakness—especially the premium ones with fancy ingredients. Total game-changer 😂 Also, not sure how she feels about it, but I got a Miruto automatic litter box—makes my life way easier and has been super helpful! Oh, and the cat tower I bought? She&#39;s never touched it. After a month of rejection, it sadly became oversized trash…</li>
</ul>
<h1>Finally</h1>
<p>Thanks to everyone who shared their first impressions after joining!</p>
<p>There are more and more new members at KINTO Technologies every day! We&#39;ll be posting more new-joiner stories from across divisions, so stay tuned!</p>
<p>And yes — we&#39;re still hiring! KINTO Technologies is looking for new teammates to join us across a variety of departments and roles. For more details, check it out <a href="https://www.kinto-technologies.com/recruit/">here</a>!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[「Appium Meetup Tokyo #2」開催決定！モバイルテスト自動化を極める最前線]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-15-Appium-Meetup-Tokyo-2/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-15-Appium-Meetup-Tokyo-2/</guid>
            <pubDate>Thu, 15 May 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<h1>📱 Appium Meetup Tokyo #2 開催決定！大好評のモバイルテスト自動化イベントがさらに進化して帰ってくる！</h1>
<p>技術広報Gの中西です。前回大きな盛り上がりを見せた「Appium Meetup Tokyo」の第2回目が、2025年5月28日（水）にKINTOテクノロジーズの室町オフィスで開催されます。前回はAppiumの実践的な活用事例やパフォーマンス改善の劇的な成功例が紹介され、多くの参加者から高い評価をいただきました。</p>
<h2>🎯 今回の注目ポイント</h2>
<p>今回のイベントでは、モバイルアプリのE2Eテストや自動化を一段階引き上げるためのノウハウや最先端技術を、現場で活躍するエンジニアたちが余すことなくお伝えします。</p>
<ul>
<li><p><strong>Appiumテストの失敗原因調査のコツと実践</strong>（MagicPod 脇坂 雅幸 氏）</p>
<ul>
<li>モバイルE2Eテストにおける失敗原因を迅速に特定・解決するためのデバッグ手法を具体的に解説！Appium Inspectorの活用法やWebDriverAgentに関連するトラブルシューティングの実例を共有します。</li>
</ul>
</li>
<li><p><strong>モバイルアプリのVRT環境構築に向けた取り組み</strong>（カシオ計算機 髙橋 秀徳 氏）</p>
<ul>
<li>Flutter製アプリ「CASIO WATCHES」でのAppiumを活用したビジュアルリグレッションテスト（VRT）自動化事例を紹介！スクラム開発の現場での課題解決と効率化への実践的なアプローチを詳しく解説します。</li>
</ul>
</li>
<li><p><strong>手動リンクチェック撲滅！Appiumで実現するネイティブアプリURL完全自動化</strong>（KINTOテクノロジーズ 岡 郁弥、パンヌウェイ）</p>
<ul>
<li>Webでは簡単にできるリンク先チェックの自動化ですが、ネイティブアプリでは技術的な制約から実現が難しいとされてきました。その課題をクリアし、ネイティブアプリでもリンクチェックを完全自動化するための具体的なノウハウと工夫を共有します。</li>
</ul>
</li>
<li><p><strong>Appiumで安定したスクロール量を実現するちょっとした小技</strong>（Autify 武藤 大樹 氏）</p>
<ul>
<li>実務で即活用できるノウハウ満載！小さな工夫で大きな改善をもたらすテクニックをご紹介します。</li>
</ul>
</li>
</ul>
<h2>🕒 タイムスケジュール</h2>
<table>
<thead>
<tr>
<th>時間</th>
<th>セッション内容</th>
</tr>
</thead>
<tbody><tr>
<td>18:30</td>
<td>開場</td>
</tr>
<tr>
<td>19:00</td>
<td>イベント開始</td>
</tr>
<tr>
<td>19:05</td>
<td>Appiumテストの失敗原因調査のコツと実践</td>
</tr>
<tr>
<td>19:45</td>
<td>モバイルアプリのVRT環境構築に向けた取り組み</td>
</tr>
<tr>
<td>20:05</td>
<td>休憩</td>
</tr>
<tr>
<td>20:15</td>
<td>手動リンクチェック撲滅！Appiumで実現するネイティブアプリURL完全自動化</td>
</tr>
<tr>
<td>20:35</td>
<td>Appiumで安定したスクロール量を実現するちょっとした小技</td>
</tr>
<tr>
<td>20:55</td>
<td>懇親会（現地参加のみ）</td>
</tr>
<tr>
<td>21:30</td>
<td>撤収</td>
</tr>
</tbody></table>
<h2>🏢 開催概要</h2>
<ul>
<li><strong>日時</strong>：2025年5月28日（水）19:00～21:30（18:30開場）</li>
<li><strong>会場</strong>：KINTOテクノロジーズ 室町オフィス（東京都中央区日本橋室町2-3-1 室町古河三井ビルディング16階）</li>
<li><strong>参加費</strong>：無料</li>
<li><strong>定員</strong>：現地参加20名、オンライン参加50名（Zoomウェビナー）</li>
</ul>
<h2>🌟 お申し込み方法</h2>
<p>参加希望の方は、<a href="https://appiummeetuptokyo.connpass.com/event/353600/">イベントページ</a>よりお早めにお申し込みください。ご登録はお早めに！</p>
<h2>💬 こんな方に特におすすめです</h2>
<ul>
<li>モバイルアプリのテスト自動化を次のレベルに引き上げたい方</li>
<li>Appiumの導入・活用に興味があり、具体的な成功事例を知りたい方</li>
<li>最新の自動化技術を学び、QA業務の負担を劇的に軽減したい方</li>
</ul>
<h2>🚀 モバイルテストの未来を一緒に創ろう！</h2>
<p>前回の参加者アンケートでもAppiumの実践的な活用方法やノウハウに関する関心が非常に高く、コミュニティの交流も活発でした。今回も充実した内容とともに、新たな交流の機会をご用意しております。皆さんとお会いできるのを楽しみにしています！</p>
<h2>参加を検討している方へ</h2>
<ul>
<li><strong>モバイルアプリの自動テストをこれから本格的に導入したい方</strong>  </li>
<li><strong>Appiumに興味があるが具体的な事例やノウハウが欲しい方</strong>  </li>
<li><strong>CI/CDと組み合わせた運用に関心があるエンジニアやQA担当の方</strong>  </li>
<li><strong>他社事例を参考に自社のテスト文化を改善したい方</strong></li>
</ul>
<p>上記に当てはまる方は、ぜひAppium Meetup Tokyoで最新の知見を共有し合いましょう。今後の告知や詳細情報は<a href="https://x.com/AutifyJapan">@AutifyJapan</a>や<a href="https://x.com/KintoTech_Dev">@KintoTech_Dev</a>でさせて頂きます。ご質問やご要望などがございましたら、お気軽にお寄せください。</p>
<p>Appium Meetup Tokyoでお会いできることを心より楽しみにしています。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-05-15-appium-meetup-tokyo/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Lambda + TypeScript + Express.jsでAPIをサクサク開発して低コストで運用する話]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-15-serverless-api-lambda-express/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-15-serverless-api-lambda-express/</guid>
            <pubDate>Thu, 15 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Lambda、TypeScript、Express.jsを活用し、APIを迅速に開発しつつ、特に小規模なプロダクトにおいて運用コストを最小限に抑える方法を具体的に解説します。]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは！<br>KINTOテクノロジーズでWebエンジニアをしている亀山です。<br>現在はマーケティングプロダクト開発グループという部署に所属しています。  </p>
<p>今回はサーバレス構築についてお話しします。</p>
<p>ECSなどのコンテナが常時起動しているアプリケーションは、稼働している間、たとえリクエストが来なくても起動している時間分のリソース（CPUやメモリ）に対して課金が発生します。そのため、PoC開発や極端にリクエストが少ないプロダクトにおいては特に「<strong>使っていないのにお金がかかる</strong>」状態になりがちです。<br>そのようなプロダクトに対しては、使っている間だけサーバーが立ち上がり、一定時間処理が行われないと自動で停止するような従量課金型のサーバレスアーキテクチャによってランニングコストを大幅に抑えることが可能になります。</p>
<p>そこで私たちは下記の要点でLambdaアプリケーションを構築しました。</p>
<ul>
<li>AWSのAPI Gateway + Lambdaでサーバレス開発</li>
<li>TypeScript + Expressでシンプルかつ汎用的なAPI開発</li>
</ul>
<h2>サーバレスについて</h2>
<p>AWSのサーバレス構成として広く用いられているLambdaを採用しましたLambdaはサーバレスアーキテクチャであるため、先にも述べましたが、サーバーの起動・停止やスケーリングを自動で行ってくれ、使った分だけ課金される従量課金制でコストを最小限に抑えられます。</p>
<p>一方でこのようなサーバレスAPIのデメリットとしては、<strong>コールドスタートによる応答遅延</strong>が発生する点があります。特にリクエスト数の少ない環境や、一定時間アクセスがないときにLambdaがスリープ状態になり、再度リクエストが来た際にコンテナの起動時間（実測値として応答時間約1秒）がかかるという特性があります。</p>
<p>まとめると、<strong>とにかくサクッとプロトタイプを構築したいであったり、応答遅延を許容できるようなユーザー（例えば社内メンバー）向けのツールを構築したいという方には特におすすめな</strong>インフラ構成になります。</p>
<h2>Lambdaでどれくらい費用が安くなるのか？</h2>
<p>常時実行コンテナのFargateと今回使用するサーバレス型のLambdaで料金比較をしてみます。</p>
<h3>Fargate</h3>
<p><a href="https://aws.amazon.com/jp/fargate/pricing/">AWS Fargateの料金について</a></p>
<p>0.5 vCPU でメモリ2GBを想定して1時間あたりの1タスク稼働費用は</p>
<ul>
<li>vCPU料金: 0.5 vCPU * $0.04048 / vCPU-時間 = $0.02024 / 時間</li>
<li>メモリ料金: 2GB * $0.004445 / GB-時間 = $0.00889 / 時間</li>
</ul>
<p>という計算から1時間あたり合計費用は $0.02024 + $0.00889 = $0.02913 となり、
そのため1ヶ月720時間稼働するとすると$0.02913 * 720時間 = <strong>$20.9736</strong> が1タスク月当たりかかります。（ただし、夜間停止やvCPUスペックダウンによって節約すること可能）</p>
<p>これは1環境あたりの費用なので本番環境、開発環境など複数の環境が必要であればその数だけ掛け算されます。</p>
<h3>Lambda</h3>
<p><a href="https://docs.aws.amazon.com/ja_jp/whitepapers/latest/how-aws-pricing-works/aws-lambda.html">AWS Lambdaの料金について</a>
一方でLambdaはリクエスト件数とリクエストによって一時的に起動するコンテナの計算時間（コンピューティング時間）によって計算されます。</p>
<ul>
<li>GB-秒あたり0.00001667 USD</li>
<li>1,000,000 件のリクエストあたり0.20 USD</li>
</ul>
<p>Fargateと同様の2GBで、1リクエストあたり0.5秒の計算時間、さらに1ヶ月あたり10万件のリクエストであると想定すると月当たりの合計費用は$0.02 (リクエスト費用) + $1.6667 (計算費用) = 約 <strong>$1.69</strong> が月当たりかかります。<br>さらに良いことに、環境を増やしたり、1環境あたりのLambda数を増やしたとしても、合計リクエスト数が同じであれば上記と同じ金額になります。</p>
<p>これらの計算シミュレーションからLambdaのコスト面での優位性がわかると思います。</p>
<p>特に売上の出ないかつトラフィックの少ない社内向けツールであったり、PoCのプロダクトであればこのような費用削減は特にメリットが大きくコスト面でのハードルを下げてくれることが期待できます。</p>
<h2>Expressについて</h2>
<p>サーバサイドJSのフレームワークとしてExpressを採用しました。</p>
<p>ルーティングやミドルウェアの概念が直感的に理解できるように設計されており、初めて Node.js でサーバサイド開発を行う開発者にとっても扱いやすい構成となっています。小規模なAPIから中〜大規模なアプリケーションまで、スムーズに拡張できる設計が可能です。また、ルーティングの記述も簡潔です。</p>
<pre><code class="language-TypeScript">app.get(&#39;/users/:id&#39;, (req, res) =&gt; {
  res.send(`User: ${req.params.id}`);
});
</code></pre>
<p>ログ出力には morgan、認証処理には passport、入力バリデーションには express-validator など、用途に応じて豊富なミドルウェアのライブラリを簡単に組み込むことができます。これにより、アプリケーションの機能追加や保守が容易になります。</p>
<p>AWS公式で配布されているLambdaライブラリを用いてエンドポイントを構築することも可能ですが、汎用ライブラリであるExpressを用いて構築しておけばアプリケーションの規模が拡大したタイミングでECSやApp Runnerに転用する際にエンドポイント以降のコードを転用することもLamda特化のライブラリを使用するより容易になります。</p>
<h1>開発方針</h1>
<p>本記事では、<strong>1つのLambda関数に複数の API エンドポイントをまとめて搭載する構成</strong>を採用しています。</p>
<p>これは、Lambdaの特性である「ホットスタート」を活用するためです。</p>
<p>Lambda関数は一度起動されると、一定時間はメモリ上に残り続ける「ホットスタート」の状態になります。そのため、1つのAPIがリクエストされて Lambdaが起動されたあとは、同じ関数内の他のAPIに対するリクエストも高速に処理できるようになります。</p>
<p>この性質を活かすことで、操作時のパフォーマンスの向上が期待できます。</p>
<p>ただし、Lambdaにはデプロイ可能なパッケージサイズに制限（zip圧縮時で50MB以内かつ展開後で250MB以内）があるため、アプリケーション内すべてのAPIを1つの関数に詰め込むと最終的にこの上限に到達するため、現実的ではありません。</p>
<p>そのため、<strong>画面単位や機能単位で関連のあるAPIを同一の Lambda関数にまとめる構成</strong>とし、最終的には1つのリポジトリで複数の Lambda関数を管理していくmonorepo構成を前提で進めていきます。</p>
<p>また本記事ではSAMによるローカル実行できるようにするまでをゴールとし、AWSコンソールの設定やデプロイ以降のお話は割愛させていただきます。</p>
<h1>環境構築（コーディングまでの準備）</h1>
<p>本記事では複数の Lambda関数や共有コードを管理しやすいpnpmにAWS SAM を組み合わせた環境構築方法を説明します。</p>
<p>プロジェクト全体を pnpm のワークスペースとして管理し、各 Lambda関数や共通ライブラリを独立したワークスペースとして扱います。デプロイツールにはAWS SAM（Serverless Application Model）を利用します。</p>
<p>主に以下のツールが必要になります。</p>
<ul>
<li>Node.js</li>
<li>pnpm</li>
<li>AWS CLI</li>
<li>AWS SAM CLI</li>
<li>Git (バージョン管理)</li>
</ul>
<p>Gitのインストールは割愛します。</p>
<h2>必要なツールのインストール</h2>
<h3>Node.js</h3>
<p>これまでと同様に Node.js が必要です。公式サイトから LTS 版をインストールしてください。</p>
<p><a href="https://nodejs.org/ja">Node.js公式サイト</a></p>
<p>インストール後、以下のコマンドでバージョンが表示されることを確認します。</p>
<pre><code class="language-Shell">node -v
npm -v # pnpmをインストールするために使用する
</code></pre>
<h3>pnpm</h3>
<p>依存ライブラリの管理には pnpm を使用します。pnpm は特に複数のモジュール（Lambda function）を1つのリポジトリで管理するようなmonorepo構成での依存解決やディスク使用効率に優れています。</p>
<p>以下の方法でインストールします。</p>
<pre><code class="language-Shell">npm install -g pnpm
</code></pre>
<p>または、curl などを使用する方法は pnpm の公式サイトを参照してください。 pnpm インストールガイド</p>
<p>インストール後、以下のコマンドでバージョンを確認します。</p>
<pre><code class="language-Shell">pnpm -v
</code></pre>
<h3>AWS CLI</h3>
<p>これまでと同様に AWS との連携に必要です。インストールおよび aws configure による認証情報の設定を行ってください。</p>
<p><a href="https://docs.aws.amazon.com/ja_jp/cli/latest/userguide/getting-started-install.html">AWS CLIインストールガイド</a></p>
<h3>AWS SAM CLI</h3>
<p>今回はデプロイツールとして AWS SAM (Serverless Application Model) を使用します。AWS SAM はサーバーレスアプリケーションのための IaC (Infrastructure as Code) フレームワークであり、SAM CLI はローカルでのビルド、テスト、デプロイをサポートします。</p>
<p>以下の公式サイトを参考に、お使いのOSに合わせた方法でインストールしてください。</p>
<p><a href="https://docs.aws.amazon.com/ja_jp/serverless-application-model/latest/developerguide/install-sam-cli.html">AWS SAM CLIインストールガイド</a></p>
<p>インストール後、以下のコマンドでバージョンを確認します。</p>
<pre><code class="language-Shell">sam --version
</code></pre>
<h2>プロジェクト構造とワークスペース設定</h2>
<p>プロジェクトのルートディレクトリには、monorepo全体の設定ファイルや、開発時に共通で使うツール（例: esbuild）の依存関係を定義する<code>package.json</code>を置きます。各 Lambda関数や共通ライブラリは、例えば<code>functions</code>ディレクトリの中にそれぞれ独立したサブディレクトリとして作成し、これらをpnpmのワークスペースとして定義します。</p>
<p>提供いただいた構造を参考に、基本的な構造と設定ファイルを説明します。</p>
<pre><code class="language-Shell">sample-app/             # (ルートディレクトリ)
├── functions/
│   ├── common/         # 共通コード用ワークスペース
│   │   ├── package.json
│   │   ├── src/
│   │   └── tsconfig.json
│   ├── function-1/     # Lambda関数1用ワークスペース
│   │   ├── package.json
│   │   ├── src/        # Expressアプリやハンドラコード
│   │   └── tsconfig.json
│   └── function-2/     # Lambda関数2用ワークスペース
│       ├── package.json
│       ├── src/
│       └── tsconfig.json
├── node_modules/       # pnpmによって管理される依存ライブラリ
├── package.json        # ルートのpackage.json
├── pnpm-lock.yaml      # ルートのロックファイル
├── pnpm-workspace.yaml # ワークスペース定義ファイル
├── samconfig.toml      # SAM デプロイ設定ファイル (初回デプロイで生成)
└── template.yaml       # AWS SAM テンプレートファイル
</code></pre>
<h3>ルートのpackage.json</h3>
<p>リポジトリ全体で共有するスクリプトや開発ツール（esbuild など）を定義します。</p>
<p><code>package.json</code></p>
<pre><code class="language-JSON">{
  &quot;name&quot;: &quot;sample-lambda-app-root&quot;, // プロジェクト全体を表す名前
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;Serverless Express Monorepo with SAM and pnpm&quot;,
  &quot;main&quot;: &quot;index.js&quot;,
  &quot;private&quot;: true, // ルートパッケージは公開しない設定
  &quot;workspaces&quot;: [
    &quot;functions/*&quot; // ワークスペースとなるディレクトリを指定
  ],
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;pnpm -r build&quot;, // 全ワークスペースの build スクリプトを実行
    &quot;sam:build&quot;: &quot;sam build&quot;, // SAMでのビルド (後述)
    &quot;sam:local&quot;: &quot;sam local start-api&quot;, // SAMでのローカル実行 (後述)
    &quot;sam:deploy&quot;: &quot;sam deploy&quot; // SAMでのデプロイ (後述)
  },
  &quot;devDependencies&quot;: {
    &quot;esbuild&quot;: &quot;^0.25.3&quot; // 各ワークスペースのビルドで使う esbuild をルートで管理
    // 他、monorepo全体で使う開発ツールがあればここに追加
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;
}
</code></pre>
<h3>pnpm-workspace.yaml</h3>
<p>どのディレクトリをワークスペースとして扱うかを定義します。</p>
<p><code>pnpm-workspace.yaml</code></p>
<pre><code class="language-Yaml">packages:
  - &#39;functions/*&#39; # `functions` ディレクトリ内の全てのサブディレクトリをワークスペースとする
  # - &#39;packages/*&#39; # 別のワークスペースグループがあれば追加
</code></pre>
<h2>依存ライブラリ管理 (pnpm ワークスペース)</h2>
<p>各Lambda関数や共通ライブラリに必要な依存ライブラリは、それぞれのワークスペース内の package.json に記述します。</p>
<p>例: <code>functions/function-1/package.json</code></p>
<pre><code class="language-JSON">{
  &quot;name&quot;: &quot;function-1&quot;, // ワークスペースの名前
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;description&quot;: &quot;Lambda Function 1 with Express&quot;,
  &quot;scripts&quot;: {
    &quot;build&quot;: &quot;esbuild src/app.ts --bundle --minify --sourcemap --platform=node --outfile=dist/app.js&quot;, // esbuildでビルド
    &quot;start:dev&quot;: &quot;nodemon --watch src -e ts --exec \&quot;node dist/app.js\&quot;&quot;, // ローカルテスト用のスクリプト (SAM Localとは別に用意しても良い)
    &quot;tsc&quot;: &quot;tsc&quot; // 型チェック用
  },
  &quot;dependencies&quot;: {
    &quot;@codegenie/serverless-express&quot;: &quot;^4.16.0&quot;, // Lambdaアダプター
    &quot;express&quot;: &quot;^5.1.0&quot;,
    &quot;@sample-lambda-app/common&quot;: &quot;workspace:*&quot; // 共通ライブラリへの依存
  },
  &quot;devDependencies&quot;: {
    &quot;@types/aws-lambda&quot;: &quot;^8.10.138&quot;, // Lambdaの型定義
    &quot;@types/express&quot;: &quot;^4.17.21&quot;,
    &quot;nodemon&quot;: &quot;^3.1.0&quot;,
    &quot;typescript&quot;: &quot;^5.4.5&quot;
    // esbuild はルートの devDependencies にあるのでここでは不要
  },
  &quot;keywords&quot;: [],
  &quot;author&quot;: &quot;&quot;,
  &quot;license&quot;: &quot;ISC&quot;
}
</code></pre>
<ul>
<li><p><code>@sample-lambda-app/common</code>: <code>functions/common</code>ワークスペースを指します。<code>&quot;workspace:*&quot;</code>とすることで、ローカルの<code>common</code>ワークスペースを参照するようになります。<code>common</code>ワークスペース側の<code>package.json</code>で <code>&quot;name&quot;: &quot;@sample-lambda-app/common&quot;</code>, と定義している必要があります。</p>
</li>
<li><p><code>scripts.build</code>:<code>esbuild</code>を使用して、TypeScript コードと依存ライブラリをまとめて1つの JavaScript ファイル (dist/app.js) にバンドルする例です。Lambdaにデプロイするパッケージサイズを削減するために重要なステップです。</p>
</li>
</ul>
<p>依存ライブラリのインストールは、プロジェクトのルートディレクトリで <strong>一度だけ</strong> <code>pnpm install</code> を実行します。pnpmが<code>pnpm-workspace.yaml</code>を見て、各ワークスペースの<code>package.json</code>に記述された依存関係を解決し、効率的に<code>node_modules</code>を構成します。</p>
<pre><code class="language-Shell">pnpm install
</code></pre>
<p>特定のワークスペースにライブラリを追加したい場合は、ルートから以下のコマンドを実行します。</p>
<pre><code class="language-Shell">pnpm add &lt;package-name&gt; -w &lt;workspace-name&gt; # 例: pnpm add axios -w functions/function-1
pnpm add -D &lt;dev-package-name&gt; -w &lt;workspace-name&gt; # 開発依存の場合
</code></pre>
<h1>実際にサンプルコードを書いてみる</h1>
<p>先ほど説明したディレクトリ構成は、複数function構成になるように<code>function-1</code>と<code>function-2</code>の2つのfunctionモジュールを用意し、それらのfunctionが共用部品として利用できるために<code>common</code>というモジュールを用意しています。</p>
<p>具体的にコードを書いていきます。</p>
<h2>commonのコード</h2>
<p>まず共通部品であるcommonにサンプルとしてミドルウェア関数を1つ書いてみます。</p>
<p><code>functions/common/src/middlewares/hello.ts</code></p>
<pre><code class="language-TypeScript">import { Request, Response, NextFunction } from &#39;express&#39;;
/**
 * サンプル共通ミドルウェア
 * リクエストログを出力し、カスタムヘッダーを追加します。
 */
export const helloMiddleware = (req: Request, res: Response, next: NextFunction) =&gt; {
  console.log(`[Common Middleware] Received request: ${req.method} ${req.path}`);
  // レスポンスにカスタムヘッダーを追加
  res.setHeader(&#39;X-Sample-Common-Middleware&#39;, &#39;Applied&#39;);
  // 次のミドルウェアまたはルートハンドラに進む
  next();
};
続いて、middlewares/内のエクスポートを追加します。

functions/common/src/middlewares/index.ts


export * from &#39;./hello&#39;;
// middlewares内に他のミドルウェアがあればここに追加していく
さらにワークスペースのトップレベルのsrc/でもエクスポートしてあげる必要があります。

functions/common/src/index.ts


export * from &#39;./middlewares&#39;;

// middlewaresのような共通処理が他にあればここに追加していく（utilsとか）
</code></pre>
<h2>funciton-1側のコード</h2>
<p>次にfunction-1側のコードを書いていきます。</p>
<p><code>functions/function-1/src/app.ts</code></p>
<pre><code class="language-TypeScript">import express from &#39;express&#39;;
import serverlessExpress from &#39;@codegenie/serverless-express&#39;;
import { helloMiddleware, errorHandler } from &#39;@sample-lambda-app/common&#39;; // 共通ミドルウェア、エラーハンドラをインポート
// apiRouter のインポートは不要になりました
// import apiRouter from &#39;./routes/api&#39;;
// import cookieParser from &#39;cookie-parser&#39;; // 必要に応じてインストール・インポート
const app = express();
// express標準ミドルウェアの適用
app.use(express.json()); // JSONボディのパースを有効化
// app.use(cookieParser()); // クッキーパースが必要な場合このように追加する
// 共通ミドルウェアの適用
app.use(helloMiddleware);
app.get(&#39;/hello&#39;, (req, res) =&gt; {
  console.log(&#39;[Function 1 App] Handling GET /hello&#39;);
  res.json({ message: &#39;Hello from Function 1 /hello (Simplified)!&#39; });
});
app.post(&#39;/users&#39;, (req, res) =&gt; {
  console.log(&#39;[Function 1 App] Handling POST /users&#39;);
  console.log(&#39;Request Body:&#39;, req.body); // JSONボディをログ出力
  res.status(201).json({ received: req.body, status: &#39;User created (sample)&#39; });
});
// common等にエラーハンドラミドルウェアを作成し、使用する場合は全てのミドルウェアとルート定義の後に配置する。
// app.use(errorHandler); // 本記事では作成していない
// ハンドラのエクスポート
export const handler = serverlessExpress({ app });
</code></pre>
<p>補足：このあと行うtemplate.yamlでのAPI Gatewayの設定で、/function1 を取り除いたパスが渡されるようになるので、ここに定義するルートは、API Gateway のベースパスからの相対パスになります。
例えば、API Gatewayへのリクエストが /function1/hello なら、ここに定義する /hello にマッチします。</p>
<h2>function-2側のコード</h2>
<p><code>functions/function-2/src/app.ts</code></p>
<pre><code class="language-TypeScript">import express from &#39;express&#39;;
import serverlessExpress from &#39;@codegenie/serverless-express&#39;; // ★アダプターをインポート★
import { helloMiddleware, errorHandler } from &#39;@sample-lambda-app/common&#39;; // 共通ミドルウェア、エラーハンドラをインポート
// ルーターファイルは使用しないためインポート不要
// import apiRouter from &#39;./routes/api&#39;;
// import cookieParser from &#39;cookie-parser&#39;; // 必要に応じてインストール・インポート
const app = express();
// express標準ミドルウェアの適用
app.use(express.json()); // JSONボディのパースを有効化
// app.use(cookieParser()); // クッキーパースが必要な場合このように追加する
// 共通ミドルウェアの適用
app.use(helloMiddleware);
// ルートをごとに処理を定義
app.get(&#39;/bye&#39;, (req, res) =&gt; {
  console.log(&#39;[Function 2 App] Handling GET /bye&#39;);
  res.json({ message: &#39;Goodbye from Function 2 /bye!&#39; });
});
app.post(&#39;/items&#39;, (req, res) =&gt; {
  console.log(&#39;[Function 2 App] Handling POST /items&#39;);
  console.log(&#39;Request Body:&#39;, req.body); // JSONボディをログ出力
  res.status(201).json({ received: req.body, status: &#39;Item created (sample)&#39; });
});
app.get(&#39;/status&#39;, (req, res) =&gt; {
    console.log(&#39;[Function 2 App] Handling GET /status&#39;);
    res.json({ status: &#39;OK&#39;, function: &#39;Function 2 is running (Simplified)&#39; });
});
// common等にエラーハンドラミドルウェアを作成し、使用する場合は全てのミドルウェアとルート定義の後に配置する。
// app.use(errorHandler); // 本記事では作成していない
// ハンドラのエクスポート
export const handler = serverlessExpress({ app });
</code></pre>
<p>今回サンプルなのでルート内の処理を全てアロー関数で書いていますが、実際の開発では処理が複雑になったら別のtsファイルに処理をまとめた方が良いと思います。</p>
<p>また、開発していく中でルートごとに使用するミドルウェアを使い分けたい場面などが出てくると思います。その際はexpressのRouterのライブラリを用いてAPIルーターを作成するとより柔軟に作成できますので調べて試してみてください。（参考: <a href="https://expressjs.com/ja/guide/routing.html">https://expressjs.com/ja/guide/routing.html</a> ）</p>
<h1>SAMローカル実行するための準備</h1>
<h2>AWS SAM テンプレート (template.yaml)</h2>
<p>プロジェクトのルートに<code>template.yaml</code>ファイルを作成し、デプロイするAWSリソースを定義します。Lambda関数、API Gateway、必要な IAMロールなどを記述します。</p>
<p><code>template.yaml</code></p>
<pre><code class="language-Yaml">AWSTemplateFormatVersion: &#39;2010-09-09&#39;
Transform: AWS::Serverless-2016-10-31
Description: Sample Serverless Application
Globals:
  # Functions 全体に適用する共通設定 (メモリサイズやタイムアウトなど)
  Function:
    Timeout: 30
    MemorySize: 256 # 適宜調整する
    Runtime: nodejs20.x
    Architectures:
      - x86_64
    Environment:
      Variables:
        NODE_ENV: production
Resources:
  # function-1 ワークスペースに対応するLambda関数リソース定義
  Function1:
    Type: AWS::Serverless::Function # AWS SAMで定義するサーバーレス関数
    Properties:
      FunctionName: sample-express-function-1 # AWSコンソールに表示されるLambda関数名 (任意)
      Description: Express App for Function 1 (Simplified)
      # CodeUri は SAM がコードをパッケージングする際のソースディレクトリを指す。
      # ここには、sam build 前のソースコードがあるディレクトリを指定。
      CodeUri: functions/function-1/
      # Handler は、sam build によって生成された成果物の中でのエントリーポイントを指す。
      # esbuild が src/app.ts を dist/handler.js にバンドルし、
      # その中で &#39;export const handler = ...&#39; を CommonJS の &#39;exports.handler = ...&#39; に変換するため、
      # &#39;ファイル名(拡張子なし).エクスポート名&#39; と記述する。
      Handler: handler.handler
      Events:
        # API Gateway からのトリガー設定
        Function1Api:
          Type: Api # API Gateway REST APIをトリガーとする
          Properties:
            Path: /function1/{proxy+}
            # 許可するHTTPメソッド (ANYは全てのメソッドを許可)
            Method: ANY
  # function-2 ワークスペースに対応するLambda関数リソース定義
  Function2:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName: sample-express-function-2 # AWSコンソールに表示されるLambda関数名 (任意)
      Description: Express App for Function 2 (Simplified)
      # CodeUri は function-2 ワークスペースのソースディレクトリを指す
      CodeUri: functions/function-2/
      # Handler は function-2 のビルド成果物の中でのエントリーポイントを指す
      Handler: handler.handler
      Events:
        # API Gateway からのトリガー設定 (function-2用)
        Function2Api:
          Type: Api
          Properties:
            # Function 2 が処理するAPI Gatewayパス
            Path: /function2/{proxy+}
            Method: ANY
</code></pre>
<ul>
<li><code>Transform: AWS::Serverless-2016-10-31</code>: SAM テンプレートであることを示します。</li>
<li><code>Resources</code>: デプロイする AWS リソースを定義します。<ul>
<li><code>Type:AWS::Serverless::Function</code>: Lambda関数リソースです。</li>
<li><code>CodeUri</code>: Lambda関数としてデプロイするコードがあるディレクトリを指定します。<code>functions/function-1/dist</code>のように、各ワークスペースのビルド成果物の場所を指定します。</li>
<li><code>Handler</code>: Lambda関数が実行されるときに最初に呼び出されるコード内の関数名を指定します。バンドル後のファイル (<code>dist/app.js</code>) の中でエクスポートされる関数名になります。</li>
<li><code>Events</code>: その Lambda関数をトリガーするイベントを設定します。<code>Type: Api</code>は API Gateway からのHTTPリクエストをトリガーとする設定です。<code>Path</code>と<code>Method</code>で特定のエンドポイントに紐づけます。<code>/{proxy+}</code> は、そのパス以下の全てのリクエストをキャッチするための記法です。</li>
</ul>
</li>
</ul>
<h2>ローカルでの開発とテスト (AWS SAM CLI)</h2>
<p>AWS SAM CLI を使うと、ローカル環境で Lambda関数と API Gateway をエミュレートしてテストできます。</p>
<ol>
<li><p><strong>各ワークスペースのビルド</strong>: まず、各ワークスペースのソースコードを JavaScript にビルドします。ルートディレクトリで定義したスクリプトを利用できます。</p>
<pre><code class="language-Shell">pnpm run build # functions/* 以下のそれぞれの build スクリプトが実行される
</code></pre>
<p> これにより、例えば<code>functions/function-1/dist/app.js</code>のようなビルド成果物が生成されます。</p>
</li>
<li><p><strong>SAMビルド</strong>: 次に、AWS SAM がデプロイ用のパッケージを作成するためのビルドを実行します。</p>
<pre><code class="language-Shell">sam build
</code></pre>
<p> このコマンドは<code>template.yaml</code>を読み込み、<code>CodeUri:</code>で指定された場所にあるビルド成果物を (<code>.aws-sam/build</code>ディレクトリ以下に) コピーし、Lambdaが必要とする形式に整えます。</p>
</li>
<li><p><strong>ローカルAPI起動</strong>: SAM CLI が提供するローカル API 機能を使って、API Gatewayをエミュレートし、ローカルで Lambdaコードを実行できるようにします。</p>
<pre><code class="language-Shell">sam local start-api
</code></pre>
<p> コマンド実行後、<code>http://127.0.0.1:3000</code> のようなURLでローカルサーバーが起動します。ブラウザやPostman/curl などで、<code>template.yaml</code> で定義したパス（例: <code>/function1/hello</code>）にアクセスすると、ローカルで Lambda関数が実行されます。</p>
</li>
</ol>
<p>ローカル開発中は、ソースコードを変更したら pnpm run build → sam build → sam local start-api を再実行するか、sam local start-api --watch オプションを使ってコード変更を監視させるなどの方法があります。（--watch オプションはビルドやエミュレーションの再起動を自動で行ってくれますが、実際の環境構成によっては少し工夫が必要な場合もあります）</p>
<h1>終わりに</h1>
<p>今回はTypeScriptのサーバレスをLambdaとExpressを用いて、ローカル実行するまでの方法ご紹介しました。実際にプロダクトをリリースするにあたり、AWSインフラを構築し、適切な設定をするなどの作業が必要になります。</p>
<p>特にExpressであったりmonorepo構成については初めての試みであったため躓くことがあり、備忘録も兼ねて細かくご説明したため、少々長くなってしまったかもしれません。<br>同じように困っている人にとって少しでもお役に立てていただければ幸いです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/soju.kameyama/20250515/wordcloud_coverimage_serverless-api-lambda-express.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Exploring DeepSeek R1 with Azure AI Foundry]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-03-Azure-Deep-Seek-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-03-Azure-Deep-Seek-en/</guid>
            <pubDate>Wed, 14 May 2025 13:00:00 GMT</pubDate>
            <description><![CDATA[We tested DeepSeek R1 via API, now included in Azure AI Foundry's models.]]></description>
            <content:encoded><![CDATA[<p>Hello!<br>I am Wada (<a href="https://twitter.com/cognac_n">@cognac_n</a>), a Generative AI Evangelist at KINTO Technologies (KTC), part of the Generative AI Development Project.</p>
<hr>
<h1>Trying DeepSeek R1 Using Azure AI Foundry</h1>
<h2>1. Introduction</h2>
<p>Recently, large language models (LLMs) have seen significant progress, with numerous companies announcing their own versions. One standout gaining attention is <strong>DeepSeek R1</strong>, developed by the Chinese AI company DeepSeek.<br>On January 30, 2025, Microsoft made the DeepSeek R1 model available on <strong>Azure AI Foundry</strong> and GitHub. In this article, I’ll share a summary of the process I followed to try out DeepSeek R1 using Azure AI Foundry, along with my impressions.</p>
<hr>
<h2>2. What is Azure AI Foundry?</h2>
<p><strong>Azure AI Foundry</strong> is Microsoft&#39;s <strong>comprehensive platform for AI development</strong>. It offers a unified set of tools and services for developers to <strong>build, evaluate, and deploy</strong> generative AI solutions and custom copilots.</p>
<h3>Main features</h3>
<ol>
<li><p><strong>Utilizing various AI models:</strong><br>Supports a wide range of cutting-edge and open-source models provided by partner companies, including Microsoft, OpenAI, Hugging Face, Meta, and Mistral. DeepSeek R1 was also provided as part of this initiative.</p>
</li>
<li><p><strong>Integrated AI toolchain:</strong><br>An SDK, API, and portal are provided to accelerate the entire development lifecycle, offering a seamless experience from data preprocessing to model inference and continuous monitoring.</p>
</li>
<li><p><strong>Responsible AI practices:</strong><br>It has incorporated evaluation features, safety filters, and security controls to provide mechanisms to enhance the reliability and transparency of AI.</p>
</li>
<li><p><strong>Scalability for enterprises:</strong><br>It ensures high availability and scalability on Azure&#39;s managed infrastructure, supporting enterprise-level monitoring and governance.</p>
</li>
</ol>
<p>@<a href="https://learn.microsoft.com/ja-jp/azure/ai-studio/what-is-ai-studio/">card</a></p>
<p>The Azure AI Foundry portal states that when using models through the model catalog, prompts and outputs are not shared with Microsoft or the model provider. This allows for secure use of a variety of models.</p>
<blockquote>
<p>Microsoft provides and manages the hosting infrastructure and API endpoints. Models hosted in this &quot;Model as a Service&quot; (MaaS) scenario are subject to Azure data, privacy, and security commitment. Check the details of the Azure compliance certifications applicable to Azure AI Foundry. Microsoft functions as a data processor for prompts and outputs sent and generated by models (MaaS) deployed for pay-as-you-go billing system inference. Microsoft does not share these prompts and outputs with the model provider. Additionally, Microsoft does not use these prompts and outputs to train or improve Microsoft, model provider, or third-party models.</p>
</blockquote>
<p>@<a href="https://learn.microsoft.com/ja-jp/azure/ai-studio/how-to/concept-data-privacy/">card</a></p>
<hr>
<h2>3. Building a DeepSeek R1 Environment on Azure AI Foundry</h2>
<p>From this point forward, I’ll walk through the steps to actually use DeepSeek R1 on Azure AI Foundry. Please note that the detailed screen transitions and settings are based on the specifications as of January 31, 2025. Since changes to the UI and operations occur frequently, I will quote the official documentation as much as possible.</p>
<h3>Prerequisite preparations and account settings</h3>
<ol>
<li><strong>Preparation of an Azure account</strong>  </li>
<li><strong>Access to Azure AI Foundry</strong><br>@<a href="https://learn.microsoft.com/ja-jp/azure/ai-studio/what-is-ai-studio">card</a></li>
</ol>
<h3>Steps for installing DeepSeek R1</h3>
<ol>
<li><strong>Creating a Project:</strong><br>Create a new project on the Azure AI Foundry portal. DeepSeek-R1 is available in the following regions: [eastus2, westus3, northcentralus, eastus, southcentralus, westus]. (As of January 31, 2025)<br>@<a href="https://learn.microsoft.com/ja-jp/azure/ai-studio/how-to/create-projects?tabs=ai-studio">card</a></li>
<li><strong>Selecting DeepSeek R1 model:</strong><br>Search for DeepSeek R1 in the &quot;Model Catalog&quot; and transition to the model page.<br><img src="/assets/blog/authors/s.wada/20250131/image_1.webp" alt="Transition to the model page"></li>
<li><strong>Set deployment name and check the price:</strong>  <ul>
<li>Set a deployment name of your choice.  </li>
<li>Set the content filter to ON/OFF.  </li>
<li>As of January 31, 2025, DeepSeek-R1 is provided free of charge. The price is expected to change in the future, so be sure to check back regularly for the latest information.</li>
</ul>
</li>
</ol>
<blockquote>
<p>DeepSeek R1 is currently available at no cost, though usage is subject to rate limits that may change at any time. Pricing may be updated in the future, and continued use will be subject to the new rates. As the model is in preview, a new deployment may be required to maintain access.</p>
</blockquote>
<p>   <img src="/assets/blog/authors/s.wada/20250131/image_2.webp" alt="Transition to the deployment settings screen"><br>   <img src="/assets/blog/authors/s.wada/20250131/image_3.webp" alt="Pre-deployment settings">
4. <strong>Deployment and endpoint creation:</strong><br>   An endpoint is automatically generated in the Azure AI Foundry workspace, and a URI and API key for calling the model are issued.<br>   <img src="/assets/blog/authors/s.wada/20250131/image_4.webp" alt="デプロイ完了画面"></p>
<hr>
<h2>4. A simple demo using DeepSeek R1</h2>
<p>Here, I will introduce an example code to try out inference with DeepSeek R1. Install necessary libraries.</p>
<pre><code class="language-terminal">pip install azure-ai-inference
</code></pre>
<p>Below is an example code for using the API.<br>Azure AI Foundry also provides code samples, making it very easy to try out.</p>
<pre><code class="language-python">from azure.ai.inference import ChatCompletionsClient
from azure.ai.inference.models import SystemMessage, UserMessage
from azure.core.credentials import AzureKeyCredential

client = ChatCompletionsClient(
    endpoint=&quot;&lt;YOUR_TARGET_URI&gt;&quot;,
    credential=AzureKeyCredential(&quot;&lt;YOUR_KEY&gt;&quot;),
)

deploy_name = &quot;&lt;YOUR_DEPLOY_NAME&gt;&quot;

response = client.complete(
    messages=[
        SystemMessage(content=&quot;You are a helpful assistant.&quot;),
        UserMessage(
            content=&quot;Please think about ways to promote the use of generative AI in companies. Answer in Japanese.&quot; Switch the legend to Japanese,
        ),
    ],
    max_tokens=2048,
    model=deploy_name,
)

print(response.choices[0].message.content)
</code></pre>
<h3>Example of response</h3>
<pre><code class="language-markdown">&lt;think&gt;
The first step is to clearly understand what the problem is. You need to think about how to promote the use of generative AI in the company.

Generative AI has a wide range of potential applications, so it&#39;s important to first assess and understand your company’s current situation. It&#39;s essential to identify which departments could benefit from AI adoption and what potential challenges they may face. For example, marketing, customer support, and business process automation are some potential areas.

Next, education and training are important. If employees are not provided with opportunities to learn how to use AI, it will be difficult to actually utilize it. Especially, to alleviate concerns and lack of understanding about AI, it might be a good idea to conduct workshops and training programs.

Selecting the right tools is also necessary. It is important to select a generative AI platform or tools that suit your company&#39;s needs Depending on the situation, customization may be necessary, and addressing security and data privacy issues is also required.

Support from the management is also essential. Without support from management, it will be challenging to secure the necessary budget and resources. Clearly communicating the benefits of AI implementation is essential to gaining management’s understanding and support.

Furthermore, making successful cases is effective. If certain departments successfully pilot AI and demonstrate tangible results, it becomes easier to scale adoption across other departments. Sharing the results company-wide and expanding on them can be an effective strategy.

It is also important to develop ethical guidelines and policies. Clear rules will need to be established to prevent the misuse of AI and encourage its responsible use.

Continuous evaluation and improvement also should not be overlooked. Even after implementing AI, it&#39;s crucial to evaluate its effectiveness and make improvements based on feedback to ensure ongoing progress.

It seems important to consolidate these points and consider concrete steps to move forward.
&lt;/think&gt;

To promote the utilization of generative AI within a company, it is effective to systematically follow the steps below:

---

### **1. Current situation analysis and goal setting**
- **Identifying needs**: Identify the business challenges of each department (planning, sales, development, support, etc.) and clarify the areas where generative AI can provide solutions (document creation, data analysis, customer support, etc.).
- **Prioritization**: Prioritize the implementation of use cases with high impact and feasibility (e.g., automatic response to inquiries and automated report generation).

---

### **2. Building a system and changing awareness**
- **Launching a promotion team**: Establish a promotion team consisting of representatives from management, the IT department, and each department to collaborate to secure the budget and resources.
- **Implementation of educational programs**:  
  - **Basic understanding**: Share the mechanisms, benefits, and ethical risks (e.g., data leakage and bias) of generative AI through training.  
  - **Hands-on training**: Provide hands-on training to promote learning of prompt design and output verification methods.

---

### **3. Tool selection and test operation**
- **Choosing the right AI for your company**:  
  - Compare open sources (e.g., Llama 2) and cloud services (ChatGPT Enterprise, Google Vertex AI, etc.).  
  - Select based on security standards (e.g., data encryption and whether the pre-trained model can be customized).
- **PoC (Proof of Concept)**: Conduct test operations in a limited scope to verify accuracy and business efficiency improvement.

---

### **4. Establishment of governance**
- **Development of usage guidelines**:  
  - Set clear rules to prevent the input of confidential information and implement a system for double-checking output content.  
  - Address legal risks regarding copyright and privacy.
- **Monitoring System**: Regularly monitor the output quality and ethical aspects of AI and continuously improve them.

---

### **5. Cultivating a company culture**
- **Sharing successful cases**: Boost motivation by sharing success stories from departments that have achieved positive results, through internal presentations or reports.
- **Incentive design**: Recognize and reward teams that have improved business efficiency through the use of AI.

---

### **6. Collaboration with external entities and adaptation to evolution**
- **Collaboration with vendors and experts:**: Seek support for staying up to date with the latest technological trends and for implementing necessary customizations.
- **Continuous AI Updates**: Regularly review AI usage methods in response to advancements in model accuracy and changes in regulations.

---

### **Key points**
- **Management commitment**: Demonstrating return on investment (ROI) and securing management support with a long-term perspective is crucial.
- **Improvement driven by the field:**: Utilize feedback from employees (i.e., users) to optimize tools and processes.

Generative AI is a &quot;tool,&quot; and the success or failure of its utilization depends on &quot;how humans use it.&quot; In addition to introducing technology, improving digital literacy throughout the organization and ensuring psychological safety are keys.
</code></pre>
<hr>
<h2>5. Impressions from actual use</h2>
<ol>
<li><p><strong>Response:</strong></p>
<ul>
<li>It took about 4 minutes for the response this time. It appears that the latency was caused by both the LLM&#39;s own inference time and the increased processing time resulting from heavy usage.</li>
</ul>
</li>
<li><p><strong>Output precision:</strong></p>
<ul>
<li>Since it&#39;s an inference model designed for &quot;thinking,&quot; it provides responses that convey a sense of deep insight, even when handling highly abstract or complex prompts.</li>
<li>In API usage, the thinking process is output in a format <think>enclosed by the <think> tag. This is easy to use.</li>
<li><think>Looking at the contents inside the <think> tag reveals a thinking process as if a discussion were taking place among multiple people.</li>
</ul>
</li>
<li><p><strong>Conclusion:</strong></p>
<ul>
<li>Azure AI Foundry provides a convenient way to experiment with the latest models developed by third parties.</li>
<li>In the generative AI space, where &quot;trying it out first&quot; is key, this ease of experimentation is extremely valuable.</li>
</ul>
</li>
</ol>
<hr>
<h2>We Are Hiring！</h2>
<p>KINTO Technologies is looking for passionate individuals to help drive AI adoption in our business. We’re happy to start with a casual interview. If you’re even slightly interested, feel free to reach out via the link below or through <a href="https://twitter.com/cognac_n">X DMs</a>. We look forward to hearing from you! Great place to stay!
<a href="https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303115">https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303115</a></p>
<p>Thank you for reading this far!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/s.wada/20250131/cover.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[How I Learned to Work with AI by Programming with GitHub Copilot (Mobile Engineer Edition)]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-25-MobileAdventCalendar-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-25-MobileAdventCalendar-en/</guid>
            <pubDate>Tue, 13 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We'll walk you through what it's like to develop using GitHub Copilot with VSCode, along with some tips for collaborating smoothly with AI.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>The combination of VSCode and Copilot is an absolute blast. It&#39;s so much fun that I almost forget I have a day job. I’m writing this article because I simply have to share how incredibly enjoyable this development experience has been.</p>
<p>This article is the entry for day 25 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>🎅🎄</p>
<h2>What I&#39;ll Talk About today</h2>
<p>The AI world has been on fire since last year, and this year was no exception: there&#39;s been no shortage of buzz! At KTC, we&#39;ve been diving headfirst into AI, looking for smart ways to boost our development productivity.</p>
<p>In this post, I&#39;ll shine a spotlight on what you can actually do when you pair VSCode with Copilot. Now, maybe you&#39;re thinking, “AI? Meh. It just spits out vague code and I can write better stuff faster myself.” I get it.</p>
<p>That was my take too, until I saw the new Copilot Chat and Edits features unveiled at GitHub Universe. Suddenly, coding with AI started to look… kind of fun? So today, I&#39;ll share some tips that have been genuinely useful when teaming up with Copilot Chat/Edits in real-world dev work.</p>
<p><a href="https://reg.githubuniverse.com/flow/github/universe24/attendee-portal/page/livestream?tab.day=20241029">https://reg.githubuniverse.com/flow/github/universe24/attendee-portal/page/livestream?tab.day=20241029</a></p>
<h2>Why Flutter?</h2>
<p>When doing mobile development, it&#39;s common to use the standard IDE for each OS, whether it’s iOS or Android. However, since this article focuses on programming with GitHub Copilot, we&#39;ll be working under the assumption that development is done using VSCode. With that in mind, Flutter stands out as a major mobile framework that officially supports VSCode.</p>
<p>I also got a lot out of attending <a href="https://2024.flutterkaigi.jp/">FlutterKaigi</a> recently, so for this project, I decided to use Flutter for collaborating with AI. I even contributed to the FlutterKaigi app. Just a tiny issue, but still 😀</p>
<h2>What can it actually do?</h2>
<p>Many of the features announced at GitHub Universe are still in preview, so what&#39;s available is limited. But even just using the already released Copilot Chat and Edits has made working with AI more enjoyable and clearly improved the developer experience.</p>
<p>When you hear “coding with AI,” you might picture building apps without writing any code, or having code generated automatically. And sure, auto-generating code is possible, but that&#39;s really just one small part of what AI can do. Also, generating complex, fully functional code with a single prompt? Not exactly easy. Writing the right prompt is half the battle.</p>
<p>In this article, we&#39;ll share tips for using Copilot Chat/Edits to do everyday coding tasks (like writing test code, formatting and breaking up code, and refactoring) all in collaboration with AI to help improve code quality.</p>
<p>Now, here are some tips.</p>
<h3>Code Summary</h3>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/02.png" alt="file summary"></td>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/01.gif" alt="file summring with copilot"></td>
</tr>
</tbody></table>
<p>Copilot Chat makes it easy to generate code summaries. This can speed up code comprehension during onboarding.</p>
<h3>Add comments</h3>
<table>
<thead>
<tr>
<th>BEFORE</th>
<th>AFTER</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/03-1.png" alt="before"><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/03-2.png" alt="chat"></td>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/04.png" alt="after"></td>
</tr>
</tbody></table>
<p>If you want to add comments to your code, Copilot Edits can insert them directly into your file. This helps deepen your understanding of the code. Copilot can also update existing comments, making sure they stay accurate. For example, if you add validation to the add method in the class above, you can give an instruction like the one below, and Copilot will update the comment accordingly.</p>
<table>
<thead>
<tr>
<th>BEFORE</th>
<th>AFTER</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/05-1.png" alt="before"><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/05-2.png" alt="chat"></td>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/06.png" alt="after"></td>
</tr>
</tbody></table>
<h3>Creating Test Code</h3>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/07.png" alt="select target file"></td>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/08.png" alt="selected target file"></td>
</tr>
</tbody></table>
<p>You can add files to reference (besides the current one) above the text input field in Copilot Edits. So, when asking it to write test code, just create a test file, keep it open, and add the target file to the reference list. Copilot will then generate the test code for you.</p>
<p><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/09.gif" alt="select target file"></p>
<h3>Code Formatting &amp; Splitting</h3>
<p>When building UIs with declarative languages like Flutter, deeply nested code tends to become a common issue.</p>
<table>
<thead>
<tr>
<th></th>
<th></th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/11.png" alt="sample code"></td>
<td><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/10.png" alt="ui"></td>
</tr>
</tbody></table>
<p>::: details Code before formatting</p>
<pre><code class="language-dart">class SomethingPage extends StatelessWidget {
  const SomethingPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(&#39;Something Page&#39;),
      ),
      body: Center(
        child: Container(
          padding: const EdgeInsets.all(16.0),
          color: Colors.blueAccent,
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Container(
                padding: const EdgeInsets.all(8.0),
                color: Colors.redAccent,
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Container(
                      padding: const EdgeInsets.all(4.0),
                      color: Colors.greenAccent,
                      child: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          Container(
                            padding: const EdgeInsets.all(2.0),
                            color: Colors.yellowAccent,
                            child: const Text(&#39;Deeply Nested Widget 1&#39;),
                          ),
                          Container(
                            padding: const EdgeInsets.all(2.0),
                            color: Colors.purpleAccent,
                            child: const Text(&#39;Deeply Nested Widget 2&#39;),
                          ),
                        ],
                      ),
                    ),
                  ],
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
</code></pre>
<p>:::</p>
<p>Copilot Edits can help with this too. It splits and formats the code into cleaner, more readable chunks.</p>
<p><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/12.gif" alt="auto divide"></p>
<p>:::details Formatted code</p>
<pre><code class="language-dart">class SomethingPage extends StatelessWidget {
  const SomethingPage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(&#39;Something Page&#39;),
      ),
      body: Center(
        child: Container(
          padding: const EdgeInsets.all(16.0),
          color: Colors.blueAccent,
          child: const NestedContainer(),
        ),
      ),
    );
  }
}

class NestedContainer extends StatelessWidget {
  const NestedContainer({super.key});

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: const [
        InnerContainer(),
      ],
    );
  }
}

class InnerContainer extends StatelessWidget {
  const InnerContainer({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(8.0),
      color: Colors.redAccent,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [
          DeeplyNestedContainer(),
        ],
      ),
    );
  }
}

class DeeplyNestedContainer extends StatelessWidget {
  const DeeplyNestedContainer({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(4.0),
      color: Colors.greenAccent,
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: const [
          DeeplyNestedWidget1(),
          DeeplyNestedWidget2(),
        ],
      ),
    );
  }
}

class DeeplyNestedWidget1 extends StatelessWidget {
  const DeeplyNestedWidget1({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(2.0),
      color: Colors.yellowAccent,
      child: const Text(&#39;Deeply Nested Widget 1&#39;),
    );
  }
}

class DeeplyNestedWidget2 extends StatelessWidget {
  const DeeplyNestedWidget2({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(2.0),
      color: Colors.purpleAccent,
      child: const Text(&#39;Deeply Nested Widget 2&#39;),
    );
  }
}
</code></pre>
<p>:::</p>
<h3>Refactor</h3>
<p>Even those legacy methods tangled up with complex dependencies, something that you often see in older code, can be refactored using Copilot Edits based on SOLID principles.</p>
<p>:::details Classes with methods with complex dependencies</p>
<pre><code class="language-dart">class SomethingLogic {
  Future&lt;void&gt; performComplexOperation() async {
    // Fetch data from remote server
    final response = await http.get(Uri.parse(&#39;https://example.com/data&#39;));
    if (response.statusCode != 200) {
      throw Exception(&#39;Failed to load data&#39;);
    }
    final data = jsonDecode(response.body);

    // Save data to local database
    final database = openDatabase(
      join(await getDatabasesPath(), &#39;app_database.db&#39;),
      onCreate: (db, version) {
        return db.execute(
          &#39;CREATE TABLE data(id INTEGER PRIMARY KEY, value TEXT)&#39;,
        );
      },
      version: 1,
    );
    final db = await database;
    await db.insert(
      &#39;data&#39;,
      {&#39;id&#39;: data[&#39;id&#39;], &#39;value&#39;: data[&#39;value&#39;]},
      conflictAlgorithm: ConflictAlgorithm.replace,
    );

    // Save data to shared preferences
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(&#39;data&#39;, jsonEncode(data));

    // Perform some complex logic
    if (data[&#39;value&#39;] == &#39;special&#39;) {
      // ...existing code...
      print(&#39;Special value found!&#39;);
      // ...existing code...
    } else {
      // ...existing code...
      print(&#39;Regular value found.&#39;);
      // ...existing code...
    }
  }
}
</code></pre>
<p>::::</p>
<p>:::details Code refactored using SOLID principles</p>
<pre><code class="language-dart">class DataFetcher {
  final http.Client client;

  DataFetcher(this.client);

  Future&lt;Map&lt;String, dynamic&gt;&gt; fetchData(String url) async {
    final response = await client.get(Uri.parse(url));
    if (response.statusCode != 200) {
      throw Exception(&#39;Failed to load data&#39;);
    }
    return jsonDecode(response.body);
  }
}

class DatabaseHelper {
  Future&lt;Database&gt; getDatabase() async {
    return openDatabase(
      join(await getDatabasesPath(), &#39;app_database.db&#39;),
      onCreate: (db, version) {
        return db.execute(
          &#39;CREATE TABLE data(id INTEGER PRIMARY KEY, value TEXT)&#39;,
        );
      },
      version: 1,
    );
  }

  Future&lt;void&gt; saveData(Database db, Map&lt;String, dynamic&gt; data) async {
    await db.insert(
      &#39;data&#39;,
      {&#39;id&#39;: data[&#39;id&#39;], &#39;value&#39;: data[&#39;value&#39;]},
      conflictAlgorithm: ConflictAlgorithm.replace,
    );
  }
}

class SharedPreferencesHelper {
  Future&lt;void&gt; saveData(Map&lt;String, dynamic&gt; data) async {
    final prefs = await SharedPreferences.getInstance();
    await prefs.setString(&#39;data&#39;, jsonEncode(data));
  }
}

class SomethingLogic {
  final DataFetcher dataFetcher;
  final DatabaseHelper databaseHelper;
  final SharedPreferencesHelper sharedPreferencesHelper;

  SomethingLogic(this.dataFetcher, this.databaseHelper, this.sharedPreferencesHelper);

  Future&lt;void&gt; performComplexOperation() async {
    final data = await dataFetcher.fetchData(&#39;https://example.com/data&#39;);
    final db = await databaseHelper.getDatabase();
    await databaseHelper.saveData(db, data);
    await sharedPreferencesHelper.saveData(data);

    if (data[&#39;value&#39;] == &#39;special&#39;) {
      // ...existing code...
      print(&#39;Special value found!&#39;);
      // ...existing code...
    } else {
      // ...existing code...
      print(&#39;Regular value found.&#39;);
      // ...existing code...
    }
  }
}
</code></pre>
<p>::::</p>
<p>Doing this level of refactor manually would take quite a bit of time, but with Copilot Edits, you can get it done much faster. The code is now much easier to read, so you can build on that and refactor it further based on your own experience.</p>
<h3>Others</h3>
<p>Besides that, the basic Suggestions feature is surprisingly powerful. Normally, when I run into implementation issues, I end up switching back and forth between the browser and the IDE, but now, most problems can be solved just by consulting Copilot. That means fewer distractions and better focus during development. This might seem minor, but it makes a big difference.</p>
<h2>Tips for Giving Instructions</h2>
<p>Working with AI has led to a lot of new discoveries, but I&#39;ve come to realize that giving clear instructions is one of the most important parts. Many engineers I know still find AI hard to use,but once you get used to giving it specific instructions, collaborating with AI can actually be pretty fun.</p>
<p>Here are a few instruction tips I&#39;ve picked up:</p>
<ul>
<li>Keep it simple</li>
<li>Be specific<ul>
<li>Mention which file</li>
<li>Say what to do</li>
<li>Say how to do it</li>
</ul>
</li>
<li>Don&#39;t give multiple instructions at once</li>
</ul>
<p>I especially feel that it&#39;s important for the person giving instructions to have a solid grasp of programming principles and design concepts.</p>
<h2>Summary</h2>
<p>In this post, I introduced some of the things you can do by combining VSCode and Copilot. With Copilot Chat and Edits, tasks like summarizing code, writing comments, generating test code, formatting, splitting, and refactoring become significantly easier; all through a few simple prompts. I hope that at least some of that came across.</p>
<p>In the engineering world, opinions on AI are still divided, and many of the engineers around me are still hesitant to embrace it.</p>
<p>But it&#39;s clear that the world is steadily moving toward AI, and it&#39;s only going to keep evolving and expanding what it can do. Feels like we&#39;ve entered an era where engineers need to seriously think about how we&#39;ll use AI going forward.</p>
<p>If this article got you even a little curious about collaborating with AI, I&#39;d be glad.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_25/00.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[2025年1月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-13-newcomers-introduction-25jan/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-13-newcomers-introduction-25jan/</guid>
            <pubDate>Tue, 13 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズに2025年1月に入社されたみなさまを紹介します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは、2025年1月入社の こっしー です！
本記事では、2025年1月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。
KINTOテクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h2>S.H</h2>
<p><strong>自己紹介</strong>
2025年1月入社のS.Hです。モビリティプロダクト開発部、DX開発GでPjMを担当しています。
これまでは数社のIT会社、制作会社でマーケやPdM、ディレクター、設計開発やPjM等を経験してきました。</p>
<p><strong>所属チームの体制は？</strong>
十数名のチームとなり、複数のプロダクトや案件を対応しています。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
高い技術力を持ち、社内外の勉強会も多数開催、参加者も多く、大変向上心が高いと感じました。ギャップは第一印象よりさらに想像以上に勉強熱心な方が多いところです。勉強会にオンラインで100名以上集まっているケースもあり圧巻です。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
気軽に質問したり、相談が出来る雰囲気だと思います。メンターさんだけでなく、困っていたら色々な方がフォローしてくださり働きやすい環境かと思います。</p>
<p><strong>ブログを書くことになってどう思った？</strong>
想像もしていなかったので驚きました。</p>
<p><strong>youさん→S.Hさんへの質問</strong></p>
<blockquote>
<p>KTCのDX推進に当たって感じる感想を教えていただけますか？</p>
</blockquote>
<p>グループ会社全体で見ますと、DX推進分野はとてもやれる事が多いと感じています。現場で働く方やその先にいるお客様にとってよい環境やシステムを提供できるよう尽力していきたいと思っております。</p>
<h2>呂 文佳（wenjia lu）</h2>
<p><strong>自己紹介</strong>
プラットフォームのQAを担当しております。</p>
<p><strong>所属チームの体制は？</strong>
QAが９名、チームメンバーが４名います。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
暖かいチームです。
仕事のやりがいがあります。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
みんなコミュニケーションを取り合って仕事を進めている感じです。</p>
<p><strong>ブログを書くことになってどう思った？</strong>
何を書けばよいか迷っています。</p>
<p><strong>S.Hさん→呂さんへの質問</strong></p>
<blockquote>
<p>KTCで変わってるなーと感じた会社の文化や出来事はありますか？</p>
</blockquote>
<p>変わっていると感じたことはありませんよ。</p>
<h2>I</h2>
<p><strong>自己紹介</strong>
2025年1月入社のIです。モビリティプロダクト開発部　DXソリューションGでディレクターを担当しています。これまでは、マーケやPdM、ディレクターを経験してきました。</p>
<p><strong>所属チームの体制は？</strong>
ディレクターとデザイナーが在籍し、複数のプロダクトを担当しています。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
 新しい技術もキャッチアップできる環境で、学びが多い環境だと思います。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
業務のことはもちろん、心理面も気軽に相談できるフォローがあり、働きやすいです。</p>
<p><strong>ブログを書くことになってどう思った？</strong>
入社ブログは初めて書くので驚きました。</p>
<p><strong>呂さん→Iさんへの質問</strong></p>
<blockquote>
<p>KTCで一番やりたいことを教えていただけますか？</p>
</blockquote>
<p>ユーザーの課題解決をでき、自社の収益性も高いプロダクトを作れるようになりたいです。</p>
<h2>KS</h2>
<p><strong>自己紹介</strong>
新車サブスク開発G コンテンツ開発TのKSです。
主にKINTO ONE の静的コンテンツのフロントエンドを担当しています。</p>
<p><strong>所属チームの体制は？</strong>
アシスタントマネージャー1名、リーダー1名、メンバー8名の10名体制です。
KINTO ONEサイトやコーポレートサイトなど複数サイトの開発、保守運用を行っています。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
面接時にご説明いただいていましたので、特にキャップはなかったです。
社内でたくさん勉強会が開催されていて、会社全体が技術的モチベーションが高い印象を受けました。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
チーム内はもちろん、他部署のディレクターやデザイナーの方ともコミュニケーションとりやすく、楽しく開発できる環境です。</p>
<p><strong>ブログを書くことになってどう思った？</strong>
何度か拝見させていただいたことはありましたが、書く側になるとは思っていませんでした。</p>
<p><strong>Iさん→KSさんへの質問</strong></p>
<blockquote>
<p>所属しているチームで一番HOTなプロジェクトはなんですか？</p>
</blockquote>
<p>KINTO ONEサイトのマイグレーションプロジェクト（Vue.jsからNext.jsへ）です。
自分はデザインシステムやコンポーネントの設計や開発を担当しています。</p>
<h2>李 俊起（Joonki Lee）</h2>
<p><strong>自己紹介</strong>
李俊起（イジュンギ）です。 ”リ”ではなくて”イ”ですので、間違わないでください！
プラットフォーム開発部プラットフォームグループで、開発者のための環境構築やツール開発などを行っています。</p>
<p><strong>所属チームの体制は？</strong>
スクラッチチームとマネージドプラットフォームチームに分かれていて、スクラッチチームは主にスクラッチからツールの開発を行い、マネージドプラットフォームチームはSaaSやManaged Serviceを使って環境構築を行っています。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
マネージャー層との距離感が近いと思いました。ギャップをそこまでなかったです。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
普段は静かで各自黙々と自分のタスクをこなしている感じですが、何か議論するときは皆熱くなり盛り上がります。</p>
<p><strong>ブログを書くことになってどう思った？</strong>
普段の業務についてもテックブログで公開してみたいと思いました。</p>
<p><strong>KSさん→李さんへの質問</strong></p>
<blockquote>
<p>まだ神保町オフィスに行ったことないのですが、オフィスはどんな雰囲気ですかー？</p>
</blockquote>
<p>最近のリニューアル工事で開放感があって少しおしゃれになりました。</p>
<h2>こっしー</h2>
<p>![プロフィール画像 こっしー](/assets/blog/authors/kossy/profile-img.png =250x)</p>
<p><strong>自己紹介</strong>
古代（こしろ）です 🙌
プラットフォーム開発部で KINTOサービス やその他のサービス の基盤となるクラウドインフラの構築や保守運用を担当しています！</p>
<p><strong>所属チームの体制は？</strong>
部の配下に Platform 、 DBRE 、Cloud Infrastructure 、 QA の4つのグループがあり、サービスの非機能に責務を持つ組織としてさまざまな取り組みをしています。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
設立から4年にも関わらず、オンボーディングの仕組みが整っていると感じました。また、運営しているサービスが想像以上に多く、技術的な挑戦も思っていた以上に積極的に行われており、そういった部分が良い意味でギャップとして感じました。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
とても仲が良くて、出社のたびにチームメンバーとご飯に行っています。雑談も多く、なんでも会話できる環境に助けられています。</p>
<p><strong>ブログを書くことになってどう思った？</strong>
はやくテックブログを書きたいと思っていたので、キッカケ作りにはとても良いなと思いました！</p>
<p><strong>李さん →こっしーさんへの質問</strong></p>
<blockquote>
<p>いま一番興味のある技術ワードを教えて下さい！</p>
</blockquote>
<p>「プラットフォームデザインパターン」です！Platform Engineeringの文脈において、ユーザーに応じて提供するプラットフォームの特性（Dev Teamに任せる責任範囲の違い）を意識したプラットフォームプロダクトの設計パターンに興味があります！</p>
<h2>TY</h2>
<p><strong>自己紹介</strong>
IT/IS部、コーポレートITG所属の山田です。主に従業員マスタ構築や業務効率化・改善に向けたシステム構築に携わってます。</p>
<p><strong>所属チームの体制は？</strong>
チームはInnovation Drive チームで、メンバーは9名います
自社内だけでなくグループへの支援も行っているのが特徴です。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
面接で見聞きした情報とギャップがないよう、オフィス見学もさせていただいたこともあり、ほぼなかったです。
オフィス見学の際には、名古屋にいるマネージャーの方々がわざわざ出張して対面で対応していただけたことも入社の決め手でした。とても嬉しかったです。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
皆さん建設的な議論ができる方々で、困っていれば手を差し伸べてくれる、とても働きやすい環境だと思います。</p>
<p><strong>ブログを書くことになってどう思った？</strong>
期限ギリギリに書き上げている自分を責めつつ、これを見て少しでも興味を持って入社してくれる方がいると嬉しいです！</p>
<p><strong>こっしーさん→TYさんへの質問</strong></p>
<blockquote>
<p>KTCでチャレンジしてみたい技術やプロジェクトがあれば教えて下さい！</p>
</blockquote>
<p>技術的なところだと、データスペースは触ってみたい技術です。あとはプロジェクト化までいかない細かい領域での問題解決が今のところ多いですが、ユーザー(社員)の利便性向上に関してトコトンこだわって取り組んでいきたいです。</p>
<h2>瀧川 直樹（Naoki Takigawa）</h2>
<p><strong>自己紹介</strong>
瀧川です。プラットフォーム開発部スクラッチ開発チームでエンジニアが使うツールの検証や開発をしています。フロントエンド開発をメインで担当しています！</p>
<p><strong>所属チームの体制は？</strong>
こっしーさんの説明がまとまっていたので、そちらを参考にしてもらえると↓
部の配下に Platform 、 DBRE 、Cloud Infrastructure 、 QA の4つのグループがあり、サービスの非機能に責務を持つ組織としてさまざまな取り組みをしています。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
入社時の面接でできたばかりのチームなので教育体制があまり整っていないかもと聞いていましたが、オンボーディング資料やOJT研修が準備されていて全然そんな事ないやんって思いました。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
なんでも質問しやすい雰囲気です。プラットフォーム開発部のメンバーが東京勤務が多く、月１くらいでお互いの拠点を行き来しています〜。</p>
<p><strong>ブログを書くことになってどう思った？</strong>
何こうかな…て思いました。</p>
<p><strong>TYさん→瀧川さんへの質問</strong></p>
<blockquote>
<p>今一番興味がある・ハマっている技術領域があれば教えてください！</p>
</blockquote>
<p>技術領域はAI…？AIを使った開発の効率化に興味があって、Devin、OpenHands、GithubCopilt✖️MCPなど色んなツールを並行で稼働させて、AI開発部隊を作って開発の効率化を模索しています。レビューが大変…(笑)</p>
<h2>喬 禹（Yu Qiao）</h2>
<p><strong>自己紹介</strong>
喬 a.k.a Alexです！IT/IS部、AIファーストグループで、生成AIエンジニアとして生成AIの事業推進支援やPoCを担当しています！</p>
<p><strong>所属チームの体制は？</strong>
チームメンバー6名、それぞれ異なる専門スキルを持ち、自身の小分けされた領域で活躍しています。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
面接官から感じた風土とほぼ一致していると感じたので、ギャップはありませんでした。瀧川さんの言ったように、教育体制があまり整っていないかもと入社前に聞きましたが思ったより充実で良い意味で裏切られました。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
チャンレンジしたいことを自由にチャレンジできるし、一人で解決できないことがあればいつでも助けを求めることができる環境です。</p>
<p><strong>ブログを書くことになってどう思った？</strong>
またテックブログを書きたくなってきました！</p>
<p><strong>瀧川さん→Alexさんへの質問</strong></p>
<blockquote>
<p>いつも使っている会議の時に付けているヘッドフォンは何というやつですか？おすすめのポイントとかあれば教えて欲しいです！</p>
</blockquote>
<p>これは前々職でもらったものです。コロナの初期に会社がフルリモートに切り替わったタイミングで、チーム全員にノイズキャンセリング機能が優れたヘッドフォンを購入する必要がありました。その時、上司と一緒にヨドバシカメラに行って選びました。</p>
<h2>you</h2>
<p>![プロフィール画像 you](/assets/blog/authors/you/tanuki.jpeg =250x)</p>
<p><strong>自己紹介</strong>
プラットフォーム開発部・Cloud Infrastructure Gの 劉(you)です！主にAWSを中心に社内のインフラの構築・運用・改善など様々な業務に触り合っています。</p>
<p><strong>所属チームの体制は？</strong>
Cloud Infrastructure Gは現在、９人体制でマネージャー・各リーダーともフラットに会話して業務をする事ができる環境です。Gの皆さんが意識高く仕事を取り組んでいますので、モチベーションが上がる毎日を過ごしてます。</p>
<p><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong>
会社の進化が早すぎると感じました。内部の体制とか業務の進め方とか、私が思った以上に整えていて感心しました。ギャップがあるとしたら、コミュニケーションも取りやすくて技術的なチャレンジが会社全体で起きてる、すごくいい方向でした。</p>
<p><strong>現場の雰囲気はどんな感じ？</strong>
リスクテイクがすごく忠実していて、業務に関して自由に意見交換ができる素晴らしい環境です！</p>
<p><strong>ブログを書くことになってどう思った？</strong>
次のテクブログのネタを考えています！</p>
<p><strong>Alexさん→youさんへの質問</strong></p>
<blockquote>
<p>今後KTCでどんなことに挑戦してみたいですか？</p>
</blockquote>
<p>KTCの技術と文化を進化して行きたいです。一人で成し遂げられることは本当に少ないと思いますので、私ができることの最大を発揮して影響を与えられるように頑張ります！今は新技術の習得・発信を中心にやりたい所です。</p>
<h2>さいごに</h2>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTO テクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Rebuilding the Broker Architecture]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-04_rebuild_broker_architecture-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-04_rebuild_broker_architecture-en/</guid>
            <pubDate>Mon, 12 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Rebuilding the Broker Architecture for Cost Efficiency]]></description>
            <content:encoded><![CDATA[<p>Hello, I&#39;m Xu Huang from the KINTO ID Platform team. For the past few years now, we have been providing user authentication and authorization systems (known internally as UserPool) across multiple countries. To support this, we adopted a broker model that connects Userpools across multiple regions, building and operating an architecture that allows them to share authentication and authorization information with each other. Last year, as part of our cost optimization efforts, we redesigned and migrated this architecture. In this post, I’ll walk you through what changed and why.</p>
<p>In our initial deployment, we used AWS Aurora Global Database (referred to as Global DB) as part of our global deployment strategy. To minimize access load and latency, we deployed Slave DBs in close proximity to each UserPool and placed Broker servers in the same regions as the corresponding Slave DBs.</p>
<p>(Due to Global DB limitations, only one Master DB is allowed, and it supports up to five Slave DBs.)</p>
<p><img src="/assets/blog/authors/xu.huang/image-20241205-042437.png" alt=""></p>
<p>As shown in the diagram above, we gradually rolled out the UserPool service to the regions outlined in blue. To enable centralized user management, we designed the system so that unique IDs would be issued from a central aggregation point and synchronized this information to each region’s sub-DB for local management.</p>
<p><img src="/assets/blog/authors/xu.huang/image-20241216-044016.png" alt=""></p>
<h3>Phase 1: Migrating from Global DB to a Normal DB and Removing Write-Only Applications</h3>
<p>The original architecture prioritized minimizing access load by deploying servers across multiple regions. However, in reality, the system had not yet reached a scale that required such complexity, leading to unnecessary operational costs. To optimize the setup, we conducted a thorough evaluation and concluded that a Global DB setup was not essential for our current usage. We then redesigned the system to allow direct R&amp;W access from the Broker to a shared common DB.</p>
<p>The diagram below illustrates the updated architecture:</p>
<p><img src="/assets/blog/authors/xu.huang/image-20241216-050301.png" alt=""></p>
<h3>Phase 2: Consolidating the Broker</h3>
<p>While Phase 1 significantly delivered cost savings, we looked for further optimization opportunities. This led us to consider whether we could consolidate the system into a single Broker instance. However, there was one challenge: as an identity provider, we also offer redirect URLs to external third party services. If those URLs were to change, it would require the third parties to update their configurations as well. So we started thinking about how we might migrate without changing the domain. With support from our infrastructure team, we realized that by updating DNS settings in Route53 and routing traffic through CloudFront to the new unified server, we could avoid changing the domain altogether and transition to a unified Broker.</p>
<p><img src="/assets/blog/authors/xu.huang/image-20241216-051940.png" alt=""></p>
<p><img src="/assets/blog/authors/xu.huang/image-20241216-053228.png" alt=""></p>
<p>When implementing the design as shown above, we were concerned about the impact on latency caused by the increased physical distance between servers, particularly from the UserPool to the now-centralized Broker. So, we measured it.</p>
<p>The results showed that communication between the UserPool and the Broker became about 10% slower, but since the Broker was now located in the same region as the database, DB communication became faster. Overall, there was no significant impact in end-to-end performance before and after the architecture change, so we proceeded with planning for the Phase 2 migration.</p>
<h3>Results</h3>
<p>Through these two phases, we optimized the system architecture to better align with the actual usage patterns of our business operations.</p>
<p>Going forward, we will continue reviewing system functionality and regularly working on cost-efficiency efforts.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[技術資料をもっと広く！生成AIで実現するMarkdown変換の自動化]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-11-droidlab-markdownGpt-blog/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-11-droidlab-markdownGpt-blog/</guid>
            <pubDate>Mon, 12 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Androidチームの勉強会『Droid Lab』の技術資料を広く共有するため、ConfluenceやPPT形式の資料をMarkdown形式に自動変換するGPTを導入した事例を紹介します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>はじめまして。KINTOテクノロジーズでAndroidアプリ開発を担当しているYenaです。
キャリアはAndroid開発から始まり、スマートテレビアプリやWebのバックエンド・フロントエンド、APIの開発など、幅広い領域に携わってきました。
現在はAndroid開発に取り組む傍ら、チームと自身の成長を目指して、社内勉強会「Droid Lab」の企画・運営も行っています。</p>
<p>この勉強会で取り上げた技術内容をより広くチーム外にも共有するため、資料をテックブログとして公開する取り組みを進めて行こうと考えましたが、以下のような課題がありました。</p>
<p>資料は主にConfluenceやPPT形式で作成されているため、ブログに投稿するにはMarkdown形式への変換が必要であり、そのまま公開ができず、作業手間がかかることです。</p>
<p>この変換作業の負担が大きく、ブログ執筆に十分な時間を確保することが難しく、情報発信が思うように進まない状況が続いていました。</p>
<p>こうした課題を解決するため、生成AIであるChatGPTの<strong>カスタムGPT</strong>を活用し、ConfluenceやPPTなどの元資料を自動的にMarkdown形式へ変換できる仕組みを導入しました。</p>
<p>現在は試験運用を進めており、変換作業の負担軽減や情報共有の効率化といった効果が期待されています。</p>
<p>ここでは、私たちが実際に導入したGPTの作成手順と設定内容についてご紹介します。</p>
<h2>GPTを作成する方法</h2>
<h3>1. GPT作成</h3>
<ul>
<li><ol>
<li>ログイン後、左上の「Explore」をクリック
<img src="/assets/blog/authors/yena/Screenshot01.png" alt="Open Screenshot 2025-04-10 at 14.48.58.png"></li>
</ol>
</li>
<li><ol start="2">
<li>My GPTカテゴリから「Create GPT」を選択
<img src="/assets/blog/authors/yena/Screenshot02.png" alt="Open Screenshot 2025-04-11 at 12.04.41.png"></li>
</ol>
</li>
<li><ol start="3">
<li>GPTビルダーでNameとプロフィール画像を設定
   DALL·Eで名前に合う画像も自動生成可能(下記のイメージはDALL·Eで作成したNameを考えてAIが作成してくれたイメージです)
<img src="/assets/blog/authors/yena/DALLE.png" alt="Open DALLE.png">
<img src="/assets/blog/authors/yena/Screenshot03.png" alt="Open Screenshot 2025-04-10 at 15.05.52.png"></li>
</ol>
</li>
<li><ol start="4">
<li>GPT詳細情報の設定</li>
</ol>
</li>
</ul>
<p><img src="/assets/blog/authors/yena/Screenshot04.png" alt="Open Screenshot 2025-04-10 at 15.07.11.png"></p>
<ul>
<li><p><strong>① Description（概要説明）</strong>
このGPTが何をするものかを、1〜2行で簡潔に説明します。</p>
<blockquote>
<p>例：
Droid Lab資料を元にMarkdown形式の技術ブログを自動作成します。ブログ記事作成用のブランチは以下の形式に従ってください（ブランチ名：sample/YYYY-MM-DD-&lt;テーマ&gt;）</p>
</blockquote>
</li>
<li><p><strong>② Instructions（動作したいルール）</strong>
GPTがどのように振る舞うべきか、どんな出力をするか、制約などを細かく記述します。ここがプロンプトの核になります。
以下は本GPTが従うべき出力ルールです</p>
<ul>
<li>YAMLの下にMarkdown本文を出力（※うちの場合、決まった形式があるのでそれをYAMLで定義）</li>
<li>見出し → <code>## 見出し</code></li>
<li>リスト → <code>- 項目</code></li>
<li>インラインコード → <code>コード</code></li>
<li>コードブロック → <code>kotlin</code> など、開閉セットで記述</li>
<li>画像 → <code>![説明](URL)</code></li>
<li>⚠️ 文の構成や文体、順番は変更しないこと</li>
<li>出力は必ず <code>Markdown で始まり、</code> で終わるコードブロックで包むこと</li>
<li>社内API URLや機密名称が含まれる場合は「【非公開情報の可能性あり】」と明記し、修正案を提示すること</li>
<li>出力が長くても途中で止めず、すべてを1回で返すこと</li>
</ul>
</li>
<li><p><strong>③ Conversation starters（使い始めの例）</strong>
ユーザーがどう使えばよいかを示す例文を登録します。</p>
<blockquote>
<p>例：ConfluenceメモをMarkdown形式の技術ブログに変換してください。</p>
</blockquote>
</li>
<li><p><strong>④ Knowledge（知識ファイル）</strong>
GPTにアップロードしておく補助資料（スタイルガイド、サンプル記事など）を指定します。</p>
<ul>
<li>社内ブログスタイルガイド</li>
<li>過去の公開記事サンプルなど
※ 機密情報が含まれないことを確認してアップロード</li>
</ul>
</li>
<li><p><strong>⑤ Capabilities（機能設定）</strong></p>
<ul>
<li>Web Search</li>
<li>Canvas</li>
<li>DALL·E</li>
<li>Code Interpreter
→ 必要に応じて機能のオン・オフが可能！
<img src="/assets/blog/authors/yena/ScreenshotOn.png" alt="Screenshot05.png"></li>
</ul>
</li>
<li><ol start="5">
<li>GPT詳細情報の設定
全部入力したら、右上の「Create」ボタンをClickしてGPT作成は<strong>完了</strong>です。
<img src="/assets/blog/authors/yena/Create.png" alt="Open Screenshot 2025-04-10 at 15.07.11.png"></li>
</ol>
</li>
</ul>
<h2>２. GPTの設定</h2>
<p>ここでは、GPTに対してどのような動作ルールや制約を設定しているかを紹介します。
以下は、社内ブログフォーマットに準拠し、GPTによるMarkdown変換を正しく行うために設定した主な機能です。</p>
<table>
<thead>
<tr>
<th>機能</th>
<th>説明</th>
</tr>
</thead>
<tbody><tr>
<td>✍️ Markdown形式への変換</td>
<td>貼り付けた文章の文体や構成を変えずに、そのままMarkdown構文へ変換します</td>
</tr>
<tr>
<td>📄 YAMLメタ情報の自動付与</td>
<td>自社ブログ形式に合わせて postId、title、excerpt を自動生成します</td>
</tr>
<tr>
<td>🧱 構文保持＆出力形式の統一</td>
<td>Markdown構文が壊れないよう、すべてを1つのコードブロックで出力します</td>
</tr>
<tr>
<td>🔐 セキュリティーCheck機能</td>
<td>社内APIや内部コード名を検出し、「【非公開情報の可能性あり】」としてマーク＋修正案を提示します</td>
</tr>
<tr>
<td>⚠️ 出力の中断防止ロジック</td>
<td>長文でも途中で止まらず、構文が破損しないようにすべてを1回で出力します</td>
</tr>
<tr>
<td>⚠️ 日本語・英語Check</td>
<td>表記ゆれや誤字、不自然な表現を検出し、必要に応じて修正案を提示します</td>
</tr>
</tbody></table>
<h2>２-1. 🧠 GPTの詳細仕様（プロンプト設定内容）</h2>
<p>このGPTは、プロの技術ブログライター兼Markdown変換ツールとして機能します。ユーザーがConfluenceや社内メモの文章を貼り付けた際、元の構成・順番・文体を一切変更せずに、以下のフォーマットに従って出力します。</p>
<h3>1. YAMLメタ情報の出力ルール</h3>
<p>ブログ記事として公開するためには、YAMLメタ情報（タイトル、日付、カテゴリなど）を冒頭に定義する必要があります。<br>このGPTでは、以下のルールに従って、貼り付けた文章から自動的にYAMLヘッダーを生成します。</p>
<ul>
<li>YAMLは、あらかじめ決められた形式で <code>yaml</code> コードブロック内に出力してください。</li>
<li>「title」や「excerpt」は、本文中から推測・抽出して自動補完してください。</li>
<li>タイトルがない場合は <code>&#39;記事タイトルをここに&#39;</code> のまま出力してください。</li>
<li>記事が非常に長い場合は、<code>Markdown</code> コードブロックを複数に自動分割し、ユーザーが「続けて」と言わなくても完了まで出力を続けてください。</li>
</ul>
<h4>カテゴリの自動判定ルール</h4>
<p>以下のキーワードが含まれる場合、<code>category</code> を対応するものに自動で置き換えてください：</p>
<ul>
<li>Kotlin, Compose, MVVM, KMP → &quot;Android&quot;</li>
<li>GitHub, CI/CD, CodeBuild → &quot;DevOps&quot;</li>
<li>Lint, アーキテクチャ, コーディング規約 → &quot;Architecture&quot;</li>
<li>Confluence, Markdown, GPT → &quot;Tooling&quot;</li>
<li>Firebase, AWS, S3 → &quot;Cloud&quot;</li>
<li>勉強会, 社内共有, ナレッジ → &quot;Team&quot;</li>
<li>AI, ChatGPT, Prompt, 自然言語処理 → &quot;Generative AI&quot;
複数に該当する場合は、<strong>最も出現頻度が高いカテゴリを優先</strong>してください。<blockquote>
<p>💡 以下は、社内ブログで使用しているYAMLヘッダー形式の例です（社外環境ではそのまま使用できない場合があります）
[例]</p>
</blockquote>
</li>
</ul>
<pre><code>---
postId: &quot;&lt;自動生成されたID&gt;&quot;
title: &quot;&lt;記事タイトル&gt;&quot;
excerpt: &quot;&lt;要約文&gt;&quot;
coverTitle: &quot;&lt;表紙用の見出し&gt;&quot;
coverImage: &quot;&lt;画像パス（社内ブログ用）&gt;&quot;
date: &quot;&lt;ISO形式の日付&gt;&quot;
category: &quot;&lt;カテゴリ名&gt;&quot;
....
---
</code></pre>
<h3>2. Markdown本文の出力ルール</h3>
<p>Markdown変換のルール：</p>
<ul>
<li>見出し → <code>## 見出し</code></li>
<li>リスト → <code>- 項目</code></li>
<li>インラインコード → <code>コード</code></li>
<li>コードブロック → 例: <code>kotlin</code>（※開いたら必ず閉じる）</li>
<li>画像 → <code>![説明](画像URL)</code></li>
</ul>
<p>⚠️ 文の構成、順番、文体は変更しないこと<br>⚠️ Markdown構文を壊さないよう、すべて閉じタグまで正確に出力すること</p>
<h3>3.  出力後の自動チェック機能</h3>
<ul>
<li><strong>すべての出力（YAML + Markdown本文）を1つのコードブロック内（<code>Markdown</code>）にまとめてください</strong></li>
<li>出力の最初は <code>Markdown</code> で開始し、<strong>最後には対応するコードブロック閉じタグで終了</strong>すること</li>
<li>長文であっても、出力が途中で止まることなく、<strong>1回で全文が返されること</strong></li>
</ul>
<h4>3-1. 🔐セキュリティーCheck</h4>
<p>以下の内容が含まれていないかを検査してください：</p>
<ul>
<li>社内APIのURL</li>
<li>内部ライブラリ名</li>
<li>プロジェクトコード名</li>
<li>顧客IDなど機密情報
検出された場合は、「【非公開情報の可能性あり】」とマークし、<strong>公開用の修正案</strong>をあわせて提示します。</li>
</ul>
<p><strong>検出例（スクリーンショット）</strong>
<img src="/assets/blog/authors/yena/Screenshot05.png" alt="Open Screenshot05.png"></p>
<h4>3-2.⚠️ 日本語・英語Check</h4>
<ul>
<li>日本語の誤字、助詞の誤用、不自然な表現、文末の統一性など</li>
<li>英語のスペルミス、文法エラー、不自然な言い回しなど</li>
</ul>
<p>必要に応じて、「⚠️ 文章チェック:」の見出しの下に<strong>修正案</strong>を出力してください。<br>ただし、文章の順番・構成・文体は一切変更せず、<strong>指摘のみを行うこと</strong>。</p>
<p><strong>検出例（スクリーンショット）</strong>
<img src="/assets/blog/authors/yena/Screenshot06.png" alt="Open Screenshot05.png"></p>
<h2>4. 使用方法のデモ</h2>
<h3>4-1. 実行ステップ</h3>
<p>💬 <strong>ステップ1</strong>：変換したいTEXTを入力
💬 <strong>ステップ2</strong>：カスタムGPTに貼り付けて送信
<img src="/assets/blog/authors/yena/Test01.png" alt="Open Test01.png">
💬 <strong>ステップ3</strong>：Markdown形式で出力
<img src="/assets/blog/authors/yena/Test02.png" alt="Open Test02.png"></p>
<h2>5. 導入の結果、得られた効果</h2>
<p>このGPTを導入したことで、Markdown変換の効率化や情報発信の品質向上など、さまざまな効果が得られました。<br>ちなみに、<strong>この記事も本GPTを使ってMarkdown形式に自動変換しています。</strong></p>
<table>
<thead>
<tr>
<th>項目</th>
<th>Before</th>
<th>After</th>
<th>効果</th>
</tr>
</thead>
<tbody><tr>
<td>Markdown変換作業</td>
<td>30分〜2時間以上</td>
<td>数十秒〜数分</td>
<td>約80%以上の工数削減</td>
</tr>
<tr>
<td>フォーマット統一</td>
<td>個人差あり</td>
<td>自動で安定出力</td>
<td>品質と読みやすさ向上</td>
</tr>
<tr>
<td>セキュリティ確認</td>
<td>手動確認</td>
<td>自動検出とマーク</td>
<td>安心して公開可能</td>
</tr>
<tr>
<td>文章確認</td>
<td>手動確認</td>
<td>自動検出とマーク</td>
<td>安心して公開可能</td>
</tr>
<tr>
<td>利用者</td>
<td>Markdown形式になれた方中心</td>
<td>Markdown形式に不慣れなメンバーも利用可</td>
<td>活用範囲が拡大</td>
</tr>
</tbody></table>
<h2>6. まとめ</h2>
<p>Droid Labで得た知見をもっと気軽に、広くチーム内外にシェアできるように
そんな思いから、Markdown変換の手間を減らすためにこのGPTを導入しました。</p>
<p>生成AIを活用することで、これまで時間がかかっていた変換作業が一気に効率化され、品質やセキュリティの面でも安心して発信できるようになりました。</p>
<p>今後は、PPTの直接アップロードや会議要約機能の追加など、さらに便利にしていく予定です。<br>開発チーム全体のナレッジ共有が、もっとスムーズになる未来を目指していきます！</p>
<h2>🚀 今後の活用拡大</h2>
<ul>
<li><p><strong>日本語から英語への変換対応機能の追加</strong><br>グローバル発信や海外メンバーとの共有に対応</p>
</li>
<li><p><strong>PPTアップロード対応</strong><br>手動でのコピーが不要となり、ファイルをアップロードするだけで変換が可能な仕組みを開発中</p>
</li>
<li><p><strong>会議要約GPTの導入</strong><br>会議ログや議事録を貼り付けるだけで、自動的にサマリやToDoを抽出できるよう最適化</p>
</li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yena/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[We will be a Gold Sponsor of TSKaigi 2025]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-10-TSKaigi-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-10-TSKaigi-en/</guid>
            <pubDate>Sat, 10 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We will be a Gold Sponsor of TSKaigi 2025]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello!
I am Ren.M, working on the front-end development of <a href="https://up.kinto-jp.com/">KINTO ONE (Used Vehicles)</a>.
I am pleased to announce that KINTO Technologies Corporation will be a Gold Sponsor at TSKaigi 2025, which will be held on Friday, May 23, and Saturday, May 24, 2025.</p>
<h1>What is TSKaigi 2025</h1>
<p>TSKaigi is Japan&#39;s largest technology conference themed around TypeScript.</p>
<p><strong>Event Dates: May 23 (Fri) and 24 (Sat), 2025</strong>
<strong>Venue: Bellesalle Kanda (Chiyoda-ku, Tokyo)</strong> 
<strong>Official Website: <a href="https://2025.tskaigi.org/">https://2025.tskaigi.org/</a></strong></p>
<h1>Sponsored LT</h1>
<p>On the second day, from 11:20 to 11:30, our company&#39;s Torii(<a href="https://x.com/yu_torii">@yu_torii</a>) will be holding a sponsored LT!</p>
<p>He is scheduled to talk on the theme of <strong>Backend Code-First OpenAPI Schema-Driven Development</strong>
so please look forward to it!</p>
<h1>Sponsored Booth</h1>
<p>We have prepared a survey about TypeScript at our booth!
Those who complete the survey can try their luck at our capsule toy machine and receive original novelty items as a gift!
<img src="/assets/blog/authors/Ren.M/TSKaigi/novelty.webp" alt="ノベルティ"></p>
<h1>We Are Hiring！</h1>
<p>At KINTO Technologies, we are looking for new team members to work with us!
We are open to casual interviews to start with. If you are even slightly interested, please apply through the link below!
<a href="https://hrmos.co/pages/kinto-technologies/jobs/0000127">https://hrmos.co/pages/kinto-technologies/jobs/0000127</a></p>
<h1>Conclusion</h1>
<p>If you are interested, please visit our booth!
We look forward to meeting you at the event!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/Ren.M/TSKaigi/logo.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[TSKaigi 2025のゴールドスポンサーを務めます]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-10-TSKaigi/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-10-TSKaigi/</guid>
            <pubDate>Sat, 10 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[TSKaigi 2025のゴールドスポンサーを務めます]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは！
<a href="https://up.kinto-jp.com/">KINTO ONE(中古車)</a>のフロントエンド開発をしていますRen.Mです。</p>
<p>この度、KINTOテクノロジーズ株式会社は
2025年5月23日(金)・24(土)にて開催されるTSKaigi 2025ゴールドスポンサーを務めます。</p>
<h1>TSKaigi 2025とは</h1>
<p>TSKaigiは、日本最大級のTypeScriptをテーマとした技術カンファレンスです。</p>
<p><strong>開催日程：2025年5月23日(金)・24日(土)</strong>
<strong>会場：ベルサール神田（東京都千代田区）</strong>
<strong>公式サイト：<a href="https://2025.tskaigi.org/">https://2025.tskaigi.org/</a></strong></p>
<h1>スポンサーLT</h1>
<p>2日目の11:20〜11:30に弊社の鳥居(<a href="https://x.com/yu_torii">@yu_torii</a>)によるスポンサーLTを行います！
<strong>「バックエンドのコードファーストなOpenAPIスキーマ駆動開発」</strong>
というテーマでトークを予定していますのでお楽しみに！</p>
<h1>スポンサーブース</h1>
<p>ブースではTypeScriptにまつわるアンケートを用意しています！
回答いただけた方にはガチャガチャを回していただき、オリジナルノベルティをプレゼントします！
<img src="/assets/blog/authors/Ren.M/TSKaigi/novelty.webp" alt="ノベルティ"></p>
<h1>We Are Hiring！</h1>
<p>KINTOテクノロジーズでは一緒に働く仲間を探しています！
まずは気軽にカジュアル面談からの対応も可能です。少しでも興味のある方は以下のリンクからご応募ください！
<a href="https://hrmos.co/pages/kinto-technologies/jobs/0000127">https://hrmos.co/pages/kinto-technologies/jobs/0000127</a></p>
<h1>おわりに</h1>
<p>興味をお持ちの方はぜひブースまでお越しください！
当日みなさまに会場でお会いできるのを楽しみしています！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/Ren.M/TSKaigi/logo.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[I Tried Transfer Family Web App]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-01-20-transfer-familiy-web-apps-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-01-20-transfer-familiy-web-apps-en/</guid>
            <pubDate>Fri, 09 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[記事の説明を入力]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, this is Matsuo from the Cloud Infrastructure Group.
Our team is responsible for taking care of a wide range of infrastructure-related tasks across the company, from design to operations.</p>
<p>This time, I would like to introduce the <strong>AWS Transfer Family web apps</strong>, which were announced at re:Invent.
<a href="https://aws.amazon.com/jp/about-aws/whats-new/2024/12/aws-transfer-family-web-apps/">https://aws.amazon.com/jp/about-aws/whats-new/2024/12/aws-transfer-family-web-apps/</a>
In this article, I will give an overview of the web apps, the steps to create them, how to set them up, and share my impressions after trying them out. I hope you will find this helpful.</p>
<h1>Overview</h1>
<p>To put it simply <strong>AWS Transfer Family web apps</strong> let you build applications to manage files in S3 buckets directly from a browser without writing any code.
Previously, accessing S3 through a GUI required dedicated tools like the AWS Console or WinSCP, both of which also needed configuration.
This created challenges for non-technical and casual users as it made operation difficult.</p>
<p>The new AWS Transfer Family web apps feature an intuitive interface that is easy to use, even without technical knowledge.
Additionally, they are integrated with IAM Identity Center and S3 Access Grants, enabling fine-grained access controls.</p>
<h1>Pricing</h1>
<p>AWS Transfer Family web apps are billed hourly based on the number of units (each support up to 250 sessions for 5 minutes per session).</p>
<table>
<thead>
<tr>
<th align="left">Region</th>
<th align="left">Price</th>
</tr>
</thead>
<tbody><tr>
<td align="left">Tokyo</td>
<td align="left">Number of units x 0.5 USD/hour</td>
</tr>
</tbody></table>
<p>For example, running one unit 24 hours a day will cost <strong>12 USD/day</strong>, meaning it will cost <strong>360 USD</strong> a month; so using it for long periods of time can be quite costly.
That&#39;s a little pricey for such a simple service.</p>
<p>Depending on the usage, the cost may outweigh the benefit, so it is important to use the service according to your needs.</p>
<h1>How to Create a Web App</h1>
<p>Now let&#39;s create a web app.
(Note: Terraform is not supported at the time of writing this article, so I will use the AWS Management Console.)</p>
<ul>
<li><ol>
<li>Log in to the AWS Management Console, select &quot;Web Apps&quot; from the AWS Transfer Family navigation pane, and click Create Web App. <img src="/assets/blog/authors/r.matsuo/webapp_01.png" alt=""></li>
</ol>
</li>
<li><ol start="2">
<li>For now, just set the Name tag and click &quot;Next.&quot; *<strong>IAM Identity Center configuration is required in advance</strong>. If you haven’t set it up yet, please make sure to prepare it in advance.
<img src="/assets/blog/authors/r.matsuo/webapp_02.png" alt=""></li>
</ol>
</li>
<li><ol start="3">
<li>Set the page title, logo, and favicon, then click &quot;Create.&quot; This time, I will skip the logo and favicon.. This completes the creation of the web app! It&#39;s that easy! <img src="/assets/blog/authors/r.matsuo/webapp_03.png" alt=""></li>
</ol>
</li>
</ul>
<h1>Configuring Users/Groups and S3 Access Grants</h1>
<p>Next, let&#39;s set up group assignments and S3 Access Grants for the web app.</p>
<ul>
<li><ol>
<li><strong>Group assignment</strong>
 Click &quot;User and group assignment&quot; on the web app details screen.
 <img src="/assets/blog/authors/r.matsuo/webapp_04.png" alt="">
 Since I have already created groups, I will select an existing group to assign.
 <img src="/assets/blog/authors/r.matsuo/webapp_05.png" alt=""></li>
</ol>
</li>
<li><ol start="2">
<li><strong>Setting up S3 access grants</strong>
 Next is setting up Access Grants. There are two things to set: Location and Permissions.<br> If you go to Access Grants from the S3 navigation pane, you will see the Permissions and Location tabs, from which you can use to configure access.<br> <img src="/assets/blog/authors/r.matsuo/s3_00.png" alt=""><br> Start by setting the Location. This defines the S3 bucket to be accessed, along with the IAM role that has the necessary permissions for that bucket.<br> For testing purposes, I have granted the AWS managed policy &quot;AmazonS3FullAccess&quot; this time.<br> <img src="/assets/blog/authors/r.matsuo/s3_01.png" alt=""><br> Then move to Permissions. Specify the location you configured previously, then set the sub-prefix, permission level, grantee type, IAM principal type, and the IAM principal user, and create the permission.<br> Authentication will be done through IAM Identity Center, and it is set up as shown in the image below.
 Although I have not set it this time, it seems possible to set app-specific access restrictions.<br> <img src="/assets/blog/authors/r.matsuo/s3_02.png" alt=""></li>
</ol>
</li>
<li><ol start="3">
<li><strong>Setting CORS</strong>
 Add CORS settings from <strong>Cross-Origin Resource Sharing (CORS)</strong> to the target S3 bucket so that you can view the contents of the bucket.
 <img src="/assets/blog/authors/r.matsuo/s3_03.png" alt="">
 Here’s the configuration I used:</li>
</ol>
<pre><code>[
    {
        &quot;AllowedHeaders&quot;: [
            &quot;*&quot;
        ],
        &quot;AllowedMethods&quot;: [
            &quot;GET&quot;,
            &quot;PUT&quot;,
            &quot;POST&quot;,
            &quot;DELETE&quot;,
            &quot;HEAD&quot;
        ],
        &quot;AllowedOrigins&quot;: [
            &quot;https://${ウェブアプリID}.transfer-webapp.ap-northeast-1.on.aws&quot;
        ],
        &quot;ExposeHeaders&quot;: [
            &quot;Access-Control-Allow-Origin&quot;
        ]
    }
]
</code></pre>
</li>
</ul>
<h1>Try It Out</h1>
<p>If you made it this far, you&#39;re ready to go.<br>After accessing the web app URL and completing authentication, the S3 management screen appears! 
You will see the S3 bucket you configured in Location settingss.<br><img src="/assets/blog/authors/r.matsuo/browser_01.png" alt=""></p>
<p>Now let&#39;s actually try deleting an S3 file from the target bucket. I tried to access an object from a folder link, but... It didn&#39;t work. <img src="/assets/blog/authors/r.matsuo/browser_02.png" alt=""> Turns out the IAM role used for Access Grants was missing the sts:SetContext permission. This is also mentioned in the <a href="https://docs.aws.amazon.com/transfer/latest/userguide/webapp-roles.html">User Guide</a> as well.</p>
<p>I added the settings and tried again, and was able to access the object successfully.
<img src="/assets/blog/authors/r.matsuo/browser_03.png" alt=""></p>
<p>By the way, if CORS is not configured properly, a Network Error will appear here.
<img src="/assets/blog/authors/r.matsuo/browser_04.png" alt=""></p>
<p>Now, let&#39;s select the object here and delete it.<br>Simply select the file you want to delete (in this case, test1.txt) click the ellipsis, and choose Delete.
<img src="/assets/blog/authors/r.matsuo/browser_05.png" alt=""></p>
<p>The file has been deleted from the web app.<br><img src="/assets/blog/authors/r.matsuo/browser_06.png" alt=""><br>I also confirmed that it was removed from the console.</p>
<h1>Impressions</h1>
<p>Although setting up IAM Identity Center and Access Grants was required, I was impressed by how easy it was to be able to build an S3 management app without writing any code.<br>There were a few confusing aspects due to the numerous configuration settings, but I believe that support for Infrastructure as Code (IaC) will likely make it even more user-friendly in the future.</p>
<p>This service is built on top of the <a href="https://aws.amazon.com/jp/about-aws/whats-new/2024/12/storage-browser-amazon-s3/">Storage Browser for Amazon S3</a>, which was also announced at re:Invent, so if you are comfortable with programming, you can use that service to create your own customizations.
<a href="https://aws.amazon.com/jp/about-aws/whats-new/2024/12/storage-browser-amazon-s3/">https://aws.amazon.com/jp/about-aws/whats-new/2024/12/storage-browser-amazon-s3/</a></p>
<p>I hope this blog post was helpful to you.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[コーポレートサイトリニューアル制作ストーリー「デザインの現場から」]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-17-corporate-site-renewal-story/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-17-corporate-site-renewal-story/</guid>
            <pubDate>Fri, 09 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[採用を目的にKTCらしさを表現したサイト制作の裏側を現場視点でご紹介します。]]></description>
            <content:encoded><![CDATA[<p>久しぶりのブログにどきどき。KINTOテクノロジーズ（以下KTC略）・クリエイティブ室の杉本です。 </p>
<p>弊社は会社設立から3年目にあたる2024年、コーポレートサイトをリニューアルしました。設立から3年、今後もいっしょに働く仲間を増やすため、採用活動にフォーカスしたコーポレートサイトにしたいと人事部からリクエストがあり、プロジェクトがはじまりました。 </p>
<p>採用にフォーカスしたコーポレートサイトということで、人事部はもちろんですが、会社の進むべき方向性を経営者から、現場の声を拾い上げるために、エンジニアでつくられた技術広報部のメンバーへヒアリングを行いました。 </p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/01_introduction.jpg" alt="introduction"> </p>
<p>「会社が欲しい人材は、そもそもどんな人だ？」、「我々はどんな仲間といっしょに働きたいのか」など、それぞれの視点から、課題感や希望をヒアリングすると、コーポレートサイトが果たす役割が明確になっていきました。 </p>
<p>「常にテクノロジーへの興味を持ち、最新の技術への感度が高く、 
自発的に動くエンジニア＆クリエイターへ、KTCらしさを伝えるサイトにしていこう」。この目的をもとに、コンセプトを打ち出しました。 </p>
<p>コンセプトは「The Power of Technology &amp; Creativity」。トヨタのモビリティサービスをテクノロジーとクリエイティビティでリードしていこうという気持ちを込めたワードにしました。 </p>
<p>コンセプトを立てておくのは、ひと手間なのかもしれませんが、制作過程で迷うときに、立ち戻る場所として、様々な職種の人が関わる案件ではとくに重要です。 </p>
<p>「そんな機能はいるのだろうか？」「もっと、おもしろく魅せることはできないか？」視点の違う問いにも、このコンセプトがあれば、「だから、やる」「だから、やらない」の判断がしやすくなるからです。 </p>
<h1>パーソナリティの設定</h1>
<p>次にクリエイティブ室として手がけたのは、会社を人として例えたら、どんな人で、どんなふるまいをするのかを明確にする「ブランドパーソナリティ」を設定。（※ブランドパーソナリティについては<a href="/posts/2025-04-17-corporate-site-renewal-story/#%E2%80%BB%E3%83%96%E3%83%A9%E3%83%B3%E3%83%89%E3%83%91%E3%83%BC%E3%82%BD%E3%83%8A%E3%83%AA%E3%83%86%E3%82%A3%E3%81%A8%E3%81%AF">後述</a>） </p>
<p>「ブランドパーソナリティ」は、何もないところからはじめると、全社を巻き込むレベルの開発になるので時間とカロリーを使うのですが、コーポレートサイトのローンチは、採用が目的なだけにスピードが重要。そこで、弊社ですでに明文化していた「ビジョン」、「バリュー」、「カルチャー」、「働く姿勢」をもとに進めました。 </p>
<p>導き出したKTCのパーソナリティは、ずばり「クリエイター」。創造者という役割で、私たちは「テクノロジーやクリエイティブの力でお客様にとって、最高の（使いやすく、分かりやすく、人に寄り添って、便利な）プロダクトを作る存在である」と、定義しました。 </p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/02_personality.jpg" alt="personality"> </p>
<h1>盛り上がるムードボードづくり</h1>
<p>ブランドパーソナリティが決まったので、次は具体的にコーポレートサイトの表現デザインをどう落としていくかになります。そこで、もうひと手間！担当デザイナーが手を動かす前に、クリエイティブ室全員で、ムードボードをつくりました。 </p>
<p>この作業をすることで、コンセプト同様、ビジュアル面でも立ち戻る場があるので、迷走することがなくなるので、アウトプットまでがスムーズになります。</p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/03_moodbord.jpg" alt="moodbord"> </p>
<p>自分たちが思うKTCらしいビジュアルなどを持ち寄り、デザイナーどうし、ムードボードづくりはとても盛り上がりました。 </p>
<p>ムードボードをつくることで、新たな発見もありました。それまでは、いわゆるカリフォルニアのキラキラしたテックカンパニーの勢いで制作したアウトプットになるかなと想定していたのですが、会社の存在を「我々は〜である」という視点で取り組むと、トヨタグループの現場主義を根底に据えた、日本のモノづくりのスピリットを尊重したプロフェッショナルエンジニアリング集団である（ありたい）という点が見えてきました。 </p>
<p>ムードボードは、世界基準のモダンなシステムと、ブランドパーソナリティをベースとし、使いやすさと企業ブランドのうえに、表現を載せたアウトプットを目指しました。 </p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/04_base.jpg" alt="base"> </p>
<h1>クリエイティブのジャンプアップと効率化が実現</h1>
<p>「パーソナリティ」「ムード」「サイトの目的」をクリアにしたことで、撮影のトーン、インタビュー内容、コピー、実装、すべてが一貫したつながりでつくれたので、結果的にクリエイティブのジャンプアップと効率化が叶うなと改めて思いました。デザイナー以外の関係者と話すときも、ロジカルに説明ができ、デザインの言語化までが叶いました。 </p>
<p><img src="/assets/blog/authors/aya_sugimoto/250417/05_jumpup.jpg" alt="jumpup"> </p>
<h1>おかげさまで海外アワードを受賞</h1>
<p>こうして、リニューアルができたコーポレートサイトですが、おかげさまで国際的なWebデザインアワードである「CSS Design Awards」をはじめとする、複数の海外Webデザインアワードを受賞することができました。 </p>
<p>みなさんもご覧になって、もしよければ、私たちに興味をもっていただければと思います！ </p>
<p>コーポレートサイトはこちら！ 
<a href="https://www.kinto-technologies.com/">https://www.kinto-technologies.com/</a></p>
<h4>※ブランドパーソナリティとは</h4>
<p>ブランド＝企業を人間に例えるとどんな要素、性格で構成されているかを示したものです（アーキタイプ）。一般的に「人柄」を12の属性に分けたフレームワークを活用し、企業の性格、思考、行動、振る舞い、持ち味などを探求していきます。これを持っていると、統一されたブランドイメージを常に発信することができるので、to Cでなくても、今回のようなコーポレートサイト制作やイベントで配るグッズまで、ターゲットとなる人に同じ印象でアピールすることができます。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aya_sugimoto/250417/00_main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[社内イベントロゴ、こんなふうに制作しました！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-09-Chohonbukai-logo/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-09-Chohonbukai-logo/</guid>
            <pubDate>Fri, 09 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[イベントのロゴ制作を担当の裏側やこだわったポイントをゆるっとご紹介]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは！KINTOテクノロジーズでデザイナーをしているKです。
普段はECサイトのUI/UXデザインをメインで担当していますが、時々コミュニケーションデザインも担当させてもらう機会があります。</p>
<p>2024年11月に開催された社内イベント「超本部会」で、そのイベントの顔となるロゴの制作を担当することになりました！せっかくなので、今回はその制作の裏側やこだわったポイントをゆるっとご紹介したいと思います。</p>
<h1>ロゴって何？</h1>
<p>ロゴって、ただの飾りじゃなくて、そのブランドやイベントの「顔」になるものですよね。</p>
<p>パッと見ただけで「これはあのイベントだな！」「これみたことある！」ってわかるし、見た人にどんな印象を持ってもらうかを大きく左右します。</p>
<p>「なんとなくカッコいいから」という理由だけで作るのではなく、「このイベントってどんな雰囲気？」「どんなメッセージを伝えたい？」「見た人にどんな印象を持ってもらいたい？」を考えながらデザインしていくことが大切だなと、改めて実感しました。</p>
<h1>コンセプトを固める：イベントの核を探る</h1>
<p>まずはイベント全体のアートワークを監修するアートディレクターの方にヒアリング。
イベントの目的や伝えたいメッセージを整理しながら、ロゴに込めるコンセプトを考えていきました。</p>
<p>超本部会イベントコンセプトは「主体性」と「交流」。</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_002.jpg" alt="concept"></p>
<ul>
<li>仲間と主体的につながる場であること</li>
<li>自分たちが「主体的に行動できる会社だ」と実感できること</li>
<li>仲間とワイワイ楽しむエネルギーが、次の挑戦への活力になること</li>
</ul>
<p>こうした思いを、「自由に楽しめるデザイン」「仲間と盛り上がれるエネルギッシュな雰囲気」で表現したいと考えました。</p>
<h1>デザインの方向性を探る</h1>
<p>次は、コンセプトをもとに、どんなビジュアルが合うかを探るフェーズ。
「自由に楽しめるデザイン」「仲間と盛り上がれるエネルギッシュな雰囲気」をどう表現するか？を考えたときに浮かんできたのが、「オタク文化×テクノロジー」というテーマでした。</p>
<p>イベントには、開発やクリエイティブに関わるメンバーが多く参加します。そういった参加者たちにとって、アニメやゲーム、メカ、マンガといった“オタク的”なカルチャーは、親しみがあってワクワクする要素のひとつ。そこにテクノロジーの未来感を掛け合わせることで、より自由で、ポジティブな熱量を持った世界観をつくれると考えました。</p>
<p>検討したビジュアル要素はこんな感じ：</p>
<ul>
<li><p><strong>メカ・ロボット・特撮の要素</strong>：無機質な硬さやメカっぽさを取り入れる</p>
</li>
<li><p><strong>漫画・コミック的な要素</strong>：勢いのある文字や吹き出しのようなシェイプを試す</p>
</li>
<li><p><strong>デジタル感のあるタイポグラフィ</strong>：少し未来感のある雰囲気に</p>
</li>
</ul>
<p>まずは手描きでラフスケッチを描きながら、アイデアをどんどん出していきました。</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_003.jpg" alt="sketch"></p>
<h1>ラフ案を選定して、データ化へ</h1>
<p>スケッチから方向性が見えてきたところで、Illustratorを使って本格的にデータ化。</p>
<ul>
<li><p>ラフを整理しながら、ベースとなる形を作成</p>
</li>
<li><p>微妙なニュアンスの違いで複数バリエーションを制作</p>
</li>
<li><p>アートディレクターと「どれが一番コンセプトを体現できているか」を検討</p>
</li>
</ul>
<p>もちろんボツ案もたくさん出ましたが、この試行錯誤のプロセスこそが、いいデザインを生むためには欠かせないと改めて感じました。</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_004.jpg" alt="date"></p>
<h1>ディテールの磨き込み</h1>
<p>ラフが固まったら、仕上げフェーズです。ここでは、細かい部分まで納得がいくように調整を重ねていきました。</p>
<p>そして、<strong>ロゴの印象を決定づける大きな要素のひとつが「フォント」。</strong></p>
<p>例えば、丸みを帯びたフォントは柔らかさや親しみやすさを感じさせ、シャープなフォントは洗練された印象を与えます。ちょっとした違いが、ロゴ全体の雰囲気を大きく左右するんですよね。</p>
<p>今回は既存のフォントに頼らず、オリジナルでフォントを書き起こしました。イベントのキーワードである「主体性」と「交流」をイメージしながら、以下の点を意識して調整：</p>
<ul>
<li><p><strong>視認性の向上</strong>：読みやすく、文字の幅や縦横比、余白を整えることで全体にまとまりを持たせる</p>
</li>
<li><p><strong>曲線の調整</strong>：パスの数を減らして、滑らかで整った印象に</p>
</li>
<li><p><strong>漢字とカタカナの調和</strong>：並んだ時に違和感が出ないよう、形状の統一感を意識</p>
</li>
</ul>
<p>赤い基準線と見比べると、最終的な形は大きく変化しました。フォント選びや細部の形の違いが、ロゴの印象を大きく左右することを、改めて体感したプロセスでした。</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_005.jpg" alt="beforeafter"></p>
<h1>ロゴの完成：遊び心と汎用性のバランス</h1>
<p>そしてついにロゴが完成しました！</p>
<p><img src="/assets/blog/authors/kanae_kato/250430/techblog_006.jpg" alt="finalize"></p>
<ul>
<li><p>遊び心を取り入れつつ、いろんな場面で使いやすい汎用性をキープ</p>
</li>
<li><p>サブカル×テクノロジーのミックス感が自然に表現できた</p>
</li>
<li><p>シェイプやフォントにこだわることで、全体の完成度がUP！</p>
</li>
</ul>
<h1>まとめ</h1>
<p>ロゴデザインって、本当にちょっとした差で印象が大きく変わるものなので、「これだ！」と思えるまでやりきることが大事だなと、今回改めて思いました。</p>
<p>今回は「なんとなくカッコいい」だけじゃなく、「このイベントらしさがちゃんと伝わって、いろんな場所で使えるデザイン」に仕上げられたかなと思っています。</p>
<p>この記事が、誰かのデザインのヒントや気づきになればうれしいです！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/kanae_kato/250430/techblog_001.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[What If We Introduced an NFT Stamp Rally at the KINTO Technologies All-Hands?]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-19-What_if_we_introduced_an_NFT_stamp_rally_at_the_KINTO_Technologies_town_hall-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-19-What_if_we_introduced_an_NFT_stamp_rally_at_the_KINTO_Technologies_town_hall-en/</guid>
            <pubDate>Thu, 08 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We introduced an NFT stamp rally as part of the team activity at our in-person all-hands meeting. In this blog, I’ll share how using NFTs helped facilitate cross-department communication, the impact it had, and future possibilities for using NFTs.]]></description>
            <content:encoded><![CDATA[<br>
This article is the entry for day 19 in the [KINTO Technologies Advent Calendar 2024](https://qiita.com/advent-calendar/2024/kinto-technologies)🎅🎄
<br>

<h1>1. Tech Blog Debut</h1>
<h2>Hello!</h2>
<p>I&#39;m Shiori (<a href="https://twitter.com/shor_t8q">@shor_t8q</a>) from the Generative AI Development Project. I am currently in charge of training, use case development, and technical support for generative AI! (Though I chose Web3 as the topic for my first Tech Blog post.)</p>
<p>This blog is about how we used NFTs at a team activity during the CHO All-Hands Meeting (an in-person meeting event that brought together all employees across all offices) held on November 28, 2024. This is the perfect content for anyone interested in the in-house planning and deployment of large-scale company events, incorporating NFTs into internal activities, or learning more about the company culture at KINTO Technologies.</p>
<br>

<h2>Premise</h2>
<p>The NFT initiative introduced in this blog was made possible through the cooperation and dedication of all the organizers of the CHO All-Hands Meeting, the activities team, the novelty team, and the Creative Office.</p>
<p>Furthermore, this blog is not sponsored by any of the companies providing the NFT services discussed here. The purpose of this blog is solely to share the experiences, creativity, insights, and lessons learned by those in charge of the activities and novelties.</p>
<br>

<h1>2. About the CHO All-Hands Meeting</h1>
<br>

<p>![01_chohonbukai](/assets/blog/authors/shiori/2024-12-19/01_chohonbukai.png =750x)</p>
<br>

<h2>2-1. What is a CHO All-Hands Meeting?</h2>
<p>A CHO All-Hands Meeting is a company-wide event held by KINTO Technologies approximately once or twice a year. The CHO All-Hands Meeting covered in this blog was held on November 28, 2024, with approximately 300 attendees in person and around 50 attendees online. The meeting took place from 3:00 PM to 9:00 PM and was a highly exciting event with a wide variety of content.</p>
<br>

<h2>2-2. Being Part of Organizing the activities and novelties</h2>
<p>About a month and a half before the CHO All-Hands Meeting, members from various departments came together to form the organizing team. I was assigned to be part of the team in charge of activities and novelties.</p>
<br>

<p>![02_novelty](/assets/blog/authors/shiori/2024-12-19/02_novelty.png =750x)</p>
<br>

<p>To promote communication across the company, we decided to create desk nameplates as novelty items. The design was handled by J-san from the Creative Office.</p>
<br>

<h2>2-3. Concept of the CHO All-Hands Meeting</h2>
<p>The event&#39;s concept was &quot;KTC is Made by You”, which was shaped by gathering input from executives, department heads, and managers about their thoughts on teamwork, company culture, and their vision for the future.</p>
<br>

<p>![03_concept](/assets/blog/authors/shiori/2024-12-19/03_concept.png =750x)</p>
<br>

<h1>3. Activities at the CHO All-Hands Meeting</h1>
<br>

<h2>3-1. What is a team activity?</h2>
<p>It was the final section of the event, held at the end of the meeting from 7:00 p.m. to 9:00 p.m.</p>
<p>Based on the concept of the CHO All-Hands Meeting, the activities team set the following two goals:</p>
<ul>
<li>To promote communication among participants and between members of different departments.</li>
<li>To deliver an experience that leaves participants feeling grateful they joined.</li>
</ul>
<br>

<h2>3-2. What led to the use of NFTs in the team activity?</h2>
<p>The activity featured an NFT stamp rally, but why choose that format?  I was originally in charge of novelties, but the idea started when I wondered, &quot;What if the novelty items were NFTs?&quot; and &quot;Could this serve as inspiration for future NFT and Web3 projects?&quot; I was also motivated by the desire to try something new and different. </p>
<p>With that in mind, I began exploring ways to incorporate NFTs that aligned with the theme of the CHO All-Hands Meeting. Through online research, I discovered the concept of an &quot;NFT stamp rally.&quot; Around the same time, members of the organizing team shared that a digital stamp rally had been used at the previous CHO All-Hands Meeting. That inspired us to take it a step further and evolve the concept into an NFT stamp rally. From that point on, I joined the team activity planning discussions, and we began exploring how to bring the NFT stamp rally to life.</p>
<br>

<p>![04_nft_idea](/assets/blog/authors/shiori/2024-12-19/04_nft_idea.png =400x)</p>
<br>

<h1>4. What Is an NFT?</h1>
<br>

<h2>4-1. NFTs Are Unique Digital Assets</h2>
<p>NFT stands for &quot;Non-Fungible Token.&quot; NFTs are one-of-a-kind digital assets that cannot be exchanged for other tokens due to their uniqueness and characteristics. In addition, NFTs use blockchain technology to ensure transparency in transactions. For example, Bitcoin is a fungible token, meaning one Bitcoin holds the same value as another, whereas NFTs are non-fungible because each NFT holds its own unique value.</p>
<br>

<p>![05_nft](/assets/blog/authors/shiori/2024-12-19/05_nft.png =400x)</p>
<br>

<p>Because of their non-fungible nature, NFTs have become a new tool to enhance fan engagement in art, games, music, movies, sports, entertainment, and corporate marketing activities, and their application in other industries is expanding as well. While NFTs provide a means of monetization for creators and a new marketing tool for companies, improving the transparency and traceability of transactions, they also come with challenges such as the environmental impact from the large amount of electricity it requires during mining, legal risks, and price volatility due to market fluctuations.</p>
<br>

<h1>5. NFT Stamp Rally</h1>
<br>

<h2>5-1. Is it feasible to hold an NFT stamp rally at the team activity?</h2>
<h3>Conducting a survey</h3>
<p>To explore whether it was feasible to distribute and exchange NFTs as part of the team activity, we began by conducting some research.</p>
<p>Through this process, we came across a company called <a href="https://www.sushitopmarketing.com/">Sushi Top Marketing</a> which has an extensive track record with NFT use cases including organizing NFT stamp rallies. They are also promoting a new concept called “Token Graph Marketing.”</p>
<br>

<h3>What is Token Graph Marketing?</h3>
<p>Token Graph Marketing is a new marketing approach that uses blockchain technology to reach users while protecting their privacy. Users&#39; hobbies, preferences, and behavioral history can be analyzed based on their NFTs and other token information.</p>
<br>

<p>![06_token_graph_marketing](/assets/blog/authors/shiori/2024-12-19/06_token_graph_marketing.png =750x)</p>
<p>Source: <a href="https://www.sushitopmarketing.com/service/tokengraphmarketing">What is Token Graph Marketing?</a></p>
<br>

<h3>Initial Ideas</h3>
<p>I had several discussions with the vendor based on the following initial ideas:</p>
<table>
<thead>
<tr>
<th><strong>Content</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>Novelty Concept</strong></td>
<td>Activating communication among members who rarely interact with each other, stimulate communication and promote innovation, fostering a sense of unity, and provide novelties that appeal across age and gender.<br><br>The solution should not deviate from this core concept.</td>
</tr>
<tr>
<td><strong>Accessibility</strong></td>
<td>Since the stamp rally would be held on a single day, it needed to be intuitive and easy to use<br><br>Must be supported for both app and via QR code scanning.</td>
</tr>
<tr>
<td><strong>Value as a Novelty</strong></td>
<td>Showcase NFTs as a new type of novelty item, something participants can transfer to their own wallet and keep as a digital asset.</td>
</tr>
<tr>
<td><strong>Innovation</strong></td>
<td>Reflect the company’s emphasis on creativity and technology by trying something that hasn’t been done before.</td>
</tr>
<tr>
<td><strong>Brand Alignment</strong></td>
<td>Customize the UI to reflect the company’s brand identity, including its colors and character.</td>
</tr>
<tr>
<td><strong>Concept Visualization</strong></td>
<td>For example: “Each department has a different NFT stamp. When Employee A speaks with Employee B from another department, A receives a stamp from B’s department.” This encourages interdepartmental interaction.</td>
</tr>
</tbody></table>
<br>

<h2>5-2. Blockchain Technology Behind the NFT Stamp Rally</h2>
<p>For the NFT stamp rally, we chose to use Astar, a next-generation blockchain platform known for its compatibility with smart contract and dApps (decentralized apps).  <a href="https://astar.network/">Astar</a> is part of the <a href="https://polkadot.com/">Polkadot</a> ecosystem, enabling interoperability between different blockchains.</p>
<br>

<p>![07_astar](/assets/blog/authors/shiori/2024-12-19/07_astar.png =750x)</p>
<br>

<h2>5-3. NFT Stamp Rally Concept</h2>
<h3>Idea brainstorming</h3>
<p>We received four initial proposals for the NFT stamp rally:</p>
<table>
<thead>
<tr>
<th><strong>Proposal</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>NFT Card Exchange</strong></td>
<td>Participants collect NFTs by exchanging digital cards. Additional NFTs can be unlocked upon exchange.</td>
</tr>
<tr>
<td><strong>Dynamic NFT Stamp Rally</strong></td>
<td>Collecting specific NFT pairs unlocks special NFTs. Different combinations yield different results.</td>
</tr>
<tr>
<td><strong>NFT Distribution via LINE</strong></td>
<td>Commemorative NFTs are distributed through an official LINE account. Those who hold them receive a gift.</td>
</tr>
<tr>
<td><strong>NFT Business Cards &amp; Stamp Rally</strong></td>
<td>NFC or QR-enabled cards are handed out before the event. These cards serve as novelties and as tools for collecting NFTs during the rally.</td>
</tr>
</tbody></table>
<p>We discussed and refined the proposals based on the following criteria:</p>
<br>

<h3>Criteria</h3>
<table>
<thead>
<tr>
<th><strong>Criteria</strong></th>
<th><strong>Description</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>Workload</strong></td>
<td>Can everything be prepared in about one month?</td>
</tr>
<tr>
<td><strong>Usability</strong></td>
<td>Is it intuitive, with little to no manuals or explanations?</td>
</tr>
<tr>
<td><strong>Operability</strong></td>
<td>Can it run smoothly, in a relaxed atmosphere in an event with 300 participants?</td>
</tr>
<tr>
<td><strong>Simplicity</strong></td>
<td>Can it be used without complex operations or steps?</td>
</tr>
<tr>
<td><strong>Accessibility</strong></td>
<td>Can it be accessed without restrictions, needing third-party services?</td>
</tr>
<tr>
<td><strong>Innovation</strong></td>
<td>Does it offer an interesting NFT experience?</td>
</tr>
<tr>
<td><strong>Security</strong></td>
<td>Does it ensure data privacy and protection?</td>
</tr>
<tr>
<td><strong>Scalability</strong></td>
<td>Can it handle simultaneous access from a large number of users?</td>
</tr>
<tr>
<td><strong>Compatibility</strong></td>
<td>Is it mobile-friendly and Web3-compatible?</td>
</tr>
<tr>
<td><strong>Budget Alignment</strong></td>
<td>Is it achievable within the budget?</td>
</tr>
</tbody></table>
<p>Combining the ideas and our requirements, we finalized the following plan:</p>
<br>

<h2>5-4. NFT Stamp Rally Planning Details</h2>
<h3>Overview of the NFT stamp rally</h3>
<p>We issued 14 NFTs, covering each department, management team, and a completion certificate.</p>
<p>Participants would visit different zones during the activity (such as spots where the management and award recipients are present), collect NFTs that are different from the ones they already hold, and engage in communication with members from other departments or the management team, whom they don&#39;t often have the chance to interact with. This promotes the start of new projects and fosters relationship-building.</p>
<p>A gamified element was added, in which participants who collected all 13 NFTs could earn a final “completion” NFT, which was redeemable for a prize.</p>
<br>

<h3>NFT Collection Points</h3>
<p>Each participant’s name tags (distributed at check-in) would contain an NFC tag -explained later- and a QR code, allowing participants to collect NFTs through either NFC tapping or QR scanning. Providing both options ensured  accessibility for participants who couldn’t use NFC.</p>
<br>

<h3>NFT Stamp Rally Flow</h3>
<ol>
<li>Participants visit each zone.</li>
<li>Interact with members from other departments and management.</li>
<li>Post what they discussed in the chat linked to the department&#39;s NFT (sharing the conversations in the NFT-linked chat adds unique value to each department&#39;s NFT).</li>
<li>Exchange NFTs with the members they interacted with and collect 13 NFTs.</li>
<li>Earn the completion NFT.</li>
<li>Post their Slack name in the completion NFT chat.</li>
<li>The event staff later award prizes via Slack.</li>
</ol>
<h3>NFT Design</h3>
<p>Since this was a company-wide event, we incorporated KINTO&#39;s brand character, <em>Kumobii</em>, into the NFT designs to strengthen the sense of unity and identity.</p>
<br>

<p>![nft_collections](/assets/blog/authors/shiori/2024-12-19/08_nft_collections.png =750x)</p>
<br>

<p>The NFT artwork was created by Momoi-san from the Creative Office.</p>
<h3>NTF Innovations</h3>
<p>We added original value to NFTs by:</p>
<ul>
<li>Each department having its own unique design.</li>
<li>NFT names were written in both Japanese and English to accommodate global members.</li>
<li>A unique collection name and event date attribute were added to each NFT.</li>
<li>All NFTs included the keyword &quot;KTC is Made by You,&quot; the theme of the CHO All-Hands Meeting. When combined, the NFTs formed the full theme of the activity.</li>
</ul>
<br>

<h1>6. Challenges in Realizing an NFT Stamp Rally</h1>
<br>

<h2>6-1. How Should NFTs Be Exchanged? What channel should be used? What technology should be adopted?</h2>
<h3>Using NFT x NFC tags</h3>
<p>As an element of this get-together, we placed importance on adding small surprises and discoveries, such as &quot;Oh, I didn&#39;t know that was possible!&quot;. By combining ideas from service providers and the organizing team members, we made it possible to receive an NFT simply by tapping a smartphone on an NFC chip embedded in a name tag.</p>
<p>*NFC (Near Field Communication) is a <strong>technology and standard that enables wireless communication between devices in close proximity.</strong>. It&#39;s used in a wide range of applications, including smartphones, IC transit cards, credit cards, and smart appliances.</p>
<br>

<h3>How to write NFT information ito NFC tags?</h3>
<p>NFC tag: <a href="http://www.amazon.co.jp/dp/B0DFW9WTRW">www.amazon.co.jp/dp/B0DFW9WTRW</a></p>
<p>NFC writing app: <a href="https://apps.apple.com/jp/app/nfc-tools/id1252962749">https://apps.apple.com/jp/app/nfc-tools/id1252962749</a></p>
<p>We procured NFC tags from Amazon manually wrote NFT URLs for 13 types and approximately 300 people. To cover potential failures, we also prepared NFT QR codes for all participants. This process was carried out manually (the organizers, thyt-san and Oka-san, did the writing.)</p>
<p>The completed tags were inserted into name holders.</p>
<br>

<p>![09_nfctag](/assets/blog/authors/shiori/2024-12-19/09_nfctag.png =400x)</p>
<br>

<h2>6-2. How were the rules communicated to all participants?</h2>
<p>During the host&#39;s introduction before the start of the NFT stamp rally, the “NFT Stamp Rally Guide” was projected on screen, while the host and I worked together to make sure everyone was aware of the rules of the NFT stamp rally.</p>
<br>

<p>![10_guide](/assets/blog/authors/shiori/2024-12-19/10_guide.png =750x)</p>
<br>

<h2>6-3. Since NFT exchange is done on a browser-based web app, how can participants avoid losing track of the NFTs they have acquired?</h2>
<p>I worked with the event MCs to provide the following information through the projected materials:</p>
<ul>
<li>The web app runs in a browser.</li>
<li>Don’t use incognito mode.</li>
<li>After acquiring your first NFT, add a NFT list shortcut to your home screen.</li>
<li>Take a screenshot of your NFT backup code displayed during the NFT exchange.</li>
</ul>
<br>

<h2>6-4. How to issue completion NFTs</h2>
<p>Participants who collected all NFTs could show their NFT list to staff to receive a special completion NFT.</p>
<br>

<p>![11_nft_list](/assets/blog/authors/shiori/2024-12-19/11_nft_list.png =400x)</p>
<br>

<h1>7. Lessons Learned</h1>
<br>

<h2>7-1. NFT exchange became the only activity everyone had time for</h2>
<p>Although we got to meet many team members we don&#39;t usually talk to, it was difficult to even find time to eat. It was difficult to share conversations with members in the chats linked to NFTs as originally envisioned, and achieving the goal of &quot;accumulating the insights and ideas of department members in each department&#39;s NFTs and turning them into unique value for each department&#39;s NFT&quot; proved challenging. In addition, the original plan envisioning people exchanging NFTs across zones (spots where management, honored members, awarded members, would stay) didn’t pan out either. At an activity that encourages free communication, there was no need to set up zones.</p>
<br>

<h2>7-2. We should have supported NFT type-checking</h2>
<p>Participants commented that, since it was possible to obtain multiple NFTs from any member of the same Department A, participants often ended up with similar designs, such as multiple NFTs from Department A. This made it difficult to check which types of NFTs were still missing. This should have been considered in advance and addressed.</p>
<br>

<h2>7-3. The completion process should&#39;ve been simpler.</h2>
<p>As mentioned in 5-4, things didn’t go as smoothly as expected.</p>
<p>Asking participants to post a screenshot of their completed NFT list in Slack could have helped. It would have made it easier for staff to confirm completions.</p>
<p>Also, by reporting completion in an open space such as a Slack channel, &quot;My colleague, so and so, has completed it!&quot; would have motivated others and encourage more participation. </p>
<br>

<h2>7-4. More flexibikity in choosing channels for NFT touchpoints.</h2>
<p>We manually wrote NFT URLs for 300 people onto the NFC tags. However, considering the potential for human error and the amount of workload involved, it may be better to explore other touchpoints and channels for larger events.</p>
<br>

<h2>7-5. Provide more proactive English support</h2>
<p>About 25% of KINTO Technologies&#39; employees are international. Although presentation slides were prepared in both Japanese and English, but when speaking with several international members at the activities, we found ourselves giving additional explanations in English during the event. Things would have gone even more smoothly if we had shared the presentation materials on the Slack channel beforehand and provided explanations in English at the start of the stamp rally.</p>
<br>

<h1>8. NFT Stamp Rally Results</h1>
<br>

<h2>8-1. 10% of Participants Completed the Rally!!!</h2>
<p>Out of approximately 300 participants, 30 people, or <strong>10% of the participants completed the NFT stamp rally</strong>.</p>
<br>

<p>![12_nft_complete](/assets/blog/authors/shiori/2024-12-19/12_nft_complete.png =400x)</p>
<br>

<h2>8-2. Overall results</h2>
<p>A total of <strong>1,734 NFTs</strong> were distributed. On average, participants <strong>exchanged 6.6 NFTs per person</strong>, with a <strong>median of 6</strong>. That means <strong>each participant talked to members from around 6 different departments</strong>.</p>
<p>One of the goals was to interact with members from other departments at the team activity, so it can be said that the NFT stamp rally fulfilled that goal.</p>
<table>
<thead>
<tr>
<th><strong>Metric</strong></th>
<th><strong>Result</strong></th>
</tr>
</thead>
<tbody><tr>
<td><strong>Completed participants</strong></td>
<td>30</td>
</tr>
<tr>
<td><strong>Total NFTs issued</strong></td>
<td>1734</td>
</tr>
<tr>
<td><strong>Average NFTs exchanged</strong></td>
<td>6.6</td>
</tr>
<tr>
<td><strong>Median NFTs exchanged</strong></td>
<td>6</td>
</tr>
</tbody></table>
<br>

<h1>9. Future Posibilities for NFTs</h1>
<p>We learned that NFT stamp rallies can visualize movement and engagement at in-person networking events. Interestingly, it allowed us to see how many NFTs each department issued, which enabled us to identify which departments were most active in communicating with other departments.</p>
<p>This experience expanded our vision on how NFTs could be used both online and offline in KINTO Technologies&#39; KINTO product offerings, such as MaaS-related apps and member services. For instance, I thought that NFTs could be effective for approaching customers based on their hobbies and preferences, rather than traditional attributes such as age and gender, and for visualizing customer communication and behavior.</p>
<br>

<p>![13_products.jpeg](/assets/blog/authors/shiori/2024-12-19/13_products.png =750x)</p>
<br>

<h1>10. Conclusion</h1>
<p>Through the NFT stamp rally initiative, we realized the potential that can arise from the fusion of new technologies and ideas. It was a valuable experience to create an opportunity for members from different departments to interact and engage with technology in a fun way during a team activity. We’ll keep exploring how to integrate new technologies in engaging ways and share our findings along the way. If you&#39;re thinking about trying a project involving NFTs, AI, or Web3, go for it! I&#39;m sure you&#39;ll discover something interesting!</p>
<p>Thank you for reading until the end!</p>
<p><br><br></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/shiori/2024-12-19/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Docker HubからECR Public Galleryへ移行する]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-28-migrate_from_dokcerhub_to_publicecr/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-28-migrate_from_dokcerhub_to_publicecr/</guid>
            <pubDate>Thu, 08 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Docker Hubの代わりにECR Public GalleryからパブリックイメージをPullするための設定内容や手順を解説]]></description>
            <content:encoded><![CDATA[<h1>自己紹介</h1>
<p>2025年3月からKTCに入社したtetsuと申します。
前職ではインフラエンジニア(オンプレ・クラウド)をしており、KTCではPlatformエンジニアとしてジョインしました。</p>
<p>旅行と自然が好きで大きな休みのたびにどこか遠出にしています。</p>
<h1>概要</h1>
<p>本記事では、Github Actionsのワークフローにて、Docker Hubからパブリックコンテナイメージ(JDK, GO, nginxなど)をPullしていた設定を、ECR Public GalleryからPullするように移行する手順や設定を紹介します。</p>
<p>2025年4月1日から、未認証ユーザによるDocker HubのパブリックコンテナイメージPullに対して制限が厳しくなるという話でした。
具体的には、認証されていないユーザによるPullは、ソースIPアドレスあたり10回のPull/時間に制限されることになります。
詳しくは<a href="https://docs.docker.com/docker-hub/usage/pulls/">こちら</a>。</p>
<p>Github Actionsのワークフローを実行する仮想マシンは全ユーザが共有されるため、Docker HubからみたソースIPアドレスも限られたものになります。よって上記の制限により、Github Actionsを使ってコンテナのBuildを行うにあたりネックとなるため、対応策を練る必要がありました。</p>
<h1>前提構成</h1>
<p>弊社では以下のような構成でGithub Actionsを利用し、コンテナのBuildを自動化していました（ざっくり抽象化した構成になります）。</p>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_DockerHub_Arch1.png" alt="構成図"></p>
<h1>対応策の検討</h1>
<p>Pull制限問題への対応策をいくつか検討しました。</p>
<h2>Personal Access Token(PAT)を使用してDocker HubにログインしたうえでPull</h2>
<p>「そもそもDocker Hubに認証すればいいじゃん」という声が聞こえてきます。
Docker HubのPATを発行して、Github ActionsのワークフローでそのTokenを利用してdocker loginすることで認証できます。
それによりPull制限を回避することができます。</p>
<p>ただし、PATは個人に紐づきます。
弊社ではGithub Actionsをチームで共用して使用するため、個人にTokenが紐づいてしまうと、ライセンス管理の面で好ましくないと考えています。</p>
<h2>Organization Access Token(OAT)を使用してDocker HubにログインしたうえでPull</h2>
<p>上記の方法と同じになるのですが、異なる点はOATという組織に紐づいた共有Tokenで認証するという点になります。</p>
<p>この共有Tokenを利用するには、Docker DesktopのライセンスをTeamもしくはBusinessにする必要があります。</p>
<h2>Github Container Registry(GHCR)へ移行</h2>
<p>Githubが提供するGithub Container Registry(GHCR)からコンテナイメージをPullする方法です。
Github Actionsのワークフローで{{ secrets.GITHUB_TOKEN }}を使用して認証することで、コンテナイメージをPullすることができます。
ただし、コンテナイメージの検索をするのが少し癖があり、検索しづらいです（Docker Hubで提供されているバージョンと比較するのが難しい印象です）。</p>
<h2>ECR Public Galleryへ移行</h2>
<p>AWSが提供するECR Public GalleryからコンテナイメージをPullする方法です。
ECR Public GalleryにIAMを利用して認証するか、認証しないかで制限が異なりますが、基本的には無料で使うことができます。</p>
<p>ECR Public Galleryに未認証ユーザはソースIPアドレスあたり以下のような制限になります。</p>
<ul>
<li>1回/秒のPull</li>
<li>500GB/月のPull</li>
</ul>
<p>一方で認証ユーザについてはアカウント単位で以下のような制限になります。</p>
<ul>
<li>10回/秒のPull</li>
<li>5TB/月を超える転送については0.09USD/GB(5TBまでは無料)</li>
</ul>
<p>以下ドキュメントにて詳細な上限が記載されています。
<a href="https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/public/public-service-quotas.html">https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/public/public-service-quotas.html</a>
<a href="https://aws.amazon.com/jp/ecr/pricing/">https://aws.amazon.com/jp/ecr/pricing/</a></p>
<blockquote>
<p>AWS アカウントを利用していない場合、パブリックリポジトリから転送されるデータはソース IP によって制限されます。</p>
</blockquote>
<p>ECR Public GalleryにはDocker公式のコンテナイメージがあり、それがDocker Hubのコンテナイメージと対応しています。
そのため運用上利用しやすく、容易に移行がしやすいです。</p>
<h1>案比較</h1>
<p>上記の案をまとめてQCD評価しました。
比較表は以下の通りです。</p>
<table>
<thead>
<tr>
<th>案</th>
<th>Quality</th>
<th>Cost</th>
<th>Delivery</th>
</tr>
</thead>
<tbody><tr>
<td>PATを使用してDocker Hubにログイン</td>
<td>× <br> - 個人トークンに依存する状態のため組織としては好ましくない <br> - 利便性については現状と変わりなし</td>
<td>〇 追加費用なし</td>
<td>〇 対応が工数が少ないためすぐ実装可能</td>
</tr>
<tr>
<td>OATを使用してDocker Hubにログイン</td>
<td>〇 現状から変わりなし</td>
<td>× 利用ユーザに応じてライセンス費用が増える</td>
<td>×　ライセンス変更にリードタイムがかかる</td>
</tr>
<tr>
<td>GHCRへ移行</td>
<td>△ Docker Hubで利用しているイメージとの対応が検索しづらい</td>
<td>〇 追加費用なし</td>
<td>〇　対応が工数少ないためすぐ実装可能</td>
</tr>
<tr>
<td>ECR Public Galleryへ移行</td>
<td>〇 Docker Hubで利用しているイメージとの対応が検索がしやすい</td>
<td>〇 追加費用なし</td>
<td>〇　対応工数が少ないためすぐ実装可能</td>
</tr>
</tbody></table>
<ul>
<li>PATとOATを利用する案は現状から利便性が変わらない点は優位である</li>
<li>GHCRはGithubの{{ secrets.GITHUB_TOKEN }} を利用することで容易に実装ができるが、ECR Public Galleryと比べてコンテナイメージの検索がしづらい</li>
<li>ECR Public GalleryはIAMポリシーで設定変更が必要であるが、軽微な修正であるので対応工数はそこまで増えない</li>
</ul>
<p>上記を踏まえて、弊社では、対応工数が少なく、追加費用がかからない、かつ利便性の高い「ECR Public Galleryへ移行」案を採用することにしました。</p>
<p>※ 各個人・各社によって環境は変わるので、必ずしもこの選択がよいとは限りません。</p>
<h1>ECR Public Galleryへ移行するための設定</h1>
<p>コンテナイメージの取得元修正、Github Actionsのワークフローを定義するYAMLファイル、AWSでの設定が必要になるためそれらを設定します。</p>
<h2>構成図</h2>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_DockerHub_Arch2.png" alt="構成図"></p>
<h2>コンテナイメージの取得元修正</h2>
<h3>コンテナイメージの検索</h3>
<p>Dockerfileやdocker-compose.ymlなどでどこからコンテナイメージをPullするかを指定しているかと思います。今回はDockerfileでJDKのコンテナイメージの取得元をDocker HubからECR Public Galleryに移行するケースで説明します。</p>
<p>以下のようにDockerfileのFROM句があるとします。</p>
<pre><code>FROM eclipse-temurin:17.0.12_7-jdk-alpine
</code></pre>
<p>ECR Public Galleryにこのイメージが存在するかを<a href="https://gallery.ecr.aws/search?page=1">こちら</a>で検索します。
今回だと:の前のDocker Hubの公式コンテナイメージ（今回は<code>eclipse-temurin</code>）を検索して、by Dockerになっているものを選択します。</p>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_image_tag1.png" alt="image_tag1"></p>
<p>「image tags」を選択して、イメージ一覧を表示します。</p>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_image_tag2.png" alt="image_tag2"></p>
<p>Docker Hubの公式コンテナイメージのタグ（今回は<code>17.0.12_7-jdk-alpine</code>）をimage tagsの検索欄に入力し、対象イメージを検索します。そして、「Image URI」をコピーします。</p>
<p><img src="/assets/blog/authors/t.sugiyama/TechBlog_image_tag3.png" alt="image_tag3"></p>
<h3>コンテナイメージの取得元修正</h3>
<p>FROM句に修正後のコンテナイメージのURIをペーストします。</p>
<p>今回だと、以下のように修正になります（当初のURIと比べてpublic.ecr.aws/docker/library/が追加されています）。</p>
<pre><code>FROM public.ecr.aws/docker/library/eclipse-temurin:17.0.12_7-jdk-alpine
</code></pre>
<p>これでECR Public GalleryからコンテナイメージをPullするように設定ができました。</p>
<h2>AWSの設定</h2>
<p>ECR Public Galleryに認証してログインした状態でPullできるようにするため、IAMロールとIAMポリシーを作成します。</p>
<h3>IAMロール</h3>
<p>Github公式ドキュメントで手順の案内があるので、こちらを参考にしてください。
<a href="https://docs.github.com/ja/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services">https://docs.github.com/ja/actions/security-for-github-actions/security-hardening-your-deployments/configuring-openid-connect-in-amazon-web-services</a></p>
<p>IDプロバイダの構築 -&gt; IAMロールの構築の順に対応となります。</p>
<h3>IAMポリシー</h3>
<p>ECR Public GalleryからPullするためのアクションを許可するIAMポリシーを作成します。
以下ドキュメントを参考にしています。
<a href="https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/public/docker-pull-ecr-image.html">https://docs.aws.amazon.com/ja_jp/AmazonECR/latest/public/docker-pull-ecr-image.html</a></p>
<pre><code>{
  &quot;Version&quot;: &quot;2012-10-17&quot;,
  &quot;Statement&quot;: [
    {
      &quot;Sid&quot;: &quot;GetAuthorizationToken&quot;,
      &quot;Effect&quot;: &quot;Allow&quot;,
      &quot;Action&quot;: [
        &quot;ecr-public:GetAuthorizationToken&quot;,
        &quot;sts:GetServiceBearerToken&quot;
      ],
      &quot;Resource&quot;: &quot;*&quot;
    }
  ]
}
</code></pre>
<p>このIAMポリシーを上記で作成したIAMロールにアタッチをします。</p>
<h2>Github ActionsにECR Public Galleryへのログイン処理の追加</h2>
<p>ECR Public Galleryに認証した状態でログインするよう、Github Actionsのワークフローを定義するYAMLファイルでログイン処理を追加します。</p>
<p>弊社の環境ですと、Docker Buildのステップの前に以下の記載を追加します。</p>
<pre><code>    ## ECR Public Galleryへログイン
    - name: Login to ECR Public Gallery
      id: login-ecr-public
      run: |
        aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws
</code></pre>
<p>※ ECR Public Galleryがus-east-1リージョンに存在するため、<code>--region us-east-1</code>で明示的に指定します。</p>
<h1>さいごに</h1>
<p>Github Actionsのワークフローにて、Docker Hubからパブリックコンテナイメージ(JDK, GO, nginxなど)をPullしていた設定を、ECR Public GalleryからPullするように移行する手順や設定を紹介しました。</p>
<p>この記事が皆様の開発や業務に役立てば幸いです！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ゼロから始める OSS 貢献：Terraform の AWS Provider に新規リソースを追加するための 11 のステップ]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-30-terraform-aws-provider-contribution-dynamodb-zeroetl/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-30-terraform-aws-provider-contribution-dynamodb-zeroetl/</guid>
            <pubDate>Wed, 07 May 2025 11:00:00 GMT</pubDate>
            <description><![CDATA[初めて Terraform の AWS Provider に新規リソースを追加するコントリビューションに取り組みたいと考えている方に向けて、初回から効率的に取り組んでもらえるような情報を提供することを目指します。]]></description>
            <content:encoded><![CDATA[<p>こんにちは。 DBRE チーム所属の <a href="https://x.com/_p2sk_">@p2sk</a> です。</p>
<p>DBRE（Database Reliability Engineering）チームでは、横断組織としてデータベースに関する課題解決やプラットフォーム開発に取り組んでいます。</p>
<p>先日、OSS のリポジトリ <a href="https://github.com/hashicorp/terraform-provider-aws">terraform-provider-aws</a> に対してコントリビュートする機会がありました。具体的には、AWSが 2024 年 10 月に一般提供を開始した DynamoDB or S3 と Redshift との マネージドなデータ連携を Terraform で管理できるようにする「<a href="https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/redshift_integration">aws_redshift_integration</a>」というリソースを新規実装しました。<a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105">PR は既にマージ</a>されており、<a href="https://github.com/hashicorp/terraform-provider-aws/releases/tag/v5.95.0">v5.95.0でリリース</a>され、機能が使えるようになりました。</p>
<p>私は今回が初めての OSS 貢献だったので、完走できるか不安な部分もありましたが、生成 AI の力も借りながら PR の作成までやり切ることができました。</p>
<p>新機能が AWS で GA されてから、Terraform で管理できるようになるまで（＝terraform-provider-aws に新規リソースとして実装されるまで）、数ヶ月かかる場合もあります。そのような場合に、実装されるのを待つ代わりに自分で実装する選択肢を取れるようになったことは、大きなアドバンテージだと感じました。そこで本記事では、私と同じように初めて Terraform の AWS Provider に新規リソースを追加するコントリビューションに取り組みたいと考えている方に向けて、初回から効率的に取り組んでもらえるような情報を提供することを目指します。</p>
<p>いずれはコーディングエージェントに issue を渡すだけで PR 作成まで行ってくれる未来になっているかもしれませんが、現時点ではそこまでは難しい印象で、同じような境遇の方の参考になれば幸いです。</p>
<h1>今回追加したリソースについて</h1>
<p>今回は以下の 2 つの機能を管理できるリソースを追加しました。それぞれ簡単に説明します。</p>
<ul>
<li>DynamoDB から Redshift への zero-ETL 統合</li>
<li>S3 から Redshift への event integration</li>
</ul>
<p>zero-ETL 統合は、ETL パイプラインの構築が不要な、マネージドなデータ連携機能です。「zero-ETL 統合」という機能は、最初は Aurora MySQL と Redshift 間のデータ連携として提供が開始され、その後複数のデータソースとターゲットに対象が拡大しています。アーキテクチャ図を以下に示します。</p>
<p>![DynamoDB から Redshift への zero-ETL 統合のアーキテクチャ図](/assets/blog/authors/m.hirose/2025-04-23-11-13-17.png =700x)
<em>出展：AWS - <a href="https://aws.amazon.com/jp/blogs/news/get-started-with-amazon-dynamodb-zero-etl-integration-with-amazon-redshift/">Amazon Redshift との Amazon DynamoDB ゼロ ETL 統合の始め方</a></em></p>
<p>S3 から Redshift への event integration も同様に、S3 バケットに追加されたファイルを自動かつ高速に Redshift に連携する機能です。</p>
<p>これらは機能としては別々ですが、<a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html">Integration を作成する API は同一</a>です。terraform-provider-aws のリソースは API にマップされるため、この API を Terraform 対応させることで、上記 2 つの機能が同時に実装されることになります。したがって、追加したリソース自体は 1 つだけです。</p>
<h1>リソース追加の判断基準</h1>
<p><a href="https://hashicorp.github.io/terraform-provider-aws/add-a-new-resource/">公式ドキュメント</a>に以下の記載があります。</p>
<blockquote>
<p>New resources are required when AWS adds a new service, or adds new features within an existing service which would require a new resource to manage in Terraform. Typically anything with a new set of CRUD API endpoints is a great candidate for a new resource.</p>
</blockquote>
<p>和訳：</p>
<blockquote>
<p>AWS が新しいサービスを追加したり、既存のサービスに新しい機能を追加したりする場合、Terraform で管理するための新しいリソースが必要になります。一般的に、新しい CRUD API エンドポイントセットを持つものは、新しいリソースの候補として最適です。</p>
</blockquote>
<p>ということで、<code>新しい CRUD API エンドポイントセットを持つかどうか</code> が大きな判断基準となりそうです。今回はこれを満たしており、新規リソース追加の実装を行いました。</p>
<h1>Contribution の流れ</h1>
<p>流れは<a href="https://hashicorp.github.io/terraform-provider-aws/">公式のドキュメント</a>に非常によくまとまっています。</p>
<ol>
<li>開発環境を準備</li>
<li>コードのデバッグ（今回は新規リソース追加のため、スキップ）</li>
<li>コードを変更</li>
<li>テストを書く</li>
<li>継続的インテグレーション</li>
<li>変更ログを更新</li>
<li>プルリクエストを作成</li>
</ol>
<p>上記の項目をベースにしつつ、本記事で推奨する手順を以下に整理します。また、各項目でかかる労力を個人の実体験をベースに「★」の数で示します。詳細な手順は公式のドキュメントの確認が必要ですが、実際に取り組んで得られた知見をまとめているため、ぜひ参考にしてください。</p>
<ol>
<li>関連 issue の調査または作成 ★</li>
<li>対応する AWS API と SDK の事前調査 ★</li>
<li>開発環境を準備 ★</li>
<li>対象リソースの動作検証・依存リソースのコード化 ★★★</li>
<li>scaffolding ツールでベースとなるコードを生成 ★</li>
<li>コード修正と動作確認 ★★★★★</li>
<li>テストを書く ★★★</li>
<li>継続的インテグレーション用のテストのローカル実行 ★★</li>
<li>ドキュメント修正 ★</li>
<li>プルリクエストを作成 ★</li>
<li>変更ログを作成して push ★</li>
</ol>
<p>以降は各手順の詳細について触れていきますが、開発をスタートする前に知っておくと良さそうなことを先にまとめます。</p>
<h1>留意事項</h1>
<h3>複数種類の SDK による書き方が混在</h3>
<p>terraform-provider-aws では、リポジトリ内で異なる SDK を用いた 2 パターンの書き方が混在しています。</p>
<ul>
<li><a href="https://developer.hashicorp.com/terraform/plugin/framework">Terraform plugin framework</a><ul>
<li>現時点で使用が推奨されている、新しい SDK</li>
</ul>
</li>
<li><a href="https://developer.hashicorp.com/terraform/plugin/sdkv2">Terraform Plugin SDKv2</a><ul>
<li>旧 SDK のため新規利用は非推奨。ただし、この SDK で実装されたリソースのバグ修正などで使うこともある</li>
<li>サポート終了の v1 でのコードもまだ存在している可能性もあるため、正確には 3 パターンかも</li>
</ul>
</li>
</ul>
<p>従って、生成 AI に調査やコーディングを依頼する場合は Terraform plugin framework を対象にすることをプロンプトに含める方が良いです。</p>
<p>この辺りの歴史的経緯などに興味のある方は、<a href="https://chatgpt.com/c/67ff1569-e854-800f-befd-b3684340141f">ChatGPT Deep Research のリサーチ結果</a>を、ハルシネーション混入の可能性に留意いただきつつご確認ください。</p>
<h3>ライセンスについて</h3>
<p>Terraform 本体は 2023年に<a href="https://www.hashicorp.com/en/blog/hashicorp-adopts-business-source-license">ライセンスが BSL に変更</a>され、OSS の定義からは外れていますが、terraform-provider-aws は引き続き MPL 2.0 で、OSS のままになっています。</p>
<p>各種プロバイダは Terraform から fork された<a href="https://github.com/opentofu/opentofu">opentofu</a> でも使われているようです。<a href="https://github.com/opentofu/terraform-provider-aws">opentofu 用の AWS Provider</a> も terraform-provider-aws から fork されているため、プロバイダーにコントリビュートすると、間接的に Terraform と opentofu 両方に貢献できることになりそうです。</p>
<p>この辺りの経緯に興味のある方は <a href="https://chatgpt.com/share/68006d80-01b8-800f-a854-a08f02fbcb53">ChatGPT Deep Research のリサーチ結果</a>をご覧ください。（ハルシネーションに関して同様にご留意ください）</p>
<p>以降では、実際の手順について解説します。なお、記事内でのテストの実行時間などについては、以下の環境におけるおおまかな体感の値です。</p>
<ul>
<li>マシン : MacBook Pro</li>
<li>チップ : Apple M2 Pro</li>
<li>メモリ : 32 GB</li>
</ul>
<h1>1. 関連 issue の調査または作成</h1>
<p>PR の作成時は、関連 issue へのリンクを記載します（例：下図「Relations」）。そのため、関連する issue を最初に探して、なければ作成します。</p>
<p>![関連 issue の記載](/assets/blog/authors/m.hirose/2025-04-17-12-45-46.png =700x)</p>
<p>既に issue が作成されている場合は、他の誰かが取り組んでいるケースもあります。issue のコメントを一読し、「まだ誰も着手していなさそうか」などは確認しておくと良さそうです。</p>
<p>今回は既に issue が作成されていたため、PR 作成時は既存 issue へのリンクを記載しました。</p>
<h1>2. 対応する AWS API と SDK の事前調査</h1>
<p>リソースを新規実装するためには、go の SDK（aws-sdk-go-v2）が該当リソースの CRUD 操作に対応している必要があります。基本的には GA されたタイミングで SDK も提供されると推測しますが、多少のラグはあるかもしれません。terraform-provider-aws の go.mod も、該当リソースに対応したバージョンにアップデートされている必要がありますが、<a href="https://github.com/hashicorp/terraform-provider-aws/commits/main/go.mod">メンテナーにより頻繁に更新されている印象</a>なので、自分でアップデートしなくても、メンテナーにより最新化されている可能性が高い、という認識で良さそうです。</p>
<p>今回は、以下のリファレンスをブックマークに登録しておくことで、開発途中に都度参照できて便利でした。生成 AI に参照させる場合も重宝すると思います。</p>
<p>API リファレンス</p>
<ul>
<li><a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html">https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html</a></li>
<li><a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_ModifyIntegration.html">https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_ModifyIntegration.html</a></li>
<li><a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DeleteIntegration.html">https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DeleteIntegration.html</a></li>
<li><a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DescribeIntegrations.html">https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_DescribeIntegrations.html</a></li>
</ul>
<p>SDK リファレンス</p>
<ul>
<li><a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.CreateIntegration">https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.CreateIntegration</a></li>
<li><a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.ModifyIntegration">https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.ModifyIntegration</a></li>
<li><a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DeleteIntegration">https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DeleteIntegration</a></li>
<li><a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DescribeIntegrations">https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.DescribeIntegrations</a></li>
</ul>
<p>当初は DynamoDB の zero-ETL 統合を Terraform 対応させるのがモチベーションだったのですが、リファレンスを見ると API の SourceARN パラメータのパターンが下図の通り S3 にも対応していることがわかり、S3 の integration も同時に検証しなければいけないことに気づけました。このように、検証のスコープが想定より広い場合もあるため、リファレンスの入出力は最初に一通り確認しておくと良さそうです。</p>
<p>![CreateIntegration()のSourceARN](/assets/blog/authors/m.hirose/2025-04-17-15-43-29.png =700x)
<em>出展：AWS - <a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html">Redshift CreateIntegration API Reference</a></em></p>
<p>また、リソースの種類によっては Delete が存在しないものや、Modify が存在しない場合もあるかもしれませんので、その場合は提供されている API だけ実装すれば OK です。例えば、Aurora MySQL と Redshift 間の zero-ETL 統合は、GA 時点では <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/rds#Client.CreateIntegration">Create</a> / <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/rds#Client.DeleteIntegration">Delete</a> / <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/rds#Client.DescribeIntegrations">Describe</a> のみ提供され、後で <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/rds#Client.ModifyIntegration">Modify</a> が提供されたようです。</p>
<p>Redshift は SDK のディレクトリとして redshift と redshiftserverless が存在し、片方でいいのか両方実装すべきなのか迷いましたが、<a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshiftserverless">redshiftserverless</a> 側には該当の API が存在しなかったのと、redshift 側の関数で serverless 用の Integration も作成できたことから、redshift 側だけ実装すれば良いと判断しました。</p>
<h1>3. 開発環境を準備</h1>
<p><a href="https://hashicorp.github.io/terraform-provider-aws/development-environment/">公式のドキュメント</a> に記載の手順通りに実行すれば OK です。ただし、実際のリソースを作成して動作確認を行う <code>make testacc</code> はこの時点では不要です。<code>make test</code> も実行しなくても良いかもしれませんが、私の環境では 3-40 分ほどかかりました。</p>
<p><a href="https://hashicorp.github.io/terraform-provider-aws/development-environment/#using-the-provider">Using the Provider</a> に記載の手順を実行することで、手元で build した Provider を使って Terraform コマンドを実行できるようになります。実行時に以下のような warning が表示されることを確認できれば OK です。</p>
<p><img src="/assets/blog/authors/m.hirose/2025-04-17-16-01-59.png" alt="build した Provider を使った場合の warning"></p>
<p>これで、自分で build した Provider が Terraform 実行時に使われていることが確認できます。なお、後述の「受け入れテスト」というテスト経由でも動作確認自体はできるのですが、build と動作確認のサイクルを高速に回すためには、自分で build した Provider を使って Terraform コマンドを実行するのが良いと思います。個人的には、この手順で動作確認することで、普段と同じ Terraform の使い方の中で動作が確認できてイメージしやすかったです。<a href="https://qiita.com/minamijoyo/items/3a7467f70d145ac03324#%E3%83%97%E3%83%AD%E3%83%90%E3%82%A4%E3%83%80%E3%81%AE%E3%83%87%E3%83%90%E3%83%83%E3%82%B0">詳細にデバッグしたい場合は delve を使うと良さそう</a>です。</p>
<h1>4. 対象リソースの動作検証・依存リソースのコード化</h1>
<p>コーディングを開始する前に、まずは追加しようとしている新規 AWS リソースの動作確認を手元で行っておくと理解が深まります。この時、大抵は新規の AWS リソースを作成する前に、依存しているリソースを作成する必要があると思います。例えば、今回は以下の AWS リソースに依存しています。（正確には、Source は プロビジョニングされた Redshift / Redshift Serverless / S3 のいずれかだけで OK）</p>
<ul>
<li>aws_vpc</li>
<li>aws_subnet</li>
<li>aws_security_group</li>
<li>aws_dynamodb_table</li>
<li>aws_dynamodb_resource_policy</li>
<li>aws_redshift_subnet_group</li>
<li>aws_redshift_parameter_group</li>
<li>aws_redshift_cluster</li>
<li>aws_redshift_resource_policy</li>
<li>aws_kms_key</li>
<li>aws_kms_alias</li>
<li>aws_kms_key_policy</li>
<li>aws_redshiftserverless_namespace</li>
<li>aws_redshiftserverless_workgroup</li>
<li>aws_s3_bucket</li>
<li>aws_s3_bucket_public_access_block</li>
<li>aws_s3_bucket_policy</li>
<li>aws_iam_role</li>
<li>aws_iam_role_policy</li>
<li>aws_redshift_cluster_iam_roles</li>
</ul>
<p>このような依存リソースは、この時点で tf ファイルとしてコード化しておくことを強くお勧めします。理由は以下の通りです。</p>
<ul>
<li>検証、開発が 1 日で終わらない場合はコストがかかるため、都度 apply &amp; destroy したい</li>
<li>後述の「受け入れテスト」でほぼ同様の Configuration を用意する必要があるので、最初に用意しておくと後で楽<ul>
<li><code>terraform fmt</code> でフォーマットもしておくと後述の CI テストのローカル実行もより楽に</li>
</ul>
</li>
</ul>
<p>生成 AI を活用することで HCL のコーディングはかなり時短できると思います。依存リソースのコード化が完了したら、あとはコンソールか AWS CLI を使って手動で対象リソースを作成して動作検証します。</p>
<h1>5. scaffolding ツールでベースとなるコードを生成</h1>
<p><a href="https://hashicorp.github.io/terraform-provider-aws/add-a-new-resource/">新しいリソースを追加する場合</a>は、<a href="https://hashicorp.github.io/terraform-provider-aws/skaff/">Skaff</a> という scaffolding ツールを使ってベースとなるコードを生成することが推奨されています。</p>
<p>リソースタイプ名は<a href="https://hashicorp.github.io/terraform-provider-aws/naming/#resources-and-data-sources">命名規則</a>が決まっており、<code>aws_${サービス名}_${AWSリソース名}</code> となります。AWS リソース名は、sdk の関数名に従う必要があります。例えば今回は「CreateIntegration」関数が提供されているため、AWS リソース名は「Integration」となります。サービス名は<a href="https://github.com/hashicorp/terraform-provider-aws/tree/main/internal/service">リポジトリの service ディレクトリ</a>の値を使えば良さそうです。</p>
<p>したがって、今回のリソースタイプ名は <code>aws_redshift_integration</code> となります。この名前はブランチ名にも反映する必要があり、<code>f-aws_redshift_integration</code> としました。skaff では AWS リソース名を指定すれば良いので、該当サービスのディレクトリへ移動した後に、以下のコマンドを実行しました。</p>
<pre><code>$ pwd
/Users/masaki.hirose/workspace/terraform-provider-aws/internal/service/redshift
$ skaff resource --name Integration
</code></pre>
<p>Skaff を実行することで、リソース用のコード、テストコード、ドキュメントの 3 ファイルが生成されるようです。生成されたファイルは<a href="https://github.com/hashicorp/terraform-provider-aws/commit/bc67695fd2362702d5fb79e328747682ecdb0cac">こちら</a>からご確認いただけますが、コメントが手厚く記載された親切なファイルになっています。また、これらの初期状態のファイル達と、<a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105/files">最終的にマージされたコード</a>を比較すると、どこを修正すべきかイメージがつきやすいと思います。</p>
<h1>6. コード修正と動作確認</h1>
<p>生成されたコードをベースに、動作するように修正していきます。<a href="https://hashicorp.github.io/terraform-provider-aws/add-a-new-resource/">ドキュメントに記載の通り</a>、Resource Schema をまずはコーディングして、その次に CRUD ハンドラを実装していきます。</p>
<p>Terraform plugin framework では、CRUD ハンドラがそれぞれ「Create」「Read」「Update」「Delete」という直感的な関数名になっています。例えば、初めて <code>terraform apply</code> して新規リソースが作られる際に、ここで実装した Create() 関数が呼ばれます。その中で go の SDK の対応する関数（この場合は <a href="https://pkg.go.dev/github.com/aws/aws-sdk-go-v2/service/redshift#Client.CreateIntegration">CreateIntegration</a>）が実行され、内部的には対応する AWS API（この場合は <a href="https://docs.aws.amazon.com/ja_jp/redshift/latest/APIReference/API_CreateIntegration.html">CreateIntegration</a>）が実行され、リソースが作成される、という流れになります。</p>
<p><code>terraform apply</code> で Replace を伴わない修正のみが実行される場合は Update() 関数が、<code>terraform destroy</code> でリソースを削除する場合は Delete() 関数が実行されます。リソース情報を読み取る必要がある場面では都度 Read() 関数が呼ばれます。</p>
<h3>Resource Schema の実装</h3>
<p>Schema() 関数の中で、Terraform が受け付ける引数（arguments）および出力する属性（attributes）をスキーマ情報として定義します。以下のコードのように、Attributes マップに各フィールドを定義します。各属性はキーが Terraform 上の名前（snake case）、値が schema.Attribute インタフェースを実装した構造体で、schema.MapAttribute や schema.StringAttribute などから適切なものを用います。</p>
<pre><code>// 修正後の Schema() 関数の一部を抜粋
func (r *integrationResource) Schema(ctx context.Context, req resource.SchemaRequest, resp *resource.SchemaResponse) {
	resp.Schema = schema.Schema{
		Attributes: map[string]schema.Attribute{
			&quot;additional_encryption_context&quot;: schema.MapAttribute{
				CustomType:  fwtypes.MapOfStringType,
				ElementType: types.StringType,
				Optional:    true,
				PlanModifiers: []planmodifier.Map{
					mapplanmodifier.RequiresReplace(),
				},
			},
			names.AttrARN: framework.ARNAttributeComputedOnly(),
			names.AttrDescription: schema.StringAttribute{
				Optional: true,
			},
			&quot;integration_name&quot;: schema.StringAttribute{
				Required: true,
			},
</code></pre>
<p>上記のように、SDK のリファレンスで必須になっているパラメータには <code>Required: true</code> を指定し、変更時にリソース置き換えが必要な場合は <code>RequiresReplace()</code> modifier を付与します。適切な modifier を使い分けるのが個人的には難しかったです。modifier は自分でも実装でき、1 つだけどうしても自前で実装が必要と判断して実装したのですが、PR 作成後にメンテナーによって既存の modifier に置き換えられていました。迷った場合は <a href="https://github.com/hashicorp/terraform-plugin-framework/tree/main/resource/schema/stringplanmodifier">stringplanmodifier</a> など、対象の型に対応する modifier で提供されている関数を最初に理解して、使えないか確認するのが良いと思います。PR 作成後のメンテナーからのフィードバックを通して、基本的には既存の modifier でカバーできるのだなという学びがありました。</p>
<p>合わせて、ResourceModel 構造体も定義します。</p>
<pre><code>type integrationResourceModel struct {
	AdditionalEncryptionContext fwtypes.MapValueOf[types.String] `tfsdk:&quot;additional_encryption_context&quot;`
	Description                 types.String                     `tfsdk:&quot;description&quot;`
	IntegrationARN              types.String                     `tfsdk:&quot;arn&quot;`
	IntegrationName             types.String                     `tfsdk:&quot;integration_name&quot;`
	KMSKeyID                    types.String                     `tfsdk:&quot;kms_key_id&quot;`
	SourceARN                   fwtypes.ARN                      `tfsdk:&quot;source_arn&quot;`
	Tags                        tftags.Map                       `tfsdk:&quot;tags&quot;`
	TagsAll                     tftags.Map                       `tfsdk:&quot;tags_all&quot;`
	TargetARN                   fwtypes.ARN                      `tfsdk:&quot;target_arn&quot;`
	Timeouts                    timeouts.Value                   `tfsdk:&quot;timeouts&quot;`
}
</code></pre>
<h3>CRUD ハンドラと関連処理の実装</h3>
<p>CRUD ハンドラはいずれも、SDK への入力構造体を組み立てて、SDK の関数を呼び出す流れで実装します。また、CRUD ハンドラ内で利用する関数も合わせて実装します。これには、以下のようなものがあります。</p>
<ul>
<li>該当リソースの情報を取得するための finder 関数</li>
<li>リソースの作成、修正、削除が完了するまで待つ waiter 関数</li>
<li>リソースのステータスを取得する status 関数 </li>
<li>リソースを全削除する sweeper 関数（テスト向けの機能であり、必須ではない模様）</li>
</ul>
<p>注意点として、service によっては wait.go / find.go など専用の go ファイルが用意されており、その場合はそのファイルに処理を追記する必要があります。そうでない場合は、実装中のファイルに全て処理を記述して問題ないようです。今回対象となった redshift サービスでは wait.go などが存在しており、そちらに追記する形をとりました。</p>
<h3>リソースの登録</h3>
<p>実装が完了したあとは、Terraform Provider にそのリソースを認識させる登録処理を行います。以下のようなアノテーションが必要ですが、skaff で生成されたコードにすでに入っているので自分で記述する必要はありません。削除しないようにだけ注意しておけば OK です。</p>
<pre><code>// @FrameworkResource(&quot;aws_redshift_integration&quot;, name=&quot;Integration&quot;)
func newIntegrationResource(context.Context) 
</code></pre>
<p>上記アノテーションを記述したら、プロジェクトルートディレクトリで <code>make gen</code> を実行します。これにより、各サービスパッケージ内の service_package_gen.go が再生成され、新規実装したリソースも Provider 登録されます。ここまできたら、<code>make build</code> でビルドし、通れば実際に <code>terraform apply</code> などで動作を確認できる状態になります。</p>
<h3>動作確認</h3>
<p>新規実装したリソースを HCL で記述し、<code>terraform apply</code> して動作確認します。手順「# 4. 対象リソースの動作検証・依存リソースのコード化」において、すでに依存リソースはコード化が終わっているので、ここでは新規実装したリソースだけを、別のディレクトリの新規ファイルに記述し、別の state で管理します。これにより、動作確認したい新規リソースだけを apply &amp; destroy できるので確認スピードが上がります。もしくは以下のようにターゲットを指定することで、1 ファイルに全て記載している場合でも個別に apply する方法を取るのでもいいと思います。</p>
<pre><code>terraform plan -target=new_resource_type.hoge -out=myplan
terraform apply myplan
</code></pre>
<h1>7. テストを書く</h1>
<p>terraform-provider-aws では、以下の 3 つのテストがあります。</p>
<ul>
<li>受け入れテスト （Acceptance Tests）<ul>
<li>Terraform を実行し、AWS 上にリソースを作成したり、更新、削除できることを確認するテスト。実際にリソースを操作するので金銭的費用が発生。そのため「実行は任意」とドキュメントに書かれている。</li>
</ul>
</li>
<li>ユニットテスト<ul>
<li>関数レベルのテスト。今回は不要と判断し、未作成。</li>
</ul>
</li>
<li>CI テスト<ul>
<li>PR 作成後に実行される、lint / format などを含む包括的なテスト。</li>
</ul>
</li>
</ul>
<p>CI テストはすでに用意されているものを実行するだけなので、受け入れテストとユニットテストが、コントリビューターが書くべきテストになります。ユニットテストは複雑な処理などを実装した場合に書くことが推奨されており、今回は不要と判断し、受け入れテストだけを書きました。受け入れテストでは、以下のコードのように、テストに必要な AWS リソースを HCL で記述する必要があります。</p>
<pre><code>func testAccIntegrationConfig_base(rName string) string {
	return acctest.ConfigCompose(acctest.ConfigVPCWithSubnets(rName, 3), fmt.Sprintf(`
data &quot;aws_caller_identity&quot; &quot;current&quot; {}
data &quot;aws_partition&quot; &quot;current&quot; {}
resource &quot;aws_security_group&quot; &quot;test&quot; {
  name   = %[1]q
  vpc_id = aws_vpc.test.id
  ingress {
    protocol  = -1
    self      = true
    from_port = 0
    to_port   = 0
  }
  ...
</code></pre>
<p>ステップ「4. 対象リソースの動作検証・依存リソースのコード化」で既にコード化が完了していると、ここはほぼコピペで行けるので非常に楽でした。テストを実行する場合は、以下のように関数名を指定すると関数単位で実行できます。</p>
<pre><code>make testacc TESTS=TestAccRedshiftIntegration_basic PKG=redshift
</code></pre>
<p>リソース単位で一気にテストするには、以下のように「_」以降を削除して実行します。</p>
<pre><code>make testacc TESTS=TestAccRedshiftIntegration PKG=redshift
</code></pre>
<h1>8. 継続的インテグレーション用のテストのローカル実行</h1>
<p>terraform-provider-aws リポジトリは、コードの品質を確保するための厳格な CI パイプラインを持っています。PR 作成後に実行されるものですが、ローカルでこれらのチェックを実行できる機能が提供されているので、できる限りローカル上でパスするように修正しておくと親切かと思います。</p>
<p>完全なチェックは <code>make ci</code> で実行できますが、私の環境だと数時間かかったので、まずは <code>make ci-quick</code> で検出された問題を修正してから <code>make ci</code> を実行するのが、待ち時間を最小化できると思います。</p>
<p>私の場合は何度か修正を行い、ローカル環境で <code>make ci-quick</code> を実行した際に全てのチェックをパスできましたが、<code>make ci</code> 実行時は一箇所だけ GNUmakefile を修正しないとエラーになる箇所がありました。これは自分の環境特有の問題かもしれないため、PR に含めずローカルだけの修正で回避しました。</p>
<p><a href="https://hashicorp.github.io/terraform-provider-aws/continuous-integration/">ドキュメント</a>に記載がありますが、<code>make testacc-lint-fix</code> を最初に実行すると、<code>terrafmt</code> 関連の問題だけは自動で修正してくれるようなので、最初に実行しておくのも一手かと思います。</p>
<h1>9. ドキュメント修正</h1>
<p>skaff で生成されたドキュメントを修正します。ここで書いた内容が、<a href="https://registry.terraform.io/providers/-/aws/5.95.0/docs/resources/redshift_integration">よく見るドキュメント</a>として反映されます。既存のドキュメントを参考に書いていけば問題ないと思います。</p>
<h1>10. プルリクエストを作成</h1>
<p>こちらは特に迷うことはないと思います。</p>
<h1>11. 変更ログを作成して push</h1>
<p><a href="https://hashicorp.github.io/terraform-provider-aws/#6-update-the-changelog">公式のドキュメント</a>に沿って問題なく作成できると思います。ファイルの命名規則的に PR の番号が必要なので、まず PR を出して、その後に変更ログを作成して push すれば OK です。</p>
<p>ここまでで、PR を作成するまでの流れをお伝えしました。以降では、本取り組みにより得た知見をシェアします。</p>
<h1>メンテナーによる修正内容</h1>
<p>先日、PR は無事にマージされ、<a href="https://github.com/hashicorp/terraform-provider-aws/releases/tag/v5.95.0">v5.95.0でリリース</a>され、機能が使えるようになりました。マージまでに、メンテナーによりコードが修正されていたので、どのような修正が行われたかをご紹介します。</p>
<h3>schema.Attribute から ID の削除</h3>
<p>skaff で生成されたコードにも以下のコメントが記述されていましたが、気づけずに ID 属性を残していたところ、不要とのことで削除されていました。AWS API のリファレンスを見て残すか決めた方が良さそうです。</p>
<pre><code>// Only include an &quot;id&quot; attribute if the AWS API has an &quot;Id&quot; field, such as &quot;IntegrationId&quot;
names.AttrID: framework.IDAttribute(),
</code></pre>
<h3>変数名などの変更</h3>
<p>この修正がもっとも多く、自分の命名規則の確認が甘かったと思われます。一方で、「resourceIntegrationModel」という構造体名は skaff によって自動で生成されたものですが、「integrationResourceModel」という修正が加えられており、この辺りは skaff による命名規則の実装に若干の不備があるのかもしれません。</p>
<h3>自作の modifier を既存の modifier へ置き換え</h3>
<p>ある問題を解決するために、自分で plan modifier を実装したのですが、<a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105/commits/97781b020e741c9a1179cc970aaba803ed0b4779">既存の modifier に修正</a>されていました。</p>
<p>ここは自分でも自信がない箇所だったので、<a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105#discussion_r2028088558">手厚めに PR にコメントを残しておいた</a>ところ、以下のコメントもいただき、既存の modifier をもっとよく調べておくべきだったと反省しました。しかし、「なぜこの実装にしたのか」を詳細に示しておいたことで、メンテナーが修正の判断を行いやすい状態にはできたかなと思います。</p>
<blockquote>
<p>This can be accomplished with the RequiresReplaceIfConfigured plan modifier.</p>
</blockquote>
<p>この修正は LLM の力を借りて導き出せなかったかを確認するために、<a href="https://chatgpt.com/share/6806fea9-4ee8-800f-b68a-5acf8467d053">実装中に使っていたプロンプトを修正して LLM に渡した</a>ところ、上記の既存 modifier での修正案を提案してくれました。開発時は modifier を自作するしかないと判断して LLM を使っていたためプロンプトの指示が具体的になりすぎていたのが良くなかったようです。LLM の使い方の改善余地もあるという学びを得ました。</p>
<h3>受け入れテスト時のチェック項目の追加</h3>
<p><a href="https://github.com/hashicorp/terraform-provider-aws/pull/42105/commits/b0d61d3c8497b27a3f0ace2554cb08d836e4f599#diff-e58168bb583bffa00f127c71c11074222f40f3c5875bc76f4d348c5ee063af99R56-R60">こちらのコミット</a>にあるように、受け入れテスト時は、そのテストシナリオによって「リソースの作成」を期待するのか、「リソースの更新」を期待するのかまで記述できることを知りました。これにより、意図しないリソースの再作成が発生した場合に気づける、といった効果が期待できそうです。</p>
<h1>AWS リソース作成にかかったコスト</h1>
<p>今回は受け入れテストも自分で実行し、かつ個別に動作検証も行ったため、AWS リソースの作成による金銭的コストが発生しました。Terraform で IaC 化して、使わないときはこまめに destroy していましたが、合計で約 50$ ほどかかりました。大半は Redshift のコストであり、ここは実装しようとするリソース次第で大きく変わってきそうです。</p>
<h1>その他の所感</h1>
<h3>学び：標準化のために膨大なエネルギーが注がれている</h3>
<p>Terraform 関連のような、数千人規模のコントリビューターが関わっているリポジトリでは、「誰が走っても同じゴールにたどり着くためのレール」の整備が欠かせません。標準化が甘いと、メンテナー（レビュワー）の負担が大きくなり、機能のリリーススピードが落ちてしまいます。そのような背景から、以下のような様々な情報やツールの提供によってコードの標準化を強く推進する意思を感じました。</p>
<ul>
<li>豊富なドキュメントの提供<ul>
<li>貢献種別（バグ修正、新規リソース追加など）ごとの詳細な手順の整理</li>
<li>命名規則などのルールの記載</li>
</ul>
</li>
<li>専用ツール「skaff」による scaffolding<ul>
<li>「これを修正していけばOK」なベースとなるコードを自動生成</li>
</ul>
</li>
<li>ローカルで実行可能な CI テスト<ul>
<li>lint / format / test など、様々な観点で徹底的にチェック可能</li>
<li>ローカルでパスしておけば、PR 時の CI も全てパスする可能性が高く、メンテナーの負担が下がる</li>
</ul>
</li>
</ul>
<p>特に、CI テストと同等なものをローカルで実行できるようにするためにエネルギーを注いでいるのは、下記の通り<a href="https://hashicorp.github.io/terraform-provider-aws/continuous-integration/">ドキュメント</a>からも伝わってきます。</p>
<blockquote>
<p>NOTE: We&#39;ve made a great effort to ensure that tests running on GitHub have a close-as-possible equivalent in the Makefile.</p>
</blockquote>
<p>和訳</p>
<blockquote>
<p>注: GitHub で実行されるテストについては、Makefile に可能な限り同等のコードが含まれるよう最大限の努力を払っています。</p>
</blockquote>
<p>これにより、例えば <a href="https://github.com/hashicorp/terraform-provider-aws/blob/main/names/attr_consts_gen.go">const で定義している定数</a>については、以下のように値をハードコーディングしていると定数を使うように促されるなど、細かい点までコード内の記述の揺らぎが最小化される力が働きます。</p>
<p><img src="/assets/blog/authors/m.hirose/2025-04-21-11-11-18.png" alt="CI テスト実行時の検出項目"></p>
<p>このように、テスト項目は非常に細かく多岐に渡りますが、逆にいうと、受け入れテストとローカルでの CI テストをパスした状態であれば、初めての PR であっても自信を持って作成できました。</p>
<p>私が所属する DBRE チームでは、<a href="https://blog.kinto-technologies.com/posts/2023-05-29-serverless-with-monorepo-nx-en/">DevOps のスペシャリスト</a>により上記のような scaffolding から format, lint, test までの開発フローを一通り整理してくれていたため、今回の開発の流れにも違和感なく取り組むことができ感謝しています。</p>
<h3>反省点：生成 AI の活用方法に改善の余地あり</h3>
<p>初見のリポジトリに対する理解速度を上げるために、Copilot などでリポジトリをインデックス化しておけば、コード理解がより速くなったと反省しています。一方で、今回のように異なる SDK によるコードが混在するリポジトリの場合は、前提として現在推奨されている SDK を指定した上で質問するなどの工夫も必要だと感じました。実際、Plan Modifier 周辺を Deep Research で調べて Web 上の Issue に書かれていた解決策を試したものの、旧 SDK での解決方法だったため、動作しませんでした。代わりに、関連ソース一式を LLM に渡したところ、ほぼ修正不要のコードが提案されて問題を解決できました。LLM をもっとうまく活用してキャッチアップや開発速度を上げていきたいです。</p>
<h3>苦労した点：異なる種類の SDK によるコードの混在</h3>
<p>前述の通り、異なる種類の SDK によるコードが混在しているため、「既存コードが全て参考になるわけではない」という状況で、これに気づくのに少し時間がかかりました。例えば、sweeper 関数の実装では、現在の SDK（Terraform plugin framework） と 以前の SDK とで実装方法が異なります。今回の実装対象サービスは redshift ですが、<a href="https://github.com/hashicorp/terraform-provider-aws/tree/main/internal/service/redshift/sweep.go">redshift の sweeper 関数を実装するファイル</a>ではまだ現在の SDK での実装が行われておらず、旧 SDK での実装を参考にしてコードを書いたけど動かない、といった問題に遭遇しました。現在の SDK で実装されている関数を別サービスから探して参照することで解決しましたが、参考にする既存コードが現在の推奨 SDK による記法かどうかは気をつけると良さそうです。</p>
<h1>手順別の AI と人力の使いわけ</h1>
<p>最後に、各手順を AI と人力どちらで行った方がいいのか、主観ですが現時点での見解を表にまとめます。今回の開発完了後に、検証目的で同じタスクを <a href="https://devin.ai/">AI エンジニアの Devin</a> にも実施させてみましたが、公式ドキュメントにも書いてある通り、細かくタスクを分解して渡す必要がありそうでした。もちろん、現時点での見解なので、AI の進化によって変化していくと思われます。</p>
<table>
<thead>
<tr>
<th>手順</th>
<th>AI / 人力</th>
<th>備考</th>
</tr>
</thead>
<tbody><tr>
<td>1. 関連 issue の調査または作成</td>
<td>人力</td>
<td>Web検索または <a href="https://github.com/hashicorp/terraform-provider-aws/issues">GitHub Issues</a> ページを自分で調べるのが最速</td>
</tr>
<tr>
<td>2. 対応する AWS API と SDK の事前調査</td>
<td>人力</td>
<td>自分で検索するのが早い</td>
</tr>
<tr>
<td>3. 開発環境を準備</td>
<td>人力</td>
<td>自分でセットアップした方が早い</td>
</tr>
<tr>
<td>4. 対象リソースの動作検証・依存リソースのコード化</td>
<td>AI＋人力</td>
<td>依存リソースのコード化は LLM 活用が有効</td>
</tr>
<tr>
<td>5. scaffoldingツールでベースとなるコードを生成</td>
<td>人力</td>
<td>自分で実行した方が早い</td>
</tr>
<tr>
<td>6. コード修正と動作確認</td>
<td>AI＋人力</td>
<td>ベースを LLM に書かせ、細部は自分で仕上げる</td>
</tr>
<tr>
<td>7. テストを書く</td>
<td>AI＋人力</td>
<td>ベースを LLM に書かせ、細部は自分で仕上げる</td>
</tr>
<tr>
<td>8. CI用テストのローカル実行</td>
<td>AI or 人力</td>
<td>AI に任せると自動でパスするようにコードを修正してくれるかもしれないが、テスト実行時間が長いので製品によってはクレジット消費が大きくなる懸念</td>
</tr>
<tr>
<td>9. ドキュメント修正</td>
<td>AI＋人力</td>
<td>マージ済みドキュメントを参考として渡して LLM に下書きを作らせると良さそう</td>
</tr>
<tr>
<td>10. プルリクエストを作成</td>
<td>人力</td>
<td>自分で行うのが早そう</td>
</tr>
<tr>
<td>11. 変更ログを作成して push</td>
<td>人力</td>
<td>自分で行うのが早そう</td>
</tr>
</tbody></table>
<h1>まとめ</h1>
<p>Terraform Provider へのコントリビューションは敷居が高く感じていましたが、ガイドがしっかりと整備されており、scaffolding ツールやテストフレームワークが充実しているので、慣れればスムーズに進められると感じました。初回だったのでドキュメントの読み込みに時間がかかりましたが、次回からはより高速に開発を進められると思います。AWS の新機能リリースをいち早く Terraform 化したいという方は、ぜひ取り組んでみてください。その際に、本記事が少しでも参考になれば幸いです。</p>
<p>KINTO テクノロジーズ DBRE チームでは一緒に働いてくれる仲間を絶賛募集中です！カジュアルな面談も歓迎ですので、 少しでも興味を持っていただけた方はお気軽に <a href="https://twitter.com/_p2sk_">X の DM</a> 等でご連絡ください。併せて、<a href="https://twitter.com/KintoTech_HR">弊社の採用 Twitter</a> もよろしければフォローお願いします！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/m.hirose/conver_image_hirose_terraform.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[A Look Back To This Year And Into 2025]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024-en/</guid>
            <pubDate>Wed, 07 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Reflecting back at 2024 and the outlook of KINTO Technologies in 2025]]></description>
            <content:encoded><![CDATA[<p>Hi, this is Kageyama, from KINTO Technologies! As is customary every year, I&#39;d like take a moment to reflect on 2024 and share some thoughts on what’s ahead in 2025.</p>
<h2>Review of 2024</h2>
<p>Looking back, the number of challenges we needed to address has increased compared to a year ago. In response, our team grew, and the organization expanded. At the same time, even with that growth, I believe believe we’ve made a conscious effort to preserve the strengths of being an in-house development team.</p>
<p>One of the major milestones this year was the full-scale launch of the Dealership DX Project, supporting the digital transformation of Toyota dealerships across Japan. We’ve already rolled out tools aimed at reducing the workload for dealerships for creating estimates, as well as customer management apps. In addition, AI-driven product development has been progressing steadily. As AI adoption and cloud usage advance, the scope of security and governance are also expanding. To that end, we have established a dedicated cloud security unit and began reinforcing security around AI-related projects.</p>
<p>Internally, we have also moved steadily incorporating AI. The AI integration with Slack, our primary communication platform, is steadily becoming the norm across the company. There have also been cases where even non-engineering employees have used generative AI to develop an application. We have also started offering generative AI training sessions for our group companies and dealerships.</p>
<h3>Progress of Key Initiatives</h3>
<p>In <a href="https://blog.kinto-technologies.com/posts/2023-12-25-LookBack2023/">last year&#39;s Advent Calendar</a>, I wrote about three key priorities we aimed to improve:</p>
<ul>
<li>Technical proficiency</li>
<li>Development productivity</li>
<li>Release speed</li>
</ul>
<p>First, on technical proficiency, we have expanded our technichal support for Toyota Motor Corporation and its group companies. We also developed use cases that have been well received by various IT vendors. One highlight has been our Tech Blog which attracted an impressive 120,000 unique users this year. As more people started contributing to it, we have received valuable feedback from both inside and outside the company.</p>
<p>Next, in terms of development productivity, we&#39;ve been using Findy Team+ to quantitatively measure team performance, which has led to initiatives aimed at improving the efficiency of individual processes. The company has also introduced systems that integrate generative AI, such as GitHub Copilot, which has increased employees&#39; awareness around development productivity, sparking new initiatives including efforts to increase overall throughput across the entire development lifecycle, from business planning to release.</p>
<p>An finally, regarding of release speed, our main focus has been on streamlining the early stages of development, particularly the process until the requirements definition phase. By having the development teams lead the requirements definition process as well, we’re beginning to steer the business side toward a product development approach that is more feasible and demands less development effort. We are still in the trial-and-error stage, but I would like to further spread this awareness of speed throughout the company.</p>
<h2>Outlook for 2025</h2>
<p>So, what will we focus on next year? I plan to center our efforts around two major &quot;firsts&quot;:</p>
<ul>
<li>AI First</li>
<li>Release First (Fastest Release)</li>
</ul>
<h3>AI First</h3>
<p><strong>AI first</strong> means exactly what it says, and we will be strengthening AI initiatives through:</p>
<ul>
<li>Seamless integration of AI across all products</li>
<li>Development of more AI-driven products</li>
<li>Leading AI adoption at dealerships and throughout the Toyota Group.</li>
</ul>
<p>Thanks to the efforts of all our employees so far, we are in a strong position to accelerate these efforts in 2025. I am optimistic that we will be able to generate significant output next year.</p>
<p>Moving forward, I believe it&#39;s important to adopt a mindset of actively incorporating AI in product development; even for products that don&#39;t necessarily require AI. I look forward to seeing new ideas and cross-functional collaborations that will help bring our AI-first approach to life.</p>
<h3>Release First</h3>
<p><strong>Release First (Fastest Release)</strong> is about using our expertise and technology to deliver products as quickly as possible. What it takes to achieve the fastest release can vary depending on the business owner and the nature of the product. This year, our focus was on enhancing productivity in both the requirements definition phase and the implementation process for specific projects and teams. Next year, instead of solely focusing on improving the productivity of specific stages, I’d like each product team to collaborate closely with stakeholders and consider how and where they can shorten the entire release cycle to achieve the fastest possible product releases.</p>
<p>I also believe we need to fully embrace the concept of  MVP (Minimum Value Product). That doesn’t mean every product will follow the same development or delivery model. Our strength as an in-house development team lies in our ability to thoughtfully assess product value while still aiming for the shortest possible release timelines. This balance is one of our greatest contributions to the business, and we will push even further in this direction next year.</p>
<h3>User First and Organizational Intensity</h3>
<p>To support <strong>AI First</strong> and <strong>Release First</strong>, we will be reemphasizing two foundational themes in 2025: <strong>User First</strong> and <strong>Organizational Intensity</strong>.</p>
<p><strong>User First</strong> means going beyond serving end users of our services: we also need to proactively engage with our business partners and the customers they serve. By identifying what truly matters to users and customers, we can elevate the value and impact of our work. That is why I see methodologies like user research as essential skills to develop.</p>
<p><strong>Intensity</strong> is insipired by a <a href="https://www.jleague.jp/a-to-z/intensity/">Soccer Term</a>. One of KINTO Technologies&#39; key strengths is our team of highly skilled individuals. But to achieve even greater outcomes as an in-house development team, we need to further raise the collective capabilities of our teams and organization. I encourage everyone to reflect on the mindset and actions needed to help KTC become a truly high-intensity organization.</p>
<h2>Full Throttle</h2>
<p>While we didn’t explicitly include technical expertise and communication skills in our 2025 themes, these have become deeply embedded in our culture and are now expected. They remain timeless pillars at KTC, and I hope each of us will continue to nurture them and strengthen them. We will continue to give our best next year as well, with no shortcuts.</p>
<p>Hitoshi Kageyama, Executive Vice-President, KINTO Technologies </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Swift Package Manager Is Now Available for the My Route App!]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-01-20-myroute-ios-spm-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-01-20-myroute-ios-spm-en/</guid>
            <pubDate>Fri, 02 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[The My Route app has migrated from CocoaPods to Swift Package Manager.]]></description>
            <content:encoded><![CDATA[<p>My name is Ryomm and I am developing the app <em>my route</em> (iOS) at KINTO Technologies. The my route app&#39;s library management tool has finally migrated from CocoaPods to Swift Package Manager (SPM)!</p>
<h1>Introduction</h1>
<p>The my route app used to use CocoaPods.</p>
<p>However, around summer 2024, CocoaPods issued the following announcement.
<a href="https://blog.cocoapods.org/CocoaPods-Support-Plans/">https://blog.cocoapods.org/CocoaPods-Support-Plans/</a></p>
<p>That was an announcement stating that CocoaPods would be transitioning to maintenance mode. It doesn&#39;t mean it will become unusable, and they will still support security issues and other concerns. However, support for GitHub issues and the addition of new features will no longer be provided.</p>
<p>If you do this, you may find yourself in a situation where you want to add a new library only to find that CocoaPods is no longer supported, or that you are unable to fix a problem with CocoaPods itself.</p>
<p>There had been talk of migrating to SPM at my route for some time, but the announcement of CocoaPods gave us the impetus to finally embark on the migration to SPM.</p>
<h1>What is Swift Package Manager?</h1>
<p>SPM is a genuine library management tool provided by Apple.
<a href="https://github.com/swiftlang/swift-package-manager">https://github.com/swiftlang/swift-package-manager</a></p>
<p>Speaking of library management tools for iOS, there are other third-party tools such as CocoaPods and Mint. The biggest difference between these and SPM is that SPM is directly integrated into Xcode. This means that by simply updating <code>Package.resolved</code> when necessary, the latest specified version of the library will be imported when you open or build a project. This means that I no longer need to tell my team to run <code>pod install</code> every time I update the version.</p>
<h1>Migration Complete!</h1>
<p>It’s simply a matter of transferring everything, but there were a few stumbling points and tips that I encountered, so I’d like to share them with you.</p>
<h2>The Specified Version of the Library Is Not Downloaded</h2>
<p>I had a problem where, when switching to the desired version, even after executing &quot;Reset Package Caches&quot; or &quot;Update to Latest Package Versions&quot; in Xcode, the version specified in Package.swift would not be downloaded. Even deleting DerivedData doesn&#39;t fix this.</p>
<p>This is because residual caches remain in locations such as <code>~/Library/Caches/org.swift.swiftpm</code> and <code>~/Library/org.swift.swiftpm</code>. When I delete these, the correct version of the library is downloaded.</p>
<h2>Build Doesn&#39;t Pass Only During Testing</h2>
<p>My route has multiple schemas, each of which depends on slightly different libraries. So, it has a structure separated from the project&#39;s own Package.swift as shown below. <img src="/assets/blog/authors/ryomm/2024-12-24/01.png" alt="Configuration"></p>
<p>We create a Dependencies package in the same workspace as the project file and manage the library within this package so that it becomes a one-to-one product (framework) with each schema. These products are linked to each target&#39;s <code>Frameworks, Libraries, and Embedded Content</code> as shown in the screenshot on the right.</p>
<p>However, with this configuration, I encountered a problem where <code>xcodebuild build</code> passed, but <code>xcodebuild build-for-testing</code> did not. This command corresponds to Run and Test in the Xcode GUI.</p>
<p>Typically, items in a package are tested within that same package. However, with the above configuration, tests are run on the project’s main target instead. This means that the tests are executed outside the package. So yes... this is technically an improper setup... which I do plan to fix, but for now the tests are included in the same package, which allows us to proceed with testing preserving the current configuration.</p>
<p>It seems that the cause was the different behavior around the linker between Run and Test.</p>
<p>The build product for Run is assumed to be 1), and the build product for Test is assumed to be 2). <img src="/assets/blog/authors/ryomm/2024-12-24/02.png" alt=""> 1) includes SPM in Dependencies, but 2) does not. Therefore, if a test in the MainTarget depends on a library in Dependencies, the build will fail.</p>
<p>A simple solution is to move the libraries required for testing to the SPM in MainTarget, which will be included in the build for both Run and Test. To fundamentally solve the problem, it is a good idea to simplify the tests in one package, as mentioned above.</p>
<h2>Putting SwiftLint and LicensePlist on SPM</h2>
<p>Since I want to include SwiftLint and LicensePlist in the project&#39;s Build Phase so that they are executed during the build, I create a separate package in a location independent of the workspace.</p>
<pre><code>Project/
├── Test.xcworkspace
├── Test.xcodeproj
├── Test/
│   └── ...
├── ...
└── tools // &lt;- this!
    ├── Package.swift
    └── Package.resolved
</code></pre>
<p>I placed the libraries I wanted to include in the Build Phase, such as SwiftLint and LicensePlist, into the newly created &quot;tools&quot; package. Then, I created a shell script, which I executed only the first time to download the libraries locally.</p>
<pre><code class="language-bash">SPM_PACKAGE_PATH=tools
SPM_BUILD_PATH=tools/.build/release

echo &quot;swiftlint&quot; &amp;&amp; $SPM_BUILD_PATH/swiftlint --version || swift run -c release --package-path $SPM_PACKAGE_PATH swiftlint --version
echo &quot;license-plist&quot; &amp;&amp; $SPM_BUILD_PATH/license-plist --version || swift run -c release --package-path $SPM_PACKAGE_PATH license-plist --version
</code></pre>
<p>Then, it becomes executable at <code>tools/.build/release/swiftlint</code>, so I add this to the Build Phase. <img src="/assets/blog/authors/ryomm/2024-12-24/03.png" alt="Build Phase"></p>
<p>The same applies to LicensePlist. Even when executing from platforms like Bitrise, it can be confirmed that LicensePlist is executed by executing the shell first, followed by building the project.</p>
<h1>Conclusion</h1>
<p>It&#39;s been a few months since the migration, and while there are still some issues, I feel that replacing libraries has become easier. When I want to check whether an issue is caused by a library, I personally find it convenient to quickly check it using the app-version of Praygrounds for a quick test.</p>
<p>Now that SPM is integrated into the project, it has also become possible to use Apple libraries that are only available via SPM. In the future, I would like to use these libraries to improve our implementation.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ryomm/2024-12-24/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[インフラエンジニアが生成AI Agentでフロントエンド～バックエンドまで作ってみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-05-02-VSCodeAgentMode/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-05-02-VSCodeAgentMode/</guid>
            <pubDate>Fri, 02 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[生成AIのAgent機能を使ってシステム開発]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1>自己紹介</h1>
<p>初めまして！
KINTOテクノロジーズのCloud InfrastructureグループでManagerをやってる辻です</p>
<p>2020年5月に入社している地味に古株な人です
新卒からずっとインフラ(オンプレ/クラウド両方)エンジニアしています</p>
<h1>概要</h1>
<p>こないだVSCodeでのGitHub Copilot Agent Mode(以降Agent)が正式リリースされましたね(2025/4/16現在)
ちょうどいいのでVSCode Agentを使って何か便利なもんでも作ってみましょう</p>
<h1>前提</h1>
<ul>
<li>私は根っからのインフラエンジニアです<ul>
<li>フロントエンドどころかバックエンドもほぼワカランです</li>
</ul>
</li>
<li>その代わりインフラは(特にAWS)はチョットデキル</li>
<li>この記事では深いTechなことには触れません</li>
</ul>
<h1>やりたいこと</h1>
<ul>
<li>Agentを使ってインフラ以外できないエンジニアがどこまで行けるのか挑戦する</li>
<li>日々の <strong>業務で利用できる実用的なシステムをMVP(Minimum Viable Product)</strong> で開発する</li>
<li><strong>絶対にコードは自分で書かない</strong></li>
</ul>
<h1>じゃあ何を作ろうか？？</h1>
<p>KINTOテクノロジーズのインフラは基本的にすべてIaC(Terraform)で作成/管理されています
terraformを使ったことがある人ならわかると思いますが、planの内容って慣れてないと見づらいですよね？
<strong>なので今回はterraformのplanが見やすくなるナニカを作りたいと思います</strong></p>
<h1>作りたいもの</h1>
<ul>
<li>terraform planを見やすくする<ul>
<li>どうせなら生成AI使って要約とかしたい</li>
<li>Agentが作るんだからちょっとかっこいいフロントエンドも作ってもらう</li>
</ul>
</li>
</ul>
<h1>開発環境</h1>
<p>今回使用した開発環境は下記です　※ご参考までに</p>
<table>
<thead>
<tr>
<th align="center">項目</th>
<th align="center">使ったもの</th>
</tr>
</thead>
<tbody><tr>
<td align="center">OS</td>
<td align="center">Linux(WSL2)</td>
</tr>
<tr>
<td align="center">IDE</td>
<td align="center">VSCode(1.99.3)</td>
</tr>
<tr>
<td align="center">Agent</td>
<td align="center">GitHub Copilot Agent</td>
</tr>
<tr>
<td align="center">生成AIモデル</td>
<td align="center">Claude 3.7 Sonnet</td>
</tr>
<tr>
<td align="center">インフラ</td>
<td align="center">AWS</td>
</tr>
</tbody></table>
<h1>やってみましょう</h1>
<p>まず最初にやることはアレですね。そうプロンプトを考えましょう
今回は超ざっくりにやりたいことを↓のプロンプトでAgentに指示しました</p>
<pre><code>terraform planの結果をブラウザで確認する仕組みを実装したいです

AWS構成：Cloudfront+S3+Lambda + Bedrock
案
1.ローカルでterraform plan -out hoge.planを実行し、plan内容をファイルに出力
2.hoge.planをs3に送信
3.lambdaが起動し、terraform show hoge.planを実行し、人が見れる形にしてs3に出力
4.terrafrom showの結果をBedrockが解析し、planの要約を出力
5.ブラウザからアクセスを実施し、要約とplanの内容を人が見る

上記案で実施しようと考えています
もっと良い案があれば提案してください。もしなければ上記を作る手伝いをしてください
コードを作成する必要があるのであれば、/home/mizuki/配下にディレクトリを作成し、そこに出力してください
</code></pre>
<p>え？もっといいプロンプトあるって？
細かいことは置いておきましょう
なぜなら私はインフラしかできないエンジニアです</p>
<p>そしたら賢いAgent君は私のやりたいことを300%ぐらいパワーアップさせて対応してくれました</p>
<pre><code>お客様のご要望は、Terraform planの結果をブラウザで確認するシステムの実装についてですね。ご提案いただいた案は実現可能であり、良く練られています。まずはその案を評価し、いくつかの改善点を提案した後、実装のお手伝いをさせていただきます。

# 現在の案の評価

1. ローカルでterraform plan -out hoge.planを実行し、plan内容をファイルに出力
2. hoge.planをS3に送信
3. LambdaがS3のイベントをトリガーに起動し、terraform show hoge.planを実行して人間が読める形式でS3に出力
4. Bedrockがterraform showの結果を解析し、planの要約を生成
5. ブラウザからアクセスして要約とplanの内容を確認

# 考えられる改善点

1. **セキュリティ強化**: S3へのアクセス制限、IAM権限の適切な設定
2. **差分のビジュアル表示**: planの結果をグラフや色付きテキストで可視化
3. **履歴管理**: 過去のplan結果を保存し比較できる機能
4. **通知機能**: 重要な変更がある場合にSlackやメールで通知
5. **インタラクティブな操作**: ブラウザ上でリソースの詳細を展開/折りたたみできる機能

これらを加味した上で、実装を進めていきましょう。まずはディレクトリ構造を作成します。ディレクトリ構造を作成しました。次に、このプロジェクトの各コンポーネントを実装していきましょう。

まず、Terraformの設定ファイルを作成して、必要なAWSリソースを定義します。ディレクトリ構造の作成が完了しました。これから各コンポーネントを実装していきましょう。

まずはTerraformのインフラストラクチャを定義する`main.tf`を作成します。このファイルにはS3バケット、CloudFront、Lambda、IAMロールなどの必要なリソースを定義します。
:
:
略
</code></pre>
<p><strong>。。。。君すごいね</strong></p>
<p>Agentの目玉機能であるローカルへの操作もどんどんやってくれます
これは作業ディレクトリ作って、そこにterraformファイルなどを作成してくれている
<img src="/assets/blog/authors/m.tsuji/20250424/agent1.png" alt="Input Prompt"></p>
<p>その後は作業内容を説明してくれながら、これやっていい??って随時確認してくれます
<img src="/assets/blog/authors/m.tsuji/20250424/agent2.png" alt="Input Prompt"></p>
<p>私はPythonのことなんてナンモワカランのでとりあえず全部「accept」ボタンを連打します</p>
<p>最終的にJavaScriptやCSS及びPythonやTerraformファイルだけじゃなく、Lambdaのビルド用シェルまで作ってくれました
しかもご丁寧に使い方やデプロイ手順や改良案も添えて
<img src="/assets/blog/authors/m.tsuji/20250424/agent3.png" alt="Input Prompt"></p>
<h1>デプロイしてみましょう</h1>
<p>作ってくれたのはありがたい
けどほんとに動くの？？？
とりあえず教えてくれたデプロイ手順を愚直にやってみます</p>
<ol>
<li>terraform applyしてAWSにS3とかCloudfrontとか作って</li>
<li>ビルドしてLambda用のzip作って</li>
<li>Lambdaをデプロイして</li>
<li>JSとかCSSをS3に配置して</li>
</ol>
<p>なんのエラーも出ずにデプロイ完了しました
<img src="/assets/blog/authors/m.tsuji/20250424/aws_lambda.png" alt="Input Prompt"></p>
<h1>流石にエラーでました</h1>
<p>作ってくれた環境にアクセスしたら、流石にLambdaのエラーが出ました
私はPythonの事わからないのでエラー解析なんてできません
なので、LambdaのログをそのままAgentに渡して調べて貰いましょう</p>
<p><img src="/assets/blog/authors/m.tsuji/20250424/agent4.png" alt="Input Prompt"></p>
<p>すると予想通りログを解析して問題点の指摘及び修正までやってくれました
<img src="/assets/blog/authors/m.tsuji/20250424/agent5.png" alt="Input Prompt"></p>
<p>修正されたファイルをもう一度デプロイしたら今度はうまくいきました</p>
<h1>どんなシステムになったのか？</h1>
<p>出来上がったシステム構成ははこんな感じです！！
<img src="/assets/blog/authors/m.tsuji/20250424/aws1.png" alt="Input Prompt"></p>
<p>流れとしては下記となっており</p>
<ol>
<li>ユーザーがterraform plan結果をS3に送信</li>
<li>イベント駆動でLambdaが起動</li>
<li>LambdaがBedrockへplan結果の解析/要約を依頼</li>
<li>生成されたplan結果の要約と要約ページのHTMLファイルをS3へ配置</li>
<li>Step FunctionsがCloudFrontのキャッシュを削除</li>
</ol>
<p>ユーザーはS3へplan結果を配置するだけで全て完了します</p>
<h1>実際にアクセスしてみましょう</h1>
<p><img src="/assets/blog/authors/m.tsuji/20250424/console1.png" alt="Input Prompt"></p>
<p><strong>めっちゃかっこいいですね</strong>
じゃあそもそもの目的であったplan結果の要約はできてるのでしょうか？</p>
<p><img src="/assets/blog/authors/m.tsuji/20250424/console2.png" alt="Input Prompt">
<img src="/assets/blog/authors/m.tsuji/20250424/console3.png" alt="Input Prompt"></p>
<p><strong>完璧ですね。。。。</strong>
ちょっとインデントがおかしい気もしますが、細かいことは気にするのやめましょう</p>
<p>良いポイントとしては</p>
<ul>
<li>作成/更新/削除などの件数が画面上部に表示され見やすい</li>
<li>変更されるリソースの変更内容が説明されている</li>
<li>リスク評価が記載されており、このplanでのリスクが把握できる</li>
<li>注意点として今後改善したほうが良い内容を教えてくれる</li>
</ul>
<p>まさに求めていた内容です</p>
<h1>細かい要件追加</h1>
<p>その後も細かい修正をAgentに指示してやってもらいました</p>
<ul>
<li>時間がUTCになってるからJSTにして</li>
<li>インデントがずれてるから直して<ul>
<li>スクショを添えて</li>
</ul>
</li>
<li>planが生成されたらCloudFrontのキャッシュをクリアして</li>
</ul>
<p>などなど</p>
<p>上記の細かい要件追加も<strong>簡単</strong>にやってくれました</p>
<h1>やってみた感想</h1>
<p>想像以上に簡単にシステムが作れました
1日でこのシステムを作成しましたが、ほとんどの時間がAgentの処理待ちだったので、実作業時間は1時間程度です。私のようにインフラしかできない人間でも、フロントエンド～バックエンドまで1人で構築できました。</p>
<p>ただしPythonコードを見ると素人目でも不要なコードが存在したりしています
まだまだ改善の余地がありますが、これはMVP（Minimum Viable Product）段階での開発としては十分な成果です。今後、必要に応じてコードの最適化や機能の拡張を行っていくことができます。
重要なのは、短時間で機能するシステムを作り上げられたことです。これにより、アイデアを素早く形にし、実際の運用を通じて改善点を見出すことができます。</p>
<p>大事なのは
<strong>1人で短時間で簡単に作れた</strong>
ってことです</p>
<p>MVP(Minimum Viable Product)でのファーストステップとしてAgentは最適解だと思います</p>
<h1>まとめ</h1>
<p>Agentだけ使ってエンタープライズなシステムを作るにはまだまだ時間がかかりそうですが、プロンプト初心者の私でも簡単にシステム開発できました
何より驚いたのが「accept」ボタンを押すだけでAgentがサクサクとシステムを構築してくれる点ですね
今までは生成AIに提案された内容をコピペしたりコマンドを自分で実行したりしてましたが、今回はほぼ自分でコマンドを打っていません。「accept」ボタン押しまくっただけです
ボタン押しただけでみんなの工数削減&amp;ミス防止を可能になるシステムが作れたってことです
また、今回作ったシステムは実際に利用しており、運用フローに組み込む予定です</p>
<h1>さいごに</h1>
<p>私の所属するCloud Infrastructureグループでは一緒に働いてくれる仲間を募集しています。クラウドに自信のある方もこれからクラウドを学びたいと思っている若手の方も大歓迎です。お気軽にお問い合わせください</p>
<p>詳しくは、<a href="https://hrmos.co/pages/kinto-technologies/jobs/1811937538128224257">こちら</a>と<a href="https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303147">こちら</a>をご確認ください</p>
<p>また、グループの紹介記事もWantedlyにて掲載されています！
<a href="https://www.wantedly.com/companies/company_7864825/post_articles/968522">こちら</a>で見ることができるので、ぜひCloud Infrastructureグループについて知ってください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/m.tsuji/20250424/top.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[My First Shot at Event PM! Behind the Scenes of the Product History Conference]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-24-event-prhs-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-24-event-prhs-en/</guid>
            <pubDate>Thu, 01 May 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[My First Shot at Event PM! Behind the Scenes of the Product History Conference]]></description>
            <content:encoded><![CDATA[<h2>Hello(=ﾟωﾟ)ﾉ</h2>
<p>I&#39;m <strong>Hikaru Takeno</strong> <a href="https://x.com/t_hikarutaaaan">@t_hikarutaaaan</a>, from the Talent Acquisition team at KINTO Technologies!<br>I&#39;ve been at KTC for almost three years, but as an HR professional, I&#39;m still a newbie in my first year.</p>
<p>In this article, I’d like to share what I applied and learned as a <strong>first-time Event PM</strong>, as an HR newbie!</p>
<h2>Event Overview</h2>
<p>Event name: <strong>Product History Conference</strong>  </p>
<ul>
<li>Organizer: YOUTRUST Inc.  </li>
<li>Date: 10:00 a.m. to 8:00 p.m., Saturday, November 30, 2024  </li>
<li>Location: TODA HALL &amp; CONFERENCE TOKYO</li>
</ul>
<p>@<a href="https://lp-a.youtrust.jp/phc2024/">card</a></p>
<h2>The Beginning</h2>
<p>With less than three months to go until the event, it has been decided that KTC will join as a gold co-sponsor of the event!</p>
<blockquote>
<p>When I heard, &quot;Alright, the approval&#39;s in!!&quot; I was relieved...
But at the same time I thought, &quot;Wait, where do we even start...?&quot;</p>
</blockquote>
<p>That was when <strong>a miraculous helping hand</strong> reached out to me °˖✧◝(⁰▿⁰)◜✧˖°</p>
<p><a href="https://x.com/quindim">Kin-chan</a>, the manager of the Developer Relations Group, shared the Slack channel from another event and advised us, saying,
&quot;This is how you should approach it!&quot; ( ;∀;)<br>For someone like me who learns best through hands-on experience, it was the most easy-to-understand information. I&#39;m truly grateful.</p>
<h2>Next Steps:</h2>
<blockquote>
<p>&quot;Alright, let&#39;s take a look at the Slack channel.&quot; … (ㆆωㆆ)ｰ<br>&quot;Hmm, not very sure but let&#39;s get everyone involved together and have a meeting first!!!&quot;</p>
</blockquote>
<ul>
<li><strong>We set up a meeting</strong>.  </li>
<li>Then, we rummaged through the internal Confluence.  </li>
<li>We found <strong>meeting minutes</strong> that seemed usable.  </li>
<li><strong>Copy!!</strong></li>
</ul>
<p>![Confluence](/assets/blog/authors/takeno/prhs/prhs1.png =600x)
<em>We copied past materials!</em></p>
<p>Somehow, by rummaging through the <strong>internal Confluence and the past event-related Slack channels</strong>, we were able to resolve most of the issues!</p>
<p>Looking back, I’m really impressed and grateful for our <strong>environment where knowledge sharing comes so naturally…</strong> It was a lifesaver…</p>
<h2>On the Day of the Event</h2>
<p>Since we had finished setting up the booth the day before, we arrived at the venue at <strong>09:30 a.m.</strong>.</p>
<p>![Setting up the booth](/assets/blog/authors/takeno/prhs/prhs2.png =600x)
<em>The booth with simple setup completed</em></p>
<p>After settling down a bit, the time of opening the venue was approaching... and then...</p>
<blockquote>
<p>Someone said, &quot;<strong>Let&#39;s form a circle among all the members of the sponsoring companies!!</strong>&quot; Σ(･ω･ﾉ)ﾉ!</p>
</blockquote>
<p>So, I firmly put my arms around the shoulders of the people on both sides of me.</p>
<p>Then the event started!　As people gradually started to arrive, <strong>there was a crowd at the booth by lunchtime!</strong></p>
<p>![The booth](/assets/blog/authors/takeno/prhs/prhs3.png =600x)
<em>Many visitors showed interest!</em></p>
<p>We were talking nonstop the whole time!!
We conducted a <strong>questionnaire</strong> at the booth, and I was really happy to see that there were more people who knew about KTC than we expected.</p>
<blockquote>
<p><strong>The most common response:</strong>
&quot;I&#39;ve seen you at lots of events!!&quot;</p>
</blockquote>
<p>I was reminded once again of the collective effort of all the employees who have steadily participated in many of them!!</p>
<p>![KTC&#39;s mascot, Kumobii](/assets/blog/authors/takeno/prhs/prhs4.jpg =600x)
<em>KTC&#39;s mascot, Kumobii, is also doing its best</em></p>
<h2>Kageyama-san takes the Stage</h2>
<p>This time, we held a cross-talk session featuring our <strong>Executive Vice President Hitoshi Kageyama</strong> and <strong>Mr. Naomichi Okada, CTO of Luup Inc.</strong>!<br>With the theme &quot;Creating a New Standard of Mobility through the Use of Technology: The Future as Imagined by the Two Companies,” they spoke candidly about the challenges both companies are facing, including:</p>
<ul>
<li>The thoughts behind the founding and establishment of each company</li>
<li>The evolution of their development organizations across various phases</li>
<li>The challenges and excitement of working on product development amid numerous constraints</li>
</ul>
<p>And more.</p>
<p>![Presentation](/assets/blog/authors/takeno/prhs/prhs5.jpg =600x)
<em>Crosstalk</em></p>
<h2>What I’ve Learned</h2>
<ul>
<li><strong>It is best to set agendas and choose appropriate timings for operation meetings.</strong>
Perhaps it would be a good idea to try a fixed weekly meeting style?</li>
<li><strong>Kumobii&#39;s unstoppable popularity</strong>
Cuteness proved to be unbeatable as expected!<blockquote>
<p>Kumobii is the official mascot of KINTO Corporation.
<a href="https://corp.kinto-jp.com/mascot/">https://corp.kinto-jp.com/mascot/</a></p>
</blockquote>
</li>
<li><strong>An environment where knowledge naturally accumulates is a huge strength.</strong>.<br>Thanks to our open Slack channel culture, it&#39;s easy to find and learn from past events.</li>
<li><strong>So many incredibly experienced employees in our company</strong>  </li>
<li><strong>Hosting a post-event party was absolutely worth it!</strong>.<br>It helped keep the connections going beyond the event itself.<br>About 25 to 30 people joined.</li>
</ul>
<p>![Post-event party scene](/assets/blog/authors/takeno/prhs/prhs6.jpg =600x)<br><em>People from other companies also joined the post-event party.</em></p>
<blockquote>
<p>Thank you to the participants from X Mile Inc. <a href="https://x.com/xmile_product">@xmile_product</a>, 
Asuene Inc. <a href="https://x.com/asuene_inc_">@asuene_inc_</a>, 
and note inc.<a href="https://x.com/note_corp">@note_corp</a>!!  </p>
</blockquote>
<h2>What I Want to Carry into the Next Event</h2>
<ul>
<li><p><strong>We should think more about what kind of experience we want people to have when they visit our booth.</strong><br>I want to apply our experience to booth setting and the arrangement of novelties.</p>
</li>
<li><p><strong>I want to prepare “express cards” that allow people we’ve directly spoken to skip the selection process</strong>.<br>Since this was also an event hosted by YOUTRUST Inc., a career-focused social media, many participants were interested in changing jobs. It might have been a good idea to create a process to ensure talented individuals remain in the selection process without fail.</p>
</li>
<li><p><strong>I want to set KPIs.</strong></p>
</li>
</ul>
<h2>Looking Back</h2>
<p>Being a bit nervous, started off my role as the event PM thinking, <strong>&quot;I have no idea what’s going to happen...!&quot;</strong> with my heart racing. But in the end, it was like, <strong>&quot;We made it!!&quot;</strong> and the event finished without any major problems. This was all thanks to the operations team, who not only helped me prepare in advance by guiding me with questions like, &quot;Shouldn’t we decide on this?&quot; and &quot;What’s the status of this?&quot; but also stayed flexible and supportive on the event day itself!   </p>
<p>Really, thank you so much!!!</p>
<p>![Group photo](/assets/blog/authors/takeno/prhs/prhs7.jpg =600x)<br><em>Everyone wearing Kumobii!</em></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/takeno/prhs.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How We Reached 100 Articles on the KINTO Technologies Advent Calendar]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-24-100-article-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-24-100-article-en/</guid>
            <pubDate>Wed, 30 Apr 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for Day 23 in the <a href="https://qiita.com/advent-calendar/2024/tech_pr">Developer Relations Group Advent Calendar 2024</a>🎅🎄</p>
<h1>Introduction</h1>
<p>Hello! I am Rina (<a href="https://twitter.com/chimrindayo">@chimrindayo</a>). I am an engineer at KINTO Technologies, where I am involved in the development and operation of <a href="https://mobima.kinto-jp.com/">Mobility Market</a> and on the Developer Relations Group as well.</p>
<p>KINTO Technologies launched the Tech Blog in July 2022, and thanks to our writers and readers, we have been able to keep it running to this day. Thank you for reading as always!</p>
<p>Our Advent Calendar is one of the biggest events on our blog each year, and this time was no exception 🎄 We proudly reached <strong>a total of 100 articles up to Series 4</strong> 👏 Amazing!</p>
<p>Until we were able to release 100 articles, the Developer Relations Group members worked hard every day, coming up with various new ideas. This time, I would like to introduce some of the journey and initiatives we went through to release 100 Advent Calendar articles.</p>
<h1>Journey of the KINTO Technologies Advent Calendars</h1>
<p>The KINTO Technologies Advent Calendars were started by volunteers in 2021, prior to the launch of the Tech Blog. The following year, in April 2022, a small team called the Tech Blog Project (now Developer Relations Group) was formed, and the team launched the Tech Blog in July 2022.</p>
<p>Since the launch of the Tech Blog, we have consistently posted an Advent Calendar every year, increasing the number of articles by 25 each year ⤴️</p>
<table>
<thead>
<tr>
<th>Year</th>
<th>URL</th>
<th>Number of posts</th>
</tr>
</thead>
<tbody><tr>
<td>2021</td>
<td><a href="https://qiita.com/advent-calendar/2021/kinto-technologies">KINTO Technologies - Toyota car subscription service &quot;KINTO&quot; under development! Advent Calendar 2021</a></td>
<td>24</td>
</tr>
<tr>
<td>2022</td>
<td>April: Tech Blog Operation Team was established <br/>July: Tech Blog launched🎉</td>
<td>-</td>
</tr>
<tr>
<td>2022</td>
<td><a href="https://qiita.com/advent-calendar/2022/kinto-technologies">KINTO Technologies Advent Calendar 2022</a> <br/> <a href="https://qiita.com/advent-calendar/2022/kinto-technologies-introduction">KINTO Technologies Group Introduction Advent Calendar 2022</a></td>
<td>50</td>
</tr>
<tr>
<td>2023</td>
<td><a href="https://qiita.com/advent-calendar/2023/kinto-technologies-tech">KINTO Technologies Advent Calendar 2023</a></td>
<td>75</td>
</tr>
<tr>
<td>2024</td>
<td><a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a></td>
<td>100</td>
</tr>
</tbody></table>
<p>I will reflect on each year to review the steps we took to increase the number of articles.</p>
<p>Since we adapted our approach to fit each stage, from the initial launch to the ongoing operation of the Tech Blog, I hope this article will be helpful for anyone thinking about starting a tech blog or are currently running one but facing challenges to increase the number of contrbutors🙌</p>
<h2>2022 Advent Calendar Initiatives</h2>
<p>First, here are some of the initiatives we implemented during the early stage of the Tech Blog. Looking back, I think a defining feature of this initial phase was that we <strong>approached individuals</strong> to ask them to write for the Tech Blog. Let me introduce some of the specifics.</p>
<h3>Making it a Rule to Show Gratitude to Writers</h3>
<p>The first thing we did was to establish the rule of showing &quot;gratitude toward writers&quot; as the stance of reviewers. This rule was decided by project team led by <a href="https://x.com/aoinakanishi">Nakanishi-san</a>.</p>
<p>We have also continued to convey gratitude to writers by requesting managers and leaders, who could serve as reviewers, to do so in company-wide meetings or 1-on-1 sessions, and by expressing our gratitude in the PR (pull request) template for the Tech Blog.</p>
<p><img src="/assets/blog/authors/rina.k/100article/thanks.png" alt=""><em>Excerpt from the PR template for the Tech Blog</em></p>
<p>Even now, expressing gratitude to writers for their contributions is something that the entire Developer Relations Group values very much.</p>
<h3>Having All the Managers Write Articles</h3>
<p>The next step we took was to have all the managers write articles. In the early days of the Tech Blog, many people in the company thought, &quot;What is a tech blog? Why are we doing this?&quot; So our first priority was to make sure each group leader understood the value and purpose of the Tech Blog. We also believed that getting General Managers to contribute articles was key to spreading awareness across the company. After a 30-minute meeting with them, we successfully launched the &quot;KINTO Technologies Group Introduction&quot; series.</p>
<p><a href="https://qiita.com/advent-calendar/2022/kinto-technologies-introduction">https://qiita.com/advent-calendar/2022/kinto-technologies-introduction</a></p>
<p>Under the theme <em>letting everyone get to know our people and offices</em>, managers and leaders wrote articles for our Advent Calendar. On Christmas Day, Vice President Kageyama closed out the series with an special post titled &quot;2022 Year in Review &amp; 2023 Outlook”🎅. Since then, his article has become an annual tradition, one that&#39;s read by nearly all current and prospective employees.</p>
<p>His articles condense key aspects about KINTO Technologies, including:</p>
<ul>
<li>What kind of organization we are</li>
<li>What we achieved over the past year</li>
<li>What we aim to pursue in the year ahead
They serve as a compass for the organization.</li>
</ul>
<p>It’s a once-a-year piece Written with care and dedication.</p>
<p>Click here 👇 for his review &amp; outlook article:
<a href="https://blog.kinto-technologies.com/posts/2022-12-25-LookBack2022/">https://blog.kinto-technologies.com/posts/2022-12-25-LookBack2022/</a></p>
<h3>Studying the Structure of Each Article Together</h3>
<p>We hold 30-minute sessions to help those who are eager to write but lack experience with technical articles and need guidance on where to begin. </p>
<p>During these sessions, members of the Developer Relations Group conduct interviews with the writers, asking about:</p>
<ul>
<li>Projects they&#39;ve worked on</li>
<li>Successes, failures, and the lessons learned along the way</li>
<li>Their tech stack and tools of choice</li>
</ul>
<p>As we listen, the Developer Relations team helps the writer shape the article structure in real time, laying a solid foundation they can build on.</p>
<p>the Developer Relations team shares the proposed article structure with the writer on the spot for feedback. Most writers respond with surprise, saying things like, “This might be easier than I thought!” </p>
<p>When we launched the Tech Blog in 2022, we carried out this process with every single writer. Looking back, it might seem like a bit of a brute-force approach; but we believe that this hands-on, all-in communication style is one of our team’s greatest strengths. When we’ve shared this method with technical PR teams at other companies, it’s often met with praise, with many calling it a “wonderful initiative.”</p>
<p>There were several reasons behind this approach. </p>
<p>The main goals were to:</p>
<ul>
<li>Help every writer understand the value of the Tech Blog</li>
<li>Lower the barrier to contributing</li>
<li>Gain visibility into who is responsible for what</li>
<li>Build trusting relationships, where writers feel comfortable asking us for advice
And more.</li>
</ul>
<p>However, what we consider most important is that each writer <strong>reaffirms the value of their own work</strong>. In face-to-face interviews with them, many would say, &quot;I can write, but my daily work is just ordinary, so it&#39;s not something worth putting on a blog.&quot;</p>
<p>But that’s exactly the kind of story we want to share. It’s perfectly okay to be ordinary! There’s always someone out there facing the same challenges, and if your story reaches even one of them, then it’s absolutely worth sharing on the Tech Blog. That’s why we kept encouraging each writer: “Let’s write it together!”</p>
<p>This way of thinking is still valued by the Developer Relations Group, and it was featured in a blog post that was released the other day.👇
<a href="https://blog.kinto-technologies.com/posts/2024-12-11-gekishin/">https://blog.kinto-technologies.com/posts/2024-12-11-gekishin/</a></p>
<p>In this way, not only do we work together on the structure of each article, but we also believe it is important to help each writer feel confident about the content they are about to write.</p>
<h2>2023 Advent Calendar Initiative</h2>
<p>In the early stages of the Tech Blog&#39;s launch, we focused on encouraging first-time writers through one-on-one conversations. Next, in 2023, we shifted our focus on sustanining that momentum supporting <strong>continued</strong> contributions and increase the number of writers.</p>
<h3>Supporting Inputs</h3>
<p>One of the initiatives the Developer Relations Group undertook first in 2023 was supporting the introduction of <a href="https://business.udemy.com/">Udemy Business</a>. We believe meaningful output on the Tech Blog starts with strong input, the knowledge and skills the writers bring. To reinforce this mindset, we linked access to Udemy Business with participation in the Tech Blog: registering for an account required contributing at least one article.</p>
<p>There are actually team members who have taken Udemy courses and are currently writing articles.👇
<a href="https://blog.kinto-technologies.com/posts/2024-08-30-udemy-kotlin-coroutines-and-flow/">https://blog.kinto-technologies.com/posts/2024-08-30-udemy-kotlin-coroutines-and-flow/</a></p>
<p>In 2024, in addition to the support for the introduction of Udemy, we established a new team within the Developer Relations Group called the &quot;Learning Roadside Station&quot;. This team was created to offer comprehensive support for both input and output.</p>
<p>Read more about the background of the Learning Roadside Station Team&#39;s establishment here.👇
<a href="https://blog.kinto-technologies.com/posts/2024-04-23_%E5%AD%A6%E3%81%B3%E3%81%AE%E9%81%93%E3%81%AE%E9%A7%85%E3%81%AF%E3%81%98%E3%82%81%E3%81%BE%E3%81%97%E3%81%9F/">https://blog.kinto-technologies.com/posts/2024-04-23_%E5%AD%A6%E3%81%B3%E3%81%AE%E9%81%93%E3%81%AE%E9%A7%85%E3%81%AF%E3%81%98%E3%82%81%E3%81%BE%E3%81%97%E3%81%9F/</a></p>
<h3>Encouraging Newcomers to Write an Article</h3>
<p>In October 2023, we launched a new initiative asking all new employees to write a blog entry after joining the company.</p>
<p>The goals of this initiative are to:</p>
<ul>
<li>Give readers a feel for our company’s atmosphere</li>
<li>Reduce the hesitation around writing for the Tech Blog</li>
<li>Encourage connections among new hires</li>
</ul>
<p>By having everyone contribute from the start, we aim to lower the barrier to writing and helping newcomers understand how to approach blog writing, ultimately making it a natural practice.</p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-01-01-newcomers-introduction/">https://blog.kinto-technologies.com/posts/2024-01-01-newcomers-introduction/</a></p>
<p>Initially, the Developer Relations Group planned for each new hire to write their own entry. But when <a href="https://x.com/__ryomm">Ryomm-san</a>, who joined in October, suggested, “I’d like to co-author!” it sparked the creation of a new co-writing process.</p>
<p>This is just one example of how we’re building a culture where Tech Blog initiatives aren’t just driven by the Developer Relations Group, but shaped by contributions from employees across the company.</p>
<h3>Asking Teams to Write</h3>
<p>Next, we implemented a plan where we asked teams at the project and department-level to write for the Advent Calendar. Five groups, with five members each, participated and together filled 25 days&#39; worth of content.</p>
<p>The themes were:</p>
<ol>
<li>New Car Subscription Website Renewal Project</li>
<li>Agile Development</li>
<li>KINTO FACTORY</li>
<li>QA</li>
<li>Diversity &amp; Inclusion</li>
</ol>
<p>By encouraging people across teams to write together, we aimed to make the writing process more collaborative and approachable. Having teammates work on shared topics made it easier to ask for feedback and consult each other during the writing process.</p>
<p>We also hoped that theme-based series of articles would feel more relatable to internal readers, helping to increase engagement, and ultimately, encouraging more people to read and share the Tech Blog, both internally and on social media.</p>
<p>The actual project is introduced in the blog here.👇
<a href="https://blog.kinto-technologies.com/posts/2023-11-20-advent-calendar/#2.-%E8%A8%98%E4%BA%8B%E3%83%AA%E3%83%AC%E3%83%BC">https://blog.kinto-technologies.com/posts/2023-11-20-advent-calendar/#2.-%E8%A8%98%E4%BA%8B%E3%83%AA%E3%83%AC%E3%83%BC</a></p>
<h2>2024 Advent Calendar Initiative</h2>
<p>Finally, let us share what we’ve been working on this year. In 2024, our focus has been on <strong>expanding the Tech Blog and improving its quality</strong>.</p>
<h3>Holding a Tech Blog Study Session</h3>
<p>In August 2024, we held an internal study session led by <a href="https://x.com/_p2sk_">p2sk-san</a>, a member of the Developer Relations Group. The session, titled &quot;Reviewing the Tech Blog and Best Practices for Enhancing Dissemination Power,&quot; involved a comprehensive review of all Tech Blog articles published by KINTO Technologies.
He shared insights into what we’re doing well, along with suggestions for improvement. </p>
<p>The response from participants was overwhelmingly positive:
&quot;My motivation increased!&quot;
&quot;Made me want to try writing articles!&quot;
&quot;Let&#39;s do our best for the Advent Calendar!&quot;
The session was and full of valuable insights, but we’ll leave the deep dive for another time.</p>
<h3>Open Call for Planning &amp; Operations</h3>
<p>In previous years, the Advent Calendar was planned and led by the Developer Relations Group, but this year, we have recruited volunteers to take part. The goal was to engage members beyond the usual circle and reach new voices we hadn’t connected with before.</p>
<p>![](/assets/blog/authors/rina.k/100article/member.png =600x)</p>
<p>While we were hoping for up to five volunteers, only one person raised their hand; but that one person made a big difference. <a href="https://x.com/naka227_shima">naka_shima</a>san stepped forward and thanks to his involvement, we were able to launch a department-based article series you’ll read about later 👏</p>
<h3>Filling the Series by Division</h3>
<p>In 2024, we structured the Advent Calendar series around articles written by Groups (org. structure). A &quot;series&quot; refers to posting a total of 25 articles one for each day from December 1st to 25th</p>
<p>The groups involved were the Mobile App Development Group, where our volunteer coordinator naka_shima-san belongs, and the Developer Relations Group.</p>
<ul>
<li>Mobile App Development Group</li>
<li>Developer Relations Group</li>
</ul>
<p>The Mobile App Development Group was chosen first because it was the most actively engaged in information dissemination this year. This group stood out for not only planning and managing major sponsored events like iOSDC 2024 and DroidKaigi but also for regularly contributing to the Tech Blog. With a playful challenge of “Can the Mobile Group fill all 25 days by themselves?” they impressively took on the task and completed an entire series 🎉</p>
<p>Next, the reason why it was decided that the Developer Relations Group would fill one series was that we believed it was important for the very team leading information dissemination to actively demonstrate its output through the Tech Blog.</p>
<p>Thanks to these efforts, we released a total of 100 articles, which included:</p>
<ul>
<li>Random topics freely contributed by volunteers</li>
<li>Articles by the Mobile App Development Group</li>
<li>Articles by the Developer Relations Group</li>
<li>Translated articles (English-Japanese) by the Localization Team</li>
</ul>
<h1>Future Prospects</h1>
<p>Today I shared our journey through the Advent Calendars over the past three years: from the Tech Blog’s launch in 2022 up to the present in 2024. Thanks to everyone involved and all the efforts made, we proudly reached the milestone of releasing 100 articles in the 2024 Advent Calendar!</p>
<p>Looking ahead to 2025, we plan to focus on initiatives that make writing easier and keep motivation high for our contributors.</p>
<p>Thank you for reading and supporting us throughout 2024☺️</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[For Those Visiting the KINTO Technologies Muromachi Office]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-24-office-access-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-24-office-access-en/</guid>
            <pubDate>Mon, 28 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[For Those Visiting the KINTO Technologies Muromachi Office]]></description>
            <content:encoded><![CDATA[<h2>Hello ( º∀º )/</h2>
<p>My name is Yukachi from the Event Team on the Developers Relations Group.</p>
<p>In 2024, we began sponsoring conferences and ramped up our external communication efforts, leading to a big increase in events both inside and outside the company compared to previous years!
As we started hosting more events with external guests I realized it&#39;s a bit tricky to explain how to get to our office.
I thought, ¨Maybe I should use more photos to explain...¨ and then I thought, wait, we have the Tech Blog! 
That&#39;s how this article came to be.</p>
<h2>About the Office</h2>
<p>Our company has four locations!
Now that I think about it, I actually wrote a post introducing them two years ago!</p>
<p>@<a href="https://blog.kinto-technologies.com/posts/2022-12-10-office-introduction/">card</a></p>
<p>Out of all our locations, we host most of our events at <strong>TOKYO JCT in the Muromachi office</strong>.
It&#39;s on the 16th floor of the office building in COREDO Muromachi 2![TOKYO JCT](/assets/blog/authors/uka/access/jct.jpg =600x)
**</p>
<p><strong>Address: Muromachi Furukawa Mitsui Building (COREDO Muromachi 2), 2-3-1, Nihonbashi Muromachi, Chuo-ku, Tokyo 103-0022</strong>  </p>
<ul>
<li>Directly connected to Mitsukoshimae Station on the Tokyo Metro Ginza Line *About a 2-minute walk from the ticket gate.  </li>
<li>Directly connected to Mitsukoshimae Station on the Tokyo Metro Hanzomon Line *About a 5-minute walk from the ticket gate.  </li>
<li>Directly connected to Shin-Nihombashi Station on the JR Sobu Rapid Line *About a 5-minute walk from the ticket gate.  </li>
<li>About a 10-minute walk from Kanda Station on the JR Chuo Line, Keihin Tohoku Line, and Yamanote Line.</li>
</ul>
<p>Let me walk you through how to get here from each station with photos!</p>
<h3>Shortcut to the Access Guide</h3>
<ul>
<li><a href="#%E6%9D%B1%E4%BA%AC%E3%83%A1%E3%83%88%E3%83%AD%E9%8A%80%E5%BA%A7%E7%B7%9A%E3%80%8E%E4%B8%89%E8%B6%8A%E5%89%8D%E3%80%8F%E9%A7%85">From Mitsukoshimae Station (Tokyo Metro Ginza Line)</a></li>
<li><a href="#%E6%9D%B1%E4%BA%AC%E3%83%A1%E3%83%88%E3%83%AD%E5%8D%8A%E8%94%B5%E9%96%80%E7%B7%9A%E3%80%8E%E4%B8%89%E8%B6%8A%E5%89%8D%E3%80%8F%E9%A7%85">From Mitsukoshimae Station (Tokyo Metro Hanzomon Line)</a></li>
<li><a href="#JR%E7%B7%8F%E6%AD%A6%E5%BF%AB%E9%80%9F%E3%80%8E%E6%96%B0%E6%97%A5%E6%9C%AC%E6%A9%8B%E3%80%8F%E9%A7%85">From Shin-Nihombashi Station (JR Sobu Rapid Line)</a></li>
<li><a href="#JR%E4%B8%AD%E5%A4%AE%E7%B7%9A%E3%83%BB%E4%BA%AC%E6%B5%9C%E6%9D%B1%E5%8C%97%E7%B7%9A%E3%83%BB%E5%B1%B1%E6%89%8B%E7%B7%9A%E3%80%8E%E7%A5%9E%E7%94%B0%E3%80%8F%E9%A7%85">From Kanda Station (JR Chuo Line, Keihin-Tohoku Line, Yamanote Line)</a></li>
</ul>
<hr>
<h2>Mitsukoshimae Station (Tokyo Metro Ginza Line)</h2>
<p>Let&#39;s start with the closest stop, which is the Mituskoshimae Station on the Ginza Line!</p>
<p>![Directions to the office from the Ginza Line 1](/assets/blog/authors/uka/access/gin1.png =600x)<br><em>Exit from the Mitsukoshi Ticket Gate and turn left.</em></p>
<p>![Directions to the office from the Ginza Line 2](/assets/blog/authors/uka/access/gin2.png =600x)<br><em>There is Coredo Muromachi 1, so go through it.</em></p>
<p>![Directions to the office from the Ginza Line 3](/assets/blog/authors/uka/access/muro.png =600x)<br><em>Once you pass through, you&#39;ll find the entrance to COREDO Muromachi 2.</em></p>
<p>![Directions to the office from the Ginza Line 4](/assets/blog/authors/uka/access/es.png =600x)<br><em>Go inside, take the escalator up.</em></p>
<p>![Muromachi Office Entrance 1](/assets/blog/authors/uka/access/en1.png =600x)<br><em>The office area will be right in front of you!</em></p>
<p>:::message
<strong>During event reception time, our staff will be stationed at the entrance to the office area!</strong>
Since an access card is required to get in from COREDO Muromachi 2, we&#39;ll be keeping an eye out and opening the door for anyone who looks like they&#39;re here for the event!
:::</p>
<blockquote>
<p><strong>For appointments or non-event visits before 6:00 p.m., please use the main office entrance, located just to the left after exiting the building.</strong></p>
</blockquote>
<p>![Muromachi Office Entrance 2](/assets/blog/authors/uka/access/en2.jpg =600x)<br><em>Step out to Edozakura-street from here.</em></p>
<p>![Muromachi Office Entrance 3](/assets/blog/authors/uka/access/en3.png =600x)<br><em>This is the main entrance.</em></p>
<p>Please check in at the front desk inside the entrance, then go to the floor your host has indicated.</p>
<p>:::message
<strong>If you arrive after event reception hours, please use the same main entrance and contact the person listed in the event details, either via phone or through our official X account.</strong><br>An event staff member will come to meet you on the first floor.</p>
<p>@<a href="https://x.com/KintoTech_Dev">card</a>
:::</p>
<hr>
<h2>Mitsukoshimae Station (Tokyo Metro Hanzomon Line)</h2>
<p>It&#39;s also directly connected from Mitsukoshimae Station on the Hanzomon Line, but it&#39;s a bit of a walk...</p>
<p>![Directions to the office from the Hanzomon Line 1](/assets/blog/authors/uka/access/han1.png =600x)<br><em>Exit from the Nihombashi Gate and go straight to the right.</em></p>
<p>![Directions to the office from the Hanzomon Line 2](/assets/blog/authors/uka/access/han2.png =600x)<br><em>There is a small staircase, go down and take a slight right.</em></p>
<p>![Directions to the office from the Hanzomon Line 3](/assets/blog/authors/uka/access/han3.png =600x)<br><em>Go straight and turn left at the end.</em></p>
<p>![Directions to the office from the Hanzomon Line 4](/assets/blog/authors/uka/access/han4.png =600x)<br><em>Here it is — COREDO Muromachi 2!</em></p>
<p>![Directions to the office from the Hanzomon Line 5](/assets/blog/authors/uka/access/es.png =600x)<br><em>Go inside, take the escalator up.</em></p>
<p>![Directions to the office from the Hanzomon Line 6](/assets/blog/authors/uka/access/en1.png =600x)<br><em>The office area will be right in front of you!</em></p>
<p>:::message
<strong>During event reception time, our staff will be stationed at the entrance to the office area!</strong>
Since an access card is required to get in from COREDO Muromachi 2, we&#39;ll be keeping an eye out and opening the door for anyone who looks like they&#39;re here for the event!
:::</p>
<blockquote>
<p><strong>For appointments or non-event visits before 6:00 p.m., please use the main office entrance, located just to the left after exiting the building.</strong></p>
</blockquote>
<p>![Muromachi Office Entrance 2](/assets/blog/authors/uka/access/en2.jpg =600x)<br><em>Step out to Edozakura-street from here.</em></p>
<p>![Muromachi Office Entrance 3](/assets/blog/authors/uka/access/en3.png =600x)<br><em>This is the main entrance.</em></p>
<p>Please check in at the front desk just inside the entrance, then head to the floor your host has indicated.</p>
<p>:::message
<strong>If you arrive after event reception hours, please use the same main entrance and contact the person listed in the event details, either via phone or through our official X account.</strong>
A staff member will come to meet you on the first floor.</p>
<p>@<a href="https://x.com/KintoTech_Dev">card</a>
:::</p>
<hr>
<h2>Shin-Nihombashi Station (JR Sobu Rapid Line)</h2>
<p>It&#39;s also directly connected from Shin-Nihombashi Station, but it&#39;s a bit of a walk...</p>
<p>![Directions to the office from Shin-Nihonbashi Station 1](/assets/blog/authors/uka/access/sin1.png =600x)  </p>
<ul>
<li>After exiting the ticket gate, turn left and go straight towards Mitsukoshimae Station (Shin-Nihombashi Station only has one ticket gate!) *</li>
</ul>
<p>![Directions to the office from Shin-Nihonbashi Station 2](/assets/blog/authors/uka/access/sin2.png =600x)<br><em>Turn left towards Mitsukoshimae Station.</em></p>
<p>![Directions to the office from Shin-Nihonbashi Station 3](/assets/blog/authors/uka/access/sin3.png =600x)<br><em>Continue straight with the Ginza Line on your right.</em></p>
<p>![Directions to the office from Shin-Nihonbashi Station 4](/assets/blog/authors/uka/access/sin4.png =600x)<br><em>You&#39;ll see COREDO Muromachi 1 on your left, go inside and walk through it.</em></p>
<p>![Directions to the office from Shin-Nihonbashi Station 5](/assets/blog/authors/uka/access/muro.png =600x)<br><em>Once you pass through, you&#39;ll find the entrance to COREDO Muromachi 2.</em></p>
<p>![Directions to the office from Shin-Nihonbashi Station 6](/assets/blog/authors/uka/access/es.png =600x)<br><em>Go inside, take the escalator up.</em></p>
<p>![Muromachi Office Entrance 1](/assets/blog/authors/uka/access/en1.png =600x)<br><em>The office area will be right in front of you!</em></p>
<p>:::message
<strong>During event reception time, our staff will be stationed at the entrance to the office area!</strong><br>Since an access card is required to get in from COREDO Muromachi 2, we&#39;ll be keeping an eye out and opening the door for anyone who looks like they&#39;re here for the event!
:::</p>
<blockquote>
<p><strong>For appointments or non-event visits before 6:00 p.m., please use the main office entrance, located just to the left after exiting the building.</strong></p>
</blockquote>
<p>![Muromachi Office Entrance 2](/assets/blog/authors/uka/access/en2.jpg =600x)<br><em>Step out to Edozakura-street from here.</em></p>
<p>![Muromachi Office Entrance 3](/assets/blog/authors/uka/access/en3.png =600x)<br><em>This is the main entrance.</em></p>
<p>Please check in at the front desk inside the entrance, then go to the floor your host has indicated.</p>
<p>:::message
<strong>If you arrive after event reception hours, please use the same main entrance and contact the person listed in the event details, either via phone or through our official X account.</strong>
A staff member will come to meet you on the first floor.</p>
<p>@<a href="https://x.com/KintoTech_Dev">card</a>
:::</p>
<hr>
<h2>Kanda Station (JR Chuo Line, Keihin-Tohoku Line, Yamanote Line)</h2>
<p>Finally, here&#39;s how to get to the office from Kanda Station!</p>
<p>![Directions to the office from Kanda Station 1](/assets/blog/authors/uka/access/kan1.png =600x)<br><em>Exit from the South Exit (for Nihombashi) and walk along the Starbucks.</em></p>
<p>![Directions to the office from Kanda Station 2](/assets/blog/authors/uka/access/kan2.png =600x)<br><em>Cross the street and go straight to the left.</em></p>
<p>![Directions to the office from Kanda Station 3](/assets/blog/authors/uka/access/kan3.png =600x)<br><em>Cross the street and go straight to the right.</em></p>
<p>![Directions to the office from Kanda Station 4](/assets/blog/authors/uka/access/kan4.png =600x)<br><em>Keep going straight.</em></p>
<p>![Directions to the office from Kanda Station 5](/assets/blog/authors/uka/access/kan5.png =600x)<br><em>Go straight to the left in front of COREDO Muromachi 3.</em></p>
<p>![Directions to the office from Kanda Station 6](/assets/blog/authors/uka/access/kan6.png =600x)<br><em>You&#39;ll spot COREDO Muromachi 2, but just keep going straight past it.</em></p>
<p>![Directions to the office from Kanda Station 7](/assets/blog/authors/uka/access/kan7.png =600x)<br><em>You&#39;ll see the office entrance on your left. That&#39;s the one!</em></p>
<p>![Directions to the office from Kanda Station 8](/assets/blog/authors/uka/access/en1.png =600x)<br><em>You&#39;ll find a staff member holding a sign waiting inside the office area entrance (During event hours).</em></p>
<blockquote>
<p><strong>For appointments or non-event visits before 6:00 p.m., please use the main office entrance, located just to the left after exiting the building.</strong></p>
</blockquote>
<p>:::message
<strong>If you arrive after event reception hours, please use the same main entrance and contact the person listed in the event details, either via phone or through our official X account.</strong>
A staff member will come to meet you on the first floor.</p>
<p>@<a href="https://x.com/KintoTech_Dev">card</a>
:::</p>
<hr>
<h2>Conclusion</h2>
<p>Hopefully, this post makes your visit to the KINTO Technologies Muromachi office a little smoother! Thank you for visiting! We look forward to seeing you again soon! (^_^)/</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uka/access.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[I Attended The Apple Worldwide Developers Conference (WWDC) in Person For the First Time]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-08-26-WWDC24参加レポート-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-08-26-WWDC24参加レポート-en/</guid>
            <pubDate>Fri, 25 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[A summary of the conferences and events I attended during WWDC24]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>I&#39;m super_spa3 (Hirofumi Nakano), a product manager at KINTO Technologies Corporation.</p>
<p>I had the chance to attend our very first overseas conference: the WWDC24! Although it&#39;s been a little while since the event, I&#39;d like to share my experience and some related updates here on the Tech Blog.</p>
<p>You might be wondering what a product manager has to do with iOS.</p>
<p>When I first started my career, I was actually assigned to iOS development (though I had been tinkering with Android as a hobby before that). Later, I joined my second company, KINTO Technologies, as an iOS engineer, and now I’m working as a product manager. At work, I often use App Store Connect to release apps, and I’m also a member of the Apple Developer Program, continuing to develop apps on my own.</p>
<p>One of the apps I&#39;m currently involved in is called the KINTO Kantan Moushikomi App (KINTO Easy Application App), which allows users to seamlessly estimate costs and complete the screening process for the KINTO New Vehicle Subscription.</p>
<p><a href="https://kinto-jp.com/entry_app/">https://kinto-jp.com/entry_app/</a></p>
<h1>How I Got to Attend WWDC24</h1>
<p>Participation in WWDC is determined by a lottery system. Around March each year, Apple sends out notification emails to members registered in the Apple Developer Program.</p>
<p>Members can then click on the provided link to apply, and only those selected in the lottery get the chance to attend the event in person.</p>
<p>However, I had tried applying multiple times during my previous job but was never successful. So, when I applied this time, I didn&#39;t have high expectations and just pressed the apply button.</p>
<p>On the day of the lottery results, I opened my inbox and saw an email &quot;Great News, Hirofumi!&quot;. As I read through the message, I realized I had actually been selected!</p>
<p>![](/assets/blog/authors/hirofumi.nakano/WWDC24/WWDC24_Accepted.png =750x) <em>A clear sign of good news email</em></p>
<p>It turned out that I was the only one from our Mobile App Development Group who got selected. After getting approval from President Kotera, I was set to attend the WWDC24!</p>
<h1>Eve of the Event at Apple Infinite Loop Campus</h1>
<p>On the day before the main event, I visited Apple&#39;s former headquarters, Apple Infinite Loop One Campus, to collect event merchandise and my name badge. ![](/assets/blog/authors/hirofumi.nakano/WWDC24/Apple_Infinite_Loop_Entrance.jpg =750x) <em>The entrance to Infinite Loop One is very minimalistic and stylish</em> ![](/assets/blog/authors/hirofumi.nakano/WWDC24/WWDC24_Badge.jpg =750x) <em>My name badge during the event displayed TOYOTA FINANCIAL SERVICE CORPORATION, where our apps are published.</em></p>
<p>The SWAG contents seem to change every year, and this year, it included a picnic mat. It&#39;s perfect now that travel is unrestricted post-COVID. How about some open-air coding? ![](/assets/blog/authors/hirofumi.nakano/WWDC24/WWDC24_Swag.jpg =750x) <em>WWDC24 swag bag and additional pins I got from Apple Park</em> ![](/assets/blog/authors/hirofumi.nakano/WWDC24/WWDC24_Pins.jpg =750x) <em>The Apple Vision Pro badge is cool!</em></p>
<p>Once entered the venue, I saw everyone taking photos in front of the WWDC24 logo. Since the event was held in the courtyard, I decided to strike a pose like on TBS&#39;s Another Sky. ![](/assets/blog/authors/hirofumi.nakano/WWDC24/WWDC24_Board.jpg =750x) <em>The 3D WWDC24 board, I took a photo here afterwards!</em> ![](/assets/blog/authors/hirofumi.nakano/WWDC24/Apple_Infinite_Loop_Garden.jpg =750x) <em>This is my Another Sky rendition, Apple Infinite Loop Campus! (lol)</em></p>
<p>With food and drinks in hand, I had the chance to interact with fellow developers working within the Apple ecosystem. Since I attended alone, I actively looked for someone to talk to. Whenever I saw an open seat I asked if I could sit next to them and started chatting about what we were developing, which led to great networking moments.</p>
<h1>Main Event at Apple Park</h1>
<p>The main event starts early in the morning. Check-in was scheduled for 7 a.m., but I heard that many people planned to arrive even earlier. So, I decided to get there well ahead of schedule and lined up before 7 a.m. at Apple Park Visitor Center, located next to the Apple Park, which houses an Apple Store, a café, a diorama, and a terrace. It seems that Japanese people are particularly good at early mornings because a sizable group of Japanese was already at the front of the line, eagerly waiting for the event to begin. As the clock approached 8 a.m., excitement built up, and the crowd spontaneously broke into chants of &quot;dub-dub-dee-see!&quot;, hyping up the atmosphere (of course, I joined in with full enthusiasm!)</p>
<p>Then, at 8 o&#39;clock, the gates opened. I rushed to the Keynote venue and managed to secure a seat in the sixth row from the front. ![](/assets/blog/authors/hirofumi.nakano/WWDC24/WWDC24_Keynote_Stage.jpg =750x) <em>It&#39;s so close!</em></p>
<p>I had free time from this point until 10 a.m., so I grabbed breakfast, explored Apple Park, and hopped on a Zoom call with my team members in Tokyo, who were staying up late to watch the Keynote live. Here is a tweet from that time:</p>
<p> <a href="https://twitter.com/ioco95/status/1800239213851574355">https://twitter.com/ioco95/status/1800239213851574355</a></p>
<p>Since my seat was on the right side of the stage, I got a behind-the-scenes glimpse of Tim Cook and Craig Federighi as they prepared to go on stage. Craig&#39;s movements were incredibly acrobatic this year.</p>
<p>The highlight of this year&#39;s Keynote was Apple Intelligence. But personally, what caught my attention the most was the Math Notes feature, which allows handwritten equations to be instantly solved, and the Mac Virtual Display in visionOS 2, which transforms into an ultra-wide monitor view on Apple Vision Pro (even though I don&#39;t own an Apple Vision Pro yet.)</p>
<p>At lunch, Caffe Macs offered a variety of regional dishes on both sides of the dining area. Some people even lined up multiple times to try different meals.</p>
<p>In the afternoon, I watched the Platform State of the Union inside Apple Park.</p>
<h1>Events at In-Person Labs and Exploring the Venue</h1>
<p>And finally, it&#39;s time for the In-Person Labs. This time is for meeting Apple engineers and staff, who can answer questions and help troubleshoot challenges in different areas. Since I had pre-booked a session for the Design Labs, I headed up to the third-floor area. ![In-Person Labs divided into different areas within the venue](/assets/blog/authors/hirofumi.nakano/WWDC24/WWDC24_Labs_Session.jpg =750x)</p>
<p>At the Design Labs, I was able to hear direct feedback from Apple employees on the KINTO Kantan Moushikomi App, as well as what the designers saw as potential challenges. It&#39;s a rare opportunity to get feedback from Apple employees, so this session was incredibly valuable.</p>
<p>One lesson I learned from the Design Labs was the importance of ensuring that apps are accessible internationally by using TestFlight or other means. When I applied for the event, I included the App Store URL in the pre-registration form, assuming that Apple employees would be able to access it. However, since our app was Japan-only, they weren&#39;t able to check it in advance. As a result, I had to spend part of the limited time explaining the app and its challenges before I could ask my questions. (Something to keep in mind for anyone attending in the future!)</p>
<p>After the Design Labs session, I walked around several areas to get answers to the questions I had from our in-house engineers. In between sessions, I also took some time to explore Apple Park (both the courtyard and inside the buildings). ![With the Apple Park courtyard in the background, yay!](/assets/blog/authors/hirofumi.nakano/WWDC24/Apple_Park_with_Rainbow.jpg =750x) <em>There were a lot of people who looked like they wanted a photo, so I offered, &quot;Would you like me to take a picture for you?&quot; and naturally ended up making new friends.</em></p>
<h1>Attending the OMT Conference</h1>
<p>Actually, the on-site WWDC event only takes place for three days—Sunday, Monday, and Tuesday. While sessions run from Monday to Friday, the on-site event doesn&#39;t extend.</p>
<p>So, I also attended the One More Thing (OMT) Conference held at a hotel near Cupertino, where Apple Park is located, from Tuesday to Friday. ![](/assets/blog/authors/hirofumi.nakano/WWDC24/OMTConf_Entrance.jpg =750x) <em>A lineup of familiar sponsors</em> ![](/assets/blog/authors/hirofumi.nakano/WWDC24/OMTConf_Badge.jpg =750x) <em>I got badges here, too. It says Pro Ticket, but it&#39;s actually a free ticket</em></p>
<p>OMT Conference was divided into speaker sessions (Main Room) and consultation sessions (Big &amp; Small Tents) where attendees could discuss various topics with experts.</p>
<p>We could listen to talks from different speakers or discuss specific topics with experts.</p>
<p>In addition, on Thursday afternoon, there was a workshop by Paul Hudson. Originally, it was planned to focus on SwiftData, but since there weren&#39;t many updates in WWDC24, he adapted the content and led a three-hour workshop on &quot;What&#39;s New in SwiftUI&quot; instead. I often check out Hacking with Swift, but this was my first time attending one of Paul&#39;s workshops in person. I was impressed by how amazing he is as a teacher and how quickly he answered questions.</p>
<h1>Attending Swift Social 2024 and Core Coffee</h1>
<p>In addition to attending WWDC and OMT conference, I also joined two community events.</p>
<p>The first was Swift Social 2024. This event was organized by the Swift community and rented out a bar in downtown San Jose to celebrate Swift&#39;s 10th anniversary. ![](/assets/blog/authors/hirofumi.nakano/WWDC24/Swift_Social_Board.JPG =750x) *Happy 10th Anniversary! * ![](/assets/blog/authors/hirofumi.nakano/WWDC24/Swift_Social_Sign.JPG =750x) <em>Stylish SWIFT logo sign</em></p>
<p>During the event, participants competed in a Swift-related quiz using Kahoot, racing to answer as quickly as possible. The top scorers won exclusive Swift merchandise as prizes. ![](/assets/blog/authors/hirofumi.nakano/WWDC24/Swift_Social_Play_Kahoot.jpg =750x) <em>I made it pretty far, but alas!</em></p>
<p>The second was Core Coffee. This gathering took place every day during the WWDC week at different locations, and the session I joined was held on the rooftop terrace of
 Apple Park Visitor Center, right next to Apple Park. Perhaps due to its proximity, a few Apple employees also joined.</p>
<p>Discussions naturally centered around the topics announced at WWDC24, sharing their thoughts and interests. One particularly lively debate was about how a color tint on the home screen might affect. I also had the chance to hear about the work styles of Apple employees and the products they are involved in, which made me feel even more engaging.</p>
<h1>Establishing Standards For Attending Overseas Conferences</h1>
<p>After I attended WWDC, several other conferences took place on the U.S. West Coast, and more members from our company began participating in overseas events as well.</p>
<p>We are also working on establishing internal guidelines for attending international conferences.</p>
<p>These global events offer valuable inspiration, and bringing it back home helps boost both individual and team motivation.</p>
<h1>Conclusion</h1>
<p>Attending WWDC for the first time was a long-awaited experience. It reignited my passion for development and game me the opportunity to build new connections.</p>
<p>Everyone at WWDC is involved in developing something within the Apple ecosystem, and through conversations, I learned about the apps they were building, their roles, and the challenges they face in development. (Of course it was&#39;t just developers, there were also product managers and other contributors to the ecosystem.)</p>
<p>It was incredibly inspiring that I also met several attendees whose primary job was not app development, but who were passionate about building apps as a hobby.</p>
<p>I also had the chance to connect with developers from Japan, and some of those I spoke with during the event became familiar faces after returning home.</p>
<p>If you&#39;re involved in product development for the Apple ecosystem, I highly recommend attending WWDC. Connecting with developers from around the world is truly exciting!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/hirofumi.nakano/WWDC24/Apple-WWDC24-event-announcement-hero.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[GitHub Copilot Agentを使ってSQL作成を効率化！]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-25-GithubCopilotAgentを使ってSQL作成を効率化/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-25-GithubCopilotAgentを使ってSQL作成を効率化/</guid>
            <pubDate>Fri, 25 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Github Copilot AgentとPythonを使ってSQL作成をスムーズにするための方法を解説します。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは、分析プロデュースグループの平田です！  </p>
<p>アナリストとして働いている私が、毎日取り組んでいるSQL作成業務の効率化について、今回はお話しします。<br>この記事では、「Github Copilot Agent」と「Python」を使って、数百行に及ぶ複雑なSQLを書き上げる業務をどう効率化したのか、その試行錯誤のプロセスと結果、そして今後の改善について語ります。</p>
<p>【まとめ】<br>✔︎ テーブル情報を事前に用意して、生成AIにSQLを作成してもらう<br>✔︎ 作成したSQLをPythonで自動実行・チェックする仕組みを実装<br>✔︎ エラーが発生したら自動で修正してもらい、業務効率をアップ！</p>
<h2>背景：日々のSQL作成業務とその悩み</h2>
<p>普段、私が直面しているのはこんな課題です…   </p>
<ul>
<li><p><strong>生成AIとの煩雑なやり取り</strong><br>  生成AIにテーブルの情報やデータ型、日付フォーマットなどを会話の度に何度も説明する必要があり、その都度手間がかかっていました。  </p>
</li>
<li><p><strong>膨大なSQLの作成</strong><br>  マーケティングに使うユーザー抽出や分析用データ作成など、何百行ものSQLを書かなければならず、複雑な加工ロジックが散在している。 </p>
</li>
<li><p><strong>試行錯誤の繰り返し（ループ）</strong><br>  作成したSQLをコピーして実行、エラーが返るとまたエラーログを渡して修正依頼…この繰り返しがボトルネックに。<br>  自分で修正すると、Github Copilotが作成した最新版と差分が生まれ、次の修正を依頼したときに先祖返りしてしまうことも。</p>
</li>
</ul>
<h2>試行錯誤！生成AIとPythonで自動化フローを構築</h2>
<p>そこで、こんな流れで業務の効率化に挑戦しました。</p>
<h3>自動化フローの全体像</h3>
<ol>
<li><p><strong>前提情報の登録</strong><br> 各テーブルの構成、データ型、サンプルデータ、サンプルSQL、加工Tipsをそれぞれのpromptファイルにまとめます。  </p>
</li>
<li><p><strong>生成AIでSQL生成</strong><br> 全体フローのプロンプト（「テーブル情報をもとにSQLを生成&amp;保存、実行チェック」）を生成AIに渡して、自動でSQLファイルを作成します。  </p>
</li>
<li><p><strong>Pythonで実行・チェック</strong><br> 生成されたSQLをPythonスクリプトで実行。エラーがあればエラーログをフィードバックし、自動修正を促す仕組みにしています。</p>
</li>
</ol>
<h2>具体的な取り組みポイント</h2>
<p>以下は、私が実際に構築したディレクトリ構成とファイル例です：</p>
<p><img src="/assets/blog/authors/hirata/github_copilot_sql/directory_sample.png" alt="directory_sample.png"></p>
<ul>
<li><p>rules / conversation_rules.prompt.md<br>  基本的な会話のルール。
  テーブル情報をもとにSQLを生成&amp;保存、pythonファイルを実行してSQLのが正しいかをチェックすること。また、SQL作成の決まりや会話の好みを記載。  </p>
</li>
<li><p>tables / &lt;テーブル名&gt;.prompt.md<br>  テーブルの情報を記載。
  サンプルデータを載せることでデータの特徴を判断してもらう。</p>
<pre><code>## テーブル名
users

## 説明
ユーザー情報のテーブル。user_idをキーにorderテーブルと紐付け可能

## sample SQL
```sql
select
  user_id as &quot;顧客ID&quot;,
  name as &quot;顧客名&quot;,
  address as &quot;住所&quot;
from
  users
```

## sample data
| 顧客ID | 顧客名   | 住所                             |
| ------ | -------- | -------------------------------- |
| 1      | 山田太郎 | 東京都新宿区西新宿        |
| 2      | 佐藤花子 | 大阪府大阪市中央区難波    |
| 3      | 鈴木次郎 | 愛知県名古屋市中区栄       |
</code></pre>
</li>
<li><p>sql /<br>  作成されたSQLをファイルで保存してもらう。</p>
</li>
<li><p>utils / query_checker.py<br>  SQL作成後このpythonファイルを実行し、SQLが正しいかをチェック。</p>
</li>
</ul>
<h2>実際のGithub Copilot Agentの回答例</h2>
<p>実際の動きが確認できる簡単な例をお見せします。</p>
<pre><code>[in] 今月何回メール配信したかをユーザー別に抽出するSQLを作成して！
</code></pre>
<pre><code>[out]
まずスキーマ情報を確認してから、メール配信の分析に関する
SQLを作成しましょう。メール配信に関するテーブル情報を探します。

&quot;› Searched for files matching /prompts/tables/**, 14 matches&quot; 

配信履歴に関するスキーマ情報が見つかりました！ 
まずはその内容を確認して、今月の配信回数を抽出するSQLを作成していきましょう。

Read メール配信履歴.prompt.md 

スキーマ情報を確認できました。[メール配信履歴テーブル]というテーブルに格納されているようです。 
今月（2025年4月）のメール配信回数を集計するSQLを作成していきます。 

このテーブルには以下の重要な列があります： 
 • email_id： メルマガID 
 • delivery_timestamp： 配信日時 
 • delivery_status： 配信ステータス 

それでは、今月のメール配信回数を集計するSQLを作成し、ファイルに保存しましょう。 
ファイル名は メール配信集計_今月.sql です。

このSQLでは以下の処理が実行されます： 
 ・ 2025年4月1日から4月30日までの期間に配信されたメールを対象にします。 
 ・ 「配信完了」ステータスのメールのみを集計します。 
 ・ ユーザーごとに、配信回数、成功数、失敗数、および総配信数を集計します。 
 ・ 最後に全体の合計も表示します。

Pythonでクエリを検証してみますか？
検証のため、先ほど作成したSQLを実行してみましょう。  

python3 query_checker.py メール配信集計_今月.sql

SQL検証が完了しました！  クエリは正常に検証され、実行可能と確認されました。  
テスト実行では構文エラーもなく、正しい実行計画が生成されました。
</code></pre>
<h2>結果と今後の展望</h2>
<p>嬉しいことに、一連のプロセスが無事稼働し、SQL作成の自動化に向けた第一歩となりました！<br>とはいえ、完璧ではありません。今後取り組みたい課題や工夫は…？</p>
<h3>● 現在の課題</h3>
<ul>
<li><p><strong>レビュー作業の手間</strong><br>  AIが生成したSQLのレビューは、どうしても自分で作成する場合よりミスを見落としがち。<br>  検証する仕組みや、実際にSQLを書かない人でもレビューを完遂できる手法については、いまだ試行錯誤の段階にあり、改善していきたいところです。
  また、生成AIの進化によって、これらの課題が解決されることにも期待しています！</p>
</li>
<li><p><strong>意図通りのデータ抽出のチェック</strong><br>  要件定義の不備や、私の頭の中の情報がうまく言葉にできていないケースがあり、意図通りの処理かを自動判定するのは難しい。細かいニュアンスや意図の伝え方がまだまだ課題です。</p>
</li>
</ul>
<h3>● 次へのチャレンジ</h3>
<ul>
<li><p><strong>件数チェックの自動化</strong><br>  レビュー高度化のファーストステップとして、抽出した件数が意図通りかどうかのチェック機能を実装していきたい。</p>
</li>
<li><p><strong>「秘伝のタレ」とも言えるデータ加工方法の蓄積</strong><br>  使い込むほどに見えてくる効果的なデータ加工テクニックを、プロンプトにどんどん追加していきたい。</p>
</li>
<li><p><strong>分析の自動化への拡張</strong><br>  最終的には、SQLの作成から抽出データの分析まで、ある程度自動で行える体制を目指します！</p>
</li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[How We Reduced AWS Costs by 65%—and What We Discovered Beyond That]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-23-aws_cost_reduction-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-23-aws_cost_reduction-en/</guid>
            <pubDate>Thu, 24 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[How We Reduced AWS Costs by 65%—and What We Discovered Beyond That]]></description>
            <content:encoded><![CDATA[<p>This article is the entry for day 23 in the<a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>🎅🎄</p>
<h1>1. Introduction</h1>
<p>Hi there! I’m Nishida, a backend engineer at KINTO FACTORY.</p>
<p>Today, I’d love to share how we managed to slash our AWS costs.</p>
<h1>2. What Made Us Start Working on Cost Reduction?</h1>
<p>At KINTO Technologies, we use Amazon QuickSight to visualize our AWS usage fees, making it easy to track costs for each product.</p>
<p>A little while after launching KINTO FACTORY, we casually checked the project’s costs, and to our surprise, it turned out to be the second most expensive product in the entire company. We certainly didn’t anticipate incurring such high costs so early on. This &quot;Wait, what?!&quot; moment served as the catalyst for our applications team to spring into action and begin working on cost reduction efforts.</p>
<h1>3. What We Actually Did</h1>
<p>Now, let’s dive into the specific cost-cutting steps we took.</p>
<h3>What We Actually Did</h3>
<p>When we broke down the costs, ECS Fargate stood out as the clear front-runner in expenses. Not too surprising, since KINTO FACTORY’s applications run on ECS Fargate but still, we figured there had to be ways to optimize it.</p>
<p>The first thing that caught our eye was: &quot;Wait... are the number of instances in the development environment the same as production?&quot; That definitely didn’t make sense. The development environment shouldn&#39;t require nearly as much computing power. So, we reduced the number of instances in the development environment to only those that were absolutely essential.</p>
<p>Upon further investigation, we discovered that there is another Fargate launch type called Fargate Spot that is different from the usual one. Fargate Spot is a system that lets you tap into unused AWS resources, offering discounts of up to 70% compared to the regular Fargate. Honestly, why wouldn’t you use it?</p>
<blockquote>
<p> Fargate Spot allows Amazon ECS tasks that can handle interruptions to run at a much lower cost. It works by running tasks on AWS’s spare compute capacity. When that capacity is needed elsewhere, tasks are interrupted, with a two-minute warning that is provided to wrap things up.</p>
<p>-- <a href="https://docs.aws.amazon.com/en_us/AmazonECS/latest/developerguide/fargate-capacity-providers.html">https://docs.aws.amazon.com/en_us/AmazonECS/latest/developerguide/fargate-capacity-providers.html</a></p>
</blockquote>
<p>That said, as noted in the documentation, Fargate Spot relies on spare compute capacity, so tasks may occasionally be interrupted if AWS reclaims those resources. Applying this to the production environment wasn’t really an option, so the settings were adjusted for the development environment, where occasional interruptions wouldn’t cause any issues.</p>
<p><img src="/assets/blog/authors/m.nishida/20241223/fargate_spot.jpg" alt="fargate_spot"></p>
<h3>Automating Startup and Shutdown of the Development Environment</h3>
<p>The development environment had also been running 24/7, just like production environment. To avoid unnecessary costs, it was set up to automatically shut down during off-hours — late nights, weekends, and holidays — when it wasn’t needed.</p>
<p>Step Functions were used to automate the start-up and shutdown processes for both the application and the database.</p>
<p><img src="/assets/blog/authors/m.nishida/20241223/stepfunctions_image.png" alt="stepfunctions"></p>
<p>In the first stage, EventBridge is used to send start and stop triggers to Step Functions based on schedules set with cron.</p>
<p>One key point: database startup takes time. Starting ECS right after the DB starts can lead to connection errors. To prevent that, the system first checks the DB status before launching ECS.</p>
<p>For reference, here’s a sample code (YAML file) showing the Step Functions workflow:  </p>
<pre><code class="language-yaml">    &quot;DB Startup&quot;: {
      &quot;Type&quot;: &quot;Task&quot;,
      &quot;Parameters&quot;: {
        &quot;DbClusterIdentifier&quot;: &quot;${db_cluster_identifier}&quot;
      },
      &quot;Resource&quot;: &quot;arn:aws:states:::aws-sdk:rds:startDBCluster&quot;,
      &quot;Next&quot;: &quot;Wait&quot;
    },
    &quot;Wait&quot;: {
      &quot;Type&quot;: &quot;Wait&quot;,
      &quot;Seconds&quot;: 300,
      &quot;Next&quot;: &quot;Checking DB status after startup&quot;
    },
    &quot;Checking DB status after startup&quot;: {
      &quot;Type&quot;: &quot;Task&quot;,
      &quot;Parameters&quot;: {
        &quot;DbClusterIdentifier&quot;: &quot;${db_cluster_identifier}&quot;
      },
      &quot;Resource&quot;: &quot;arn:aws:states:::aws-sdk:rds:describeDBClusters&quot;,
      &quot;Next&quot;: &quot;Check if startup is complete&quot;
    },
    &quot;Check if startup is complete&quot;: {
      &quot;Type&quot;: &quot;Choice&quot;,
      &quot;Choices&quot;: [
        {
          &quot;Variable&quot;: &quot;$.DbClusters[0].Status&quot;,
          &quot;StringEquals&quot;: &quot;available&quot;,
          &quot;Next&quot;: &quot;Start up each service&quot;
        }
      ],
      &quot;Default&quot;: &quot;Wait&quot;
    },
</code></pre>
<h3>Moving to a Serverless Architecture</h3>
<p>Next, while reviewing the batch processing for a certain feature, it became clear that the process (which took less than a minute) was running as a resident service on ECS.</p>
<p>This setup wasn’t exactly cost-friendly too, so we moved to a serverless architecture using Lambda. Lambda, with its pay-as-you-go model based on request count and execution time, is perfect for short, quick processes or tasks that don’t need to run constantly.</p>
<p><img src="/assets/blog/authors/m.nishida/20241223/serverless.jpg" alt="serverless"></p>
<h1>4. Cost-Saving Results</h1>
<p>The efforts paid off quickly, with costs dropping by <strong>65%</strong> compared to the peak. Less than half; who would’ve thought?</p>
<p><img src="/assets/blog/authors/m.nishida/20241223/aws_cost.jpg" alt="aws_cost"></p>
<p>Seeing just how much room there was to cut costs was honestly surprising.</p>
<h1>5. Conclusion</h1>
<p>This time, the focus was on sharing cost reduction efforts.</p>
<p>The steps taken weren’t complicated or anything fancy, but they still delivered solid results.</p>
<p>Along the way, some valuable insights emerged. Cost reduction isn’t just about saying, “Look, we saved money!” </p>
<ul>
<li>By reviewing and eliminating unnecessary resources, we were able to gain better visibility into the entire system.</li>
<li>It also provided a great opportunity to revisit the architecture.</li>
<li>Asking questions like, &quot;Is this resource really necessary?&quot; naturally became part of the process.</li>
</ul>
<p>That mindset shift brings long-term benefits, making the effort more than worth it.</p>
<p>Hopefully, this can be helpful for anyone facing similar challenges.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[React Emailを活用した自動メール配信で、アプリの休眠ユーザーを再訪させる]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-24-react-email-user-retention/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-24-react-email-user-retention/</guid>
            <pubDate>Thu, 24 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[アプリの休眠ユーザーを再訪させるために、React Emailを使用した施策を行いました。React Emailの実装方法や効果について説明します。]]></description>
            <content:encoded><![CDATA[<p>こんにちは、<a href="https://ppap.kinto-jp.com/prismjapan">Prism Japan</a>でエンジニアを担当している宇田川です。
React Emailを活用したメールの自動配信を行うマーケティング施策について紹介したいと思います。</p>
<h2>マーケティング施策において、直面していた課題</h2>
<p>Prism Japanは、2022年8月にローンチされ、サービス開始当初から、様々なマーケティング施策を通じてユーザーを獲得してきました。しかし、一度獲得したユーザーが継続的にサービスを利用し続けてもらえるわけではありません。サービス開始から約2年半が経過しましたが、依然として休眠ユーザーが増加傾向にあります。</p>
<p>この問題に対処するため、プッシュ通知による再訪施策を実施していましたが、いくつか課題に直面しました。</p>
<ul>
<li>通知設定をオフにしているユーザーには、プッシュ通知は届かない。</li>
<li>再訪を促すプッシュ通知を配信しても、アプリをアンインストールしているユーザーには届かないため、狙った効果を得られない。</li>
</ul>
<p>実際、プッシュ通知の許諾率は約48%にとどまっており、この許諾率と、アンインストール済みユーザーを考慮すると、通知が届くユーザー数は、かなり絞られてきます。さらに、他のアプリからの通知もあるため、他の通知に埋もれがちです。このようにプッシュ通知で行える再訪施策の効果には限界がありました。</p>
<p>一方で、会員登録時にメールの登録をお願いしているのですが、こちらで登録されたメールの許諾率は、約90%と非常に高い水準を維持しています。アプリを削除していても、退会していないユーザーにはメールを届けられるため、再訪施策に適したマーケティングチャネルです。</p>
<p>しかし、運用面を考えるといくつか課題がありました。</p>
<p>まず、マーケティング担当のリソースが限られており、施策立案からSNS運用まで幅広い業務を、一人で担当している状況でした。メールのコンテンツ制作においては、ランキングの手動集計や適切な画像選定、レイアウト設計などに、工数が多く必要となります。このように、マーケティング担当のリソースを考えると、頻繁な配信が困難です。そのため、効果的なマーケティング手段と認識しつつも、頻度高くメール配信を行うことは、運用負荷を考えると現実的ではありませんでした。</p>
<h2>メールの作成を自動化するために、React Emailを使うことに</h2>
<p>そこで「<strong>メールの作成から配信までを自動化できないか</strong>」という発想が生まれました。</p>
<p>コンテンツに表示する情報を自動的に収集し、メールコンテンツを自動で作成し、スケジュールされた日時に決められたレイアウトで自動的に配信できる仕組みを構築できれば、限られた人的リソースの中でも、ユーザーに合ったメールを送信できます。</p>
<p>しかし、HTMLメールを自動的に作成する処理を考える際に、エンジニアとして、実装方法に頭を悩ませました。</p>
<p>HTMLを直接操作するような処理を実装してしまうと、再利用性が低く、受信するメーラーによって表示が異なるといった問題も発生します。そして、将来的に、コンテンツの差し替えが発生することを見据えると、再利用性が高く、新しいコンテンツを柔軟に追加できるような実装が必要です。</p>
<p>このような悩みを抱える中で発見したのが「React Email」というライブラリです。</p>
<p>この「React Email」には、下記のような特徴があります。</p>
<ul>
<li>JSXでHTMLメールを作成できる</li>
<li>リアルタイムのプレビュー機能</li>
<li>コンポーネント化による高い再利用性</li>
</ul>
<p>特に重要なのは、再利用可能なコンポーネント化によって、新しいコンテンツの作成が必要になった際に、容易に追加できる点です。また、Reactで記述されていることにより、掲載内容の動的な差し替えも容易になります。このような利点は、ユーザーの行動や興味に基づいて、コンテンツを差し替えることにより、<strong>パーソナライズされたコンテンツの提供</strong>を、低コストで行うことができます。全ユーザーに対して、同一のコンテンツを一斉配信するのではなく、各ユーザーの関心に合わせたコンテンツ配信を行うことで、<strong>高い再訪率やエンゲージメント向上</strong>が期待できます。</p>
<p>React Emailを利用することで、メール配信施策における課題を効果的に解決できる見通しが立ち、効率的なユーザー再訪施策の実現に向けて、施策を進めることができました。</p>
<h2>React Emailを使ったHTML生成</h2>
<p>ここからは、実装部分について触れていきたいと思います。
実装としては、メールのHTMLを生成する部分に、React Emailを利用しています。
React Emailのrender関数を使って、JSXからHTMLを生成する流れを採用しました。</p>
<p>まずは、以下のようなコンポーネントを作成します。</p>
<pre><code class="language-javascript">import React from &quot;react&quot;;

const AppCheckSection = () =&gt; {
  return (
    &lt;div style={{ padding: &quot;20px 0&quot;, borderBottom: &quot;1px dashed #cccccc&quot; }}&gt;
      &lt;div&gt;
        &lt;p&gt;
          詳しいスポットの情報やアクセス情報はアプリで確認してみましょう。
          &lt;br /&gt;
          他にも、アプリではあなたにだけのおすすめスポットを掲載中！
        &lt;/p&gt;
        &lt;a
          style={{
            padding: &quot;10px 70px&quot;,
            background: &quot;rgb(17,17,17)&quot;,
            borderRadius: &quot;5px&quot;,
            textAlign: &quot;center&quot;,
            textDecoration: &quot;none&quot;,
            color: &quot;#fff&quot;,
            display: &quot;inline-block&quot;,
            marginBottom: &quot;10px&quot;,
          }}
        &gt;
          &lt;span&gt;アプリをチェック&lt;/span&gt;
        &lt;/a&gt;
        &lt;br /&gt;
        &lt;a href=&quot;https://deeplink.sample.hogehoge/&quot;&gt;
          うまく開かない方はこちら
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default AppCheckSection;
</code></pre>
<p>このように、メールを構成するための、コンポーネントを作成していきます。</p>
<p>その後、親コンポーネントで、作成したコンポーネントを組み合わせるだけで、メールのテンプレートが完成します</p>
<pre><code class="language-typescript">import React from &#39;react&#39;;
import AppCheckSection from &#39;../shared/AppCheckSection&#39;;
import FooterSection from &#39;../shared/FooterSection&#39;;
import RankingHeaderSection from &#39;./RrankingHeader&#39;;
import RankingItems from &#39;./RankingItem&#39;;

export type RankingContents = {
  imageURL: string;
  name: string;
  catchPhrase: string;
};

export type WeeklyRankingProps = {
  areaName: string;
  contents: RankingContents[];
};

const WeeklyRanking: React.FC&lt;WeeklyRankingProps&gt; = ({ areaName, contents }) =&gt; {
  return (
    &lt;div style={{ backgroundColor: &#39;#f4f4f4&#39;, padding: &#39;20px 0&#39; }}&gt;
      &lt;div&gt;
        &lt;RankingHeaderSection /&gt;
        &lt;RankingItems areaName={areaName} contents={contents} /&gt;
        &lt;AppCheckSection /&gt;
        &lt;FooterSection /&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

export default WeeklyRanking;
</code></pre>
<p>そして、メールHTMLの生成には、React Emailのrender関数を使用します。</p>
<p>fetchRegionalRankingDataを用いて、住んでいる地域ごとに、異なるコンテンツ情報を取得して、メールを作成することができます。</p>
<pre><code class="language-typescript">import { render } from &#39;@react-email/render&#39;;
import { WeeklyRankingEmail } from &#39;../emails/weekly-ranking&#39;;
import { fetchRegionalRankingData } from &#39;./ranking-service&#39;;

export async function generateWeeklyRankingEmail(areaName: string): Promise&lt;string&gt; {
  const contents = await fetchRegionalRankingData(region);
  
  const htmlContent = await render(await WeeklyRanking({ areaName, contents }));  
  return emailHtml;
}
</code></pre>
<p>render 関数で生成された HTML は、SaaS サービスの API を介して送信されるメールの本文として使用されます。</p>
<p>バッチ処理では、EventBridge によってスケジュールされたタイミングで ECS が起動し、メールの作成および送信処理が実行されます。</p>
<p>実際に送信されるメールは、以下のような内容になります。</p>
<p>   <img src="/assets/blog/authors/udagawa/react-email-user-retention/react-email-user-retention-02.png" alt="email example"></p>
<p>画像では、関東地域に絞られた内容が表示されていますが、ユーザーが設定した地域に応じて内容を柔軟に変更できる仕組みを実装しています。なので、居住地域が、大阪であれば関西地方のランキングがメールとして配信されます。</p>
<p>   <img src="/assets/blog/authors/udagawa/react-email-user-retention/react-email-user-retention-04.png" alt="react email preview"></p>
<p>そして、React Emailでは、プレビュー機能があり、普通にReactで開発するときと同じように、メールの実装を進めることができます。プレビューなしで実装することは、かなり難易度が高いので、この機能は非常に助かります。この機能を活用し、マーケティング担当の方とレイアウトについて、適宜確認しながら実装作業を進められました。</p>
<p>   <img src="/assets/blog/authors/udagawa/react-email-user-retention/react-email-user-retention-03.png" alt="email used component"></p>
<p>コンポーネント化により、ランキング以外にもフッターやアプリ起動促進セクションなど、様々な要素を再利用可能なパーツとして構成しました。これにより、新規コンテンツの作成においても既存コンポーネントを組み合わせることで、効率的かつ一貫性のあるメール配信が可能になっています。</p>
<p>スケジューリングされたメール配信では、同じようなコンテンツの繰り返しになってしまい、ユーザーの興味低下や、最悪の場合は迷惑メールに登録されてしまい受信を拒否されてしまう可能性があります。自動化されたシステムであっても、<strong>ユーザーの関心を継続的に引きつけるコンテンツ提供</strong>が求められるはずです。</p>
<p>このような状況を踏まえると、素早く提供するコンテンツの変更が出来るように、コンポーネント化による再利用性の高い設計が、重要だと考えています。</p>
<h2>メールを自動配信した効果</h2>
<p>React Emailとバッチ処理による自動メール配信を開始した結果、配信した日(2月22日)の付近は、インストール数が増加していました。これは、メールを見た休眠ユーザーが、アプリに興味を持ち、<strong>再インストールを促すことができた</strong>と考えています。また、メールを配信日付近のDAUが大きく上昇し、メール自動配信の施策開始以降のDAUは持続的に増加傾向を示していました。</p>
<p>このように、アプリをアンインストールしたユーザーも含む、休眠ユーザーの再訪を促すことに成功しました。
   <img src="/assets/blog/authors/udagawa/react-email-user-retention/react-email-user-retention-01.png" alt="email result"></p>
<h2>まとめ</h2>
<p>React Emailを活用した自動メール配信により、人の手を介さずに<strong>休眠ユーザーの掘り起こし</strong>や、<strong>DAUを増加</strong>させることに成功しました。</p>
<p>アプリ開発で休眠ユーザーが多く、マーケティングリソースに限りがあるという課題は多くのマーケティング担当者が頭を抱えているのではないでしょうか。React Emailを利用したメール作成の自動化は、メールのコンテンツ内容を毎週考える負荷を軽減し、<strong>効率的で、効果的なマーケティング活動</strong>をすることができます。さらに、コンテンツの改善を継続的に行い、それを<strong>素早くリリース</strong>するためにも、「React Email」は非常に有用だと感じました。</p>
<p>また、コミュニケーション手段が多様化した現代であっても、<strong>ユーザーの興味に沿ったコンテンツ</strong>を届ければ、マーケティングチャネルとして、メール配信は有効に機能することがわかりました。</p>
<p>再訪ユーザーが伸び悩んでいたり、休眠ユーザーの掘り起こしでお悩みの方は、ぜひこのアプローチを検討してみてはいかがでしょうか。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Overcoming Challenges in the In-House Rollout of the 10X Innovation Culture Program [Part 2]]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-23-Google-10x-program-in-summer-2-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-23-Google-10x-program-in-summer-2-en/</guid>
            <pubDate>Wed, 23 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Overcoming Challenges in the In-House Rollout of the 10X Innovation Culture Program [Part 2]]]></description>
            <content:encoded><![CDATA[<p>Hello, this is HOKA from the Organizational Human Resources Team in the Human Resources Group.</p>
<p>This blog is a continuation of <a href="https://blog.kinto-technologies.com/posts/2024-12-22-Google-10x-program-in-summer-1/">Overcoming Challenges in the In-House Rollout of the 10X Innovation Culture Program [Part 1]</a></p>
<h2>August: Lessons learned from Speaking at Google Cloud Next Tokyo &#39;24</h2>
<p>Google Cloud Next Tokyo &#39;24, held by Google Cloud Japan, LLC on August 1–2, 2024, is a flagship event bringing together business leaders, innovators, and engineers for a major cloud conference. The event featured a variety of programs, including keynote speeches, live sessions, and hands-on sessions, all centered around key business topics like generative AI and security. </p>
<p>For more details, click here -&gt; <a href="https://cloudonair.withgoogle.com/events/next-tokyo-24">https://cloudonair.withgoogle.com/events/next-tokyo-24</a></p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program6.jpeg" alt=""></p>
<p>From our company, Kisshi, General Manager of the Development Support Division, and Awache, Manager of the Database Reliability Engineering Group, took the stage at the 10X Innovation Culture Program&#39;s experience workshop. The workshop was a great opportunity to highlight real-world examples of the 10X Innovation Culture Program in action.</p>
<p>Leading up to the actual event, we had numerous meetings, rehearsed at the Google office, and had lunch together, giving us plenty of time to interact with people from Google.</p>
<p>And then the real thing began.</p>
<p>The extraordinary atmosphere of the spacious venue at Pacifico Yokohama naturally created a feeling of &quot;let’s learn something new today&quot; kind of mindset.</p>
<p>First, a representative from Google introduced us to 10X.  The speaker shared, in down-to-earth and relatable words, the background, reasons, and results behind Google’s promotion of the 10X Innovation Culture Program both inside and outside the company. Hearing this, the KINTO Technologies (KTC) members in the audience couldn’t help but think, &quot;10X is exactly what we need too!&quot; and the energy in the room instantly lifted.</p>
<p>After that, Kisshi and Awache took the stage.</p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program7.jpeg" alt=""></p>
<p>Kisshi and Awache talked about the following three topics:</p>
<ol>
<li>The background to the cultural transformation initiative and why we decided to implement the 10X Innovation Culture Program</li>
<li>Key takeaways and important points learned from actually implementing the program</li>
<li>Future plans and next steps</li>
</ol>
<p>In particular, they shared the following key takeaways from their experience implementing the 10X Innovation Culture Program:</p>
<ul>
<li>It&#39;s important to discard the notion that &quot;only Google can do it&quot; and just give it a try without overthinking it.</li>
<li>It is important to develop internal facilitators to achieve in-house independence from the bottom up.</li>
<li>Culture is not something that is created in one leap, but rather something that is noticed and experienced many times before becoming ingrained.</li>
</ul>
<p>Many questions came up during the Q&amp;A session. One of them was, “10X sounds amazing, but isn’t it difficult to actually implement?” While significant results from the 10X Innovation Culture Program have yet to emerge, we hope these efforts can provide at least a little inspiration.</p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program8.jpeg" alt=""></p>
<p>Having attended the Google Cloud Next Tokyo &#39;24, I felt an overwhelming difference from the 10X Innovation Culture Program created and held internally in July. What stood out were:</p>
<ul>
<li>A dedicated venue, separate from the office, puts participants in the right mindset to truly listen.</li>
<li>Hearing &quot;10X&quot; directly from Google employees carries real weight and resonates deeply.</li>
<li>The event felt discussion-heavy, but perhaps a stronger focus on input and learning would be even more impactful.</li>
<li>With that foundation, participants may naturally start thinking, &quot;I want to bring 10X thinking into my daily work.&quot;</li>
</ul>
<p> Having these as hypotheses, plans are in place to put them to the test at the 10X Innovation Culture Program this fall.</p>
<h2>September: Participating in the 10X Facilitator Training</h2>
<p>First, we decided to start by training facilitators who can guide smooth understanding and discussion of 10X concepts. Our hypotheses were:</p>
<ul>
<li>&quot;10X&quot; when shared by Google employees, resonates strongly.</li>
<li>What seemed like a discussion-focused setting turned out to need more input.</li>
</ul>
<p>The reason? Those running the 10x program didn’t have enough clarity or input themselves on what it really is, making it tough to convey to others. After reaching out to Google for advice, they kindly agreed to set up facilitation trainings.</p>
<p>Selecting members for the facilitation training was a challenge. But since the 10X Innovation Culture Program is, at its core, a leadership program, managers were chosen to participate.</p>
<h3>Facilitator Training Day</h3>
<p>At the beginning of the training, Kota-san from Google, who is the main person in charge of this session, said, &quot;It&#39;s going to be extremely intense.&quot; The goal is to hear something once, remember it, and be able to say it yourself. It’s designed to let you experience two phrases that Google lives by:</p>
<ul>
<li>Steal with pride</li>
<li>Feedback is a gift!</li>
</ul>
<p>&quot;You’re about to spend more time thinking about 10X than Google employees do&quot;, was what we were told.</p>
<p>&quot;What on earth is about to begin?&quot; The training kicked off in that atmosphere.</p>
<p>[First half of the training] Input Time
Just as Kota-san mentioned, the first part of the session was all about learning. A Googler gave a presentation on the six key elements of 10X, and KTC employees had some solid &quot;input time&quot; to really understand each one.</p>
<p>Take the first element, DEI, for example.</p>
<ul>
<li>At Google, 50% of employees have taken the training <a href="https://rework.withgoogle.com/jp/guides/unbiasing-raise-awareness#introduction">Unconscious Bias @ Work</a>, which helps raise awareness of unconscious bias.</li>
<li>On top of that, they run weekly <a href="https://rework.withgoogle.com/jp/guides/unbiasing-gather-data-and-measure-decisions#measure-outcomes">Googlegeist</a> to track how well they’re doing in areas like inclusiveness and equity.</li>
</ul>
<p>We got to hear not just about these concrete initiatives, but also the impact they’ve had. Similarly, for each of the six 10X elements, the presenter shared real examples from Google, along with their own personal thoughts and stories from working there.</p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program9.jpeg" alt=""></p>
<p>[Second half of the training] Output Time
After hearing the presentation from Google, the KTC members split into groups of six. Each person took one of the six elements, memorized it, and gave a presentation that included their own personal story or experience. The listeners weren’t just passive after each presentation, everyone gave feedback on what worked well and what could be improved. We also received feedback from the Google team. The whole process might’ve felt a bit intense, just like Kota-san warned, but it was the perfect chance to &quot;steal with pride&quot; from Google’s approach and truly embrace the idea that &quot;feedback is a gift.&quot; </p>
<p>And the feedback wasn’t just technical. Ideas started flowing, too: &quot;We should be sharing the vision more across the whole company,&quot; &quot;What’s the current status of our goal-setting process?&quot;, &quot;I want to be able to give more concrete examples: let’s increase opportunities to talk directly with executives!&quot; and so on. More and more ideas bubbled up. By learning about 10X, we got to witness that “aha!” moment when people started comparing it to where KTC stands today.</p>
<p>In fact, after the training, a lot of KTC members shared feedback such as: &quot;I always thought 10X was great, but it felt like something distant, like it was a distant thing. But by memorizing it, tying it to my own experiences and presenting it to others in such a short time, it suddenly became something I truly owned.&quot; And getting immediate feedback from the Google team on the spot was such a valuable opportunity.</p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program10.jpeg" alt=""></p>
<h2>October: Preparing for the Third 10X Innovation Culture Program</h2>
<p>I wrote this blog in October, after completing the facilitator training and while gearing up for the third round of the 10X Innovation Culture Program. The third 10X iteration will be divided into two days. It&#39;s a brand-new approach for us. Additionally, 47% of participants this time are joining for the first time. Will they be even more eager and engaged than the group in July? Will all the effort we put into the September’s facilitator training pay off? I’ll share how it all turns out in a follow-up post on the TechBlog.</p>
<p>To be continued in the 10X Innovation Culture Program: The Struggles and Challenges of Building It In-House [Part 2], coming January 2025.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[12月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-23-newcomers-introduction-24dec/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-23-newcomers-introduction-24dec/</guid>
            <pubDate>Wed, 23 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズに2024年12月に入社されたみなさまを紹介します。]]></description>
            <content:encoded><![CDATA[<h1>こんにちは</h1>
<p>こんにちは、2024年12月入社のまきどんです！
本記事では、2024年12月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。
KINTOテクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>Fsk</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/newcomers/Fsk.png" alt="Fsk"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>業務システム開発部 業務システムGでフロントエンド開発を担当しています。
 これまで主にNextjsを用いたフロントエンド開発を中心に携わっており、皆さんにとって使いやすい画面づくりを心がけています。
 まだまだ学ぶことも多いですが、少しでも皆さんのお役に立てるよう頑張っていきたいと思っています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>自分を含めて5人体制です。PM１名、フロントエンドエンジニア２名、バックエンドエンジニア２名です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>copilot、chatGPTなど生成AIを使用するのはすごく助かります。
 入社前は少し緊張していましたが、皆さんが温かく迎えてくださったので、すぐに安心しました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>困ったときにすぐ相談できる雰囲気があるのはすごくありがたいです。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>自分の考えや感じていることを皆さんに共有する機会ができて、とても良いと思っています。</li>
</ul>
</li>
<li><strong>フランクさん → Fskさんへの質問</strong><ul>
<li><strong>もしロボットに1つだけ面倒な日々のタスクを任せられるとしたら、どの作業を任せたいですか？</strong><ul>
<li>無論、掃除です！毎日掃除に時間を取られるのはもったいないですし、その時間を他のことに使いたいですね。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>高橋</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/newcomers/takahashi.png" alt="takahashi"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>オウンドメディアＧ、兼マーケティングプロダクト開発でプロジェクトマネージャーを担当しています。
 クライアント/事業部の良き隣人として、また、エンジニアと事業部間の橋渡し役としてメンバーが同じ目標に向かって歩めるように日々活動しています。</li>
<li>前職ではWEBデザイナーとしての経験を積み、開発部へ異動してからは、内製の会員、決済、ポイント、施設情報などプラットフォーム系を兼任するマネージャーやってました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>オウンドメディアＧはPM1名、エンジニア２名。マーケティングプロダクト開発は静的コンテンツの開発に特化して、チームリーダー1名、PM1名、テックリード1名、エンジニア２名です。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>第一印象はオフィスが静か（前職では営業が同じフロアですぐ近くにいてうるさかった）。</li>
<li>ギャップは開発スタイルがグループごとに異なるのでマインドシフトが柔軟にできるように身構えておく事くらい。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>静か。飲料の缶をゴミ箱に捨てるときにちょっと気を遣う。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>社内の自己紹介ではモンハンが好きだしか伝わってなかったと思うので書く機会があってよかったです。</li>
</ul>
</li>
<li><strong>Fskさん → 高橋さんへの質問</strong><ul>
<li><strong>ワールドとワイルズ、どっちが好き？ww</strong><ul>
<li>これからのアップデートを期待してワイルズ！ワールドに負けじと10年以上遊べてほしい！</li>
</ul>
</li>
<li><strong>現在、生成AIはデザイン分野で活用されていますが、多くのエンジニアが「AIプロンプト」エンジニアと呼ばれています、この現象について、どう考えていますか？</strong><ul>
<li>著作権や肖像権に障らぬように気を付けつつ利用する…であれば、生成AIに任せても良いモノもあると思います。ただ、創造力を競うようなコンクールやコンテストなどの場に出るとなった時はふさわしくないと考えます。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>呂</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/newcomers/Lyu.png" alt="Lyu"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>現在、業務システム開発部の業務システムGに所属し、主にバックエンドのシステム開発を担当しています。日々の業務では、社内の業務効率化やデータ連携を目的とした各種システムの設計・実装・運用保守を行っており、安定性と拡張性を重視した開発に取り組んでいます。</li>
<li>以前はIBMに勤務しており、日本国内の大規模な病院向けに医療情報システムの開発プロジェクトに従事していました。要件定義から設計、開発、導入、アフターサポートまで、幅広い工程を経験しており、現場のニーズに寄り添ったシステムづくりを意識してきました。</li>
<li>これまでの経験を活かしつつ、より実用的で価値のあるシステムを提供できるよう、日々技術力と業務理解の向上に努めています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>自分を含めて5人体制です。PM１名、フロントエンドエンジニア２名、バックエンドエンジニア２名です。みんなは各領域・業務でのプロでチーム内で非常に勉強できました。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>入社して最初に感じたのは、同僚同士の関係がとても温かく、上下関係にとらわれずに気軽にコミュニケーションが取れる雰囲気があることでした。また、社内イベントが充実していることや、さまざまなサークル活動が活発に行われている点も印象的でした。福利厚生制度も社員に優しく、とても働きやすい環境だと感じました。</li>
<li>入社前に想像していた印象と比べても、大きなギャップはなく、むしろ想像以上に良い職場環境だと実感しています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>とても明るく楽しい雰囲気です。日々の業務に関する検討はもちろんのこと、ちょっとした面白い出来事やアイデアも気軽に共有し合える環境が整っています。メンバー同士の距離も近く、誰とでもすぐに打ち解けられるので、安心して働くことができます。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>自分の体験をこうして共有できることを、とても嬉しく思います。日々の業務や感じたことを通じて、少しでも誰かの参考になれば幸いです。</li>
</ul>
</li>
<li><strong>高橋さん → 呂さんへの質問</strong><ul>
<li><strong>もしKINTOで車を購入するとしたらどの車に乗りたい？</strong><ul>
<li>やはり「クラウン」に乗りたいですね。昔からずっとかっこいいと思っていましたし、実は日頃の業務でテストデータを作成する際にも、この車種をよく使っていて、ちょっとした愛着があります（笑）。</li>
<li>また、社員割引の福利制度を活用すれば、非常に手頃な価格でクラウンに乗れるのも魅力的です。さらに、充実した保険制度など、お客様にも優しいサービスが揃っていて、本当に素晴らしいと感じています。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>まきどん</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/avatar.jpg" alt="Yuya"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>12月入社のまきどんです。モビリティプロダクト開発部のマーケティングプロダクト開発Gに所属しています。業務では分析・機械学習周りを担当しています。主な役割は、データから課題を抽出し施策立案や出口戦略のご提案、機械学習を用いたシステムの設計等でサポートさせて頂いています。</li>
<li>前職ではスタートアップで建築×ITを主軸とした事業のPMを担当していました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>私が所属しているデータ分析・ML活用チームはPM/PdM1名、スクラムマスター1名、エンジニア6名の計8名で構成されています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>第一印象は大企業の傘下のため、やはり堅実なイメージがありました。しかし入社してみるとslackに生成AIが活用されていたり、システムにもAI活用を推進していたりと、想定していたよりずっとテックカンパニーかつベンチャー気質のあるスピード感を持った会社だと感じました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>同じチーム内はもちろんのこと、他部署でも悩んだり困ったことがあればすぐに相談でき、安心して働ける環境です。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>実はエントリーより先に<a href="https://blog.kinto-technologies.com/posts/2025-02-25-cursor-vs-copilot/">テックブログ</a>を執筆させていただきました。ブログ自体書いた経験が無かったのですが、グループ内で相談・協力してくれたので不安なく書けたのが非常に良い経験になりました。今後も新しい知見や経験を社内外に展開していけるよう頑張ります！</li>
</ul>
</li>
<li><strong>呂さん → まきどんへの質問</strong><ul>
<li><strong>これまでの業務の中で、最も誇りに思っていることは何ですか?</strong><ul>
<li>機械学習ツールを用いてアウトプットしていたものを内製化することで、コストダウンとクリック率UPを実現できました！</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>ニーゼン フランク(Frank Neezen)</h1>
<p><img src="/assets/blog/authors/yuya_sakamaki/newcomers/Frank.png" alt="Frank"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>ビジネスディベロップメントグループのフランク・ニーゼンです。</li>
<li>正式にはビジネス開発マネージャーという肩書きですが、主な役割はテクニカルアーキテクトとして、グローバルフルサービス製品の設計と実装を支援することです。</li>
<li>私のバックグラウンドはコンサルティングで、Salesforceを活用してお客様の技術的・運用的なニーズに応えるためのアドバイスに注力してきました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>体制は自分含め、さまざまなスキルを持つ4人のメンバーで構成されています。開発チームと密に連携し、グローバル向けのフルサービスリース事業ソフトウェアソリューションを開発しています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>アムステルダムのSalesforceから東京のKTCへの異動は驚くほどスムーズでした！当初は文化の違いに慣れることができるか不安もありましたが、素晴らしいオリエンテーションと神保町オフィスの心強いメンバーにより全く問題ありませんでした。初日から温かく歓迎いただいたおかげで、スムーズに馴染むことができました。どちらかと言いますと、一番苦労したのは日本語がまだ話せないため、例えば銀行の手続きや住民登録など、生活の準備を整えることでした。でもKTCのメンバーにはたくさん助けてもらえましたのでなんとかうまくいきました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>我々チームは全員神保町オフィス在中でして、多くのエンジニアと隣り合わせに座っています。雰囲気はオープンでプロフェッショナルですが、リラックスできる環境です。良いチーム感があり、みんな自分の仕事を成功させたいと思っているメンバーばかりです。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>過去にSalesforce関連の記事を書いたことがあります。KTCに入社するまでの個人的なストーリーを書いたりなど、共有できるところは喜んでシェアしていきたいと思います！</li>
</ul>
</li>
<li><strong>まきどん → フランクさんへの質問</strong><ul>
<li><strong>日本へ来て驚いたことがあれば教えて下さい！</strong><ul>
<li>日本の安全さには本当に驚かされます。世界最大の都市である東京のどこを歩いていても、全く不安を感じません！さらに本当に驚くべきことは、財布や携帯電話などをなくしても、ほとんどの場合手元に戻ってくることです。自分がなくしたことにすら気づいていなかったのに、誰かが私のところに来て、失くした物を届けてくれたことも何度かありました。本当に新鮮で素晴らしい体験です！</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTO テクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Want to Load a Local JSON in a Flutter Multi-Package? Here’s How!]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-23-MobileAdventCalendar-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-23-MobileAdventCalendar-en/</guid>
            <pubDate>Tue, 22 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This is a story about dealing with asset loading in Flutter projects that use a multi-package structure.]]></description>
            <content:encoded><![CDATA[<h2>Introduction</h2>
<p>Managing assets in a multi-package Flutter project can get tricky, especially when it comes to loading local JSON files. This requires a different approach than a normal single package project, leaving developers scratching their heads. In this article, we’ll break down how to load local JSON files effectively in a multi-package Flutter project.</p>
<p>This article is the entry for day 23 in the<a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>🎅🎄</p>
<h2>Test Project Setup</h2>
<p>For this study, we prepared a simple project managed with multiple packages. Here’s how it’s structured:</p>
<pre><code>🎯 Dart SDK: 3.5.4
🪽 Flutter SDK: 3.24.5
🧰 melos: 6.2.0
</code></pre>
<pre><code>├── .github
├── .vscode
├── app/
│   ├── android/
│   ├── ios/
│   ├── lib/
│   │   └── main.dart
│   └── pubspec.yaml
├── packages/
│   ├── features/
│   │   ├── assets/
│   │   │   └── sample.json
│   │   ├── lib/
│   │   │   └── package1.dart
│   │   └── pubspec.yaml
│   ├── .../
├── analysis_options.yaml
├── melos.yaml
├── pubspec.yaml
└── README.md
</code></pre>
<h2>Load a File in Asset</h2>
<p>You’ll often see explanations showing that, in a typical single-package project, you can load assets like this:</p>
<pre><code class="language-yaml:pubspec.yaml">flutter:
  assets:
    - assets/ # Specify the assets folder
</code></pre>
<pre><code class="language-dart">import &#39;package:flutter/services.dart&#39; show rootBundle;

Future&lt;String&gt; loadAsset() async {
  return await rootBundle.loadString(&#39;assets/sample.json&#39;);
}
</code></pre>
<p>The official explanation is pretty much the same.
<a href="https://docs.flutter.dev/ui/assets/assets-and-images">https://docs.flutter.dev/ui/assets/assets-and-images</a></p>
<p>I built a simple Widget that simply reads a JSON file from an Asset and displays it as a String in a Text widget.</p>
<pre><code class="language-dart">import &#39;dart:convert&#39;;

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter/services.dart&#39; show rootBundle;

class LocalAssetPage extends StatefulWidget {
  const LocalAssetPage({super.key});

  @override
  LocalAssetPageState createState() =&gt; LocalAssetPageState();
}

class LocalAssetPageState extends State&lt;LocalAssetPage&gt; {
  String _jsonContent = &#39;&#39;;

  @override
  void initState() {
    super.initState();
    _loadJson();
  }

  Future&lt;void&gt; _loadJson() async {
    final response = await rootBundle.loadString(&#39;assets/sample.json&#39;);
    final data = await json.decode(response);
    setState(() {
      _jsonContent = json.encode(data);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(&#39;Local Asset Page&#39;),
      ),
      body: Center(
        child: _jsonContent.isEmpty
            ? const CircularProgressIndicator()
            : Text(_jsonContent),
      ),
    );
  }
}
</code></pre>
<p>But this method doesn’t work for loading assets across multiple packages.</p>
<p><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_23/02.png" alt="Asset load error!"></p>
<p><br><br><br>
🤨
<br><br><br></p>
<h2>Loading Assets with flutter_gen</h2>
<p>In most cases, loading Assets across multiple packages can be handled easily with flutter_gen. flutter_gen is a tool that generates code from Asset paths and localization files, enabling type-safe asset loading. It also natively supports loading Assets across multiple packages.</p>
<p><a href="https://github.com/FlutterGen/flutter_gen">https://github.com/FlutterGen/flutter_gen</a></p>
<p>To load Assets from multiple packages with flutter_gen, the following settings are required.</p>
<pre><code class="language-yaml:pubspec.yaml">flutter_gen:
  assets:
      outputs:
        package_parameter_enabled: true
</code></pre>
<p>With this setting, running flutter_gen will generate code for loading assets from multiple packages.</p>
<p><img src="/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_23/03.png" alt="flutter_gen generated codes"></p>
<p>You can then use this generated code to load Assets in a type-safe way. Here’s how the previous example looks when rewritten using flutter_gen:</p>
<pre><code class="language-dart">import &#39;package:{YOUR_PACKAGE_NAME}/gen/assets.gen.dart&#39;;

Future&lt;String&gt; loadAsset() async {
  return await rootBundle.loadString(Assets.sample);
}
</code></pre>
<p>Assets from multiple packages can be loaded with code like this:</p>
<pre><code class="language-diff">import &#39;dart:convert&#39;;

+ import &#39;package:feature_flutter_gen_sample/gen/assets.gen.dart&#39;;
import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter/services.dart&#39; show rootBundle;

class FlutterGenSamplePage extends StatefulWidget {
  const FlutterGenSamplePage({super.key});

  @override
  FlutterGenSamplePageState createState() =&gt; FlutterGenSamplePageState();
}

class FlutterGenSamplePageState extends State&lt;FlutterGenSamplePage&gt; {
  String _jsonContent = &#39;&#39;;

  @override
  void initState() {
    super.initState();
    _loadJson();
  }

  Future&lt;void&gt; _loadJson() async {
+   final response = await rootBundle.loadString(Assets.sample);
-   final response = await rootBundle.loadString(&#39;assets/sample.json&#39;);
    final data = await json.decode(response);
    setState(() {
      _jsonContent = data.toString();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(&#39;FlutterGen Sample&#39;),
      ),
      body: Center(
        child: _jsonContent.isNotEmpty
            ? Text(_jsonContent)
            : const CircularProgressIndicator(),
      ),
    );
  }
}
</code></pre>
<p>The file paths are structured, which makes things much neater! Asset management for team development is now a breeze.</p>
<p>But sometimes, you might prefer not to rely on tools too much, right? Let’s look at how to handle that next.</p>
<h2>Loading Assets without flutter_gen</h2>
<p>Of course, you can also load Assets from multiple packages without flutter_gen. In that case, you can load Assets by specifying the path as follows.</p>
<p>:::message
<strong>packages/<em>{package name}</em>/<em>{folder path}</em>/file name</strong>
:::</p>
<ul>
<li><em><strong>Package name</strong></em> is the name specified in the pubspec.yaml of the package in which the asset is stored.</li>
<li><em><strong>Folder path</strong></em> is the path specified under assets in that Package&#39;s pubspec.yaml.</li>
</ul>
<pre><code class="language-yaml:pubspec.yaml">name: local_asset

...

flutter:
  assets:
    - assets/
</code></pre>
<pre><code class="language-diff">import &#39;dart:convert&#39;;

import &#39;package:flutter/material.dart&#39;;
import &#39;package:flutter/services.dart&#39; show rootBundle;

class LocalAssetPage extends StatefulWidget {
  const LocalAssetPage({super.key});

  @override
  LocalAssetPageState createState() =&gt; LocalAssetPageState();
}

class LocalAssetPageState extends State&lt;LocalAssetPage&gt; {
  String _jsonContent = &#39;&#39;;

  @override
  void initState() {
    super.initState();
    _loadJson();
  }

  Future&lt;void&gt; _loadJson() async {
+   final response = await rootBundle.loadString(&#39;packages/local_asset/assets/sample.json&#39;);
-   final response = await rootBundle.loadString(&#39;assets/sample.json&#39;);
    final data = await json.decode(response);
    setState(() {
      _jsonContent = json.encode(data);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(&#39;Local Asset Page&#39;),
      ),
      body: Center(
        child: _jsonContent.isEmpty
            ? const CircularProgressIndicator()
            : Text(_jsonContent),
      ),
    );
  }
}
</code></pre>
<p>This way, you can load Assets from multiple packages without flutter_gen. For small projects or solo development, this approach should work just fine.</p>
<p>Not gonna lie; without fully understanding these path rules, there was a lot of trial and error: trying relative paths, tweaking settings... And hitting build errors over and over. It was a struggle.</p>
<h3>About Paths Specified in pubspec.yaml</h3>
<p>When managing assets locally, pay close attention to the paths you specify in pubspec.yaml. Assets paths are set relative to pubspec.yaml, but watch out for differences between:</p>
<ul>
<li><code>/assets</code> and</li>
<li><code>/assets/{subfolder}</code>.</li>
</ul>
<p>For example, if you move a JSON file into a subfolder like this:</p>
<pre><code>├── packages/
│   ├── features/
│   │   ├── assets/
│   │   │   └── jsons/
│   │   │       └── sample.json  &lt;&lt;&lt; HERE
│   │   ├── lib/
│   │   │   └── package1.dart
│   │   └── pubspec.yaml
│   ├── .../
</code></pre>
<p>I assumed that specifying <code>/assets</code> would also include files in <code>/assets/{subfolder}</code>, but even after changing the loadString path to <code>packages/local_asset/assets/jsons/sample.json</code>, it still wouldn&#39;t load. When you move files into a subfolder, you need to explicitly include that subfolder in pubspec.yaml, like this:</p>
<pre><code class="language-yaml:pubspec.yaml">flutter:
  assets:
    - /assets/jsons/
</code></pre>
<p>Now, you can load <code>packages/local_asset/assets/jsons/sample.json</code>. If you manage assets in subfolders, make sure to add those subfolders to pubspec.yaml.</p>
<p>By the way, the examples so far have used <code>assets</code> folder, but you can also change the name of it. As long as the pubspec.yaml and actual path configuration match, assets can also be loaded without any problems from folders other than <code>assets</code> folder.</p>
<h2>Summary</h2>
<p>This time, we covered how to load local JSON files in a Flutter multi-package setup. Since my main focus is iOS development, I thought it would be easy, but it turned out to be more challenging than expected. There’s limited information available on working in multi-package environments, so I&#39;d like to share more insights as I continue learning in the future.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/HiroyaHinomori/mobile_advent_calendar_2024_12_23/01_en.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[iOS] すぐできる！CIクレジット超ドケチ節約術]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-21-SavingCICreditTips/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-21-SavingCICreditTips/</guid>
            <pubDate>Mon, 21 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[my routeアプリ(iOS)におけるCIクレジットの節約術をご紹介]]></description>
            <content:encoded><![CDATA[<p>KINTOテクノロジーズで my route(iOS) を開発しているRyommです。
CIクレジットの節約のため、 いくつか取り組んできたことを紹介します。</p>
<h1>はじめに</h1>
<p>弊プロジェクトにおいては、CIツールとしてBitriseを利用しています。
昨年は通常のユニットテストに加えて <a href="https://blog.kinto-technologies.com/posts/2024-04-17-SnapshotTest/">スナップショットテストを導入</a> したり、 <a href="https://blog.kinto-technologies.com/posts/2024-12-24-myroute-ios-spm/">SPMに移行</a> したりしました。
気付くとBitrise CIの1回あたりの実行時間が多いときは約25分ほどに膨れ上がり、多くの実装が行われた月は予算を超過してしまうこともしばしば発生するようになってしまいました。
Bitriseは契約分を超過すると高額になってしまうので、執筆時点の為替レートでは超過分のCI1回の実行に約400円ほど掛かっている計算です。たっっか！
そういうわけで、クレジットが超過しそうになるとCIを動かさないためにPRのマージも必要最低限に抑える動きが生まれてしまっていました。</p>
<p>この状況を打破すべく取り組んできた、弊プロジェクトにおけるクレジット節約術を紹介します。</p>
<h1>CLIツールのセットアップを見直す</h1>
<p>BitriseのBuild結果を見ると、どのステップにどのくらい時間が掛かったのか見ることができます。</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/01.png" alt="BitriseのBuild結果(Before)">
<em>BitriseのBuild結果</em></p>
<p>これを見ると、「Script Runner」にて12分も掛かっていることがわかります。
これは、swiftLintやLicensePlistのセットアップを行っているステップです。<a href="https://blog.kinto-technologies.com/posts/2024-12-24-myroute-ios-spm/#swiftlint%E3%82%84licenseplist%E3%82%92spm%E3%81%AB%E4%B9%97%E3%81%9B%E3%82%8B">以前執筆した記事</a>にて紹介しましたが、Build Phaseで実行するためにworkspaceとは独立して作成したパッケージにて、ライブラリを落として使えるようにしています。</p>
<p>ま〜たしかにこれは時間かかるよね〜というところなので、短縮していきます。
幸いここで使いたいライブラリはBuild Tool Pluginに対応しているため、そちらに移し替えていくことでこのステップを省くことができます。</p>
<p>元々 license_plist.yml や .swiftlint.yml などの設定は済んでいるため、ただプロジェクトの Package Dependencies にパッケージを追加し、ターゲットの Build Phase の Run Build Tool Plug-ins にプラグインを追加してあげればOKです。</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/02.png" alt="Build Phaseの設定">
<em>Build Phaseの設定</em></p>
<p>LicensePlistはプラグインだと <code>outputPath</code> の場所の指定が効かないため、<a href="https://github.com/mono0926/LicensePlist?tab=readme-ov-file#integrate-into-build---build-tool-plugin">README</a>にあるように Settings.bundle の下にライセンスのファイルを移動するようBuildPhaseに含める必要があります。
また、パッケージはFrameworksでリンクしているパッケージではなく、アプリ本体に含める必要があります。</p>
<p>これで「Script Runner」のステップが丸々不要になったので、12分の短縮...そして消費クレジットも半分になりました！🎉
<img src="/assets/blog/authors/ryomm/2025-04-21/03.png" alt="BitriseのBuild結果(After)">
<em>BitriseのBuild結果</em></p>
<p>さらにプロジェクト構成が単純化し、セットアップやバージョン更新の際に別途シェルを実行するような必要もなくなりました。</p>
<p>今回のケースでは全てBuild Tool Pluginに対応していたため構成ごと変更しましたが、別のアプローチとして <a href="https://github.com/mtj0928/nest">nest</a> も試しました。こちらは既存のCLIツールを別パッケージに分けて管理する構成のまま、CI時間を短縮させることができます。</p>
<p>tools ディレクトリ配下にあったCLIツールをインストールするためのパッケージをnestに置き換えます。</p>
<pre><code>Project/
├── Hoge.xcworkspace
├── Hoge.xcodeproj
├── Test/
│   └── ...
├── ...
└── tools
    └── nextfile.yaml // ここを置き換える
</code></pre>
<p><code>nest bootstrap nestfile.yaml</code> を実行すると <code>tools/.nest/bin</code> 内にバイナリがインストールされていることが確認できるので、これを Build Phase で実行されるように設定します。</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/04.png" alt="nestを使ったswiftlintの実行">
<em>Build Phaseでswiftlintを設定</em></p>
<p>Build Tool Plugin に対応していなかったりする場合には有用かもしれません。</p>
<h1>テストを見直す</h1>
<p>弊プロジェクトでは、1つのテストターゲット内に全てのテストが詰め込まれていたため、常に全てのテストが実行されていました。</p>
<p>またその中でもスナップショットテストは全て実行すると1時間ほど掛かる非常に重いテストのため、リファレンス画像と比較するメソッドはCI上ではコメントアウトして実行されないようにしていました。しかし、比較前の非同期の描画処理の待機などは実行されてしまうため、失敗時はタイムアウトが積もり積もって長い時間待つこととなり、これもクレジットを食い潰す要因の一つとなっていました。</p>
<p>そこで、CI上では動かしていないスナップショットテストを別のテストターゲットへ切り離し、TestPlanを使って実行するテストをコントロールするようにしてみました。</p>
<p>まずは、時間のかかるスナップショット用のテストターゲットを作成します。
<img src="/assets/blog/authors/ryomm/2025-04-21/05.png" alt="テストターゲットの作成">
<em>テストターゲットを作成</em></p>
<p>既存のテストターゲットを参考にターゲットの設定を行ったら、 Build Phases の Compile Sources もしくは各テストファイルの File inspector より Target Membership から、スナップショットテストを新規作成したターゲットへ移していきます。
このとき、移動したテストファイルが元のテストターゲット内のテストファイルと依存関係があるとビルドできなくなるため、都度依存を切り離していきます。</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/06.png" alt="ターゲットを移し替える">
<em>ターゲットを変更していく</em></p>
<p>次に、TestPlanを作成します。
TestPlanとは、実行するテストとテストの設定をまとめたものです。その際、実行するテストを指定できる範囲はテストターゲット単位です。
このためにテストターゲットを分離させました。</p>
<p>TestPlanはスキーマに紐づけることができ、弊アプリではスキーマとTestPlanが1対1になるようにしています。
そしてCI上で使用するためのスキーマ用のTestPlanにおいては、スナップショットテストを実行しないようにします。</p>
<p><img src="/assets/blog/authors/ryomm/2025-04-21/07.png" alt="TestPlanの設定">
<em>TestPlanの設定</em></p>
<p>実際に動かしてみると、CI上では失敗しない限り大きくは実行時間は変わりません。しかし、ローカルにおけるテスト体験は大きく改善しました。ロジックのみの変更でもスナップショットテストが実行されてしまっていたのが、チェックを外すだけで実行しないようにできるため、大幅な時間短縮が実現しました。</p>
<p>元々CI上では動かしていなかったテストをそもそも実行しないようにした形ですが、クレジット利用状況とのバランスを調整した上でスナップショットテストも実行できるようにしていきたいと考えています。</p>
<h1>おわりに</h1>
<p>ここに紹介したもの以外にも、型推論に時間が掛かっているコードを修正したり、使用していないアセットを削除することなどもビルド時間の短縮につながります。
これらの取り組みによってCI1回あたりの時間は平均約22分程度から12分程度まで短縮され、45%ほどのクレジット節約が実現しました。</p>
<p>今回はすぐにできる範囲としてビルド前後の時間短縮が主でしたが、次はビルド自体の時間ももっと短縮したいです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ryomm/2025-04-21/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Introducing the Internal Slack Chatbot Leveraging LLM]]></title>
            <link>https://blog.kinto-technologies.com/posts/torii-ai_tool_slack-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/torii-ai_tool_slack-en/</guid>
            <pubDate>Mon, 21 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article introduces AI tool, our internal chatbot that leverages LLM within Slack. We will explore its RAG capabilities and its translation function, which utilizes Slack reactions. Internal generative AI tool is designed for rapid development without requiring frontend development. The goal is to enable employees to seamlessly integrate generative AI into their daily Slack interactions, enhancing efficiency and collaboration.]]></description>
            <content:encoded><![CDATA[<h2>1. Introduction</h2>
<p>Hello, this is Torii (<a href="https://x.com/yu_torii">@yu_torii</a>) from the Common Service Development Group. I&#39;m a fullstack software engineer, primarily working on both backend and frontend development. As part of the KINTO Member Platform Development Team, I focus on frontend engineering while also contributing to internal initiatives involving generative AI.</p>
<p>This article introduces our internal generative AI tool, our internal chatbot powered by LLM and integrated into Slack. We’ll explore its RAG capabilities and its translation function, which works through Slack reactions.</p>
<hr>
<p>Internal generative AI tool is designed to facilitate generative AI adoption within the company by enabling employees to naturally interact with AI in their daily Slack conversations. The goal is to eliminate the need for frontend development and deploy it quickly within the company, enhancing efficiency and collaboration. Internal generative AI tool aims to be a dependable assistant for improving work efficiency and streamlining information sharing within the company.</p>
<p>By the way, the character was generated by team members of the Creative Group as a surprise reveal for the KINTO Technologies General Meeting (KTC CHO All-Hands Meeting). A big thank you to them!</p>
<hr>
<p>This initiative was carried out in collaboration with Wada-san (<a href="https://x.com/cognac_n">@cognac_n</a>), who is leading the Generative AI Development Project. We have been driving the adoption of generative AI within the company through various improvements, such as introducing a RAG pipeline, setting up a local development environment, and implementing a translation and summarization feature using Slack emoji reactions.</p>
<p>:::details Click here for Wada-san&#39;s article</p>
<p>@<a href="https://blog.kinto-technologies.com/posts/2024-01-26-GenerativeAIDevelopProject-en/">card</a><br>@<a href="https://blog.kinto-technologies.com/posts/2024-08-20-prompty-review-en/">card</a><br>@<a href="https://www.wantedly.com/companies/company_7864825/post_articles/878277">card</a></p>
<p>:::</p>
<p>Note that this article does not go into detail about RAG or generative AI technology itself. Instead, it focuses on the implementation process and feature enhancements. Additionally, Internal generative AI tool&#39;s LLM runs on Azure OpenAI.</p>
<h2>What You Will Gain from This Article</h2>
<ul>
<li><p><strong>How to use chat functions powered by generative AI in a Slack Bot</strong> 
This article introduces an implementation example of a chatbot that combines Slack and LLM, allowing users to trigger translations and summaries using emoji reactions or natural message posts.</p>
</li>
<li><p><strong>Techniques for retrieving Confluence data, including HTML sanitization</strong> 
Learn how to fetch Confluence documents using Go and sanitize HTML to prepare text for summarization and embedding.</p>
</li>
<li><p><strong>Implementing a simple RAG pipeline using FAISS and S3</strong>
This section explains the steps and considerations for setting up a simple RAG pipeline using the FAISS indexing and S3. While the response speed may not be very fast, this approach provides a cost-effective way to integrate a basic RAG system.</p>
</li>
</ul>
<p>:::message</p>
<ul>
<li>This article is based on the implementation status at the time of writing. Please note that there are still many areas for improvement.</li>
<li>Since this is an internal tool, it is not designed for production-level performance.</li>
<li>Additionally, this article does not cover details on setting up the development environment, such as deployment with AWS SAM, building a pipeline with Step Functions, or configuring CI/CD with GitHub Actions.</li>
</ul>
<p>:::</p>
<p>:::details Table of Contents (Expanded)</p>
<ul>
<li><a href="#1-%E3%81%AF%E3%81%98%E3%82%81%E3%81%AB">1. Introduction</a>  </li>
<li><a href="#%E3%81%93%E3%81%AE%E8%A8%98%E4%BA%8B%E3%81%A7%E5%BE%97%E3%82%89%E3%82%8C%E3%82%8B%E3%81%93%E3%81%A8">What You Will Gain from This Article</a>  </li>
<li><a href="#2-%E5%85%A8%E4%BD%93%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%AE%E6%A6%82%E8%A6%81">2. Overview of the Overall Architecture</a>  </li>
<li><a href="#3-slack-bot%E3%81%AB%E3%82%88%E3%82%8B%E7%94%9F%E6%88%90ai%E3%82%92%E6%B4%BB%E7%94%A8%E3%81%97%E3%81%9F%E3%83%81%E3%83%A3%E3%83%83%E3%83%88%E6%A9%9F%E8%83%BD">3. AI-Powered Chat Function Using Slack Bot</a>  <ul>
<li><a href="#%E3%83%81%E3%83%A3%E3%83%83%E3%83%88%E6%A9%9F%E8%83%BD%E3%81%A8%E3%83%AA%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E6%A9%9F%E8%83%BD">Chat Function and Reaction Function</a>  </li>
<li><a href="#%E3%83%81%E3%83%A3%E3%83%83%E3%83%88%E6%A9%9F%E8%83%BD%E5%88%A9%E7%94%A8%E3%82%B7%E3%83%8A%E3%83%AA%E3%82%AA">Chat Function: Usage Scenario</a>  </li>
<li><a href="#%E3%83%AA%E3%82%A2%E3%82%AF%E3%82%B7%E3%83%A7%E3%83%B3%E6%A9%9F%E8%83%BD%E5%88%A9%E7%94%A8%E3%82%B7%E3%83%8A%E3%83%AA%E3%82%AA">Reaction Feature: Usage Scenario</a>  </li>
<li><a href="#%E3%81%93%E3%81%AE%E6%A7%8B%E6%88%90%E3%81%AE%E3%83%A1%E3%83%AA%E3%83%83%E3%83%88">Benefits of This Configuration</a>  </li>
<li><a href="#35-%E5%AE%9F%E9%9A%9B%E3%81%AE%E5%88%A9%E7%94%A8%E4%BE%8B">3.5 Actual Use Cases</a>  </li>
<li><a href="#3%E7%AB%A0%E3%81%BE%E3%81%A8%E3%82%81">Summary of Chapter 3</a></li>
</ul>
</li>
<li><a href="#4-%E5%AE%9F%E8%A3%85%E6%96%B9%E9%87%9D%E3%81%A8%E5%86%85%E9%83%A8%E8%A8%AD%E8%A8%88">4. Implementation Policy and Internal Design</a>  <ul>
<li><a href="#%E5%85%A8%E4%BD%93%E7%9A%84%E3%81%AA%E5%87%A6%E7%90%86%E3%83%95%E3%83%AD%E3%83%BC">Overall Process Flow</a>  </li>
<li><a href="#%E6%9D%A1%E4%BB%B6%E5%88%86%E5%B2%90%E3%81%AB%E3%82%88%E3%82%8B%E6%A9%9F%E8%83%BD%E5%88%A4%E5%AE%9A">Function Determination by Conditional Branching</a>  </li>
<li><a href="#%E3%82%B5%E3%83%8B%E3%82%BF%E3%82%A4%E3%82%BA%E3%81%AE%E5%BD%B9%E5%89%B2">The Role of Sanitization</a>  </li>
<li><a href="#rag%E5%88%A9%E7%94%A8%E3%82%92confluence%E5%8F%82%E7%85%A7%E3%81%AB%E9%99%90%E5%AE%9A">Limiting RAG Usage to Confluence References</a>  </li>
<li><a href="#%E6%8B%A1%E5%BC%B5%E6%80%A7%E4%BF%9D%E5%AE%88%E6%80%A7%E3%81%B8%E3%81%AE%E8%80%83%E6%85%AE">Considerations for Scalability and Maintainability</a></li>
</ul>
</li>
<li><a href="#5-%E3%82%B3%E3%83%BC%E3%83%89%E4%BE%8B%E3%81%A8%E8%A8%AD%E5%AE%9A%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E7%B4%B9%E4%BB%8B">5. Introduction to Code examples and Configuration Files</a>  <ul>
<li><a href="#51-go-slack%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E5%8F%97%E4%BF%A1%E3%81%A8%E8%A7%A3%E6%9E%90">5.1 [Go] Receiving and Parsing Slack Events</a>  </li>
<li><a href="#52-go-html%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88%E3%81%AE%E3%82%B5%E3%83%8B%E3%82%BF%E3%82%A4%E3%82%BA">5.2 [Go] HTML Text Sanitization</a>  </li>
<li><a href="#53-python-llm%E5%95%8F%E3%81%84%E5%90%88%E3%82%8F%E3%81%9B%E3%81%AE%E5%85%B7%E4%BD%93%E4%BE%8B">5.3 [Python] Example of an LLM Query</a>  </li>
<li><a href="#54-python-rag%E6%A4%9C%E7%B4%A2%E5%91%BC%E3%81%B3%E5%87%BA%E3%81%97%E4%BE%8B">5.4 [Python] Example of a RAG Search Call</a>  </li>
<li><a href="#55-python-embedding%E3%81%A8faiss%E3%82%A4%E3%83%B3%E3%83%87%E3%83%83%E3%82%AF%E3%82%B9%E5%8C%96">5.5 [Python] Embedding and FAISS Indexing</a></li>
</ul>
</li>
</ul>
<p>:::</p>
<hr>
<h2>2. Overview of the Overall Architecture</h2>
<p>Here, we will explain the process by which internal generative AI tool returns answers using generative AI from two perspectives: - The generative AI chat function with users - The process of indexing Confluence documents.</p>
<h3>Processing Generative AI Chat Functions with Users</h3>
<ol>
<li><p><strong>Calling internal generative AI tool on Slack</strong> There are two ways to call internal generative AI tool: via chat or with a reaction emoji. Users can call internal generative AI tool by mentioning it in a channel or sending a direct message to request a generative AI response. A reaction call allows you to request a translation or summary of a message by adding a translation/summary emoji reaction.</p>
</li>
<li><p><strong>Go Slack Bot Lambda</strong><br>The bot that handles Slack events is built with Go and runs on AWS Lambda. It receives questions and reactions, then determines processing based on the request type. When using Azure OpenAI for LLM queries or retrieving embedded data, this component generates requests and forwards them to Python Lambda.</p>
</li>
<li><p><strong>Request to LLM and RAG reference in Python Lambda</strong><br>Python Lambda is divided into functions responsible for LLM queries, RAG references, and related processes. It receives requests from Go Lambda, queries the LLM, and generates an answer using RAG.</p>
</li>
<li><p><strong>Returning an answer to Slack</strong> The generated answer is sent back to Slack via Go Slack Bot Lambda. The translation and summary functions that can be invoked via emoji reactions are also integrated into this flow.</p>
</li>
</ol>
<p>:::details Architecture diagram (processing user requests)</p>
<pre><code class="language-mermaid">flowchart LR
    subgraph &quot;Processing user requests&quot;
    A[&quot;User(Slack)&quot;] --&gt;|&quot;Question・Emoji&quot;| B[&quot;Go Slack Bot(Lambda)&quot;]
    B --&gt;|&quot;Request&quot;| C[&quot;Python Lambda RAG/LLM&quot;]
    C --&gt;|&quot;Answer&quot;| B
    B --&gt;|&quot;Answer&quot;| A
    end
</code></pre>
<p>:::</p>
<h3>The Process of Indexing Confluence Documents</h3>
<p>To use the RAG pipeline, you need to prepare your Confluence documents in a way that makes them easy to summarize and embed. These preprocessing steps are structured into workflows using StepFunctions and are executed automatically on a scheduled basis.</p>
<ul>
<li><p><strong>Document retrieval and HTML sanitization (Go implementation)</strong><br>A Lambda function implemented in Go retrieves documents from the Confluence API, cleans up HTML tags, and makes the text more manageable.<br>The sanitized text is output as JSON.</p>
</li>
<li><p><strong>Summary processing (Go + Python Lambda invocation)</strong><br>Summarization aims to refine the text, making it easier to process with Embedding and RAG.<br>The Go implementation of Lambda invokes a Python Lambda that processes requests to the Azure OpenAI Chat API, shortens the text, and converts it back to JSON.</p>
</li>
<li><p><strong>FAISS indexing and S3 storage (Indexer Lambda)</strong><br>The Indexer Lambda embeds the summarized text and generates a FAISS index, then stores the index and meta information in S3.<br>This enables instant retrieval of indexed data upon a query, ensuring the RAG pipeline runs smoothly.</p>
</li>
</ul>
<p>:::details Architecture diagram (Confluence document indexing flow)</p>
<pre><code class="language-mermaid">flowchart LR
    subgraph &quot;Indexing Preparation StepFunctions&quot;
    D[&quot;Go Lambda (Document Retrieval/HTML Sanitization)&quot;]
    D --&gt; E[&quot;Go Lambda (summary/Python call)&quot;]
    E --&gt; F[&quot;Indexer Lambda(Embedding/FAISS/S3)&quot;]
    end
</code></pre>
<p>:::</p>
<p>By combining this pre-processing with request-time processing, internal generative AI tool enables generative AI responses that incorporate company-specific knowledge with simple operations in Slack. In the following chapters, we&#39;ll dive deeper into these components.</p>
<hr>
<h2>3. AI-Powered Chat Function Using Slack Bot</h2>
<p>The previous chapter provided an overview of the architecture. In this chapter, we’ll focus on how users can make use of internal generative AI tool’s features with simple, natural interactions in Slack, and how these features can benefit them.<br>Here, we will illustrate what internal generative AI tool can do and which scenarios it can be useful in, while the next chapters will systematically explain the implementation details.</p>
<h3>Chat Function and Reaction Function</h3>
<p>internal generative AI tool offers a variety of generative AI capabilities, powered by natural interactions within Slack.</p>
<ul>
<li><p><strong>Chat function</strong>:<br>By posting a question, attaching files or images, including external links, or sending a message in a specific format, users can trigger LLM queries or, in certain cases, RAG searches to obtain relevant answers.<br>By integrating AI into Slack, an everyday tool, users can seamlessly adopt generative AI without the need to learn new environments or commands.</p>
</li>
<li><p><strong>Reaction function</strong>:<br>By simply adding specific emoji reactions to a message, users can trigger translations, delete messages, and perform other actions—enabling additional operations without requiring commands.</p>
</li>
</ul>
<h3>Chat Function: Usage Scenario</h3>
<ol>
<li><p><strong>Basic Questions and Answers</strong><br>Simply posting a question allows users to receive AI-generated responses through LLM.  </p>
<ul>
<li>Example scenario:<br>Asking &quot;What are the steps for this project?&quot; provides an instant answer, taking into account thread history and speaker context for a more accurate response.</li>
</ul>
</li>
<li><p><strong>File, image, and external link processing</strong><br>Attaching a file and asking &quot;Summarize this&quot; will generate a summary of the document.<br>Uploading an image allows internal generative AI tool to extract text and provide relevant answers.<br>Sharing an external link enables the bot to analyze and summarize webpage content, incorporating it into the LLM-generated response.  </p>
<ul>
<li>Example scenario:<br>-Get a short summary of meeting notes from a text file. -Extract text information from an image. -Generate a concise summary of an external article.</li>
</ul>
</li>
<li><p><strong>Confluence page lookup (RAG integration)</strong><br><code>:confluence: By using index:Index name</code>, users can perform a RAG-based search on Confluence pages containing company documentation, such as internal rules and application procedures.  </p>
<ul>
<li>Example scenario:<br>If company policies and procedures are documented in Confluence, users can instantly access relevant information tailored to internal workflows.</li>
<li>Example scenario:<br> Easily retrieve project-specific settings and instructions. that would otherwise be difficult to search for.</li>
</ul>
</li>
</ol>
<h3>Reaction Feature: Usage Scenario</h3>
<p>Adding specific emoji reactions makes calling up functions even more intuitive.</p>
<ul>
<li><p><strong>Translation</strong>:<br>Adding a translation emoji automatically translates the message into the specified language, helping break down language barriers and improve communication.</p>
</li>
<li><p><strong>Message deletion</strong><br>Unnecessary internal generative AI tool responses can be deleted instantly by adding a single emoji, keeping Slack channels organized.</p>
</li>
</ul>
<h3>Benefits of This Configuration</h3>
<ul>
<li><p><strong>Seamless and intuitive AI usage</strong>:<br>Users can use AI without needing new commands—they simply interact with Slack as they normally do (e.g., posting messages, adding emojis reactions), reducing learning costs.</p>
</li>
<li><p><strong>Embedding AI into everyday tool (Slack)</strong>:<br>By integrating generative AI directly into Slack, AI can be naturally incorporated into daily workflows without friction. Additionally, reaction-based interactions enable users to perform actions like translation and deletion without typing commands, making AI even more accessible.</p>
</li>
<li><p><strong>Scalable and easily extendable</strong>:<br>If new models or additional features need to be introduced, the existing flow (chat-based queries and emoji interactions) can be easily expanded by adding conditions.</p>
</li>
</ul>
<h3>3.5 Actual Use Cases</h3>
<p>Below are some real-world examples of how internal generative AI tool is used within Slack.</p>
<h4>Example 1: Image Recognition</h4>
<p>When you attach an image to a message, internal generative AI tool recognizes the text within the image and responds with its content.</p>
<p>![Image Recognition](/assets/blog/authors/torii/2024-12-23_ai_tool_slack/images/image-context.png =500x) <em>Image Recognition</em></p>
<h4>Example 2: Answers Based on Confluence Documentation</h4>
<p>By using <code>:confluence:</code> emoji, users can retrieve answers based on the Confluence documentation.</p>
<p>![Answer based on Confluence documentation](/assets/blog/authors/torii/2024-12-23_ai_tool_slack/images/image-confluence-rag.png =500x) <em>Answer based on Confluence documentation</em></p>
<p>As shown above, internal generative AI tool can be triggered from workflows, allowing it to function similarly to a prompt store.</p>
<h4>Example 3: Requesting English Translations via the Translation Reaction</h4>
<p><strong>Add translation reactions to messages that users want to translate into English</strong>  </p>
<p>To share a Japanese message with an English speaker, simply add the <code>:ai_tool_translate_to_english:</code> reaction emoji. This will automatically translate the message into English. ![English translation reaction](/assets/blog/authors/torii/2024-12-23_ai_tool_slack/images/image-translation.png =500x) <em>English translation reaction</em></p>
<p>To prevent misunderstandings and ensure users do not overly rely on automatic translations, a notification in multiple languages clarifies that the translation was AI-generated. Additionally, we provide instructions on how to use the feature to encourage adoption. Besides English, internal generative AI tool supports translation into multiple languages.</p>
<h3>Summary of Chapter 3</h3>
<p>In this chapter, we focused on the user perspective—&quot;what can be done with what operations.&quot; Building on the usage scenarios discussed here, the following chapters will systematically explain the implementation details. We will cover: - How to integrate Go-based bot with Python Lambdas. - How Slack events are processed and how RAG search works. - The details of the file extraction and sanitization processes.</p>
<hr>
<h2>4. Implementation Policy and Internal Design</h2>
<p>In this chapter, we will explain the implementation policy and internal design behind how internal generative AI tool provides a variety of generative AI functions through Slack messages and reactions.<br>This section focuses on the overall concept, role distribution, and scalability considerations. Specific code snippets and configuration files will be provided in the next chapter.</p>
<h3>Overall Process Flow</h3>
<ol>
<li><p><strong>Slack event reception (Go Lambda)</strong><br>Events occurring in Slack—such as message posts, file attachments, image uploads, external link insertions, and emoji reactions—are sent to an AWS Lambda function written in Go via the Slack API.<br>The Go function then analyzes these events and determines the appropriate action based on user intent (e.g., normal chat, translation, Confluence reference).</p>
</li>
<li><p><strong>Text processing and sanitization on the Go side</strong><br>Text is extracted from external links or files and added to the prompt as context. When referencing external links, meaningful tags such as <code>table</code>, <code>ol</code>, <code>ul</code> are preserved while unnecessary tags are removed to optimize token usage.  </p>
</li>
<li><p><strong>LLM queries and RAG searches on the Python side</strong><br>If necessary, the Go function invokes a Python Lambda for LLM queries or a separate Python Lambda for RAG searches (e.g., for Confluence references).<br>For example, if <code>:confluence:</code> is included in the request, the Go function calls the RAG search Lambda. If no index is specified, it defaults to the primary index.<br>Otherwise, the text is passed to the LLM Lambda for standard query processing.</p>
</li>
<li><p><strong>Reply and display in Slack</strong><br>The Python Lambda returns the generated response to the Go function, which then posts it back to Slack.<br>This enables users to access advanced features through familiar Slack interactions—such as emojis, keywords, and file attachments—without needing to memorize commands.</p>
</li>
</ol>
<h3>Function Determination by Conditional Branching</h3>
<p>Processing is routed based on specific emojis (e.g., <code>:confluence:</code>), keywords, the presence of files or images, and whether an external link is included.<br>To add new features, simply introduce new conditions on the Go side and, if necessary, extend the logic for invoking the corresponding Python Lambdas (e.g., for LLM or RAG tasks).</p>
<h3>The Role of Sanitization</h3>
<p>Sanitizing on the Go side removes unnecessary HTML tags to improve token efficiency and ensure clean input for the model.<br>Key structural elements such as table, ol, and ul are retained to preserve the information structure and maintain useful context for the model.</p>
<h3>Limiting RAG Usage to Confluence References</h3>
<p>RAG search is only performed when explicitly specified with <code>:confluence:</code>.<br>By default, summarization, translation, and Q&amp;A tasks are handled via direct LLM queries, ensuring RAG logic is triggered only for Confluence references.<br>Embedding generation for Confluence documents and FAISS index updates are handled periodically by StepFunctions, ensuring that the latest index is always available for queries.</p>
<h3>Considerations for Scalability and Maintainability</h3>
<p>Conditional branching based on emojis, keywords, or the presence of files/images minimizes the number of code modifications required when introducing new features, enhancing maintainability.<br>The separation of concerns—where text formatting and sanitization are handled on the Go side, while LLM queries and RAG searches are managed on the Python side—improves code clarity and facilitates future model replacements or additional processing logic.</p>
<p>In the next chapter, we will introduce specific code snippets and configuration examples based on these design principles.</p>
<hr>
<h2>5. Introduction to Code examples and Configuration Files</h2>
<p>This chapter introduces a brief implementation example based on the implementation policy and design concepts explained in Chapter 4.</p>
<p>This chapter contains the following sections:</p>
<ul>
<li><p>5.1 [Go] Receiving and parsing Slack events<br>Explains how to use Slack&#39;s Events API to receive and process events such as messages and emoji reactions.</p>
</li>
<li><p>5.2 [Go] HTML text sanitization<br>Provides an example of sanitizing HTML when referencing external links.</p>
</li>
<li><p>5.3 [Python] Example of an LLM query<br>Shows how to query an LLM using a Python-based Lambda function.</p>
</li>
<li><p>5.4 [Python] Example of a RAG search call<br>Demonstrates how to perform a RAG search call, such as for Confluence lookups.</p>
</li>
<li><p>5.5 [Python] Embedding and FAISS indexing.
Provides an example of Lambda code that periodically embeds Confluence documents and updates the FAISS index.</p>
</li>
</ul>
<h3>5.1 [Go] Receiving and Parsing Slack Events</h3>
<p>This section explains the basic steps for using the Slack Events API to receive and analyze events with Go code on AWS Lambda.<br>We will also cover the settings on the Slack side (OAuth &amp; Permissions, event subscription) and how to check the scopes required when using the <code>chat.postMessage</code> method (such as <code>chat:write</code>), to clarify the necessary preparations before implementation.</p>
<h4>Configuration Steps on Slack Side</h4>
<ul>
<li><p><strong>Create an app and check the App ID</strong>:<br>Create a new app at <a href="https://api.slack.com/apps">https://api.slack.com/apps</a>.<br>Once created, find your App ID (a string starting with <code>A</code>) on the Basic Information page (<code>https://api.slack.com/apps/APP_ID/general</code>, where <code>APP_ID</code> is a unique ID for your app).<br>This App ID identifies your Slack App and can be used to access the URLs for <code>OAuth &amp; Permissions</code> and <code>Event Subscriptions</code> pages described below.</p>
</li>
<li><p><strong>Granting scopes via OAuth &amp; Permissions</strong>:<br>Visit the OAuth &amp; Permissions page (<code>https://api.slack.com/apps/APP_ID/oauth</code>) and add the necessary scopes to Bot Token Scopes.<br>For example, if the <code>chat.postMessage</code> method is needed to post messages to a channel, checking this page (<a href="https://api.slack.com/methods/chat.postMessage">https://api.slack.com/methods/chat.postMessage</a>) under &quot;Required scopes&quot; will indicate that <code>chat:write</code> is required.<br>After granting the scope, click &quot;reinstall your app&quot; to apply the changes in your workspace. Then, the changes will be reflected.</p>
<p>![Checking required scopes](/assets/blog/authors/torii/2024-12-23_ai_tool_slack/images/image.png =500x) <em>Checking required scopes</em> ![Setting scope](/assets/blog/authors/torii/2024-12-23_ai_tool_slack/images/image-1.png =500x) <em>Setting scope</em></p>
</li>
<li><p><strong>Enabling the Events API and subscribing to events</strong>:<br>Enable the Events API on the &quot;Event Subscriptions&quot; page (<code>https://api.slack.com/apps/APP_ID/event-subscriptions</code>) and set the AWS Lambda endpoint described below in &quot;Request URL&quot;.<br>Add the events you want to subscribe to, such as <code>message.channels</code> or <code>reaction_added</code>. This allows Slack to send a notification to the specified URL whenever a subscribed event occurs.</p>
<p>![Event Subscriptions Explanation](/assets/blog/authors/torii/2024-12-23_ai_tool_slack/images/image-3.png =500x)</p>
</li>
</ul>
<h4>Event Reception and Analysis on AWS Lambda</h4>
<p>Once the configuration is complete on the Slack side, Slack will send a POST request to AWS Lambda via API Gateway whenever a subscribed event occurs.</p>
<h5>Step 1: Parsing Slack Events</h5>
<p>Use the <code>slack-go/slackevents</code> package to parse the received JSON into an <code>EventsAPIEvent</code> structure.<br>This makes it easier to identify event types, such as URL validation and CallbackEvent.</p>
<pre><code class="language-go">func parseSlackEvent(body string) (*slackevents.EventsAPIEvent, error) {
    event, err := slackevents.ParseEvent(json.RawMessage(body), slackevents.OptionNoVerifyToken())
    if err != nil {
        return nil, fmt.Errorf(&quot;Failed to parse Slack event: %w&quot;, err)
    }
    return &amp;event, nil
}
</code></pre>
<p>@<a href="https://github.com/slack-go/slack">card</a></p>
<h5>Step 2: Handling URL Verification Requests</h5>
<p>When setting up the integration, Slack will initially send an event with <code>type=url_verification</code>. To verify the URL, simply return the <code>challenge</code> value as received. Once verified, Slack will continue sending event notifications.</p>
<pre><code class="language-go">func handleURLVerification(body string) (events.APIGatewayProxyResponse, error) {
    var r struct {
        Challenge string `json:&quot;challenge&quot;`
    }
    if err := json.Unmarshal([]byte(body), &amp;r); err != nil {
        return createErrorResponse(400, err)
    }
    return events.APIGatewayProxyResponse{
        StatusCode: 200,
        Body:       r.Challenge,
    }, nil
}
</code></pre>
<p>@<a href="https://api.slack.com/authentication/verifying-requests-from-slack">card</a></p>
<h5>Step 3: Verifying Signatures and Ignoring Retry Requests</h5>
<p>Slack includes a request signature that allows verification of authenticity (implementation omitted).<br>Additionally, in case of a failure or outage, Slack may resend the request as a retry. The <code>X-Slack-Retry-Num</code> header can be used to identify retry attempts and prevent processing the same event multiple times.</p>
<pre><code class="language-go">func verifySlackRequest(body string, headers http.Header) error {
    // Signature verification process (omitted)
    return nil
}

func isSlackRetry(headers http.Header) bool {
    return headers.Get(&quot;X-Slack-Retry-Num&quot;) != &quot;&quot;
}

func createIgnoredRetryResponse() (events.APIGatewayProxyResponse, error) {
    responseBody, _ := json.Marshal(map[string]string{&quot;message&quot;: &quot;Ignoring Slack retry request&quot;})
    return events.APIGatewayProxyResponse{
        StatusCode: 200,
        Headers:    map[string]string{&quot;Content-Type&quot;: &quot;application/json&quot;},
        Body:       string(responseBody),
    }, nil
}
</code></pre>
<h5>Step 4: Handling CallbackEvent</h5>
<p>The <code>CallbackEvent</code> includes actions such as message postings and adding reactions. At this stage, the system checks whether <code>:confluence:</code> is included in the message, if a file is attached, or if a translation-related emoji is present. Based on this assessment, it proceeds to text processing and Python Lambda invocation, as described in section 5.2 and beyond.</p>
<pre><code class="language-go">// handleCallbackEvent processes callback events (covered in Section 5.1).
func handleCallbackEvent(ctx context.Context, isOrchestrator bool, event *slackevents.EventsAPIEvent) (events.APIGatewayProxyResponse, error) {
    innerEvent := event.InnerEvent

    switch innerEvent.Data.(type) {
    case *slackevents.AppMentionEvent:
        // Processing for AppMentionEvent (details explained in 5.2)
    case *slackevents.MessageEvent:
        // Processing for MessageEvent (details explained in 5.2)
    case *slackevents.ReactionAddedEvent:
        // Processing for ReactionAddedEvent (details explained in 5.2)
    }

    return events.APIGatewayProxyResponse{Body: &quot;OK&quot;, StatusCode: http.StatusOK}, nil
}
</code></pre>
<h5>Complete Handler Code Example</h5>
<p>These steps combine to define an AWS Lambda handler.</p>
<p>:::details Complete code example of the handler</p>
<pre><code class="language-go">func handler(ctx context.Context, request events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) {
    event, err := parseSlackEvent(request.Body)
    if err != nil {
        return createErrorResponse(400, err)
    }

    if event.Type == slackevents.URLVerification {
        return handleURLVerification(request.Body)
    }

    headers := convertToHTTPHeader(request.Headers)
    err = verifySlackRequest(request.Body, headers)
    if err != nil {
        return createErrorResponse(http.StatusUnauthorized, fmt.Errorf(&quot;Failed to validate request: %w&quot;, err))
    }

    if isSlackRetry(headers) {
        return createIgnoredRetryResponse()
    }

    if event.Type == slackevents.CallbackEvent {
        return handleCallbackEvent(ctx, event)
    }

    return events.APIGatewayProxyResponse{Body: &quot;OK&quot;, StatusCode: 200}, nil
}

func convertToHTTPHeader(headers map[string]string) http.Header {
    httpHeaders := http.Header{}
    for key, value := range headers {
        httpHeaders.Set(key, value)
    }
    return httpHeaders
}

func createErrorResponse(statusCode int, err error) (events.APIGatewayProxyResponse, error) {
    responseBody, _ := json.Marshal(map[string]string{&quot;error&quot;: err.Error()})
    return events.APIGatewayProxyResponse{
        StatusCode: statusCode,
        Headers:    map[string]string{&quot;Content-Type&quot;: &quot;application/json&quot;},
        Body:       string(responseBody),
    }, err
}
</code></pre>
<p>:::</p>
<h4>Summary of 5.1</h4>
<p>In this section, we explained how to obtain the Slack App&#39;s App ID, grant scopes using OAuth &amp; Permissions, and configure event subscriptions in Event Subscriptions. We also covered the process of receiving and parsing Slack events, including handling URL verification, signature validation, ignoring retry requests, and processing CallbackEvents.<br>From section 5.2 onwards, we will introduce specific examples of CallbackEvent processing, text handling in Go, and sending queries to Python Lambda.</p>
<h3>5.2 [Go] HTML Text Sanitization</h3>
<h4>Sanitizing External Link References</h4>
<p>HTML text retrieved from external links may contain unnecessary tags such as <code>script</code> and <code>style</code>, which are not needed for generating responses. Passing this directly to the LLM increases the token count, leading to higher model costs and potentially reducing response accuracy. The following code uses the <code>bluemonday</code> package for basic sanitization. It removes unnecessary tags while preserving important ones like <code>table</code>, <code>ol</code>, and <code>ul</code>, ensuring the text remains well-structured and readable. Additionally, the <code>addNewlinesForTags</code> function inserts line breaks after specific tags, improving text formatting. This helps optimize queries to the model by ensuring that only the necessary information is passed in a structured and efficient format.</p>
<p>@<a href="https://github.com/microcosm-cc/bluemonday">card</a></p>
<pre><code class="language-go">func sanitizeContent(htmlContent string) string {
    // Basic sanitization
    ugcPolicy := bluemonday.UGCPolicy()
    sanitized := ugcPolicy.Sanitize(htmlContent)

    // Allow specific tags in a custom policy
    customPolicy := bluemonday.NewPolicy()
    customPolicy.AllowLists()
    customPolicy.AllowTables()
    customPolicy.AllowAttrs(&quot;href&quot;).OnElements(&quot;a&quot;)

    // Add line breaks after specific tags to improve readability
    formattedContent := addNewlinesForTags(sanitized, &quot;p&quot;)

    // Apply final sanitization after enforcing the custom policy
    finalContent := customPolicy.Sanitize(formattedContent)

    return finalContent
}

func addNewlinesForTags(htmlStr string, tags ...string) string {
    for _, tag := range tags {
        closeTag := fmt.Sprintf(&quot;&lt;/%s&gt;&quot;, tag)
        htmlStr = strings.ReplaceAll(htmlStr, closeTag, closeTag+&quot;\n&quot;)
    }
    return htmlStr
}
</code></pre>
<p>This process ensures that the model receives only text with unnecessary tags removed, improving response accuracy and cost efficiency. By preserving essential structures such as tables and bullet points while inserting line breaks after specific tags, the model can better interpret the provided context.</p>
<h3>5.3 [Python] Example of an LLM Query</h3>
<p>Below is an example of how to query an LLM (e.g. Azure OpenAI) in Python. With <code>OpenAIClientFactory</code>, you can dynamically switch models and endpoints, enabling the reuse of a common client creation process across multiple Lambda handlers.</p>
<h4>Client Creation Process</h4>
<p><code>OpenAIClientFactory</code> dynamically generates a client for either Azure OpenAI or OpenAI, depending on <code>api_type</code> and <code>model</code>.<br>Since API keys and endpoints are retrieved from environment variables and secret management services, code modifications are minimized even when updating models or configurations.</p>
<pre><code class="language-python">import openai
from shared.secrets import get_secret

class OpenAIClientFactory:
    @staticmethod
    def create_client(region=&quot;eastus2&quot;, model=&quot;gpt-4o&quot;) -&gt; openai.OpenAI:
        secret = get_secret()
        api_type = secret.get(&quot;openai_api_type&quot;, &quot;azure&quot;)

        if api_type == &quot;azure&quot;:
            return openai.AzureOpenAI(
                api_key=secret.get(f&quot;azure_openai_api_key_{region}&quot;),
                azure_endpoint=secret.get(f&quot;azure_openai_endpoint_{region}&quot;),
                api_version=secret.get(
                    f&quot;azure_openai_api_version_{region}&quot;, &quot;2024-07-01-preview&quot;
                ),
            )
        elif api_type == &quot;openai&quot;:
            return openai.OpenAI(api_key=secret.get(&quot;openai_api_key&quot;))

        raise ValueError(f&quot;Invalid api_type: {api_type}&quot;)
</code></pre>
<h4>LLM Query Processing</h4>
<p>The <code>chatCompletionHandler</code> function extracts <code>messages</code>, <code>model</code>, <code>temperature</code>, and other parameters from the JSON received in the HTTP request. It then queries the LLM using the client generated by <code>OpenAIClientFactory</code>.<br>Responses are returned in JSON format. If an error occurs, a properly formatted error response is generated using a common error handling function.</p>
<pre><code class="language-python">import json
from typing import Any, Dict, List
import openai
from openai.types.chat import ChatCompletionMessageParam
from shared.openai_client import OpenAIClientFactory

def chatCompletionHandler(event: Dict[str, Any], context: Any) -&gt; Dict[str, Any]:
    request_body = json.loads(event[&quot;body&quot;])
    messages: List[ChatCompletionMessageParam] = request_body.get(&quot;messages&quot;, [])
    model = request_body.get(&quot;model&quot;, &quot;gpt-4o&quot;)

    client = OpenAIClientFactory.create_client(model=model)

    temperature = request_body.get(&quot;temperature&quot;, 0.7)
    max_tokens = request_body.get(&quot;max_tokens&quot;, 4000)
    response_format = request_body.get(&quot;response_format&quot;, None)

    completion = client.chat.completions.create(
        model=model,
        stream=False,
        messages=messages,
        max_tokens=max_tokens,
        frequency_penalty=0,
        presence_penalty=0,
        temperature=temperature,
        response_format=response_format,
    )

    return {
        &quot;statusCode&quot;: 200,
        &quot;body&quot;: json.dumps(completion.to_dict()),
        &quot;headers&quot;: {
            &quot;Content-Type&quot;: &quot;application/json&quot;,
            &quot;Access-Control-Allow-Origin&quot;: &quot;*&quot;,
            &quot;Access-Control-Allow-Methods&quot;: &quot;OPTIONS,POST&quot;,
            &quot;Access-Control-Allow-Headers&quot;: &quot;Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token&quot;,
        },
    }
</code></pre>
<p>This mechanism allows different Lambda handlers to make LLM queries using the same procedure, ensuring flexibility in adapting to models and endpoint changes.</p>
<h3>5.4 [Python] Example of a RAG Search Call</h3>
<p>This section provides instructions on performing a Retrieval Augmented Generation (RAG) search in Python.<br>By vectorizing internal knowledge, such as Confluence documents, and performing similarity searches using the FAISS index, it is possible to integrate highly relevant information into LLM responses.</p>
<p>A key consideration is the handling of the <code>faiss</code> library. <code>faiss</code> is a large package and may exceed the capacity limits of the Lambda Layers. To work around this, it is common to use EFS or containerize the Lambda function.<br>To simplify deployment, the <code>setup_faiss</code> function dynamically downloads and extracts <code>faiss</code> from S3, then adds it to <code>sys.path</code>, making <code>faiss</code> available at runtime.</p>
<h4>What is FAISS?</h4>
<p>FAISS (Facebook AI Similarity Search) is an approximate nearest neighbor search library developed by Meta (Facebook). It provides tools for creating indexes to efficiently search for similar images and text.</p>
<p>@<a href="https://note.com/npaka/n/nb766e344a4fc">card</a></p>
<h4>FAISS Setup Using the <code>setup_faiss</code> Function</h4>
<p>To use FAISS in the Lambda environment, the <code>setup_faiss</code> function performs the following steps:</p>
<ol>
<li><p><strong>Build and archive the faiss package in a local/CI environment</strong><br>Developers install the <code>faiss-cpu</code> package in a CI environment such as GitHub Actions and package the necessary binaries into a <code>tar.gz</code> archive.</p>
</li>
<li><p><strong>Upload to S3</strong><br>The archived <code>faiss_package.tar.gz</code> is uploaded to an S3 bucket.<br>By storing the package in an appropriate bucket and path (e.g., for staging or production), the Lambda function can dynamically retrieve it during execution.</p>
</li>
<li><p><strong>Dynamic loading with <code>setup_faiss</code> when running Lambda</strong><br>In the Lambda execution environment, the <code>setup_faiss</code> function downloads and extracts <code>faiss_package.tar.gz</code> from S3 at startup and adds it to <code>sys.path</code>.<br>This enables the Lambda function to run <code>import faiss</code>, allowing for efficient vector searches using embeddings.</p>
</li>
</ol>
<h5>Example: Uploading the FAISS Package to S3 Using GitHub Actions</h5>
<p>The following GitHub Actions workflow demonstrates how to install <code>faiss-cpu</code>, package it for Lambda use, and upload it to S3.</p>
<p>This setup uses GitHub Actions Secrets and Environment Variables to manage AWS credentials and S3 bucket names securely, avoiding hardcoded values.</p>
<p>@<a href="https://docs.github.com/ja/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-an-environment">card</a></p>
<pre><code class="language-yaml">name: Build and Upload FAISS
on:
  workflow_dispatch:
    inputs:
      environment:
        description: Deployment Environment
        type: environment
        default: dev

jobs:
  build-and-upload-faiss:
    environment: ${{ inputs.environment }}
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-python@v5
        with:
          python-version: &quot;3.11&quot;

      # Install required packages (faiss-cpu)
      - name: Install faiss-cpu
        run: |
          set -e
          echo &quot;Installing faiss-cpu...&quot;
          pip install faiss-cpu --no-deps

      # Archive the faiss binary
      - name: Archive faiss binaries
        run: |
          mkdir -p faiss_package
          pip install --target=faiss_package faiss-cpu
          tar -czvf faiss_package.tar.gz faiss_package

      # Set AWS credentials (configure Secrets or Roles based on your environment)
      - name: Configure AWS credentials
        uses: aws-actions/configure-aws-credentials@v3
        with:
          aws-access-key-id: ${{ secrets.CICD_AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.CICD_AWS_SECRET_ACCESS_KEY }}
          aws-region: ap-northeast-1

      # Upload to S3
      - name: Upload faiss binaries to S3
        run: |
          echo &quot;Uploading faiss_package.tar.gz to S3...&quot;
          aws s3 cp faiss_package.tar.gz s3://${{ secrets.AWS_S3_BUCKET }}/lambda/faiss_package.tar.gz
          echo &quot;Upload complete.&quot;
</code></pre>
<p>In the above example, <code>faiss_package.tar.gz</code> is uploaded to S3 with the key <code>lambda/faiss_package.tar.gz</code>.</p>
<h4>Dynamic Loading Process on Lambda side (<code>setup_faiss</code> function)</h4>
<p>The <code>setup_faiss</code> function handles the dynamic loading of FAISS at runtime. It downloads <code>faiss_package.tar.gz</code> from S3, extracts it to the <code>/tmp</code> directory, and appends the package path to <code>sys.path</code>. This enables <code>import faiss</code> to be executed within Lambda, allowing FAISS index lookups to be performed.</p>
<pre><code class="language-python"># setup_faiss example: Download the FAISS package from S3 and add it to sys.path
import os
import sys
import tarfile
from shared.logger import getLogger
from shared.s3_client import S3Client

logger = getLogger(__name__)

def setup_faiss(s3_client: S3Client, s3_bucket: str) -&gt; None:
    try:
        import faiss
        logger.info(&quot;faiss has already been imported.&quot;)
    except ImportError:
        logger.info(&quot;faiss not found. Downloading from S3.&quot;)
        faiss_package_key = &quot;lambda/faiss_package.tar.gz&quot;
        faiss_package_path = &quot;/tmp/faiss_package.tar.gz&quot;
        faiss_extract_path = &quot;/tmp/faiss_package&quot;

        # Download the package from S3 and extract it
        s3_client.download_file(bucket_name=s3_bucket, key=faiss_package_key, file_path=faiss_package_path)
        with tarfile.open(faiss_package_path, &quot;r:gz&quot;) as tar:
            for member in tar.getmembers():
                member.name = os.path.relpath(member.name, start=member.name.split(&quot;/&quot;)[0])
                tar.extract(member, faiss_extract_path)

        sys.path.insert(0, faiss_extract_path)
        import faiss
        logger.info(&quot;faiss was imported successfully.&quot;)
</code></pre>
<h4>RAG Search Using Embeddings and FAISS Indexes</h4>
<p>The <code>search_data</code> function loads the FAISS index retrieved from S3 locally and searches for documents that best match the query. Documents are vectorized using the Embeddings client (Azure OpenAI or OpenAI) generated by the <code>get_embeddings</code> function, enabling fast searches using <code>faiss</code>.</p>
<pre><code class="language-python">from typing import Any, Dict, List, Optional
from langchain_community.vectorstores import FAISS
from langchain_core.documents.base import Document
from langchain_core.vectorstores.base import VectorStoreRetriever
from shared.secrets import get_secret
from shared.logger import getLogger
from langchain_openai import AzureOpenAIEmbeddings, OpenAIEmbeddings

logger = getLogger(__name__)

def get_embeddings(secrets: Dict[str, str]):
    api_type: str = secrets.get(&quot;openai_api_type&quot;, &quot;azure&quot;)

    if api_type == &quot;azure&quot;:
        return AzureOpenAIEmbeddings(
            openai_api_key=secrets.get(&quot;azure_openai_api_key_eastus2&quot;),
            azure_endpoint=secrets.get(&quot;azure_openai_endpoint_eastus2&quot;),
            model=&quot;text-embedding-3-large&quot;,
            api_version=secrets.get(&quot;azure_openai_api_version_eastus2&quot;, &quot;2023-07-01-preview&quot;),
        )
    elif api_type == &quot;openai&quot;:
        return OpenAIEmbeddings(
            openai_api_key=secrets.get(&quot;openai_api_key&quot;),
            model=&quot;text-embedding-3-large&quot;,
        )
    else:
        logger.error(&quot;An invalid API type specified.&quot;)
        raise ValueError(&quot;Invalid api_type&quot;)

def search_data(
    query: str,
    index_folder_path: str,
    search_type: str = &quot;similarity&quot;,
    score_threshold: Optional[float] = None,
    k: Optional[int] = None,
    fetch_k: Optional[int] = None,
    lambda_mult: Optional[float] = None,
) -&gt; List[Dict]:
    secrets: Dict[str, str] = get_secret()
    embeddings = get_embeddings(secrets)
    db: FAISS = FAISS.load_local(
        folder_path=index_folder_path,
        embeddings=embeddings,
        allow_dangerous_deserialization=True,
    )

    search_kwargs = {&quot;k&quot;: k}
    if search_type == &quot;similarity_score_threshold&quot; and score_threshold is not None:
        search_kwargs[&quot;score_threshold&quot;] = score_threshold
    elif search_type == &quot;mmr&quot;:
        search_kwargs[&quot;fetch_k&quot;] = fetch_k or k * 4
        if lambda_mult is not None:
            search_kwargs[&quot;lambda_mult&quot;] = lambda_mult

    retriever: VectorStoreRetriever = db.as_retriever(
        search_type=search_type,
        search_kwargs=search_kwargs,
    )
    results: List[Document] = retriever.invoke(input=query)

    return [{&quot;content&quot;: doc.page_content, &quot;metadata&quot;: doc.metadata} for doc in results]
</code></pre>
<h4>Asynchronous Downloads and Lambda Handlers</h4>
<p>Within <code>async_handler</code>, <code>setup_faiss</code> is executed, and the FAISS index file is retrieved from S3 using <code>download_files</code>. Afterward, <code>search_data</code> performs a RAG search, and the results are returned in JSON format.</p>
<pre><code class="language-python">import asyncio
import json
import os
from shared.s3_client import S3Client
from shared.logger import getLogger
from shared.token_verifier import with_token_verification

logger = getLogger(__name__)
RESULT_NUM = 5

@with_token_verification
async def async_handler(event: Dict[str, Any], context: Any) -&gt; Dict[str, Any]:
    env = os.getenv(&quot;ENV&quot;)
    s3_client = S3Client()
    s3_bucket = &quot;bucket-name&quot;

    setup_faiss(s3_client, s3_bucket)

    request_body_str = event.get(&quot;body&quot;, &quot;{}&quot;)
    request_body = json.loads(request_body_str)
    query = request_body.get(&quot;query&quot;)
    index_path = request_body.get(&quot;index_path&quot;)

    local_index_dir = &quot;/tmp/index_faiss&quot;
    await download_files(s3_client, s3_bucket, index_path, local_index_dir)

    results = search_data(
        query,
        local_index_dir,
        search_type=request_body.get(&quot;search_type&quot;, &quot;similarity&quot;),
        score_threshold=request_body.get(&quot;score_threshold&quot;),
        k=request_body.get(&quot;k&quot;, RESULT_NUM),
        fetch_k=request_body.get(&quot;fetch_k&quot;),
        lambda_mult=request_body.get(&quot;lambda_mult&quot;),
    )

    return create_response(200, results)

def retrieverHandler(event: Dict[str, Any], context: Any) -&gt; Dict[str, Any]:
    return asyncio.run(async_handler(event, context))

def create_response(status_code: int, body: Any) -&gt; Dict[str, Any]:
    return {
        &quot;statusCode&quot;: status_code,
        &quot;body&quot;: json.dumps(body, ensure_ascii=False),
        &quot;headers&quot;: {
            &quot;Content-Type&quot;: &quot;application/json&quot;,
        },
    }

async def download_files(s3_client: S3Client, bucket: str, key: str, file_path: str) -&gt; None:
    loop = asyncio.get_running_loop()
    await loop.run_in_executor(None, download_files_from_s3, s3_client, bucket, key, file_path)

def download_files_from_s3(s3_client: S3Client, s3_bucket: str, prefix: str, local_dir: str) -&gt; None:
    keys = s3_client.list_objects(bucket_name=s3_bucket, prefix=prefix)
    if not keys:
        logger.info(f&quot;No file found in &#39;{prefix}&#39;&quot;)
        return
    for key in keys:
        relative_path = os.path.relpath(key, prefix)
        local_file_path = os.path.join(local_dir, relative_path)
        os.makedirs(os.path.dirname(local_file_path), exist_ok=True)
        s3_client.download_file(bucket_name=s3_bucket, key=key, file_path=local_file_path)
</code></pre>
<h4>Summary of 5.4</h4>
<ul>
<li>Avoid Lambda layer capacity issues with <code>setup_faiss</code> <code>faiss</code> dynamic loading.  </li>
<li>Asynchronous I/O and S3 usage allow FAISS index to be loaded without containerization or EFS connectivity.  </li>
<li><code>search_data</code> searches the embedded index, enabling RAG to quickly provide similar documents.</li>
</ul>
<p>This enables high-speed knowledge searches using RAG, providing LLM answers enriched with company-specific information.</p>
<h3>5.5 [Python] Embedding and FAISS Indexing</h3>
<p>This section provides an example of periodic batch processing that embeds internal company documents (such as Confluence pages) and creates or updates the FAISS index.<br>The index used in the RAG pipeline is essential for generative AI to incorporate company-specific knowledge into its responses. To maintain accuracy, we regularly update embeddings and rebuild the FAISS index, ensuring that the latest information is always accessible.</p>
<h4>Process Overview</h4>
<ol>
<li>Retrieve JSON-formatted documents from S3.  </li>
<li>Generate embeddings for the retrieved documents (using the Embeddings API from OpenAI or Azure OpenAI).  </li>
<li>Index the embedded text using FAISS.  </li>
<li>Upload the FAISS index to S3.</li>
</ol>
<p>By executing these steps periodically via Lambda batch processing or a Step Functions workflow, RAG searches will always use the latest index when queried.</p>
<h4>Step 1: Loading a JSON document</h4>
<p>Download and parse a JSON file from S3 (e.g., summarized Confluence pages) and convert it into a list of <code>Document</code> objects.</p>
<pre><code class="language-python">import json
from typing import Any, Dict, List
from langchain_core.documents.base import Document
from shared.logger import getLogger

logger = getLogger(__name__)

def load_json(file_path: str) -&gt; List[Document]:
    &quot;&quot;&quot;
    Reads a JSON file and returns a list of Document objects.
    The JSON format is expected to be: [{&quot;title&quot;: &quot;...&quot;, &quot;content&quot;: &quot;...&quot;, &quot;id&quot;: &quot;...&quot;, &quot;url&quot;: &quot;...&quot;}]
    &quot;&quot;&quot;
    with open(file_path, &quot;r&quot;, encoding=&quot;utf-8&quot;) as f:
        data = json.load(f)

    if not isinstance(data, list):
        raise ValueError(&quot;The top-level JSON structure is not a list.&quot;)

    documents = []
    for record in data:
        if not isinstance(record, dict):
            logger.warning(f&quot;Skipped record (not a dictionary): {record}&quot;)
            continue
        title = record.get(&quot;title&quot;, &quot;&quot;)
        content = record.get(&quot;content&quot;, &quot;&quot;)
        metadata = {
            &quot;id&quot;: record.get(&quot;id&quot;),
            &quot;title&quot;: title,
            &quot;url&quot;: record.get(&quot;url&quot;),
        }
        # Create a Document object combining the title and content
        doc = Document(page_content=f&quot;Title: {title}\nContent: {content}&quot;, metadata=metadata)
        documents.append(doc)

    logger.info(f&quot;Loaded {len(documents)} documents.&quot;)
    return documents
</code></pre>
<h4>Step 2: Embedding and FAISS indexing</h4>
<p>The <code>vectorize_and_save</code> function embeds the documents using the Embeddings client obtained from <code>get_embeddings</code> and creates a <code>FAISS</code> index. It then saves the index locally.</p>
<pre><code class="language-python">import os
from langchain_community.vectorstores import FAISS
from langchain_core.text_splitter import RecursiveCharacterTextSplitter
from shared.logger import getLogger

logger = getLogger(__name__)

def vectorize_and_save(documents: List[Document], output_dir: str, embeddings) -&gt; None:
    &quot;&quot;&quot;
    Embed the documents, create a FAISS index, and save it locally.
    &quot;&quot;&quot;
    # Split the document into smaller chunks using a text splitter
    text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=128)
    split_docs = text_splitter.split_documents(documents)
    logger.info(f&quot;{len(split_docs)} split documents&quot;)

    # Vectorize using embeddings and build FAISS index
    db: FAISS = FAISS.from_documents(split_docs, embeddings)
    logger.info(&quot;Vector DB construction completed.&quot;)

    os.makedirs(output_dir, exist_ok=True)
    db.save_local(output_dir)
    logger.info(f&quot;Vector DB saved to {output_dir}&quot;)
</code></pre>
<h4>Step 3: Uploading the Index to S3</h4>
<p>By uploading the locally created FAISS index to S3, it can be easily accessed by the RAG search Lambda.</p>
<pre><code class="language-python">from shared.s3_client import S3Client
from shared.logger import getLogger

logger = getLogger(__name__)

def upload_faiss_to_s3(s3_client: S3Client, s3_bucket: str, local_index_dir: str, index_s3_path: str) -&gt; None:
    &quot;&quot;&quot;
    Upload the FAISS index to S3.
    &quot;&quot;&quot;
    index_files = [&quot;index.faiss&quot;, &quot;index.pkl&quot;]
    for file_name in index_files:
        local_file_path = os.path.join(local_index_dir, file_name)
        s3_index_key = os.path.join(index_s3_path, file_name)
        s3_client.upload_file(local_file_path, s3_bucket, s3_index_key)
        logger.info(f&quot;FAISS index file uploaded to s3://{s3_bucket}/{s3_index_key}&quot;)
</code></pre>
<h4>Step 4: Running the entire flow in Lambda</h4>
<p>The <code>index_to_s3</code> function encapsulates the entire process. It downloads JSON from S3, generates embeddings, creates a FAISS index, and uploads the index to S3. This process can be executed periodically using a workflow such as Step Functions, ensuring that the index remains up to date.</p>
<pre><code class="language-python">import os
from shared.faiss import setup_faiss
from shared.logger import getLogger
from shared.s3_client import S3Client
from shared.secrets import get_secret

logger = getLogger(__name__)

def index_to_s3(json_s3_key: str, index_s3_path: str) -&gt; Dict[str, Any]:
    &quot;&quot;&quot;
    Download JSON from S3, generate embeddings, create a FAISS index, and upload the index to S3.
    &quot;&quot;&quot;
    env = os.getenv(&quot;ENV&quot;)
    if env is None:
        error_msg = &quot;ENV environment variable not set.&quot;
        logger.error(error_msg)
        return {&quot;status&quot;: &quot;error&quot;, &quot;message&quot;: error_msg}

    try:
        s3_client = S3Client()
        s3_bucket = &quot;bucket-name&quot;
        local_json_path = &quot;/tmp/json_file.json&quot;
        local_index_dir = &quot;/tmp/index&quot;

        # Set up faiss if necessary (download from S3)
        setup_faiss(s3_client, s3_bucket)

        # Download the JSON file from S3
        s3_client.download_file(s3_bucket, json_s3_key, local_json_path)
        documents = load_json(local_json_path)

        # Get Embeddings client
        secrets = get_secret()
        embeddings = get_embeddings(secrets)

        # Vectorization and FAISS indexing
        vectorize_and_save(documents, local_index_dir, embeddings)

        # Upload the index file to S3
        upload_faiss_to_s3(s3_client, s3_bucket, local_index_dir, index_s3_path)

        return {
            &quot;status&quot;: &quot;success&quot;,
            &quot;message&quot;: &quot;FAISS index created and uploaded to S3.”,
            &quot;output&quot;: {
                &quot;bucket&quot;: s3_bucket,
                &quot;index_key&quot;: index_s3_path,
            },
        }

    except Exception as e:
        logger.error(f&quot;An error occurred during the indexing process: {e}&quot;)
        return {&quot;status&quot;: &quot;error&quot;, &quot;message&quot;: str(e)}
</code></pre>
<h4>Summary of 5.5</h4>
<ul>
<li><code>load_json</code> loads a JSON file, and <code>vectorize_and_save</code> generates embeddings and creates a FAISS index.</li>
<li><code>upload_faiss_to_s3</code> uploads the local index to S3.</li>
<li><code>index_to_s3</code> consolidates the entire process, ensuring that the latest index is created and updated through regular batch processing.</li>
</ul>
<p>This enables automated batch processing to embed internal documents and maintain FAISS indexes for RAG searches.</p>
<h2>6. Summary</h2>
<p>In this article, we covered the development background and technical implementation of internal generative AI tool, out internal chatbot powered by LLM and integrated into Slack. We also outlined the steps for implementing the RAG pipeline, sanitizing Confluence documents, building a search infrastructure using Embeddings and FAISS indexes, and extending functionality with features like translation and summarization.<br>This system enables employees to seamlessly integrate generative AI into their Slack workflow, allowing them to access advanced information capabilities without needing to learn new tools or commands.</p>
<h2>7. Future Outlook</h2>
<p>We will actively work on the following improvements and expansions to further enhance internal generative AI tool.</p>
<ul>
<li><p><strong>Strengthening Azure-based deployment</strong><br>We will fully integrate with Azure services such as Azure Functions and Azure CosmosDB, significantly improving the performance and scalability of the RAG pipeline.  </p>
<ul>
<li><strong>Introducing Azure Cosmos DB Vector Search</strong><br>We will implement vector search functionality on Azure Cosmos DB for NoSQL, enabling more advanced search capabilities.<br>@<a href="https://learn.microsoft.com/ja-jp/azure/cosmos-db/nosql/vector-search">card</a></li>
<li><strong>Utilizing AI Document Intelligence</strong><br>By actively incorporating AI Document Intelligence, we aim to expand the knowledge scope of RAG and enhance information utilization across a broader range of use cases.<br>@<a href="https://learn.microsoft.com/ja-jp/azure/ai-services/document-intelligence/overview?view=doc-intel-4.0.0">card</a></li>
</ul>
</li>
<li><p><strong>Diversification and sophistication of models</strong><br>We will continue integrating cutting-edge models by expanding support beyond GPT-4o to include GPT-o1, Google Gemini, and other state-of-the-art AI models.</p>
</li>
<li><p><strong>Implementing Web UI</strong><br>To overcome the expression and interaction limitations imposed by Slack, we will develop a Web UI, allowing for more diverse interactions and the flexible deployment of new features.</p>
</li>
<li><p><strong>Enhancing prompt management</strong><br>We will template existing prompts, making them easily reusable across different use cases. Additionally, we will enhance the prompt-sharing functionality to further promote the adoption of generative AI across the company.</p>
</li>
<li><p><strong>Realizing multi-agent capabilities</strong><br>By deploying specialized agents dedicated to tasks such as summarization, translation, and RAG search, and allowing flexible combinations through an Agent Builder, we will enable more advanced and adaptable information processing.</p>
</li>
<li><p><strong>Evaluating and improving RAG accuracy</strong><br>We will build test sets and conduct automated answer evaluations to quantitatively measure accuracy and continuously improve quality.</p>
</li>
<li><p><strong>Enhancements based on user feedback</strong><br>By incorporating real-world usage data and feedback, we will optimize dialogue flows, fine-tune prompts, and strengthen external service integrations, ensuring that internal generative AI tool remains highly convenient and useful.</p>
</li>
</ul>
<p>Through these efforts, we will continue evolving internal generative AI tool, growing it into a powerful internal support tool that meets a wide range of business needs.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Overcoming Challenges in the In-House Rollout of the 10X Innovation Culture Program [Part 1]]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-22-Google-10x-program-in-summer-1-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-22-Google-10x-program-in-summer-1-en/</guid>
            <pubDate>Fri, 18 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Overcoming Challenges in the In-House Rollout of the 10X Innovation Culture Program [Part 1]]]></description>
            <content:encoded><![CDATA[<p>Hello, this is HOKA from the HR Group, Organizational Human Resources Team.</p>
<p>Today, I’d like to share what happened following <a href="https://blog.kinto-technologies.com/posts/2024-06-12-Google%E3%81%A710XInnovationCultureProgram%E3%82%92%E5%8F%97%E3%81%91%E3%81%9F%E8%A9%B1/">the 10X Innovation Culture Program in March 2024</a>.</p>
<h2>Introduction</h2>
<p>KINTO Technologies (KTC from now on) is challenging itself to create an organizational environment that fosters innovation. We are still in the process of trial and error, but I would be happy if this article were read by those considering the introduction of the <a href="https://grow.google/intl/ALL_jp/10xinnovationculture/">10X Innovation Culture Program</a> or those who want to create an organization that fosters innovation.</p>
<h2>July: We Tried Implementing The 10X Innovation Culture Program Ourselves</h2>
<p>I made a resolution even before joining the 10X Innovation Culture Program at the Google offices in March. I decided to implement the 10X Innovation Culture Program within our company. It was in March 2024 when we made the following decision: We will rollout the program every three months and review the assessment results. From the second round onward, we will commit to run the 10X Innovation Culture Program independently.</p>
<p>Awacchi and myself began preparing to run the program in the summer. The first step was to select facilitators. Since there would be no Google staff to support us this time, we needed to take on the role of facilitators in-house. When I reached out on Slack, four members stepped forward to volunteer.</p>
<p>The 10X Innovation Culture Program broadly consisted of the three main components listed below. We asked ourselves: &quot;What would I do if I were a participant?&quot; and &quot;Which approach would be best for KTC?&quot; </p>
<p>[1] A &quot;pre-meeting&quot; to watch a video and understand what the 10X Innovation Culture Program (from now on, the &quot;10X&quot;) is
[2] Understanding our current status from assessment results
[3] Discussing the elements of 10X as the theme</p>
<p> These were the only things we had to do.</p>
<p>And yet, it was so difficult!! </p>
<h3>Key Challenges:</h3>
<ul>
<li>[Time Allocation] We were unsure whether the program should take 2 hours or 3.</li>
<li>[Content] There were two groups of participants: first-time attendees and those joining for a second time. How should we move forward?</li>
<li>[Quality] The section defining what 10X is important, but when KTC members presented, it came across as if they were simply reading from a script.</li>
</ul>
<p>　</p>
<p>So, it was a trial-and-error process, starting from the very basics. Together with Awacchi and the four facilitators, despite our uncertainties, we managed to create the next program, discussing how things should unfold based on the March program.</p>
<h3>Reviewing Each Content</h3>
<p>[1] A pre-meeting to watch a video and understand what 10X is.</p>
<p>As with the previous program, we held a pre-meeting via Zoom. A facilitator introduced the concept of 10X, followed by a recorded playback of the 10X content. Lastly, we conducted an assessment questionnaire.</p>
<ul>
<li>It seemed that this time, the enthusiasm wasn&#39;t as high as it had been in March.</li>
<li>Perhaps we were lacking motivation, particularly in terms of why KTC was learning this. That was the impression of the organizing team.</li>
</ul>
<p>[2] Understanding our current status from the assessment results</p>
<p>We held our in-house 10X Innovation Culture Program at the Muromachi office on July 2, 2024. A total of 39 people participated, including those who came from the Osaka Tech Lab (our Osaka office) and Nagoya. There were both first-time and second-time participants.</p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program1.png" alt=""></p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program2.png" alt=""></p>
<p>We looked at the results of the assessment questionnaire that everyone had completed and reported whether each numerical value had gone up or down since the previous session, but the response seemed lukewarm.</p>
<ul>
<li>Maybe the participants did not feel attached to the reported numerical values because they haven&#39;t done anything special between the last program (March) and this one (July).</li>
<li>Simply receiving the assessment results may not be sufficient for participants to determine whether the outcomes were good or bad.</li>
</ul>
<p>That was the hypothesis of the organizing team.</p>
<p>[3] Discussing the elements of 10X as the theme</p>
<p>Next, we discussed how to review the past three months.</p>
<p>Some participants didn’t remember anything at all. Some first-time participants said, “Since I only watched the video, I don’t know what I’m supposed to do.” When the program was held at the Google offices the previous time, the discussions went smoothly, so this situation was unexpected for the organizing team (though looking back it seems obvious.)</p>
<p>Afterward, we discussed Autonomy, one of the key elements of 10X. As in the previous time, participants were divided into groups to write down issues and discuss potential solutions, but this time we had the below discoveries:</p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program3.jpeg" alt=""></p>
<ul>
<li>Some of them haven&#39;t been actively practicing 10X over the past three months.</li>
<li>For some reason, the atmosphere this time seemed less conducive to active discussions compared to the previous session.</li>
</ul>
<h3>Post-Feedback Survey</h3>
<p><a href="https://blog.kinto-technologies.com/posts/2024-06-12-Google%E3%81%A710XInnovationCultureProgram%E3%82%92%E5%8F%97%E3%81%91%E3%81%9F%E8%A9%B1/">Compared to last time</a>, the number of items with ratings 3 and 2 increased, and there were also some items rated as 1.</p>
<p>The motivation to promote this activities also decreased.</p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program4.jpeg" alt=""></p>
<p><img src="/assets/blog/authors/hoka/10x-program/10x-innovation-culture-program5.jpeg" alt=""></p>
<p>In the questionnaire comments, we received the following advice from participants:</p>
<ol>
<li>Overall impression:</li>
</ol>
<ul>
<li>Thank you to the organizing team for all your hard work. It was a fun event.</li>
<li>No particular comments for the moment.</li>
<li>I would love to see this implemented in other teams as well. It’s a great activity, even just for learning about other people&#39;s perspectives.</li>
</ul>
<ol start="2">
<li>We also saw feedback about the previous session held:</li>
</ol>
<ul>
<li>I would have appreciated a brief recap of the content from the previous program. There were moments when those who missed the previous program felt left behind.</li>
<li>It took me some time to refresh my memory on the issues from the previous program, so I wish there had been time for a review beforehand.</li>
<li>I felt that the discussions were more lively last time. This time, no one was proactively steering the conversation (a lack of initiative), and there was no advance preparation, so it took some time to get into the groove.</li>
</ul>
<ol start="3">
<li>On time and schedule</li>
</ol>
<ul>
<li>It was short in terms of time. It might have been better if we had more time for group work and discussions.</li>
<li>The time schedule was too tight (since things don&#39;t always go as planned, there should have been some leeway in the program. It’s too risky to plan by the minute). </li>
<li>Maybe the problem was time allocation (I think that was the goal, but overall, it didn’t seem like we were clear on the next action.)</li>
</ul>
<ol start="4">
<li>On group discussions</li>
</ol>
<ul>
<li>When forming teams for group discussions, I thought it might have been better to select members from teams that actually work together, rather than mixing people from different teams.</li>
<li>It was stimulating and refreshing to discuss with members from different teams, like in the previous program and this one. However, I also feel that it is difficult to achieve results that are directly applicable to our work. So, I thought it might be a good idea to have a conversation about 10X again among members from the same team.</li>
</ul>
<ol start="5">
<li>On facilitation and management of the workshop</li>
</ol>
<ul>
<li>There was too little time for ice-breaker activities, including self-introductions. When starting a workshop, it is very important to ease the atmosphere among unfamiliar members. We didn’t even have the time to finish self-introductions.</li>
<li>The questions in the workshop were too abstract, making it difficult for participants to come up with concrete answers.</li>
<li>Maybe the organizing team overlooked setting goals for the workshop.</li>
<li>It would have been better to set a goal at the beginning, such as telling the participants, &quot;please take this back with you.&quot;</li>
</ul>
<ol start="6">
<li>On content understanding and follow-up</li>
</ol>
<ul>
<li>It was my first time participating, so I felt that the contents seemed more suited for those who had prior knowledge, and overall, it gave an impression of being somewhat vague.</li>
<li>It might have been better to either narrow down the content for a more compact format, or extend the duration to provide more thorough explanations.</li>
</ul>
<ol start="7">
<li>Others</li>
</ol>
<ul>
<li>I don’t understand the purpose of the paper (for writing responses) that was distributed.</li>
<li>It would have been better to assign a leader to each working team from the start.</li>
</ul>
<ol start="8">
<li>Proactivity and discussions within teams</li>
</ol>
<ul>
<li>Looking back after the previous session, there were few actions taken, so I would like to allocate time for regular discussions within the team.</li>
<li>I am interested in Intrinsic Motivation (another of the 10x key concepts). I believe that combining it with Autonomy can create an even greater sense of speed.</li>
</ul>
<p>After the 10X Innovation Culture Program the facilitator members gathered to conduct a retrospective session. A lot of ideas were gathered.</p>
<p>Now, where should we start?</p>
<p>Meanwhile, I was given the opportunity by Google to speak at <a href="https://cloudonair.withgoogle.com/events/next-tokyo-24">Google Cloud Next Tokyo&#39;24</a>.</p>
<p>&lt;&lt;To be continued in Part 2&gt;&gt;</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[KINTO Technologies Competed in the ISUCON14 and Scored Over 10,000 points]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-21-KINTO-Technologies社員でISUCON14に参加しスコア1万点を超えた話-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-21-KINTO-Technologies社員でISUCON14に参加しスコア1万点を超えた話-en/</guid>
            <pubDate>Thu, 17 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We participated in ISUCON14, held on December 8th, as a team from KINTO Technologies. In this article, I’d like to share our experience and results.]]></description>
            <content:encoded><![CDATA[<p>This article is part of the day 21 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<hr>
<h1>Introduction</h1>
<p>Hello! I’m Uehara (<a href="https://twitter.com/penpen_77777">@penpen_77777</a>) from the KINTO FACTORY Development Group. I joined the company in July 2024 and have been working on backend development for KINTO FACTORY.</p>
<p>This time, KINTO Technologies employees participated in ISUCON14, which was held on December 8th, and would like to share the content and results.</p>
<h1>What is ISUCON14?</h1>
<p>ISUCON is a tuning competition organized by LINE Yahoo Japan Corporation. Participants are given a predefined web service and compete to optimize its performance as much as possible while adhering to specific regulations. The winning team receives a grand prize of 1 million yen! This year, the competition took place on Sunday, December 8th, from 10:00 AM to 6:00 PM. I (Uehara) have been participating in ISUCON since last year&#39;s ISUCON13, not only to gain knowledge about performance tuning but also to challenge myself as an engineer and improve my skills.</p>
<p>:::message
ISUCON is a trademark or registered trademark of LINE Yahoo Japan Corporation.
<a href="https://isucon.net/">https://isucon.net/</a>
:::</p>
<h1>Team &quot;ktc-isucon-bu&quot;</h1>
<p>I recruited members through the company&#39;s Slack and formed the team &quot;ktc-isucon-bu.&quot; The team members are as follows.</p>
<ul>
<li>Uehara(<a href="https://twitter.com/penpen_77777">@penpen_77777</a>)<ul>
<li>Participated in ISUCON13 last year, making this their second time.</li>
</ul>
</li>
<li>Furuya<ul>
<li>Participating in ISUCON for the first time.</li>
</ul>
</li>
<li>Nishida<ul>
<li>Participating in ISUCON for the first time.</li>
</ul>
</li>
</ul>
<p>ISUCON allows participants to choose from several programming languages for the initial implementation. This time, we chose Go. Since many of our team members were first-time participants, rather than aiming for a top ranking, we set a more achievable goal: scoring over 10,000 points.</p>
<h1>Preparation Before the Competition</h1>
<p>At ISUCON, it is crucial to automate or streamline repetitive tasks so that we can focus on optimizing during the actual competition. This time, we made the following preparations:</p>
<ul>
<li>Setting up environment provisioning and deployment commands</li>
<li>Developing documentation generation tools</li>
<li>Preparing measurement tools</li>
<li>Conducting individual and team practice</li>
</ul>
<h2>Setting up environment provisioning and deployment commands</h2>
<p>go-task is a task runner specialized for executing tasks in the terminal.
<a href="https://taskfile.dev/">https://taskfile.dev/</a></p>
<p>Traditionally, make is often used as a task runner, but I personally find go-task more convenient, so I chose to use it this time. (Unlike make, it requires installation, which is a minor drawback, but aside from that, I think it can be effectively utilized in real-world projects.)</p>
<p>For example, create the following taskfile.yaml and define the <code>setup</code> task and <code>deploy</code> task.</p>
<pre><code class="language-yaml">version: &#39;3&#39;

tasks:
  setup:
    cmds:
      # Write commands for environment setup
      - echo &quot;setup&quot;
  deploy:
    cmds:
      # Write the deployment command
      - echo &quot;deploy&quot;
</code></pre>
<p>After creating the file, you can execute a task by running <code>task name</code> as shown below:</p>
<pre><code class="language-bash"># Run the environment setup task
$ task setup 
setup

# Run the deploy task
$ task deploy
deploy
</code></pre>
<p>Additionally, you can pass command-line arguments when running a task, embedding them into the task execution.</p>
<pre><code class="language-yaml">version: &#39;3&#39;

tasks:
  setup:
    cmds:
      - echo &quot;setup {{ .CLI_ARGS }}&quot;
  deploy:
    cmds:
      - echo &quot;deploy {{ .CLI_ARGS }}&quot;
</code></pre>
<pre><code class="language-bash"># Execute the setup task for server isu1
$ task setup -- isu1
setup isu1

# Execute the deploy task for server isu2
$ task deploy -- isu2
deploy isu2
</code></pre>
<p>The example above is a simple demonstration, but in real use, leveraging command-line arguments allows you to easily switch target servers, making it more efficient for teams to divide and manage tasks.</p>
<p>Other benefits of using go-task include:</p>
<ul>
<li>Even if you are working in a subdirectory, you can find the taskfile.yaml in the parent directory and execute the task.<ul>
<li>You can execute tasks without worrying about the directory location.</li>
</ul>
</li>
<li>Tasks can be called from other tasks, increasing the reusability of the tasks you define.<ul>
<li>You can combine tasks, such as running a <code>setup</code> task before a <code>deploy</code> task.</li>
</ul>
</li>
<li>By describing all the tools used in ISUCON in taskfile.yaml, you can execute them just by typing the <code>task</code> command, even if you don&#39;t know how to use them (such as the required options).</li>
</ul>
<p>A template <code>taskfile.yaml</code> was prepared in advance so that variables could be modified easily during the competition, allowing for flexible responses to any issues that might arise.</p>
<h2>Developing documentation generation tools</h2>
<p>The following two tools were used:</p>
<p><a href="https://github.com/k1LoW/tbls">https://github.com/k1LoW/tbls</a></p>
<ul>
<li>Reads the contents of the database and generates documentation (Markdown) that includes ER diagram and schema descriptions.</li>
<li>Allows checking the schema definition without connecting to the database, making it useful for understanding the database structure.</li>
<li>Automatically generating documentation in the CI/CD pipeline makes it easier to track changes in the database structure.<ul>
<li>For more details, see the following article: <a href="https://devblog.thebase.in/entry/auto_generated_er_graph_by_tbls_and_github_actions">https://devblog.thebase.in/entry/auto_generated_er_graph_by_tbls_and_github_actions</a></li>
</ul>
</li>
</ul>
<p><a href="https://github.com/mazrean/isucrud">https://github.com/mazrean/isucrud</a></p>
<ul>
<li>Analyzes application code, visualizes relationships with database tables, and generates documentation (Markdown).<ul>
<li>The drawback was that the diagrams became difficult to understand as the number of functions increased, but now it&#39;s much easier to use because you can interactively narrow down the parts you want to see on a web browser.</li>
</ul>
</li>
</ul>
<h2>Preparing measurement tools</h2>
<p>The following tools were used:</p>
<p><a href="https://github.com/kaz/pprotein">https://github.com/kaz/pprotein</a></p>
<ul>
<li>Collects and visualizes access logs, slow query logs, and profile data.</li>
<li>Automatically starts collecting data via webhook when a benchmark is run, making it convenient.</li>
<li>Also records commit hashes at the time of data collection, making it easy to track which commits contributed to score improvements. ^[Encountered an issue where the Git commit hash could not be obtained properly with pprotein, so the repository was forked, the code was fixed, and a PR was submitted.
<a href="https://github.com/kaz/pprotein/pull/37%5D">https://github.com/kaz/pprotein/pull/37]</a></li>
</ul>
<p><img src="/assets/blog/authors/uehara/2024-12-20-take-part-in-isucon14/pprof.png" alt="Profile data aggregation results"> <img src="/assets/blog/authors/uehara/2024-12-20-take-part-in-isucon14/httplog.png" alt=" Access log aggregation results"> <img src="/assets/blog/authors/uehara/2024-12-20-take-part-in-isucon14/slowlog.png" alt="Slow query log aggregation results"></p>
<h2>Conducting individual and team practice</h2>
<p>For first-time participants, jumping straight into past ISUCON problems can be overwhelming and discouraging due to their high difficulty level. To help them get a feel for performance tuning, we first had them read the book Web Performance Tuning: Experts&#39; Guide.</p>
<p><a href="https://gihyo.jp/book/2022/978-4-297-12846-3">https://gihyo.jp/book/2022/978-4-297-12846-3</a></p>
<p>As we progressed through the book, we gathered as a team on weekends to work through the following practice problems.</p>
<p><a href="https://github.com/catatsuy/private-isu">https://github.com/catatsuy/private-isu</a>
(The book also provides guidance on how to optimize using private-isu)</p>
<p>Once we were able to consistently score around 100,000 to 200,000 points, we shifted our focus to solving problems from ISUCON13. At this point, we had gained enough confidence to transition into competition-focused practice, allowing us to simulate real ISUCON conditions smoothly.</p>
<h1>This year’s Task</h1>
<p><a href="https://www.youtube.com/watch?v=UFlcAUvWvrY">https://www.youtube.com/watch?v=UFlcAUvWvrY</a></p>
<ul>
<li>The theme for ISUCON 14 was improving the ride chair service &quot;ISURIDE&quot;<ul>
<li>Chair owners provide chairs for transportation.</li>
<li>Users request chairs via the app and travel to their destination.</li>
</ul>
</li>
<li>Scores are based on factors like distance traveled and ride completion rate.<ul>
<li>It is necessary to make improvements to increase user satisfaction.</li>
</ul>
</li>
</ul>
<p><a href="https://github.com/isucon/isucon14">https://github.com/isucon/isucon14</a></p>
<h1>Results</h1>
<p>The results are as follows. We surpassed our goal of 10,000 points!</p>
<ul>
<li>Final Ranking: 135th place out of 831 teams</li>
<li>Final Scores: 12514 points <img src="/assets/blog/authors/uehara/2024-12-20-take-part-in-isucon14/score.webp" alt="Team score details"> <img src="/assets/blog/authors/uehara/2024-12-20-take-part-in-isucon14/graph.webp" alt="Score trend over time"></li>
</ul>
<h1>What We Achieved</h1>
<ul>
<li>Preparation of deployment scripts (Uehara 10:00-10:30)</li>
<li>Manual for the day (Furuya 10:00)</li>
<li>Checking running processes and understanding the general configuration (Furuya 10:00)</li>
<li>Reading the application manual (Nishida 10:00)</li>
<li>Logging into MySQL and checking table sizes</li>
<li>Adding indexes (Nishida 11:21)<pre><code class="language-sql">-- chair_locations
CREATE INDEX idx_chair_id ON chair_locations(chair_id);
CREATE INDEX idx_chair_id_created_at ON chair_locations(chair_id, created_at);
-- chairs
CREATE INDEX idx_owner_id ON chairs(owner_id);
-- ride_statuses
CREATE INDEX idx_ride_statuses_ride_id_chair_sent_at_created_at
    ON ride_statuses (ride_id, chair_sent_at, created_at);
</code></pre>
</li>
<li>Preparing pprotein (Uehara 11:48)<ul>
<li>I had a hard time fixing the nginx settings and it took me over an hour</li>
</ul>
</li>
<li>Adding more indexes (Nishida 11:54)<pre><code class="language-sql">CREATE INDEX idx_ride_statuses_created_at_ride_id_chair_sent_at
    ON ride_statuses (created_at, ride_id, chair_sent_at);

-- rides
CREATE INDEX idx_chair_id_updated_at ON rides (chair_id, updated_at DESC);
</code></pre>
</li>
<li>Enabling dynamic parameters and speeding up json processing (Uehara 12:38)<pre><code class="language-go">// Changed import statements
&quot;encoding/json&quot; → &quot;github.com/goccy/go-json&quot;

// Enabled InterpolateParams when connecting to the database
dbConfig.InterpolateParams = true
</code></pre>
</li>
<li>Adding More Indexes (Uehara 13:05)<pre><code class="language-sql">-- chairs
CREATE INDEX idx_chairs_access_token ON chairs(access_token);

-- ride_statuses
CREATE INDEX idx_ride_statuses_ride_id_app_sent_at_created_at
    ON ride_statuses (ride_id, app_sent_at, created_at);

-- rides
CREATE INDEX idx_rides ON rides (user_id, created_at);

-- coupons
CREATE INDEX idx_coupons_used_by ON coupons(used_by);
</code></pre>
</li>
<li>Implementing user status cache (Furuya 14:30)<ul>
<li>In-memory cache for users and ride_statuses tables</li>
</ul>
</li>
<li>Modifying transaction scope (Nishida 14:48, 15:09)</li>
<li>Adjusting notification polling intervals (Furuya 16:27)<ul>
<li>Changed the RetryAfterMs returned in appGetNotification and chairGetNotification from 30 ms to 300 ms.</li>
</ul>
</li>
<li>Shortened chair-user matching interval (Nishida 17:18, 17:28)<ul>
<li>Shortened ISUCON_MATCHING_INTERVAL from 0.5 s to 0.1 s</li>
<li>Score nearly doubled (most effective change)</li>
</ul>
</li>
<li>Stopped bin log (Furuya 17:24)<ul>
<li>Disabled bin logs in MySQL settings (/etc/mysql/mysql.conf.d/mysqld.cnf)<pre><code>disable-log-bin=1
innodb_flush_log_at_trx_commit=0
</code></pre>
</li>
</ul>
</li>
<li>Disabled log output (Uehara 17:43)<ul>
<li>Stopped log output for nginx, MySQL,</li>
</ul>
</li>
<li>and application logs</li>
<li>Improved ownerGetChairs (Nishida 17:43)<ul>
<li>Implemented memoization for distance</li>
</ul>
</li>
<li>Adjusting the number of connections (Uehara 17:49)<pre><code class="language-go">db.SetMaxIdleConns(50)
db.SetMaxOpenConns(50)
</code></pre>
</li>
</ul>
<h1>Measures That Could Not Be Implemented Due to Time Constraints or Score Issues</h1>
<ul>
<li>Fixed matching process (Uehara 13:00-16:00)<ul>
<li>Attempted improvements but failed to resolve chair dispatch errors and had to abandon it.</li>
</ul>
</li>
<li>Caching chair access tokens (Furuya 15:36)<ul>
<li>Significantly reduced queries from 30,000 to 100. However, the score did not increase.</li>
<li>Likely due to a bottleneck elsewhere.</li>
</ul>
</li>
<li>nginx parameter tuning (Furuya 17:39)<ul>
<li>Unable to apply optimizations due to benchmark errors...</li>
</ul>
</li>
<li>Server split (Uehara 16:00-17:00)<ul>
<li>Attempted to split nginx+Go and MySQL, into two machines but encountered data inconsistency errors.</li>
<li>Later realized that both servers were running the chair-matching service, which led to data inconsistencies... (If only we had stopped one...)</li>
</ul>
</li>
</ul>
<h1>Pros</h1>
<ul>
<li>Achieved our goal of scoring over 10,000 points.<ul>
<li>Ranked 135th out of 831 teams with 12,514 points, a solid result for a team with one returning participant and two first-timers.</li>
</ul>
</li>
<li>Everyone contributed to improvement tasks,<ul>
<li>ensuring no idle time.</li>
</ul>
</li>
<li>Environment setup and deployment scripts worked smoothly.<ul>
<li>No major deployment issues,</li>
<li>except minor struggles with nginx setup for pprotein. It took me about an hour to do so, so I would like to add a note to the instructions.</li>
<li>Using automation tools significantly improved our workflow, and we plan to expand this approach.</li>
</ul>
</li>
</ul>
<h1>Points to Reflect on</h1>
<ul>
<li>Lacked a deep understanding of the application before making optimizations.<ul>
<li>In this problem, understanding the application specifications was important. Mechanically optimizing based on slow query logs or application logs didn’t lead to a significant score increase, and there were few areas where we could make effective improvements.</li>
<li>Need to develop a structured approach to understand application behavior before tuning.</li>
<li>Understanding specifications is important in daily work as well, so I want to keep this in mind moving forward.</li>
</ul>
</li>
<li>Struggled with the matching process bug, which delayed implementation.<ul>
<li>Tried to optimize using only queries without modifying the table structure, which made implementation difficult. I realized that adding appropriate columns to the tables and simplifying the application code would have made implementation much smoother.</li>
</ul>
</li>
<li>I had no prior knowledge of SSE (Server-Sent Events), so I plan to study it.<ul>
<li>It was presented as a way to update the user’s chair status in real time.</li>
</ul>
</li>
</ul>
<h1>Lessons to Apply in Future Work</h1>
<ul>
<li>Understanding the application is more critical than just focusing on technical optimizations.</li>
<li>Use task runners (go-task) and auto-documentation tools (tbls, etc.) to improve workflow efficiency.</li>
<li>Explore using SSE (Server-Sent Events) in product development.</li>
</ul>
<h1>Summary</h1>
<p>We participated in ISUCON14 and achieved a score of 12514 as the team &quot;ktc-isucon-bu&quot;, placing 135th out of 831 teams. Although there are things we need to improve on, we were pleased that we were able to achieve our goal of scoring over 10,000 points, even though some of our members were participating in ISUCON for the first time. We will use these lessons to improve further at ISUCON15.</p>
<p>Special thanks to Furuya-san and Nishida-san for joining despite it being their first ISUCON. Also, a big thank you to all KINTO Technologies employees who showed interest, even if they couldn’t participate this time. Lastly, a huge shoutout to the ISUCON organizers for hosting this incredible event.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uehara/2024-12-20-take-part-in-isucon14/thumb.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[How We Applied the Impact/Effort Matrix to Improve a Cross-Team Activity]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-20-impact-effort-matrix-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-20-impact-effort-matrix-en/</guid>
            <pubDate>Wed, 16 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We used the Impact/Effort Matrix to tackle a lack of office interaction and strengthen cross-team connections.]]></description>
            <content:encoded><![CDATA[<p>This article is the entry for day 20 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>🎅🎄</p>
<h1>How We Applied the Impact/Effort Matrix to Improve Cross-Team Interaction</h1>
<p>Hello! We are Maya and Kinoshita, team members of KINTO Technologies&#39; Developer Relations (DevRel) Group.  </p>
<h2>Introduction</h2>
<p>We previously held an internal event in Jimbocho to improve office communication and foster stronger ties between teams.<br>Here’s the article from when we held the event ↓ 
<a href="https://blog.kinto-technologies.com/posts/2023-09-19-JimbochoISM/">https://blog.kinto-technologies.com/posts/2023-09-19-JimbochoISM/</a></p>
<p>For this event, we used an Impact/Effort Matrix during the planning phase to decide on the activities.<br>This helped us organize ideas to make the event more engaging and clarify task priorities.<br>As a result, we were able to smoothly run multiple events. </p>
<p>In this article, we’ll share our approach to planning, running, and reflecting on events using the Impact/Effort Matrix.<br>We hope this article will be useful for managing your project, for event planning and hope you enjoy the read till the end.  </p>
<h2>About the Impact/Effort Matrix</h2>
<p>The Impact/Effort Matrix is a simple yet effective tool for efficiently determining the priority of projects and tasks.<br>By categorizing tasks or ideas along two axes (Impact and Effort) this matrix can provide a clear, visual way to decide where to focus your team resources.  </p>
<h3>Basic Structure</h3>
<p>The Impact/Effort Matrix consists of four quadrants:  </p>
<ol>
<li><p>Quick Wins:</p>
<ul>
<li>Tasks that deliver high impact with minimal effort.</li>
<li>They should be prioritized and executed quickly.</li>
</ul>
</li>
<li><p>Major Projects:</p>
<ul>
<li>Tasks that offer high impact but require significant effort.</li>
<li>Resource planning is crucial and should be addressed strategically.</li>
</ul>
</li>
<li><p>Fill-ins:</p>
<ul>
<li>Tasks that have low impact and require minimal effort.</li>
<li>These should be tackled when there’s extra capacity, but they’re lower priority.</li>
</ul>
</li>
<li><p>Parking Lot:</p>
<ul>
<li>Tasks that offer low impact and require a lot of effort.</li>
<li>These should generally be avoided.</li>
</ul>
</li>
</ol>
<p><img src="/assets/blog/authors/Maya.s/20241222/IEM.png" alt="Impact/Effort Matrix"></p>
<p>For more details, you can check the <a href="https://miro.com/templates/impact-effort-matrix/">Miro template description page</a>.  </p>
<h3>Why We Decided to Use the Impact/Effort Matrix</h3>
<p>After we held the first Jimbocho Information Sharing session, we received quite positive feedback from our collagues.<br>However, not all participants felt a sense of unity, so we asked ourselves: how can we create more of a lively and energetic atmosphere? So we wanted to brainstorm this together.<br>While discussing among the organizing team, we clarified our goals and selected the tasks to focus on and those to exclude.<br>During this process, we explored appropriate methods to share task priorities with the team, and decided to use the Impact/Effort Matrix, which some team members had previous experience with.  </p>
<h3>Percieved Benefits</h3>
<ul>
<li>Clear prioritization:<ul>
<li>By placing all tasks relative to each other in the four quadrants, as discussions progress and sticky notes are arranged, the tasks that should be prioritized become visually apparent.</li>
</ul>
</li>
<li>Easily shareable, with high team alignment:<ul>
<li>Building on the previous point, the discussion about what should be prioritized serves as an opportunity to align everyone’s understanding.</li>
<li>By progressing with the team’s consensus, commitment is high, and it becomes easier for everyone to take ownership and move forward together.</li>
</ul>
</li>
<li>Team resource optimization:<ul>
<li>By ensuring everyone agrees on the next actions, it helps prevent rework and ensures smoother progress.</li>
</ul>
</li>
</ul>
<h3>Challenges and Solutions</h3>
<p>As is often the case, the most challenging part for the team was decision-making.<br>During the first round of implementing the Impact/Effort Matrix, we encountered many issues with differing opinions and a lack of shared understanding. It took a lot of discussions to organize the information and reach a consensus on how to use it for improving the next event.<br>The granularity of the details was inconsistent, and the scope of what we wanted to accomplish was too broad, so we weren’t able to achieve the desired results.  </p>
<p>In the second round, we refined the granularity of the issues, clarified our objectives, and narrowed the scope, which allowed us to achieve more concrete outcomes.<br>As a result, the team’s understanding deepened, and the process moved more smoothly.</p>
<p>We were able to better define the direction for the event, and the second round was a success. We then created an action plan for the third Jimbocho Sharing Session and visualized the tasks on a Jira board.<br>At this point, several new members joined the organizing team, many of whom were experiencing the Impact/Effort Matrix for the first time.<br>Despite the presence of many newcomers, we were able to smoothly progress based on our prior experience, and successfully held the third Jimbocho Sharing Session.  </p>
<h2>Thoughts on Using the Impact/Effort Matrix</h2>
<p>Even after understanding the Impact/Effort Matrix methodology, when we tried to apply it to our own situation, there were moments of doubt in the team about whether we were on the right track.<br>However, by discussing the points that felt off or needed improvement and respecting each other&#39;s opinions, we were able to organize the various ideas that came up and clearly identify which tasks should be prioritized.  </p>
<p>During the process of making the Impact/Effort Matrix method work, there were disagreements, but by objectively evaluating each idea, we were able to move forward in a way that everyone could agree on.  </p>
<p>As preparations for the event progressed, the sense of unity within the team grew, and in the end, we received high praise from participants at the event.  </p>
<h2>Conclusion</h2>
<p>In this article, we shared our approach to planning, executing, and reflecting on events with the Impact/Effort Matrix, using the Jimbocho internal event as an example.<br>Initially, there were uncertainties and doubts, and we lacked confidence in whether we were doing things correctly. However, through repeated practice, we gradually became more comfortable with the process. Even when new team members joined midway, we were able to run the event smoothly.  </p>
<p>We hope this methodology will be helpful for the success of your own projects and events as it was for us.   </p>
<p>Furthermore, KINTO Technologies is looking for talents to join us!<br>If this article has sparked even a bit of your interest,  please feel free to reach out to us. We look forward to hearing from you!  </p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs">https://hrmos.co/pages/kinto-technologies/jobs</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[We Upgraded Apollo Kotlin to V4]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-22-apollo-kotlin-v4-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-22-apollo-kotlin-v4-en/</guid>
            <pubDate>Tue, 15 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[We Upgraded Apollo Kotlin to V4]]></description>
            <content:encoded><![CDATA[<p>This article is the entry for day 22 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<hr>
<p>Nice to meet you! I am kikumido, and I am developing the Unlimited (Android) app at KINTO Technologies. For our app, we use <a href="https://www.apollographql.com/docs/kotlin">Apollo Kotlin</a> for the <a href="https://graphql.org/">GraphQL</a> client. Apollo Kotlin v4 introduces numerous enhancements, including better performance and new features. To take advantage of these benefits, we decided to upgrade from v3 to v4.</p>
<p>In this article, I will explain in detail the process of migrating to Apollo Kotlin v4 (which was released this July), and the issues we encountered along the way. We had initially been hoping for a nice, smooth migration, but ran into some unexpected exceptions, and sometimes struggled to find solutions. I would be delighted if this article proves to be a helpful resource for those considering the upgrade themselves.</p>
<h2>Migrating From v3 to v4</h2>
<p>We will proceed in accordance with the <a href="https://www.apollographql.com/docs/kotlin/migration/4.0">official website</a>.</p>
<p><a href="https://www.apollographql.com/docs/kotlin/testing/android-studio-plugin#migration-helpers">You can also do the upgrade semi-automatically by installing a plugin into Android Studio. </a> We wanted to check what the necessary steps were as we went along, so we did it manually without using the plugin.</p>
<p>On the other hand, I was curious about how much the plugin can automate, so in the second half of this article, I will compare the manual approach with the automated process.</p>
<h3>1. Things We Had to Do</h3>
<p>When migrating from v3 to v4, there were five things we had to do in Android Studio to eliminate errors in our app. I will go through them one by one below.
:::message alert
What you need to do will very likely vary depending on the implementation status of your own apps, so please be aware that this will only be what we did for ours.
:::</p>
<h4>1.1. Upgrading the Library</h4>
<p>Anyway, first, we will update the <a href="https://github.com/apollographql/apollo-kotlin/releases">library</a>.</p>
<pre><code class="language-gradle:libs.versions.toml（v3）">apollographql = &quot;3.8.5&quot;
apollo-runtime = { module = &quot;com.apollographql.apollo3:apollo-runtime&quot;, version.ref = &quot;apollographql&quot; }
apollographql-apollo = { id = &quot;com.apollographql.apollo3&quot;, version.ref = &quot;apollographql&quot; }
</code></pre>
<p>↓</p>
<pre><code class="language-gradle:libs.versions.toml（v4）">// At the time of writing this article, v4.1.0 has been released. However, I have based my explanation on 4.0.1, which was the latest version when we did the migration ourselves.
apollographql = &quot;4.0.1&quot;
apollo-runtime = { module = &quot;com.apollographql.apollo:apollo-runtime&quot;, version.ref = &quot;apollographql&quot; }
apollographql-apollo = { id = &quot;com.apollographql.apollo&quot;, version.ref = &quot;apollographql&quot; }
</code></pre>
<p>Hmm. Instead of turning into <code>com.apollographql.apollo4</code>, <code>com.apollographql.apollo3</code> lost its “3” instead.</p>
<h4>1.2. Modifying the Gradle File</h4>
<p>This did not need to be done for v3, but wrapping in a service is required for v4.</p>
<pre><code class="language-gradle:build.gradle.kts（v3）">apollo {
    packageName.set(&quot;com.example.xxx.xxx.xxx&quot;)
    ・・・
}
</code></pre>
<p>↓</p>
<pre><code class="language-gradle:build.gradle.kts（v4）">apollo {
    service(&quot;service&quot;) {
        packageName.set(&quot;com.example.xxx.xxx.xxx&quot;)
        ・・・
    }
}
</code></pre>
<h4>1.3. Changing the Import</h4>
<p>We could see this coming when the “3” disappeared, but you need to change the import package name. Just delete the “3.”</p>
<pre><code class="language-kotlin:kotlinファイル（v3）">import com.apollographql.apollo3.*
</code></pre>
<p>↓</p>
<pre><code class="language-kotlin:kotlinファイル（v4）">import com.apollographql.apollo.*
</code></pre>
<h4>1.4. Modify the Exception Handling</h4>
<p>We changed things so that <code>execute()</code> will not throw a fetch error, so we will deal with that.</p>
<p>I expect that the approach will vary by project, but for now, we have chosen to take measures to minimize the impact on existing processes. This method consists of modifying things so that <code>DefaultApolloException</code> will be thrown under the same conditions as when a fetch error arose and <code>ApolloException</code> was thrown with v3. By adjusting the common handling, we ensured that the behavior remained the same for the calling side without modifying its implementation.</p>
<pre><code class="language-kotlin:ApiClient共通クラス（v3）">apolloClient.query(query).execute()

apolloClient.mutation(mutation).execute()
</code></pre>
<p>↓</p>
<pre><code class="language-kotlin:ApiClient共通クラス（v4）">execute(apolloClient.query(query))

execute(apolloClient.mutation(mutation))

private suspend inline fun &lt;D : Operation.Data&gt; execute(apolloCall: ApolloCall&lt;D&gt;): ApolloResponse&lt;D&gt; {
    val response = apolloCall.execute()

    if (response.data == null) {
        response.exception?.let { // Fetch error if response.data is null and response.exception is not null.
            throw DefaultApolloException(it.message, it.cause)
        }
    }

    return response
}
</code></pre>
<p>If it is difficult to migrate all in one go due to the existing implementation or the v4 support policy, <code>executeV3()</code> has been provided as a helper for migrating without changing the v3 behavior, so you can temporarily replace it with that.</p>
<pre><code class="language-kotlin:ApiClient共通クラス（v4でv3の挙動のままにする）">apolloClient.query(query).executeV3()

apolloClient.mutation(mutation).executeV3()
</code></pre>
<p>This approach is a good choice if you want to migrate to v4 gradually (for example, feature by feature).
:::message alert
Note that <code>executeV3()</code> is a deprecated method, so be aware that you’ll need to address it eventually.
:::</p>
<h4>1.5. Modifying ApolloException to DefaultApolloException</h4>
<p><code>ApolloException</code> has become a <code>sealed class</code>, so replace any places where instances were being generated with <code>DefaultApolloException</code>.</p>
<p>The above are the steps required to eliminate errors in Android Studio.</p>
<h3>2. Build</h3>
<p>Now that we’ve cleared all the errors...let’s try running the long-awaited build!</p>
<p>Drum roll, aaand...ta-daaa!</p>
<p>Yes! We got something!</p>
<p><strong>Build errors...!</strong></p>
<p>Although we didn’t expect everything to go perfectly smoothly, it was still quite a shock....</p>
<p>Okay, we will pull ourselves back together and check the error log.</p>
<h4>2.1. Checking the Error Log</h4>
<p>A flood of unfamiliar error log entries suddenly appeared. In short, it seems that KSP[^1] is missing a class required for AssistedInjectProcessingStep.[^2]</p>
<p>[^1]: For our app, we are using <a href="https://developer.android.com/build/migrate-to-ksp?hl=ja">KSP</a>.
[^2]: For our app, we are using <a href="https://developer.android.com/training/dependency-injection/hilt-android?hl=ja">Hilt</a> as a dependency injection library.</p>
<pre><code>If &#39;error.NonExistentClass&#39; is a generated type, check for compilation errors above that may have prevented its generation. Otherwise, ensure that &#39;error.NonExistentClass&#39; is included in your classpath.
e: [ksp] AssistedInjectProcessingStep was unable to process &#39;XXXXXViewModel(java.lang.String,long,com.xx.xx.XXXXXRepository)&#39; because &#39;error.NonExistentClass&#39; could not be resolved.
</code></pre>
<p>Of course, it is a class that exists in the source, and the build had been succeeding with no problems before we started doing this upgrade. Had we missed out something that was necessary for the v4 migration? We checked that and various other possibilities, but still couldn&#39;t pinpoint the issue...</p>
<p>After much investigation, we managed to get the build to succeed by using the following three methods.</p>
<ul>
<li>A. Change <code>Hilt</code> back from <code>KSP</code> to <code>kapt</code></li>
<li>B. Add some code to build.gradle.kts: Pattern 1</li>
</ul>
<pre><code class="language-gradle:build.gradle.kts（v4）">androidComponents {
    onVariants(selector().all()) { variant -&gt;
        afterEvaluate {
            val variantName = variant.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
            val generateTask = project.tasks.findByName(&quot;generateServiceApolloSources&quot;)
            val kspTask = project.tasks.findByName(&quot;ksp${variantName}Kotlin&quot;)
                as? org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompileTool&lt;*&gt;

            kspTask?.run {
                generateTask?.let {
                    setSource(it.outputs)
                }
            }
        }
    }
}
</code></pre>
<ul>
<li>C. Add some code to build.gradle.kts: Pattern 2</li>
</ul>
<pre><code class="language-gradle:build.gradle.kts（v4）">apollo {
    service(&quot;service&quot;) {
        packageName.set(&quot;com.example.xxx.xxx.xxx&quot;)
        // Start of additional code
        outputDirConnection { 
            connectToAndroidSourceSet(&quot;main&quot;) 
        }
        // End of additional code
        ・・・
    }
}
</code></pre>
<p>By the way,</p>
<ul>
<li>we are using <code>Hilt</code>’s <code>AssistedInject</code> (which is apparently sometimes not a problem depending on how it is defined);</li>
<li>we are using <code>KSP</code> in <code>Hilt</code>; and</li>
<li>we wrap it in a <code>service</code> in “1.2. Modifying the Gradle file” above.</li>
</ul>
<p>If all the above conditions are met, the issue also occurs in v3, meaning it isn&#39;t strictly a &#39;v4-specific&#39; support update. However, since we addressed it at the same time, I have included it here. For our app, we initially went with “B.” However, due to a bug in ApolloExtension’s <code>service</code>,[^3] we subsequently found that we could do it using “C,” so we adopted that.</p>
<p>[^3]: <a href="https://github.com/apollographql/apollo-kotlin/issues/4669">Issues</a> → This was also reproduced with v4.1.0, the latest version at the time of writing.</p>
<p>With the above changes, the build succeeded, allowing us to migrate to v4 while ensuring the app launched and functioned just as before!</p>
<h3>3. Dealing with Deprecated Parts</h3>
<p>Next, we will address the following two things that caused deprecation warnings.</p>
<h4>3.1. Modifying ApolloResponse.Builder</h4>
<pre><code>@Deprecated(&quot;Use 2 params constructor instead&quot;, ReplaceWith(&quot;Builder(operation = operation, requestUuid = requestUuid).data(data = data)&quot;))
</code></pre>
<p>We received the above warning and made the necessary modifications accordingly</p>
<pre><code class="language-kotlin:kotlinファイル（v3）">ApolloResponse.Builder(operation(), UUID_CONST, data).build()
</code></pre>
<p>↓</p>
<pre><code class="language-kotlin:kotlinファイル（v4）">ApolloResponse.Builder&lt;D&gt;(operation(), UUID_CONST).data(data).build()
</code></pre>
<p><code>data</code> has been moved outside, correct It seems to have been modified to follow the Builder pattern.</p>
<h4>3.2. Modifying the Error Instance Creation Processing to Builder</h4>
<p>Update any instances where a constructor was used to create an Error instance to instead use the Builder pattern.</p>
<pre><code class="language-kotlin:kotlinファイル（v3）">Error(&quot;occurred Error&quot;, null, null, mapOf(&quot;errorCode&quot; to responseCode), null)
</code></pre>
<p>↓</p>
<pre><code class="language-kotlin:kotlinファイル（v4）">Error.Builder(message = &quot;occurred Error&quot;)
    .putExtension(&quot;errorCode&quot;, responseCode)
    .build()
</code></pre>
<p>Doing the above also successfully eliminated a warning.</p>
<h3>4. Migrating Using the Plugin</h3>
<p>Using the Apollo plugin in Android Studio will automate the migration to some extent. It is very simple to do: just install the Apollo plugin in Android Studio, then tap <code>Tools &gt; Apollo &gt; Migrate to Apollo Kotlin 4...</code>. Easy, right? ![Migrate to Apollo Kotlin 4](/assets/blog/authors/kikumido/plugin.png =500x)</p>
<p>Okay, let’s check the results of running it.</p>
<p>The aforementioned</p>
<ul>
<li>1.1. Upgrading the Library</li>
<li>1.2. Modifying the Gradle File</li>
<li>1.3. Changing the Import</li>
</ul>
<p>got done automatically.</p>
<ul>
<li>1.4. Modifying the Exception Handling</li>
</ul>
<p>only consists of switching to <code>executeV3()</code>, so the appropriate action for v4 needs to be taken manually.</p>
<ul>
<li>1.5. Modifying ApolloException to DefaultApolloException</li>
</ul>
<p>Was not completed, so it must be done manually.</p>
<p>Essentially, it automates any migration tasks that can be handled mechanically. Therefore, another viable approach is to use the plugin for migration and then manually address only the necessary parts.</p>
<p>By the way, <a href="https://www.apollographql.com/docs/kotlin/testing/android-studio-plugin">this plugin not only assists with migration by graying out unused fields but is also useful for other tasks. I highly recommend installing it.</a></p>
<h3>5. Summary</h3>
<p>That concludes the discussion of migrating Apollo Kotlin from v3 to v4. It&#39;s been a while since we actually completed this, so I imagine many people have already done it themselves. Still, I hope this article provides something useful. Thank you for reading all the way to the end.</p>
<h3>6. Related links</h3>
<p><a href="https://www.apollographql.com/docs/kotlin/migration/4.0">https://www.apollographql.com/docs/kotlin/migration/4.0</a>
<a href="https://www.apollographql.com/docs/kotlin/testing/android-studio-plugin">https://www.apollographql.com/docs/kotlin/testing/android-studio-plugin</a>
<a href="https://developer.android.com/build/migrate-to-ksp?hl=ja">https://developer.android.com/build/migrate-to-ksp?hl=ja</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[既存のアプリへのKMPの適用：チームの経験と成果]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-12-KMP-implementation-story-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-12-KMP-implementation-story-ja/</guid>
            <pubDate>Mon, 14 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Kotlin Multiplatform (KMP) によるクロスプラットフォーム開発に関する私たちの経験と、それが私たちのアプリにもたらした変化について共有します。]]></description>
            <content:encoded><![CDATA[<p>この記事は、<a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTOテクノロジーズ・アドベントカレンダー2024</a> の12日目の記事です🎅🎄</p>
<p>こんにちは。「KINTOかんたん申し込みアプリ」のAndroid開発チームメンバーです。今日は、私たちの既存のアプリに Kotlin Multiplatform (KMP) を実装するプロセス、その理由、そしてそれによってもたらされた変化と改善についてお話ししたいと思います。  </p>
<p>私たちは去年からiOSとAndroidプラットフォーム間での開発効率を最大化する方法を探ってきました。<br>このプロセスの中で、KMPがチームの目に留まりました。この技術がどのように革新的に開発プロセスを改善したかを皆さんにお伝えしたいと思います。  </p>
<p>目次</p>
<ul>
<li><a href="#1-%E6%97%A2%E5%AD%98%E3%81%AE%E3%82%A2%E3%83%97%E3%83%AA%E3%81%ABkmp%E3%82%92%E5%AE%9F%E8%A3%85%E3%81%99%E3%82%8B%E7%90%86%E7%94%B1">1. 既存のアプリにKMPを実装する理由</a></li>
<li><a href="#2-%E6%97%A2%E5%AD%98%E3%81%AE%E3%82%A2%E3%83%97%E3%83%AA%E3%81%B8%E3%81%AEkmp%E3%81%AE%E7%B5%B1%E5%90%88">2. 既存のアプリへのKMPの統合</a><ul>
<li><a href="#21-%E5%85%B1%E6%9C%89%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E9%85%8D%E7%BD%AE%E6%B1%BA%E5%AE%9A">2.1 共有コードの配置決定</a></li>
<li><a href="#22-%E5%85%B1%E6%9C%89%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E6%95%B4%E7%90%86">2.2 共有コードの整理</a></li>
<li><a href="#23-kmp%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90">2.3 KMPモジュールの作成</a></li>
<li><a href="#24-%E3%83%9E%E3%83%AB%E3%83%81%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E3%82%A2%E3%83%BC%E3%82%AD%E3%83%86%E3%82%AF%E3%83%81%E3%83%A3%E3%81%A8%E3%82%A2%E3%83%B3%E3%83%96%E3%83%AC%E3%83%A9%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB">2.4 マルチモジュール・アーキテクチャとアンブレラモジュール</a></li>
<li><a href="#25-ciandroid%E3%81%8A%E3%82%88%E3%81%B3ios%E3%81%A7%E3%81%AE%E5%85%B1%E6%9C%89%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E3%83%86%E3%82%B9%E3%83%88">2.5 CI：AndroidおよびiOSでの共有コードのテスト</a></li>
</ul>
</li>
<li><a href="#3-kmp%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E9%85%8D%E5%B8%83">3. KMPコードの配布</a><ul>
<li><a href="#31-kmp%E3%82%B3%E3%83%BC%E3%83%89%E3%81%AE%E9%85%8D%E5%B8%83%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3">3.1 KMPコードの配布オプション</a></li>
<li><a href="#32-swift-package-manager-spm">3.2 Swift Package Manager （SPM）</a></li>
<li><a href="#33-%E9%85%8D%E5%B8%83%E3%81%AE%E8%87%AA%E5%8B%95%E5%8C%96">3.3 配布の自動化</a></li>
</ul>
</li>
<li><a href="#4-android%E3%81%8A%E3%82%88%E3%81%B3ios%E3%81%AE%E5%AE%9F%E8%A3%85%E6%96%B9%E6%B3%95">4. AndroidおよびiOSの実装方法</a><ul>
<li><a href="#41-%E6%A9%9F%E8%83%BD%E3%81%AE%E9%81%B8%E6%8A%9E">4.1 機能の選択</a></li>
</ul>
</li>
<li><a href="#5-kmp%E3%82%AF%E3%83%AD%E3%82%B9%E3%83%97%E3%83%A9%E3%83%83%E3%83%88%E3%83%95%E3%82%A9%E3%83%BC%E3%83%A0%E3%83%A2%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%AB%E5%AE%9F%E8%A3%85%E3%81%AB%E3%81%8A%E3%81%91%E3%82%8B%E8%AA%B2%E9%A1%8C">5. KMPクロスプラットフォームモジュール実装における課題</a></li>
<li><a href="#6-%E5%8A%B9%E6%9E%9C">6. 効果</a></li>
<li><a href="#7-%E4%BB%8A%E5%BE%8C%E3%81%AB%E5%90%91%E3%81%91%E3%81%A6%E4%BB%8A%E5%BE%8C%E3%81%AE%E8%A8%88%E7%94%BB%E3%81%A8%E8%AA%B2%E9%A1%8C">7. 今後に向けて：今後の計画と課題</a></li>
</ul>
<h1>1. 既存のアプリにKMPを実装する理由</h1>
<p>当時、私たちのチームはiOS開発リソースの不足に直面していました。<br>この課題に対処するために、AndroidチームはKotlin Multiplatform (KMP) を活用して、iOSとAndroidの両方のプラットフォームで共有されるビジネスロジックを作成することにしました。  </p>
<p>このアプローチにより、オペレーティングシステム間でのコードの重複を削減し、Androidチームは専門知識を活用してiOS開発に対応できるようになりました。  </p>
<p>この戦略は、人員の問題を軽減し、開発生産性を大幅に向上させる重要なソリューションとなり、KMPテクノロジーを既存のアプリに統合する決定的な理由となりました。  </p>
<h4><strong>[背景の概要]</strong></h4>
<ul>
<li><strong>iOS開発リソース不足への対処</strong></li>
<li><strong>Kotlinに関するAndroidチームの専門知識の活用</strong></li>
<li><strong>オペレーティングシステム間のコード重複の削減</strong></li>
<li><strong>開発生産性の向上とチーム連携の強化</strong></li>
</ul>
<p><img src="/assets/blog/authors/KMP/android-kmp.png" alt="私たちはKMPに興味を持っていました。"><br>※ ビジネスロジックをKMPライブラリにモジュール化することで、オペレーティングシステム間での重複作業を排除しましょう。</p>
<h1>2. 既存のアプリへのKMPの統合</h1>
<p>私たちは、KMPコードを実装する前に、私たちの共有コードをどこに配置し、どのように整理するかについていくつかの戦略的決定を下しました。</p>
<h2>2.1 共有コードの配置決定</h2>
<p>現在運営していますモバイルアプリには、Androidリポジトリで作業するAndroidチームと、iOSリポジトリで作業するiOSチームという2つの別々の開発チームによる典型的な設定があります。</p>
<p>KMPを導入するときに最初に生じる疑問は、「共有コードをどこに配置すべきか」ということです。</p>
<h3>オプション1：別のリポジトリ内の共有コード</h3>
<p>このオプションでは、共有コード用の新規リポジトリを作成し、AndroidリポジトリとiOSリポジトリの両方からアクセスできるようにします。このリポジトリ構造は次のようになります。</p>
<pre><code class="language-mermaid">graph TB;

subgraph Android Repository
AndroidApp
end

subgraph iOS Repository
iOSApp
end

subgraph KMP Repository
KMP
end

KMP --&gt; AndroidApp
KMP --&gt; iOSApp
</code></pre>
<h3>オプション2：Androidリポジトリ内の共有コード</h3>
<p>このオプションでは、共有コードをAndroidリポジトリに配置し、Androidチームが共有コードベースを管理できるようにします。このリポジトリ構造は次のようになります。</p>
<pre><code class="language-mermaid">graph TB;

subgraph Android Repository
KMP --&gt; AndroidApp
end

subgraph iOS Repository
KMP --&gt; iOSApp
end
</code></pre>
<h3>オプション3：AndroidリポジトリとiOSリポジトリをモノレポに統合する</h3>
<p>このオプションでは、AndroidリポジトリとiOSリポジトリをモノレポに統合し、両チームが共有コードベースにアクセスできるようにします。このリポジトリ構造は次のようになります。</p>
<pre><code class="language-mermaid">graph TB;

subgraph One Repository
KMP --&gt; AndroidApp
KMP --&gt; iOSApp
end
</code></pre>
<h3>[私たちの決定]</h3>
<p>各オプションの長所と短所を検討した結果、共有コードをAndroidリポジトリに配置することに決定しました。この決定は以下の要因に基づいています：</p>
<ul>
<li>既存のワークフローへの影響を最小限に抑える</li>
<li>共有コードベースの管理が容易になる</li>
</ul>
<h2>2.2 共有コードの整理</h2>
<p>共有コードをどこに配置するかを決めたら、次はそれをどのように整理するかを決めました。</p>
<p>私たちの既存のAndroidアプリはマルチモジュール・アーキテクチャに従っているため、共有モジュールとプラットフォーム固有のモジュールを明確に区別したいと考えました。KMPモジュールを既存のAndroidモジュールと一緒に、Androidリポジトリ内の<code>shared</code>ディレクトリに配置することにしました。例：</p>
<pre><code>:app            // Androidアプリのモジュール
:domain         // Android 固有のモジュール
:shared:api     // KMPモジュール
:shared:validation  // KMPモジュール
</code></pre>
<h2>2.3 KMPモジュールの作成</h2>
<p>KMP用のGradleモジュールは次を含みます。
1.	<code>build.gradle.kts</code>ファイル
2.	<code>src</code>サブフォルダー</p>
<p>Androidモジュールの場合、<code>com.android.library</code>プラグインを適用し、<code>android {}</code>ブロックを含めます。</p>
<pre><code class="language-kts">plugins {
    id(&quot;com.android.library&quot;)
}

android {
    // Android固有の設定
}
</code></pre>
<p>KMPモジュールの場合、マルチプラットフォームプラグインを使用し、<code>kotlin {}</code>ブロックを定義します。</p>
<pre><code class="language-kts">plugins {
    kotlin(&quot;multiplatform&quot;)
}

kotlin {
    // KMPの設定
}
</code></pre>
<p>この設定により、共有コードベースにおいてAndroid固有の要件とKMP固有の要件の両方に対応できるようになりました。</p>
<ul>
<li><h2>2.4 マルチモジュール・アーキテクチャとアンブレラモジュール</h2>
</li>
</ul>
<h3>複数モジュールの制限</h3>
<p>Androidでは、複雑なプロジェクトのためにコードを複数のモジュールに分割するのが標準です。ただし、KMPは現在、iOSに対して1つのモジュールのみの公開に対応しています。</p>
<p>例えば、共有コードベースに、<code>featureA</code>, <code>featureB</code>, <code>featureC</code>という3つのモジュールがあるとします。各モジュールは<code>data</code>モジュールに依存し、<code>data</code>モジュールは<code>api</code>モジュールに依存しています。</p>
<pre><code class="language-mermaid">graph LR;

api --&gt; data --&gt; featureA
data --&gt; featureB
data --&gt; featureC
</code></pre>
<p>これら3つのモジュールをiOSに公開したい場合、理想的なシナリオでは、iOS開発者は次のように必要なモジュールのみをインポートします。</p>
<pre><code class="language-swift">import featureA
import featureB

&lt;swift code here&gt;
</code></pre>
<p>ただし、KMPの制限により、このアプローチではiOSアプリ内でコードが重複することになります。</p>
<p>以下のような構造が望ましいのですが、</p>
<pre><code class="language-mermaid">graph LR;

subgraph KMP
api --&gt; data --&gt; featureA
data --&gt; featureB
data --&gt; featureC
end

featureA --&gt; iOSApp
featureB --&gt; iOSApp
featureC --&gt; iOSApp
</code></pre>
<p>実際には、以下のような構造（重複あり）になってしまいます。</p>
<pre><code class="language-mermaid">graph LR;

subgraph KMP
api --&gt; data --&gt; featureA
end

subgraph KMP1
api1(api copy) --&gt; data1(data copy) --&gt; featureB
end

subgraph KMP2
api2(api copy2) --&gt; data2(data copy2) --&gt; featureC
end

featureA --&gt; iOSApp
featureB --&gt; iOSApp
featureC --&gt; iOSApp


style api1 fill:#f88
style api2 fill:#f88
style data1 fill:#f88
style data2 fill:#f88
</code></pre>
<h3>アンブレラモジュール</h3>
<p>この制限を克服するために、<strong>アンブレラモジュール</strong>を導入しました。</p>
<p>アンブレラモジュールは、ソースコードを含まない「空の」モジュールであり、依存関係を管理するために使用されます。</p>
<pre><code class="language-mermaid">graph LR;

subgraph KMP
    subgraph Umbrella
        api --&gt; data --&gt; featureA
        data --&gt; featureB
        data --&gt; featureC
    end
end

Umbrella --&gt; iOSApp
style Umbrella fill:#8f88
</code></pre>
<p>こちらがbuild.gradle.ktsの例です：</p>
<pre><code class="language-kts:build.gradle.kts">kotlin {
    val xcf = XCFramework()
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach {
        it.binaries.framework {
            baseName = &quot;Umbrella&quot;
            binaryOption(&quot;bundleId&quot;, &quot;com.example.shared&quot;)
            export(project(&quot;:shared:featureA&quot;))
            export(project(&quot;:shared:featureB&quot;))
            export(project(&quot;:shared:featureC&quot;))
            xcf.add(this)
        }
    }
    sourceSets {
        val commonMain by getting {
            dependencies {
                api(project(&quot;:shared:featureA&quot;))
                api(project(&quot;:shared:featureB&quot;))
                api(project(&quot;:shared:featureC&quot;))
            }
        }
    }
}
</code></pre>
<p>アンブレラモジュールは、iOS開発者の統合プロセスを簡素化し、プラットフォーム間でシームレスかつ効率的な開発体験を確保します。</p>
<h2>2.5 CI：AndroidおよびiOSでの共有コードのテスト</h2>
<p>私たちは常にコードのテストを記述しており、共有コードも例外ではありません。プラットフォームの違いにより、一部の機能はiOSでは期待どおりに動作しない場合があります。互換性を確保するために、AndroidとiOSの両方でテストを実行します。AndroidはどのOSでもテストを実行できますが、iOSのテストはmacOSで実行する必要があります。</p>
<h1>3.KMPコードの配布</h1>
<p>KMPコードの記述が完了したら、次のステップはそれをiOSアプリに配布することです。</p>
<h2>3.1 KMPコードの配布オプション</h2>
<p>KMPコードは、ソースコードまたはバイナリで配布できます。</p>
<h3>ソースコードの配布</h3>
<p>ソースコード配布では、iOS開発者はKMPコードを自らコンパイルする必要があります。このアプローチでは、Java VMやGradleなどのツールを含むKotlinビルド環境をセットアップする必要があります。</p>
<h4>課題：</h4>
<ul>
<li>すべてのiOS開発者は、KMPビルド環境を設定する必要があります。</li>
<li>これにより、KMPコードを iOSプロジェクトに導入する際の複雑さが増します。</li>
</ul>
<h3>バイナリ配布</h3>
<p>より良いオプションはバイナリ配布です。私たちは、プリコンパイル済みのライブラリを提供することで、iOS開発者が追加のビルド環境を管理する必要性をなくし、共有コードの統合がはるかに簡単になるようにしています。</p>
<h4>利点：</h4>
<ul>
<li>iOS開発者のセットアップ作業を削減します。</li>
<li>環境間で一貫したビルドを確保します。</li>
</ul>
<h2>3.2 Swift Package Manager （SPM）</h2>
<p>iOSには主に2つの依存管理システムがあります。それは、CocoaPods と Swift Package Manager (SwiftPM)です。どちらを選択するかは、iOSチームの好みによります。幸いなことに、私たちのiOSチームは SwiftPMに完全移行しているため、SwiftPMのみに対応すれば済みます。</p>
<h3>Swift Packageとは?</h3>
<p>Swift Packageは基本的に、次を含むGitリポジトリです。</p>
<ul>
<li>Swiftソースコード</li>
<li>Package.swiftマニフェストファイル</li>
<li>Gitタグによるセマンティック・バージョニング</li>
</ul>
<h3>SwiftPMによるバイナリ配布</h3>
<p>SwiftPM 5.3以降、SwiftPMは<code>binaryTarget</code> に対応しており、ソースコードの代わりに予めコンパイルされたライブラリを配布できるようになっています。</p>
<h3>バイナリ配布によるSwift Packageの作成</h3>
<p>KMPコードをSwift Packageとして公開する方法を簡単に説明します。</p>
<ol>
<li>KMPコードを <code>.xcframework</code> にコンパイルします。</li>
<li><code>.xcframework</code> をzipファイルにパッケージ化し、そのチェックサムを計算します。</li>
<li>GitHubに新しいリリースページを作成し、リリースアセットの一部としてzipファイルをアップロードします。</li>
<li>リリースページからzipファイルのURLを取得します。</li>
<li>URLとチェックサムに基づいて <code>Package.swift</code> ファイルを生成します。</li>
<li><code>Package.swift</code> ファイルをコミットし、gitタグを追加してリリースをマークします。</li>
<li>gitタグをリリースページに関連付け、GitHubリリースを正式に公開します。</li>
</ol>
<p>詳細な手順については、[リモートSPMエクスポートに関するKMPドキュメント] を参照してください。(<a href="https://kotlinlang.org/docs/native-spm.html">https://kotlinlang.org/docs/native-spm.html</a>)</p>
<pre><code class="language-swift:Package.swift">// swift-tools-version:5.10

import PackageDescription

let packageName = &quot;Umbrella&quot;

let package = Package(
    name: packageName,
    platforms: [
        .iOS(.v13)
    ],
    products: [
        .library(
            name: packageName,
            targets: [packageName]),
    ],
    targets: [
        .binaryTarget(
            name: packageName,
            url: &quot;https://url/to/some/remote/xcframework.zip&quot;,
            checksum:&quot;The checksum of the ZIP archive that contains the XCFramework.&quot;
    ]
)
</code></pre>
<h2>3.3 配布の自動化</h2>
<p>手動での配布は時間がかかる場合があります。プロセスを円滑化するために、自動化用のGitHub Actionsワークフローを作成しました。</p>
<pre><code class="language-yaml:publish-kmp-ios.yml">name:Publish KMP for iOS
on:
  workflow_dispatch:
    inputs:
      release_version:
        description:&#39;Semantic Version&#39;
        required: true
        default:&#39;1.0.0&#39;

env:
  DEVELOPER_DIR: /Applications/Xcode_15.3.app

jobs:
  build:
    runs-on: macos-14
    steps:
      - name:Checkout
        uses: actions/checkout@master

      - name: set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version:&#39;17&#39;
          distribution: &#39;zulu&#39;

      - name:&quot;Build and Publish&quot;
        env:
          RELEASE_VERSION: ${{ github.event.inputs.release_version }}
          GH_TOKEN: ${{ github.token }}
        run: ./scripts/publish_iOS_Framework.sh $RELEASE_VERSION    
</code></pre>
<pre><code class="language-sh:publish_iOS_Framework.sh">#!/bin/sh
set -e

MODULE_NAME=&quot;&lt;your module name&gt;&quot;
VERSION=$1

# Github リリース用のバージョン名
RELEASE_VERSION=&quot;$MODULE_NAME-$VERSION&quot;

# Gitタグ名
TAG=&quot;$VERSION&quot;

TMP_BRANCH=&quot;kmp_release_$VERSION&quot;

# VERSIONがsemver仕様であるか確認
if [[ ! $VERSION =~ ^[0-9]+.[0-9]+.[0-9]+$ ]]; then
  echo &quot;VERSION should be in semver format like 1.0.0&quot;
  exit 1
fi


ZIPFILE=./shared/$MODULE_NAME/build/XCFrameworks/release/$MODULE_NAME.xcframework.zip

echo &quot;Building $MODULE_NAME $VERSION&quot;

./gradlew assembleKintoOneCoreReleaseXCFramework

echo &quot;creating zip file&quot;
pushd ./shared/$MODULE_NAME/build/XCFrameworks/release/
zip -r $MODULE_NAME.xcframework.zip $MODULE_NAME.xcframework
popd

# タグを取得
git fetch --tags

# 直前のリリースタグを取得
PREVIOUS_RELEASE_TAG=$(git tag --sort=-creatordate | grep -v ^version | head -n 1)
echo &quot;previous release tag: $PREVIOUS_RELEASE_TAG&quot;

# Github ドラフトリリースを作成
echo &quot;creating github release $RELEASE_VERSION&quot;
gh release create $RELEASE_VERSION -d --generate-notes --notes-start-tag $PREVIOUS_RELEASE_TAG
gh release upload $RELEASE_VERSION $ZIPFILE

echo &quot;retrieving asset api url&quot;
# Github リリースからアップロードされたzip ファイルのAsset APIのURLを取得
# 例: &quot;https://api.github.com/repos/{username}/{repo}/releases/assets/132406451&quot;
ASSET_API_URL=$(gh release view $RELEASE_VERSION --json assets | jq -r &#39;.assets[0].apiUrl&#39;)
# URLの末尾に拡張子(.zip)を追加
ASSET_API_URL=&quot;${ASSET_API_URL}.zip&quot;


# Package.swiftを生成
./scripts/generate_SPM_Manifest_File.sh $ZIPFILE $ASSET_API_URL

# Package.swiftをコミットし、タグを追加
git checkout -b $TMP_BRANCH

git add .
git commit -m &quot;release $VERSION&quot;
git tag -a $TAG -m &quot;$MODULE_NAME $VERSION&quot;
git push origin $TAG


# Githubリリースを更新し、新しいタグに置き換える
gh release edit $RELEASE_VERSION --tag $TAG
</code></pre>
<h1>4.AndroidおよびiOSの実装方法</h1>
<p>このプロジェクトで私たちは、Kotlin Multiplatform (KMP) を使用して、既存のアプリに新規共通モジュールを導入しました。プラットフォーム固有の潜在的な問題を最小限に抑えるために、AndroidとiOSで確実に動作する機能を慎重に選択して実装しました。OSに依存しない機能を選択して本番環境での初期テストのために実装をシンプルに保つことによって、クロスプラットフォームモジュールの確立に重点を置きました。以下は、機能選択基準と実装プロセスの概要です。</p>
<h2>4.1 機能の選択</h2>
<p>KMPを本番環境に導入する際の潜在的な問題を特定するために、プラットフォーム固有の実装に依存せず、かつ最小限の依存関係で扱える機能を優先しました。機能選択の基準に次を含めました：</p>
<ol>
<li><strong>OSに依存しない機能性</strong>：本番環境で予期しない問題が発生するのを避けるために、OSに依存しない機能を選択し、通信、ストレージ、権限など特定のOSレベル制御を必要とする要素を除外しました。</li>
<li><strong>追加ライブラリの最小化</strong>:メンテナンスのリスクを軽減するために、追加ライブラリに依存せずにKotlin標準ライブラリのみで実装できる機能を選択します。</li>
<li><strong>ライブラリの優先順位</strong>：ライブラリを選択する際に、まず公式Kotlinライブラリ、次に公式Kotlinドキュメントで推奨されているライブラリ、最後にサードパーティのライブラリ、と優先順位をつけました。</li>
</ol>
<p>これらの基準に基づいて、KMPで実装する最初のクロスプラットフォーム機能として<strong>入力検証</strong>を選択しました。そして、<strong>全角/半角文字変換</strong>機能を追加しました。</p>
<h2>Android</h2>
<h3>入力検証の実施</h3>
<p>デフォルトでは、Android実装にはライブラリ機能の不足やインターフェースの違いといった問題しかありませんが、それは大した問題ではありませんでした。</p>
<p>入力検証機能を、一般的なオブジェクト指向プログラミング (OOP) の原則に従って構造化し、再利用性と一貫性を重視しました。</p>
<p><strong>1.共通インターフェースの定義</strong>：両方のプラットフォーム間で入力を検証するための一貫した基盤を作るために、<code>Validator</code> インターフェースと <code>ValidationResult</code> インターフェースを定義しました。</p>
<pre><code class="language-kotlin">abstract class ValidationResult(
    /**
     * Informations about input and fail reason.
     */
    val arguments:Map&lt;String, Any?&gt;,
    requiredKeys:Set&lt;String&gt;
)

fun interface Validator&lt;T, R :ValidationResult&gt; {
    /**
     * @return validation result or `null` if the target is valid.
     */
    operator fun invoke(target:T):R?
}
</code></pre>
<p><strong>2.入力タイプ別のバリデータの実装</strong>：電子メールやパスワードの検証など、入力タイプごとにバリデータと結果クラスを分けて作成しました。</p>
<pre><code class="language-Kotlin">class IntRangeValidator(
    /**
     * min bound(inclusive).
     */
    val min:Int,
    /**
     * Max bound(inclusive).
     */
    val max:Int
) :Validator&lt;String, IntRangeValidationResult&gt; {
    companion object {
        const val PATTERN = &quot;0|(-?[1-9][0-9]*)&quot;

        val REGEX = PATTERN.toRegex()

        const val ARG_NUMBER = &quot;number&quot;
        const val ARG_RANGE = &quot;range&quot;
        const val ARG_PATTERN = &quot;pattern&quot;
    }

    val range = min..max

    override fun invoke(target:String):IntRangeValidationResult? {
        when {
            target.isEmpty() -&gt;
                return RequiredIntRangeValidationResult()

            !target.matches(REGEX) -&gt;
                return IllegalPatternIntRangeValidationResult(target, PATTERN)
        }

        return try {
            target.toInt(10).let { number -&gt;
                if (number !in range) {
                    OutOfRangeIntRangeValidationResult(target, range)
                } else {
                    null
                }
            }
        } catch (e:NumberFormatException) {
            OutOfRangeIntRangeValidationResult(target, range)
        }
    }
}
</code></pre>
<p><strong>3.テストコードの作成</strong>：プラットフォーム間でのモジュールの精度を検証するために、<code>kotlin-test</code>パッケージを使用して広範なテストケースを実施し、AndroidとiOSの両方で安定した機能を確保しました。</p>
<pre><code class="language-Kotlin">import kotlin.random.Random
import kotlin.test.AfterTest
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertContains
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith
import kotlin.test.assertIs
import kotlin.test.assertNotNull
import kotlin.test.assertNull

class IntRangeValidatorTest {
    private var min = 0
    private var max = 0
    private lateinit var validator:IntRangeValidator

    @BeforeTest
    fun setUp() {
        min = Random.nextInt()
        max = Random.nextInt(min + 1, Int.MAX_VALUE)
        validator = IntRangeValidator(min, max)
    }

    @AfterTest
    fun tearDown() {
        min = 0
        max = 0
    }

    @Test
    fun `invoke - decimal number string`() {
        val validator = IntRangeValidator(Int.MIN_VALUE, Int.MAX_VALUE)

        for (number in listOf(
            &quot;0&quot;,
            &quot;1&quot;,
            &quot;111&quot;,
            &quot;${Int.MAX_VALUE}&quot;,
            &quot;-1&quot;,
            &quot;${Int.MIN_VALUE}&quot;,
            &quot;${Random.nextInt(Int.MAX_VALUE)}&quot;,
            &quot;-${Random.nextInt(Int.MAX_VALUE - 1)}&quot;
        )) {
            // 以下の場合
            val result = validator(number)

            /// したがって、以下の通りになる
            assertNull(result)
        }
    }
}
</code></pre>
<h3>全角/半角文字変換の実装</h3>
<p>入力検証に加えて、文字変換機能を実装し、アプリケーション要件に基づいて全角文字と半角文字を自動的に変換しました。</p>
<p><strong>1.拡張可能なインターフェースの定義</strong>：多様で複雑な文字変換に対応するため、インターフェースを定義し、あらゆる変換処理を継承できるようにしました。Kotlinには、これを実装するのに役立つ関数型インターフェース (<code>fun interface</code>) と演算子関数 (<code>operator fun</code>) の機能が備わっています。</p>
<pre><code class="language-Kotlin">fun interface TextConverter {
    operator fun invoke(input:String):String

    operator fun plus(other:TextConverter) = TextConverter { input -&gt;
        other(this(input))
    }
}
</code></pre>
<p><strong>2.変換用のマッピング定数の定義</strong>：全角/半角文字とそれらの変換をリストアップした文字マッピングテーブルを作成し、定義済みマッピングを参照して変換できるようにしました。</p>
<pre><code class="language-Kotlin">open class SimpleTextConverter(
    val map:Map&lt;String, String&gt;
) :TextConverter {
    override operator fun invoke(input:String):String {
        var result = input
        for ((key, value) in map) {
            result = result.replace(key, value)
        }
        return result
    }
}

class RemoveLineSeparator(
    map:Map&lt;String, String&gt; = mapOf(
            &quot;\n&quot; to &quot;&quot;,
            &quot;\r&quot; to &quot;&quot;
        )
) :SimpleTextConverter(map)

object HalfwidthDigitToFullwidthDigitConverter :SimpleTextConverter(
    mapOf(
        &quot;0&quot; to &quot;０&quot;,
        &quot;1&quot; to &quot;１&quot;,
        &quot;2&quot; to &quot;２&quot;,
        &quot;3&quot; to &quot;３&quot;,
        &quot;4&quot; to &quot;４&quot;,
        &quot;5&quot; to &quot;５&quot;,
        &quot;6&quot; to &quot;６&quot;,
        &quot;7&quot; to &quot;７&quot;,
        &quot;8&quot; to &quot;８&quot;,
        &quot;9&quot; to &quot;９&quot;
    )
)

val NUMBER_CONVERTER = FullwidthDigitToHalfwidthDigitConverter +
    RemoveLineSeparator()
</code></pre>
<p><strong>3.自動変換機能</strong>：変換機能は、全角文字を半角文字に、またはその逆に自動変換するように設計されており、一貫性のある予測可能な入力体験を実現します。</p>
<p>これらのOS非依存性機能を選択し、それらをKMP で実装することで、AndroidとiOS間で確実に導入できる安定した再利用可能なモジュールを確立できました。</p>
<h2>iOSへの統合</h2>
<p>私たちのKMPコードはSwifｔ Packageとして配布され、iOSチームはXcodeGenを使用してXcodeプロジェクトファイルを管理しました。iOSアプリへのKMPコードの統合は、<code>project.yml</code>ファイルに4行のコードを追加するだけで簡単に実行できます。</p>
<pre><code class="language-diff">packages:
+  Umbrella:
+    url: https://github.com/your-org/your-android-repository
+    minorVersion:1.0.0
targets:
  App:
    dependencies:
+     - package:Umbrella
      - package: ...
</code></pre>
<p>ただし、私たちのコードはプライベートリポジトリに格納されているため、いくつかの追加設定が必要です。詳細については、以下をご覧ください：<a href="/posts/2024-12-10-credential-setup-for-private-repositories-in-swiftpm/">SwiftPMでのプライベートリポジトリの資格情報設定</a></p>
<h1>5.KMPクロスプラットフォームモジュール実装における課題</h1>
<p>KMP共通モジュールの開発中に、技術的な問題がいくつか発生しました。特に目立ったのは、基本的な機能、マルチバイト文字、符号化の処理に関する問題でした。以下は、これらの問題の概要と、それらをどのように解決したかについてです。</p>
<h2>Kotlin標準ライブラリはUnicodeコードポイントに対応していません</h2>
<p>入力検証において漢字やサロゲートペアなどのマルチバイト文字を正確に処理するために、Unicodeコードポイントベースの正規表現を実装することにしました。このアプローチにより、文字を単なる個々の文字として扱うのではなく、Unicode範囲内での文字の位置に基づいて文字を正確に一致させて検証できるようになりました。しかし、問題が発生しました。</p>
<p>Kotlinの <code>String</code> クラスは、Unicodeコードポイントの処理をネイティブに対応しておらず、この目的（特にサロゲートペア）のための公式ライブラリも提供していません。</p>
<p>そのため、コードポイントに基づいてマルチバイト文字を正確に処理するために、サードパーティのライブラリを使用しています。これにより、漢字などの複雑な文字を私たちの正規表現内でより正確に一致させることができるようになりました。</p>
<h2>非UTFの文字における符号化には対応していません</h2>
<p>レガシーシステムとの互換性を維持するには、Shift-JIS (MS932) の符号化に対応する必要がありました。しかし、KMPはShift-JIS符号化において、ネイティブに対応していません。</p>
<p>レガシーシステムへのテキスト転送では、MS932で符号化が可能であるか否かを確認する必要があり、そのため、<code>ktor-client</code>ライブラリを使用して符号化を処理することにしました。ただし、<code>ktor-client</code>のiOSバージョンはUTFベースの符号化スキームしか対応していないため、MS932符号化を実施するのは困難です。</p>
<p>MS932符号化の制限により、漢字検証のためのコードポイントの使用を断念しました。代わりに、検証に必要な漢字のリスト全体を含む定数を宣言し、必要に応じて参照できるように、これらをUnicodeコードポイントに変換しました。</p>
<h2>Unicodeコードポイントの問題</h2>
<p>全角文字と半角文字の変換を実施する際に、特定の文字間のコードポイントの不一致が発生し、単純な加算/減算アプローチが効果的でなくなりました。</p>
<p>例えば、日本語の全角文字<code>ァ&#39;</code>(<code>U+30A1</code>) と <code>ア</code> (<code>U+30A2</code>)は、コードポイントが1つ異なるだけです。対照的に、半角文字<code>ｧ</code>（<code>U+FF67</code>）と<code>ｱ</code>（<code>U+FF71</code>）は、コードポイントが10異なります。この不一致は、統一された変換アプローチが実現不可能であることを意味していました。</p>
<p>私たちは、すべての変換に対して定数マッピングテーブルを作成し、すべての全角文字と半角文字、およびそれぞれのマッピングを明確に定義することで、この問題を解決しました。このアプローチにより、変換操作においてさまざまな文字を正確に処理できるようになりました。</p>
<p>これらの問題に対処することで、私たちのKMP共通モジュールの安定性と完全性が向上し、AndroidとiOSの両方のプラットフォームで正確な機能を確保できました。</p>
<h1>6.効果</h1>
<h4>技術的な効果：</h4>
<ul>
<li><strong>プロセスの一貫性</strong>：KMPの実装により、iOSとAndroid間での動作の不一致が最小化され、QA時のエラー頻度が低下しました。</li>
<li><strong>コードの再利用性</strong>：Androidチームが検証したコードはiOSでも使用されるため、両方のプラットフォームにわたって開発効率が向上します。</li>
</ul>
<h4>OS連携と開発リソースの最適化：</h4>
<ul>
<li><strong>コミュニケーション負担の軽減</strong>：KMPにより、Androidチームはほとんどのメンテナンスを単独で処理できるようになり、iOSチームはバージョン アップグレードと軽微なメンテナンスに集中できるようになります。</li>
<li>これは、開発リソースのより効率的な使用と、チーム間の連携強化をもたらします。</li>
</ul>
<h4>プロジェクト管理上の問題：</h4>
<ul>
<li><strong>開発およびメンテナンスのコスト</strong>：初期設定には時間がかかりますが、その後は通常通り開発を続けることができます。ただし、Android固有のライブラリおよびJavaベースのライブラリの使用には制限があるため、開発コストが増加する可能性があります。</li>
<li><strong>リソースの割り当て</strong>：Androidチームに重点を置いた開発プロセスは、繁忙期におけるリソース不足につながる可能性があります。</li>
<li>KMPで実装された機能は主にAndroid チームが管理しているため、iOSチームの理解度は比較的低く、バランスの取れたリソース配分とトレーニングが求められます。</li>
</ul>
<h1>7.今後に向けて：今後の計画と課題</h1>
<h2>継続的な教育とトレーニングを通じた将来の拡張計画の実施</h2>
<p>現在、私たちのチームは、Kotlin Multiplatform（KMP）技術をより効果的に活用するために、社内教育およびトレーニングプログラムの開発と実施に取り組んでいます。このプログラムは技術的な詳細にとどまらず、チームワークやプロジェクト管理スキルの向上にも重点を置いています。これにより私たちは、技術力の向上だけでなく、プロジェクトの効果的な管理やチーム間の連携強化も目指しています。</p>
<h2>今後の計画：KMPへの共通ロジックの移行</h2>
<p>今後、私たちのチームは、より多くの共通ロジックをKMPへ移行する予定です。これにより、iOSアプリケーションとAndroidアプリケーション間でのコード再利用を最大化し、メンテナンスの複雑さを軽減することで、開発効率の向上を図ります。</p>
<h3>移行へのキーロジック：</h3>
<ul>
<li><strong>APIクライアント：</strong> BFF、OCR</li>
<li><strong>ビジネスロジック：</strong> キャッシュ管理など</li>
<li><strong>ユーティリティ：</strong> テキストの書式設定（時間、使用料）、バージョン比較（利用規約）など</li>
<li><strong>ローカルストレージ:</strong> アプリ設定、認証トークンなど</li>
</ul>
<p>これらの計画を実行することで、クロスプラットフォーム開発の効率と連携が強化され、私たちのチームがプラットフォーム間での開発タスクをより効果的に行えるようになることが期待されます。</p>
<p>お読みいただきありがとうございました。これが、まだKMPテクノロジーを適用していないチームにとって、役立つ参考資料となることを願っています。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/KMP/kmp-image1.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Sysdig Kraken Hunter ワークショップ 参加レポート]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-14-Sysdig_Kraken_Hunter_ワークショップ_参加レポート/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-14-Sysdig_Kraken_Hunter_ワークショップ_参加レポート/</guid>
            <pubDate>Mon, 14 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Sysdig Kraken Hunter ワークショップ 参加レポート]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは！KINTO テクノロジーズ セキュリティ・プライバシー グループのたなちゅーです！普段は、SIEM を活用したログ監視・分析や監視体制構築、SCoE グループ（<a href="https://blog.kinto-technologies.com/posts/2024-05-13-SCoE/">SCoE グループとは？</a>）の一部プロジェクトに参画してクラウド周りのセキュリティ業務に従事しています。自己紹介は<a href="https://blog.kinto-technologies.com/posts/2025-02-26-newcomers-introduction-oct-nov/">こちら</a>。</p>
<p>本ブログでは、2025年3月26日に名古屋駅近くにある<a href="https://corp.collabo-style.co.jp/">コラボスタイル</a>さんのイベントスペースで開催された『<a href="https://go.sysdig.com/kraken-hunter">Sysdig Kraken Hunter ワークショップ</a>』の参加レポートをお届けします。</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/%E3%82%A4%E3%83%99%E3%83%B3%E3%83%88%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%81%AE%E6%A7%98%E5%AD%90.png" alt="イベントスペースの様子.png"> <em>イベントスペースの様子</em></p>
<h1>KINTOテクノロジーズの Sysdig Secure 活用</h1>
<p>KINTO テクノロジーズでは、Sysdig Secure を主に、Cloud Security Posture Management（CSPM）と Cloud Detection Response（CDR）に利用しています。詳細については、こちらのブログにまとまっていますので、是非、ご覧ください。</p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-12-14-CloudSecurityEngineer/">KTC クラウドセキュリティエンジニアのとある一日</a></p>
<h1>『Sysdig Kraken Hunter ワークショップ』とは？</h1>
<p>まずSysdigとは、ネットワークキャプチャツールで有名な Wireshark の共同開発者である Loris Degioanni 氏が創業した企業で、同社が開発したクラウドネイティブ脅威検知のオープンソース標準である <a href="https://falco.org/">Falco</a> を軸に、クラウドやコンテナ向けのセキュリティソリューションを提供しています。弊社では、クラウド環境の権限設定やアカウント/リソース作成などのアクティビティを監視するために Sysdig Secure 使用しています。</p>
<p>『Sysdig Kraken Hunter ワークショップ』では、Amazon EKSのデモ環境に対して擬似的な攻撃を行い、Sysdigで検知や調査、対応などの一連の操作をモジュールに分けて体験するワークショップです。また、ワークショップ後の試験に合格すると、Kraken Hunter の認定バッジが付与されます。</p>
<p>本ブログでは、特に興味深かった3つのモジュールについて紹介します。</p>
<h1>モジュール１：擬似攻撃とイベント調査</h1>
<p>ここでは、実際に Amazon Elastic Kubernetes Service（Amazon EKS）のデモ環境へ擬似的な攻撃を行い、Sysdig Secureで検知や調査を行いました。</p>
<p>まず、提供されたドキュメントの手順に沿って、Amazon EKS のデモ環境へリモードコード実行（RCE）をシミュレートした以下のような擬似的な攻撃を行いました。</p>
<ul>
<li>システム上の任意のファイルの内容を取得/書き込み/実行</li>
<li>システム上へファイルをダウンロード</li>
</ul>
<p>擬似的な攻撃を行った後、ブラウザで Sysdig Secure のコンソールへアクセスし、攻撃を行った対象リソースの状態を見ると、攻撃に関連するイベントを検知していることを確認できます。</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/insights2.png" alt="insights2.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">参照：sysdig-aws workshop-instructions-JP</a></em></p>
<p>さらに調査を進めると、先ほどの攻撃を Sysdig Secure がリアルタイムで検知していることが確認できます。</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/insights3.png" alt="insights3.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">参照：sysdig-aws workshop-instructions-JP</a></em></p>
<p>このような流れで、擬似的な攻撃をして、Sysdig Secure のコンソールでどのように調査できるのか、攻撃を検知するのかを体験していきました。</p>
<p>自分で攻撃しながらどのように Sysdig Secure で調査できるのかを体験できるため、「Sysdig Secure でなにができるのか」を理解しながら進めることができたように感じました。</p>
<h1>モジュール２：ホストとコンテナの脆弱性管理</h1>
<p>このモジュールでは、ホストとコンテナの脆弱性管理に関する Sysdig Secure の機能を体験しました。弊社で開発しているプロダクトも、コンテナを使用してマイクロサービス化しているため、関心の高い内容です。</p>
<p>Sysdig Secure は脆弱性管理機能として、「ランタイム脆弱性スキャン」や「パイプライン脆弱性スキャン」、「レジストリ脆弱性スキャン」があるそうです。</p>
<p>「ランタイム脆弱性スキャン」では、過去15分以内に監視対象環境で実行されたすべてのコンテナとSysdig Secure の Agent がインストールされているすべてのホスト/ノードがリストされます。使用中の脆弱性の数と重要度に基づいて、自動的に重要度順にソートされて表示されるため、最も対応が必要なリソースを簡単に確認できます。</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/vuln1.png" alt="vuln1.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">参照：sysdig-aws workshop-instructions-JP</a></em></p>
<p>また、リストされたリソースをクリックしてドリルダウンすることで、脆弱性の詳細を確認できます。</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/vuln4.png" alt="vuln4.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">参照：sysdig-aws workshop-instructions-JP</a></em></p>
<p>「パイプライン脆弱性スキャン」では、コンテナイメージがレジストリや実行環境に置かれる前に脆弱性をスキャンします。また、「レジストリ脆弱性スキャン」では、レジストリ内のイメージの脆弱性をスキャンします。このように、コンテナイメージの開発/運用フェーズごとに脆弱性の有無をチェックすることができます。</p>
<p>脆弱性を管理するセキュリティ製品はさまざま存在しますが、Sysdig Secure のコンソールは UI が洗練されており、直感的に操作できそうだなと思いました。</p>
<h1>モジュール３：コンテナのポスチャー/コンプライアンスの管理</h1>
<p>ここでは、クラウド環境のポスチャー/コンプライアンスを管理する Sysdig Secure の機能について体験しました。みなさんもよく見聞きしている通り、クラウド環境の設定ミスに起因するセキュリティインシデントは多数報告されているため、フルクラウド環境でプロダクトを開発している弊社にとっても人ごとではないと考えており、注目している機能の一つです。</p>
<p>ポスチャー/コンプライアンス管理機能として、Sysdig Secure では、自社環境が CIS、NIST、SOC 2、PCI DSS、ISO 27001 などの一般的な規格に準拠しているか確認できます。</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/posture1.png" alt="posture1.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">参照：sysdig-aws workshop-instructions-JP</a></em></p>
<p>また、規格に準拠していないリソースをリストし、どのように修正すべきかの手順を確認することもできます。すべてのシチュエーションで実用性の高い手順を確認できるかは未知数ですが、修正手順を調査する工数を削減できることは、管理者としてありがたい配慮だなと感じました。</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/posture2.png" alt="posture2.png"> <em><a href="https://github.com/yotakeu/sysdig-aws-workshop-instructions-JP">参照：sysdig-aws workshop-instructions-JP</a></em></p>
<h1>Kraken Hunter 認定試験</h1>
<p>Kraken Hunter 認定試験では、試験専用のWebページで30〜40問ほどの問題が出題されました。ワークショップ内で紹介された事柄が出題されるため、真面目に取り組んでいれば合格を狙えると思います。</p>
<p>ワークショップの冒頭に紹介された細かな内容があり苦戦しましたが、なんとか認定試験に合格することができました！</p>
<p>以下は合格者に付与される認定バッジです。</p>
<p><img src="/assets/blog/authors/tanachu/2025-04-14_sysdig_event_report/KrakenHunter_CertBadge.png" alt="KrakenHunter_CertBadge.png"> <em>Kraken Hunter 認定バッジ</em></p>
<h1>今後の Sysdig Secure の利用</h1>
<p>弊社では Sysdig Secure を使い倒すべく、以下の利活用を検討・推進しています。</p>
<ul>
<li>CSPM については、弊社ガバナンスルールに沿った独自ポリシールールを <a href="https://www.openpolicyagent.org/docs/latest/policy-language/">Rego</a> で作成し、弊社ガバナンスに沿ったクラウドセキュリティの担保</li>
<li>CDR については、<a href="https://falco.org/">Falco</a> による独自ルールを作成し、弊社環境に則した脅威検知対象の拡大</li>
<li>コンテナワークロードのセキュリティ担保のために、CWP（Cloud workload protection）の検証と導入</li>
</ul>
<h1>まとめ</h1>
<p>今回の Sysdig Kraken Hunter ワークショップでは、Amazon EKSのデモ環境に対して擬似的な攻撃を行い、Sysdig Secure で検知や調査、対応などの一連の操作を体験しました。</p>
<p>弊社では Sysdig Secure の一部の機能のみ使用しているため、今回のワークショップで紹介された機能は、初めてのものばかりで操作に不慣れな部分もありましたが、Sysdig Secure で何ができるのかを理解する良い機会となりました。</p>
<p>また、オフラインのワークショップに参加することで、他の企業が抱える課題や取り組みなど、生の声を聞くことができました。このような機会を作ってくださった運営のみなさまに感謝いたします。</p>
<h1>さいごに</h1>
<p>私の所属するセキュリティ・プライバシー グループやこのワークショップに一緒に参加した SCoE グループでは、一緒に働いてくれる仲間を募集しています。クラウドセキュリティの実務経験がある方も、経験はないけれど興味がある方も大歓迎です。お気軽にお問い合わせください。</p>
<p>詳しくは、<a href="https://hrmos.co/pages/kinto-technologies/jobs?category=2037393016523087875">こちらをご確認ください</a>。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Flutter Development Efficiency: A Step-by-Step Guide to Automating Web Previews Using GitHub Actions and Firebase Hosting]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-20-flutter-web-preview-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-20-flutter-web-preview-en/</guid>
            <pubDate>Fri, 11 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Introduction to using Firebase Hosting and GitHub Actions to automatically generate web previews and enhance the efficiency of code review in Flutter app development]]></description>
            <content:encoded><![CDATA[<p>This article is the entry for day 20 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>🎅🎄</p>
<h1>Introduction</h1>
<p>Hello, my name is Hand-Tomi and I am a mobile app (Flutter) developer at KINTO Technologies (<strong>KTC</strong>).</p>
<p>Recently, KTC has launched the “Flutter Team”, and now is actively developing different applications. I would like to introduce some techniques we’ve implemented and found to be particularly effective. This time, I will explain how to use GitHub Actions and Firebase Hosting to provide convenient web previews during code reviews. I hope this article is helpful to you.</p>
<h3>🎯Goal</h3>
<p>The goal of this article is to create a system where a debug web page link is automatically posted as a comment by adding the comment <code>/preview</code> to a pull request.</p>
<p>![preview_comment_and_link](/assets/blog/authors/semyeong/2024-12-20-flutter-web-preview/preview_comment_and_link.png =600x)</p>
<p>When you click the link above, the Flutter project application will be displayed as shown below.</p>
<p>![preview](/assets/blog/authors/semyeong/2024-12-20-flutter-web-preview/preview.png =400x)</p>
<h3>🔍Why Use Web Preview?</h3>
<p>When reviewing code, you need to clone the source code, configure it, and build it to check how it operates, but this process takes time. On the other hand, if you set up <strong>Web Preview</strong>, you can easily and quickly check how it operates. From here on, I will introduce how to implement this system step by step.</p>
<h1>Setting up Firebase</h1>
<h3>🌐Creating a Firebase Project</h3>
<p>If you don&#39;t have a Firebase project yet, create a new project from the console.</p>
<p><img src="/assets/blog/authors/semyeong/2024-12-20-flutter-web-preview/create_firebase_project_1.png" alt="create_firebase_project_1"></p>
<p>The project name is &quot;sample&quot;.</p>
<p><img src="/assets/blog/authors/semyeong/2024-12-20-flutter-web-preview/create_firebase_project_2.png" alt="create_firebase_project_2"></p>
<p>If you don&#39;t plan to use other features, you can disable them (though there&#39;s no harm in leaving them enabled).</p>
<p><img src="/assets/blog/authors/semyeong/2024-12-20-flutter-web-preview/create_firebase_project_3.png" alt="create_firebase_project_3"></p>
<p>Wait a while</p>
<p><img src="/assets/blog/authors/semyeong/2024-12-20-flutter-web-preview/create_firebase_project_4.png" alt="create_firebase_project_4"></p>
<p>The project creation is complete!</p>
<h3>⚙️ Setting up Firebase CLI</h3>
<p>I&#39;m planning to set up Firebase Hosting for the Flutter project. Since you can easily set it up using the Firebase CLI, let&#39;s set it up in the Terminal.</p>
<h4><strong>1. Installing Firebase CLI</strong></h4>
<p>There are several ways to install the Firebase CLI, but on macOS with npm installed, you can easily install it using the following command:</p>
<pre><code>npm install -g firebase-tools
</code></pre>
<p>For installation on other environments, please see <a href="https://firebase.google.com/docs/cli?hl=ja#install_the_firebase_cli">here</a>.</p>
<h4><strong>2. Log in to Firebase CLI</strong></h4>
<p>Run the following command to log in to Firebase on the CLI.</p>
<pre><code>firebase login
</code></pre>
<h3>🔧 Setting up Firebase Hosting</h3>
<p>Now that we&#39;re ready, let&#39;s set up Firebase Hosting for the Flutter project.</p>
<h4><strong>1. Enabling webframeworks</strong></h4>
<p>To deploy a Flutter application to Firebase Hosting, you need to enable the experimental feature <code>webframeworks</code>.</p>
<pre><code>firebase experiments:enable webframeworks
</code></pre>
<h4><strong>2. Initializing Firebase Hosting</strong></h4>
<p>In the root directory of your Flutter project, run the following command to set up Firebase:</p>
<pre><code>firebase init hosting
</code></pre>
<p>After running the above command, you will be asked the following questions:</p>
<pre><code># For the Firebase project, select the sample project you created earlier.
? Please select an option: Use an existing project
? Select a default Firebase project for this directory: sample-1234 (sample)

# It&#39;s fine to answer &quot;Yes&quot; here.
? Detected an existing Flutter Web codebase in the current directory, should we use this? Yes

# It&#39;s a question about region selection. I chose the default &quot;us-central1 (Iowa).&quot;
? In which region would you like to host server-side content, if applicable? us-central1 (Iowa)

# Since I plan to create it myself, I selected &quot;No.&quot;
? Set up automatic builds and deploys with GitHub? No

i  Writing configuration info to firebase.json...
i  Writing project information to .firebaserc...

✔  Firebase initialization complete!
</code></pre>
<p>Once you answer the questions, a <code>firebase.json</code> file will be generated.</p>
<p>:::message alert If your Flutter project does not include the Web platform, you may encounter an error. In that case, run the following command to add a Web platform:</p>
<pre><code>flutter create . --platform web
</code></pre>
<p>:::</p>
<h4><strong>3. Deploy</strong></h4>
<p>Let&#39;s try deploying it.</p>
<pre><code>firebase deploy
</code></pre>
<p>When you run the above command, the Hosting URL will be displayed as shown below.</p>
<pre><code>...
✔  Deploy complete!

Project Console: https://console.firebase.google.com/project/sample-1234/overview
Hosting URL: https://sample-1234.web.app
</code></pre>
<p>If you open this URL, you can see that your Flutter project is displayed correctly.</p>
<h1>Creating GitHub Actions</h1>
<p>Next, let&#39;s create a YAML file that will be executed when you comment <code>/preview</code> on a pull request.</p>
<p><img src="/assets/blog/authors/semyeong/2024-12-20-flutter-web-preview/preview_comment.png" alt="preview_comment"></p>
<h3>🔑 Preparing your Firebase Service Account Key</h3>
<p>To deploy to Firebase through GitHub Actions, you need a Firebase service account key. To easily obtain the key, use the following command:</p>
<pre><code>firebase init hosting:github 
</code></pre>
<p>After entering the above command, you will be asked the question below. Specify the repository containing the source code in the format <code>user/repository</code>.</p>
<pre><code># Enter a GitHub repository. Ｗrite it as `user/repository`.
? For which GitHub repository would you like to set up a GitHub workflow? (format: user/repository) Hand-Tomi/sample
</code></pre>
<p>Firebase will then automatically set the service account key in the Secrets of the GitHub repository and provide you with the Secrets constant name as shown below (e.g. <code>FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234</code>). Save this constant name.</p>
<pre><code>✔  Created service account github-action-1234 with Firebase Hosting admin permissions.
✔  Uploaded service account JSON to GitHub as secret FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234.
i  You can manage your secrets at https://github.com/Hand-Tomi/sample/settings/secrets.
</code></pre>
<p>Next, you will be asked the following question, but since you have everything you need, press <code>Control+C</code> (or in the case of Windows, <code>Ctrl+C</code> ) to exit.</p>
<pre><code>? Set up the workflow to run a build script before every deploy?
</code></pre>
<h3>✍️ Create a YAML File for GitHub Actions</h3>
<p>Now it&#39;s time to create a YAML file for GitHub Actions.</p>
<p>Create a YAML file in the <code>.github/workflows</code> directory at the root of the Flutter project and add the following code:</p>
<pre><code class="language-yaml">name: Command Execute Deploy Web

on:
  issue_comment:
    types: [created]

jobs:
  deploy-web:
    if: ${{ github.event.issue.pull_request &amp;&amp; github.event.comment.body == &#39;/preview&#39; }}
    name: Deploy Web
    runs-on: ubuntu-latest
    concurrency:
      group: ${{ github.workflow }}-${{ github.event.issue.number }}
      cancel-in-progress: true

    steps:
      - uses: actions/checkout@v4
        with:
          ref: refs/pull/${{ github.event.issue.number }}/head
          fetch-depth: 0

      - name: Set Up Flutter
        uses: subosito/flutter-action@v2
        with:
          channel: &#39;stable&#39;

      - name: Install Dependencies
        run: flutter pub get
            
      - id: deploy-web
        name: Deploy to Firebase Hosting
        uses: FirebaseExtended/action-hosting-deploy@v0
        with:
          firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234 }}
          expires: 7d
          channelId: &quot;issue_number_${{ github.event.issue.number }}&quot;
        env:
          FIREBASE_CLI_EXPERIMENTS: webframeworks

      - name: Comment on Success
        if: success()
        uses: peter-evans/create-or-update-comment@v4
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
          issue-number: ${{ github.event.issue.number }}
          body: |
            ✅ Preview has been deployed.
            - **Link**: ${{ steps.deploy-web.outputs.details_url }}
</code></pre>
<p>For <code>firebaseServiceAccount</code>, specify the Secrets’constant name you created beforehand (e.g. <code>FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234</code>).</p>
<pre><code class="language-yaml">firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234 }}
</code></pre>
<p>After that, when you merge into the relevant repository and leave a comment <code>/preview</code> in the pull request, Actions will be executed automatically, and GitHub Actions will comment a link.</p>
<p><img src="/assets/blog/authors/semyeong/2024-12-20-flutter-web-preview/preview_link_comment.png" alt="preview_link_comment"></p>
<h3>💡Reference: Explanation of the YAML Code for GitHub Actions</h3>
<p>The explanation for the YAML code above is as follows:</p>
<h4><strong>Execution Timing</strong></h4>
<pre><code class="language-yaml">on:
  issue_comment:
    types: [created]
</code></pre>
<p>When you use <code>issue_comment</code>, a workflow is automatically executed when a comment is created. This comment applies not only to pull requests but also when a comment is made on an Issue.</p>
<p>In this article, I want to limit the scope to pull request comments, so I will include <code>github.event.issue.pull_request</code> in <code>if</code> of <code>jobs</code>, ensuring that only pull requests are executed.</p>
<pre><code class="language-yaml">jobs:
  deploy-web:
    if: ${{ github.event.issue.pull_request &amp;&amp; github.event.comment.body == &#39;/preview&#39; }}
</code></pre>
<p>Additionally, when using <code>issue_comment</code>, you need to change the checkout location. <code>issue_comment</code> does not have the latest commit information for the current pull request, so if you check it out as it is, it will check out to the latest commit on the default branch.</p>
<p>Therefore, you need to specify <code>ref</code> in <code>actions/checkout</code> as follows (Reference: <a href="https://github.com/actions/checkout/issues/331#issuecomment-1438220926">https://github.com/actions/checkout/issues/331#issuecomment-1438220926</a>).</p>
<pre><code class="language-yaml">- uses: actions/checkout@v4
  with:
    ref: refs/pull/${{ github.event.issue.number }}/head
    fetch-depth: 0
</code></pre>
<h4><strong>Check Comment Message</strong></h4>
<p>Check whether the comment message is <code>/preview</code>. Check <code>github.event.comment.body</code> as shown below, and only if the message is <code>/preview</code>, execute the process in the deploy-web job.</p>
<pre><code class="language-yaml">jobs:
  deploy-web:
    if: ${{ github.event.issue.pull_request &amp;&amp; github.event.comment.body == &#39;/preview&#39; }}
</code></pre>
<h4><strong>Prevention of Concurrent Execution</strong></h4>
<p>If you leave <code>/preview</code> as is and immediately comment <code>/preview</code> again, concurrent execution may occur.</p>
<pre><code class="language-yaml">concurrency:
  group: ${{ github.workflow }}-${{ github.event.issue.number }}
  cancel-in-progress: true
</code></pre>
<p>In this case, GitHub Actions prevents concurrent execution through <code>concurrency</code>.</p>
<p>The important thing is to place it under <code>jobs</code>.</p>
<pre><code class="language-yaml">jobs:
  deploy-web:
    if: ${{ github.event.issue.pull_request &amp;&amp; github.event.comment.body == &#39;/preview&#39; }}
    name: Deploy Web
    runs-on: ubuntu-latest
    concurrency:
      group: ${{ github.workflow }}-${{ github.event.issue.number }}
      cancel-in-progress: true
</code></pre>
<p>If you do not place it under <code>jobs</code> as shown above, <code>concurrency</code> will be executed without checking <code>github.event.comment.body == &#39;/preview&#39;</code> in <code>if</code>, and if you leave a comment other than <code>/preview</code> immediately after commenting <code>/preview</code>, the Action will not be executed.</p>
<h4><strong>Deploy</strong></h4>
<p>The following steps are for deploying to Firebase Hosting.</p>
<pre><code class="language-yaml">- id: deploy-web
  name: Deploy to Firebase Hosting
  uses: FirebaseExtended/action-hosting-deploy@v0
  with:
    firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234 }}
    expires: 7d
    channelId: &quot;issue_number_${{ github.event.issue.number }}&quot;
  env:
    FIREBASE_CLI_EXPERIMENTS: webframeworks
</code></pre>
<ul>
<li><code>firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SAMPLE_1234 }}</code>: This is the Firebase service account authentication key. Enter the one you previously obtained.</li>
<li><code>expires: 7d</code>: This is the expiration date. If you set it like this, your preview site will expire after 7 days.</li>
<li><code>channelId: &quot;issue_number_${{ github.event.issue.number }}&quot;</code>: This is the name of the Firebase Preview channel. If you specify a <code>channelId</code> other than <code>live</code>, it will be deployed to Firebase Preview and you can set the expiration date.</li>
<li><code>FIREBASE_CLI_EXPERIMENTS: webframeworks</code>: This uses <code>webframeworks</code>, an experimental feature of Firebase CLI. It is indispensable for Flutter Web.</li>
</ul>
<h4><strong>Link Comments</strong></h4>
<p>A comment was left for the link using <code>peter-evans/create-or-update-comment</code>. By using this, you can easily leave reactions and add or edit comments.</p>
<pre><code class="language-yaml">- name: Comment on Success
  if: success()
  uses: peter-evans/create-or-update-comment@v4
  with:
    token: ${{ secrets.GITHUB_TOKEN }}
    issue-number: ${{ github.event.issue.number }}
    body: |
      ✅ Preview has been deployed.
      - **Link**: ${{ steps.deploy-web.outputs.details_url }}
</code></pre>
<ul>
<li><code>if: success()</code>: Only if successful will this step be executed.</li>
<li><code>token: ${{ secrets.GITHUB_TOKEN }}</code>: You need a <code>GITHUB_TOKEN</code> to leave a comment. No additional setup is required.</li>
<li><code>issue-number: ${{ github.event.issue.number }}</code>: This specifies which issue to comment on. When the workflow was executed via <code>issue_comment</code>, the issue number can be confirmed with <code>github.event.issue.number</code>.</li>
<li><code>steps.deploy-web.outputs.details_url</code>: This displays the URL obtained from the deployment step above.<ul>
<li>If you want to include other information, see <a href="https://github.com/FirebaseExtended/action-hosting-deploy#outputs">here</a>.</li>
</ul>
</li>
</ul>
<h1>Conclusion</h1>
<p>The techniques introduced in this article will enable faster and easier verification of operation during code reviews than before. This will improve the development efficiency of the entire team, leading to the delivery of better products.</p>
<p>However, when introducing a web preview for debugging, it is essential to consider both security concerns and how to resolve OS-specific differences.  Using the OS&#39;s functions extensively may result in the disadvantages outweighing the advantages.</p>
<p>However, in projects that don&#39;t heavily rely on OS functions, the advantages outweigh the disadvantages, so I encourage you to introduce it into your development environment.</p>
<p>Also, be sure to check out other articles written by the Flutter team members!</p>
<ul>
<li><a href="https://blog.kinto-technologies.com/posts/2024-12-03-CustompaintQrCodeBorder">Flutter development: Designing the border of a QR code using CustomPaint and Path</a></li>
</ul>
<p>Thank you for reading this blog to the end.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/semyeong/2024-12-20-flutter-web-preview/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[生成AIの未来：いまだに解決されない課題]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-11-AI_future_challenges/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-11-AI_future_challenges/</guid>
            <pubDate>Fri, 11 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[近い将来、生成AIが直面する課題と、その潜在的な解決策について]]></description>
            <content:encoded><![CDATA[<p>先日、同僚との何気ない会話の中で、こんな言葉をふいに投げかけられました。
「AIって、あっという間にここまで来たよね。あと5年もしたら、何ができるようになるか想像もつかないよ。」</p>
<p>自分はAIの専門家ではありません。ですが、たまたまその分野について少し調べたことがあるんです。
そして、その問いに対する答えは「もっと進化してすごくなる」という単純なものではありません。</p>
<p>というのも、AIの発展には、その技術の本質に根ざした壁がいくつか存在しているのです。</p>
<p>繰り返しますが、自分は専門家ではありません。
ただ、その壁とは何か、そしてそれをどう乗り越えられる可能性があるのか、それをお話ししたいと思います。</p>
<h2>ボトルネックとしての人間</h2>
<p>ご存知のとおり、大規模言語モデルをはじめとする生成AIは、学習のためにデータを必要とします。<br>しかも、その量は<strong>膨大</strong>です。</p>
<p>そのデータは、Webクローリングやスクレイピングを通じてインターネット上の公開情報から収集されたり、本やコードのリポジトリなどから集められたりします。<br>そして重要なのは、それらのコンテンツは基本的に<strong>人間が作っている</strong>という点です。<br>ですが、私たち人間は、AIが消費できる速度で新しいデータを作るには、あまりにも遅すぎるのです。</p>
<p><a href="https://x.com/epochairesearch">Epoch AI研究所</a>のPablo Villalobosさんが執筆した<a href="https://arxiv.org/abs/2211.04325">論文</a>によると、現在の傾向が続けば、<strong>2026年から2032年の間</strong>に、高品質で公開されている人間のテキストデータが枯渇するとされています。<br>つまり、それ以降は「データを増やしてスケールさせる」というアプローチが通用しなくなるかもしれません。なぜなら、学習に使える新しい人間由来のコンテンツが、もう十分に残っていないからです。</p>
<p><img src="/assets/blog/authors/vorona/ai_challenges/trends_plot.png" alt="trends">
<em>人間が生成した公開テキストの実効ストックおよびLLM学習におけるデータ消費量の予測</em></p>
<p>データの再利用（いわゆる<strong>マルチエポック学習</strong>）は一定の効果がありますが、根本的な解決にはなりません。</p>
<p>さらに悪いことに、現在急増しているデータの中には、質の低いものが多く含まれています。例えば、スパム、SNSのコメント、極端に偏った情報、誤情報、違法なコンテンツなどです。</p>
<p>また、人間が生成するデータは、英語のような広く使われている言語に比べて、あまり普及していない言語では自然と蓄積のペースが遅くなることも指摘しておくべきかもしれません。<br>そのため、そういった言語においては「人間が作るデータ量」と「AIのデータ需要」とのギャップが、さらに深刻になる可能性があります。</p>
<p>では、どうすればよいのでしょうか？ 提案されている解決策の例としては、以下のようなものがあります：</p>
<ul>
<li><p><strong>合成データ（つまりAIが生成したデータ）を学習に使う</strong><br>分野によっては効果が見込めますが、「モデル崩壊」というリスクも伴います。この問題については次のセクションで詳しく説明します。</p>
</li>
<li><p><strong>非公開データを活用する</strong><br>つまり、企業などが保有しているデータをAI学習に利用するということです。当然ながら、法的・倫理的に重大な問題が生じます。実際に、<a href="https://arstechnica.com/information-technology/2023/08/the-new-york-times-prohibits-ai-vendors-from-devouring-its-content/?utm_source=chatgpt.com">New York Times</a>のように、自社コンテンツのスクレイピングをAIベンダーに禁止する企業も出てきています。</p>
</li>
<li><p><strong>効率性の向上</strong><br>単に大きくするのではなく、<strong>賢く学習させる</strong>というアプローチです。<br>実際、最近のモデルではその兆しが見え始めています。ChatGPTなどを使っていると、「推論（reasoning）」のような、ただの記憶の再生ではなく、複数のステップを論理的につなげる動きが見られるようになっています。</p>
</li>
</ul>
<h2>生成モデルの近親交配</h2>
<p>先ほど触れたように、学習に使えるデータ量を増やす方法のひとつが、データを<strong>生成</strong>してしまうことです。<br>ですが、これには独自のリスクがついてきます。</p>
<p><a href="https://arxiv.org/abs/2305.17493">こちらの論文</a>では、ケンブリッジ大学のZakhar Shumaylovさんが、「人間ではなく、過去のAIモデルが生成したデータで次世代のモデルを学習させると何が起こるのか？」を調査しています。</p>
<p>著者らは、<strong>モデル崩壊</strong>（Model Collapse）と呼ばれる危険なフィードバックループを指摘しています。<br>AI生成データでの学習が繰り返されると、モデルは現実世界の本来のデータ分布から徐々にズレていきます。<br>その結果、出力はより一般的に、単調に、そして歪んだものになっていくのです。特に、まれで繊細な特徴は失われやすくなります。<br>これは主に以下の2つの理由によって起こります：</p>
<ol>
<li>有限なサンプルにより<strong>統計的な誤差</strong>が世代を重ねるごとに蓄積される  </li>
<li>複雑な分布を完全には再現できないことで<strong>機能的な誤差</strong>が生じる</li>
</ol>
<p><img src="/assets/blog/authors/vorona/ai_challenges/dogs_example.png" alt="dogs"><br><em>モデル崩壊の視覚的イメージ</em></p>
<p>興味深いことに、人間が生成した元データを10%だけでも保持すれば、モデル崩壊をある程度抑える効果があります。<br>ですが、<strong>完全に防ぐことはできません</strong>。<br>人間による本物のデータを意図的に残さない限り、AIモデルはどんどん狭くて自己強化的な世界観に閉じこもっていくことになります。<br>まさに<strong>デジタルな近親交配</strong>です。</p>
<p>さらに、イーストカロライナ大学のGabrielle Steinさんは、AI同士でデータを受け渡す形の「クロスモデル学習」でこの問題が回避できるのかを<a href="https://thescholarship.ecu.edu/server/api/core/bitstreams/c16ab41b-44e2-4bce-a33e-ccd80110676f/content">検証しました</a>。<br>結論としては…<strong>あまり効果はなかったようです</strong>。</p>
<p>彼女の研究では、100%、75%、50%、25%、0%といった異なる人間データの割合でトレーニングを実施しました。<br>その結果、以下のような傾向が見られました：</p>
<ul>
<li>合成データの割合が増えるにつれて、言語的多様性が徐々に減少  </li>
<li>特定の割合で急激に崩壊が進むような「転換点」は見られなかった  </li>
<li>わずかでも人間のデータを混ぜることで、劣化のスピードは抑えられた</li>
</ul>
<p>彼女は、<strong>全体の半分以上</strong>を確実に人間が書いたと確認できるコンテンツで構成することが、モデル崩壊を初期段階で食い止める有効な対策だと提案しています。</p>
<p>インターネット上で目にするデータの多くがAIによって生成されたものであり、しかもAIの学習素材のほとんどがインターネットから集められていることを考えると、AIの未来はやや暗いものに見えてくるかもしれません。<br>AI生成コンテンツが学習データにますます入り込むことで、将来的にモデル崩壊を引き起こすリスクが高まっているのです。<br>しかし、状況を打開できるかもしれない新たな先進的アプローチも登場しつつあります。</p>
<h2>次に来るものは？</h2>
<p>ここまで紹介してきた課題を解決するために、比較的新しいアプローチが最近少しずつ登場しています。<br>恒久的な解決策とはいかないまでも、「近親交配＋データ不足による崩壊」をしばらくのあいだ<strong>先延ばしにする</strong>ことはできるかもしれません。</p>
<p>すでにひとつ例として挙げたのが、AIの「推論（Reasoning）」です。<br>これは、ChatGPTのようなモデルが最終的な回答を出す前に、複数のステップで内部的に思考・判断を行うような動きです。</p>
<p>もうひとつ有望とされているのが、<a href="https://en.wikipedia.org/wiki/Retrieval-augmented_generation"><strong>検索拡張生成（Retrieval-Augmented Generation / RAG）</strong></a>という手法です。<br>簡単に言えば、AIモデルが学習済みデータだけでなく、<strong>外部から提供されたドキュメント</strong>の情報も使って応答を生成するというアプローチです。<br>たとえば、LLMにPDFファイルを読み込ませたり、回答の前にインターネット検索させたりするようなケースが該当します。</p>
<p>とはいえ、察しがつくかもしれませんが、これも<strong>本質的なデータ不足の問題を解決するわけではありません</strong>。<br>なぜなら、モデルに与えられる新しい情報には限りがあるからです。</p>
<p>では、まだ本格的には実現されていないものの、有望とされるアプローチは何でしょうか？<br>その一例が、<strong>仮想現実環境＋エンボディド・エージェント</strong>（Synthetic Reality + Embodied Agents）という方向性です。<br>これはAI開発において従来とはまったく異なる発想です。</p>
<p>静的なデータセットから受動的に学ぶのではなく、AIエージェントを動的な仮想環境に配置し、目標達成のために行動・探索・適応をさせるのです。</p>
<p>そこで得られるのは、結果を体験し、仮説を試し、戦略を立てる中で<strong>自ら生成したデータ</strong>です。<br>それは、文脈があり、多様性があり、因果関係に基づいた、極めて質の高い情報です。<br>この方法であれば、無限に近いバリエーションを持つ環境の中で、<strong>持続可能かつ自己更新型の学習</strong>が可能になります。<br>人間が書いたテキストの枯渇に依存せず、AI自身の出力に閉じこもるリスクも避けられます。</p>
<p>……とはいえ、現時点ではまだこの段階には到達していません。</p>
<p>つまり、退屈な作業をいろいろとAIに押し付けることには成功しましたが──<br>少なくとも今後しばらくの間は、私たち自身がそれなりに働く必要がありそうです。</p>
<p>それでは、また！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/vorona/ai_challenges/cover.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[[Learning Roadside Station Podcast] WWDC Participation Report]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-19-学びの道の駅Podcast-WWDC参加報告-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-19-学びの道の駅Podcast-WWDC参加報告-en/</guid>
            <pubDate>Thu, 10 Apr 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for day 19 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<p>This is Nakanishi from Manabi-no-Michi-no-Eki (Learning Roadside Station) team. This year, the Learning Roadside Station Project was officially launched and later established as a team. We are also running an in-house podcast and we&#39;re excited to share some of its episodes in this year&#39;s Advent Calendar.</p>
<h1>What is the Learning Roadside Station?</h1>
<p>The Learning Roadside Station project was launched to enhance accessibility to the study group sessions frequently held within the company, with the goal of making them more accessible and effective. The initiative aims to support the organization of study group sessions led by enthusiastic employees and promote in-house knowledge sharing.</p>
<h1>Report on Participation in WWDC</h1>
<p>The KTC Learning Roadside Station podcast features interviews with team members organizing study group sessions in the company. These interviews are titled &quot;A Peek into the Study Session Next Door&quot;. Today, we’re changing things up a bit and interviewing Nakano-san, who recently attended WWDC (Worldwide Developers Conference) in person in the hopes to share with everyone insights and inspirations that Nakano-san brought back from attending the event in person.</p>
<h1>Interview</h1>
<p><strong>Akeda-san:</strong> Today we are joined by Nakano-san, who attended WWDC in person. Thank you for joining us.</p>
<p><strong>Nakano-san:</strong> Thank you for joining us.</p>
<p><strong>Akeda-san:</strong> First, could you please introduce yourself and tell us about the kind of work you usually do?</p>
<p><strong>Nakano-san:</strong> I am the product manager for the &quot;KINTO Kantan Moushikomi App&quot; (from now on, The App) in the Mobile App Development Group. In my work, I develop The App and synchronize it with new vehicle data from KINTO ONE on the web side. Since this requires regular updates, I participate in weekly meetings to stay informed. In addition to my work, I often serve as an MC for events within KTC.</p>
<p><strong>Akeda-san:</strong> Thank you. I have the impression that he is well-known within the company. Could you tell us what motivated you to attend WWDC?</p>
<p><strong>Nakano-san:</strong> Since The App is a mobile application, it must go through App Store review before release. Anyone with an Apple Developer Program–linked account can apply to attend WWDC. At our team we were told that it&#39;s open to anyone who wants to apply. I was an iOS engineer at my previous position here and had a developer account back then too, but unfortunately, I wasn’t selected at the time. This time, however, I was fortunate to be chosen and was able to attend.</p>
<p><strong>Akeda-san:</strong> Was it difficult to secure the funding to attend?</p>
<p><strong>Nakano-san:</strong> Well, actually, after I found out I was selected, I consulted with my manager about what to do. In the end, I took the matter to our CEO Kotera-san, prepared some materials, submitted them, and received approval. I guess it is because I was lucky that the company encourages employees to attend events.  </p>
<p><strong>Akeda-san:</strong> Could you share any episodes during your time at WWDC that you haven’t mentioned elsewhere?</p>
<p><strong>Nakano-san:</strong> WWDC was held at Apple Park, where Californian plants filled the courtyard. There were also fruit trees, and employees were told not to pick them. The developer relations staff shared some fun trivia. It was fun. For example, the staff told me how long it would take to walk around the inner and outer perimeter.</p>
<p><strong>Akeda-san:</strong> Did you learn anything from communicating with engineers and tech developers?</p>
<p><strong>Nakano-san:</strong> Yes, I had the opportunity to interact with engineers and share what apps we were each developing. There were many people creating apps on their own, and many who weren&#39;t software engineers by profession but were developing apps as a hobby. Many people said that coming to Apple Park boosted their motivation.</p>
<p><strong>Akeda-san:</strong> Did attending WWDC help you with app development or your work in any way?</p>
<p><strong>Nakano-san:</strong> I had the opportunity to receive direct advice on app development and on several challenges I was facing. As a result, I am moving forward with improvements to The App&#39;s UI and UX. The next release will reflect the advice I received.</p>
<p><strong>Akeda-san:</strong> Can someone who is not confident in English still participate in WWDC?</p>
<p><strong>Nakano-san:</strong> I don&#39;t think it&#39;s a problem at all. It&#39;s better to be able to speak English, but I think you can get by with just the flow. Since I took an Uber to my destination and could easily specify the location through it, I had no difficulties. Knowing English definitely helps, but if you have the willingness to participate, you&#39;ll be fine.</p>
<p><strong>Akeda-san:</strong> Finally, could you say a few words to everyone listening to this interview?</p>
<p><strong>Nakano-san:</strong> I hope you will have the opportunity to attend WWDC and bring new ideas to KTC. To make KTC even better, I encourage everyone to take on the challenge. Determination leads to success.</p>
<p><strong>Akeda-san:</strong> Thank you very much. That&#39;s all for the interview with Nakano-san.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2024-12-podcast/podcast_wwdc.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[[Server side Kotlin] エラーハンドリングにKotlin公式のResult型を使う]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-31-ErrorHandlingInServerSideKotlin/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-31-ErrorHandlingInServerSideKotlin/</guid>
            <pubDate>Thu, 10 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KotlinのResult型を使ってエラーハンドリングするように統一した]]></description>
            <content:encoded><![CDATA[<h1>はじめに</h1>
<p>こんにちは。Toyota Woven City Payment Solution開発Groupで決済関係のバックエンド開発を担当している塩出です。
<a href="https://blog.kinto-technologies.com/posts/2022-09-27-kotlin-moshi/">前回の記事</a>でも述べた通り、本Groupでは開発にKotlinを使用しており、webフレームワークにはKtor、ORMにはExposedを使用しています。またコードのアーキテクチャーとしてはクリーンアーキテクチャーを採用しています。</p>
<p>当初からKotlinの<a href="https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-result">Result型</a>を使ってエラーハンドリングしていましたが、開発人数が増えていることもあり、Resultとthrowが混じったコードになっていました。Resultを使っているところにthrowが入ると、型でエラーハンドリングの必要性を表現しているにもかかわらず、try-catchも必要になってきてしまう状態でした。</p>
<p>KotlinではJavaの検査例外がないので、try-catchは簡単に呼び忘れてエラーハンドリング漏れが発生してしまいます。この状況を改善すべくチーム内で話し合ってKotlinのResultを使ったエラーハンドリングの方法を統一しました。</p>
<p>今回は本Groupでどのようにエラーハンドリングを書いているのかを紹介します。</p>
<p>この記事には以下の内容は含まれません</p>
<ul>
<li>クリーンアーキテクチャーの説明</li>
<li>Ktor, Exposedの説明</li>
<li><code>kotlin-result</code>とKotlin公式のResult型との比較</li>
</ul>
<h2>アプリケーションのディレクトリ構成</h2>
<p>本題に入る前に、本Groupでのアプリケーションのディレクトリ構造について説明します。
以下にクリーンアーキテクチャーの有名な図と本Groupのディレクトリ構成を載せます。本Groupではクリーンアーキテクチャーを採用しており、アプリケーションのディレクトリ構造もそれにおおよそ則った形で構成されています。</p>
<p><img src="/assets/blog/authors/reona-shiode/error-handling/CleanArchitecture.jpg" alt="クリーンアーキテクチャー">
<em>(出典: <a href="https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html">The Clean Code Blog</a>)</em></p>
<pre><code class="language-text">App Route/
├── domain
├── usecase/
│   ├── inputport
│   └── interactor
└── adapter/
    ├── web/
    │   └── controller
    └── gateway/
        ├── db
        └── etc
</code></pre>
<p>ディレクトリとクリーンアーキテクチャー図との対応は以下の通りです。</p>
<ul>
<li><code>domain</code>ディレクトリ: <code>entities</code></li>
<li><code>usecase</code>ディレクトリ: <code>Use Cases</code></li>
<li><code>adapter/web/controller</code>ディレクトリ: <code>Controllers</code></li>
<li><code>adapter/gateway</code>ディレクトリ: <code>Gateways</code></li>
</ul>
<p>用語のズレは少々ありますが、基本的には <code>domain</code>ディレクトリが円の中心で、<code>usecase</code>ディレクトリがその外側、<code>adapter</code>以下が円の一番外側といった感じになっています。</p>
<p>なので依存を許可する方向としては以下のようになります。</p>
<ul>
<li><code>usecase</code> -&gt; <code>domain</code></li>
<li><code>adapter</code>以下 -&gt; <code>usecase</code> or <code>domain</code></li>
</ul>
<p>このような依存の方向性にすることで、webフレームワークやdatabaseの種類などに影響を受けることなく、ビジネスロジックを開発することができます。</p>
<h1>エラーハンドリングの方針</h1>
<p>基本的にエラーハンドリングは以下の方針にしています。</p>
<ul>
<li>処理失敗の場合はthrowではなくResult型を使用する</li>
<li>関数がResult型を返却するとき、throwは使わない</li>
<li>Exceptionを返却するときは独自定義したException型を利用する</li>
</ul>
<p>次の章で個別の方針についてコード例を交えながら細かく紹介していきます。</p>
<h2>関数が失敗する可能性がある場合は戻り値としてResult型を使用する</h2>
<p>KotlinではJavaのような検査例外がなく、呼び出し元にエラーハンドリングを強制する仕組みがありません。Result型を使うことで呼び出し元にエラーが返却される可能性があることを明示できるので、エラーハンドリングが漏れる可能性が低くなります。
ただし<code>Result&lt;Unit&gt;</code>のように戻り値を使用しない場合は強制させることはできません。この場合はcustom lintを定義する必要がありますが、現状定義できていません。</p>
<h3>コード例</h3>
<p>以下は簡単なコードの例です。割り算をする関数を定義する場合、通常分母がゼロの場合はエラーになります。このように関数が失敗する可能性がある場合は戻り値にResult型を指定します。この場合は <code>Result&lt;Int&gt;</code>を指定しています。</p>
<pre><code class="language-kotlin">fun divide(numerator: Int, denominator: Int) : Result&lt;Int&gt; {
    if (denominator == 0) {
        return Result.failure(ZeroDenominatorException())
    }
  return Result.success(numerator/denominator)
}
</code></pre>
<h2>Result型でExceptionを返却する場合、そのExceptionを独自定義したExceptionでラップする</h2>
<p>Repositoryなどは通常interfaceがdomainにあり実装がadapter層にあります。Use Case層からRepositoryの関数を呼び出してエラーハンドリングする場合、adapter層でサードパーティlibraryのExceptionをそのまま返却してしまうと、そのサードパーティのExceptionをUse Case層が知らないといけません。その場合、Use Caseが実質adapter層に依存してしまうことになってしまいます。図で示すと以下のような感じです。</p>
<p>![依存関係](/assets/blog/authors/reona-shiode/error-handling/dependency.png =400x)
<em>interface利用の依存とexceptionの依存(だめな例)</em></p>
<p>それを避けるため、Result型で返却するExceptionは必ず独自定義したExceptionにラップして返却するようにしています。</p>
<p>クリーンアーキテクチャーにおいてExceptionはどこの層なのか悩むポイントですが、個人的にはdomain層だと思っています。
本Groupでは複数サービスで共通のException型を使用しているので、domain libraryとして切り出しています。</p>
<p>Kotlin公式のResult型ではExceptionの型を指定できないので、実装者に独自定義したExceptionを返すように強制できないのが悩みポイントです。その場合は<a href="https://github.com/michaelbull/kotlin-result"><code>kotlin-result</code></a>の使用を検討するのが良さそうです。ただ、本GroupではdomainのコードにKotlin公式ではないサードパーティの型が入り込むのを避けたかったため、採用を見送りました。</p>
<h3>コード例</h3>
<p>以下のinterfaceがdomainに定義されているとします。</p>
<pre><code class="language-kotlin">data class Entity(val id: String)
interface EntityRepository {
  fun getEntityById(id: String): Result&lt;Entity&gt;
}
</code></pre>
<p>またサードパーティのlibraryが以下のようなmethodを持っていてそれを使う例を考えます。</p>
<pre><code class="language-kotlin">fun thirdPartyMethod(id: String): Entity {
    throw ThirdPartyException()
}
</code></pre>
<h4>NG例</h4>
<p>adapter層の実装で以下のように直接Exceptionを返却してしまうと、<code>UseCase</code>などの呼び出し元にサードパーティのExceptionが漏れてしまいます。</p>
<pre><code class="language-kotlin">class EntityRepositoryImpl : EntityRepository {
  override fun getEntityById(id: String): Result&lt;Entity&gt; {
      return kotlin.runCatching {
        thirdPartyMethod(id)
      } // This returns the third party exception
  }
}
</code></pre>
<h4>OK例</h4>
<p>サードパーティのExceptionが呼び出し元に漏れないように、独自定義したExceptionでラップします。</p>
<pre><code class="language-kotlin">class EntityRepositoryImpl : EntityRepository {
  override fun getEntityById(id: String): Result&lt;Entity&gt; {
    return kotlin.runCatching {
      thirdPartyMethod(id)
    }.onFailure { cause -&gt;
      // wrap with our own exception
      CustomUnexpectedException(cause)
    }
  }
}
</code></pre>
<h2>関数がResult型を返却するとき、<code>throw</code>は使わない</h2>
<p>もし関数がResult型を返却するか、Exceptionをthrowする場合、呼び出し側は両方に対応しないといけません。仮に関数を作った人が特定のExceptionは呼び出し元にハンドリングしてもらう必要がないと思っても、呼び出し元がハンドリングしたい場合もあります。従って明示的なthrowを使わずに Result型で統一しています。</p>
<p>DBのコネクションエラーなどは実際発生した場合 Use Case層でリカバリーすることは無理なので、adapter層でexceptionをthrowしてそのままAPIレスポンスまでscope outしても良いかもしれませんが、DB更新できないことによるサードパーティのSaaSとの不整合を検出するためのlogを出力したいこともあります。その場合throwでscope outしてしまうとアラートが適切に上がらない可能性が出てきてしまいます。</p>
<p>エラーハンドリングを要否を決めるのは呼び出し側にあると思いますので、関数作成者がエラーハンドリング不要だと思ってもResult型でExceptionを返却するようにしています。</p>
<h3>コード例</h3>
<p>リポジトリのsave関数を例に、コード例を紹介します。save関数ではEntity classを受け取り、結果を<code>Result&lt;Entity&gt;</code>で返します。</p>
<h4>NG例</h4>
<p>以下のようにConnection errorが発生したときにthrowをし、それ以外のエラーはResult型で返却するとします。</p>
<pre><code class="language-kotlin">class EntityRepository(val db: Database) {
    fun saveEntity(entity: Entity): Result&lt;Entity&gt; {
        try {
            db.connect()
            db.save(entity)
        } catch (e: ConnectionException) {
            // return result instead
            throw OurConnectionException(e)
        } catch (e: throwable) {
            return Result.failure(OurUnexpectedException(e))
        }
        return Result.success(entity)
    }
}
</code></pre>
<p>それを使うUse Case層ではSave時にエラーが発生したら何かしらのactionを取りたいとします。その場合には <code>runCatching</code> (内部ではtry-catchを利用してResult型に変換)を利用しなければなりません。</p>
<pre><code class="language-kotlin">class UseCase(val repo: EntityRepository) {
    fun createNewEntity(): Result&lt;Entity&gt; {
        val entity = Entity.new()
        return runCatching { // need this runCatching in order to catch an exception
            repo.saveEntity(entity).getOrThrow()
        }.onFailure {
            // some error handling here
        }
    }
}
</code></pre>
<h4>OK例</h4>
<p>OKな例ではすべてのExceptionを独自定義したExceptionにラップしてResult型でそのExceptionを返却します。こうすることで呼び出し元は<code>runCatching</code>を削除することができ、コードがシンプルになります。</p>
<pre><code class="language-kotlin">class EntityRepository(val db: Database) {
    fun saveEntity(entity: Entity): Result&lt;Entity&gt; {
        try {
            db.connect()
            db.save(entity)
        } catch (e: ConnectionException) {
            return Result.failure(OurConnectionException(e))
        } catch (e: Exception) {
            return Result.failure(OurUnexpectedException(e))
        }
        return Result.success(entity)
    }
}
class UseCase(val repo: EntityRepository) {
    fun createNewEntity(): Result&lt;Entity&gt; {
        val entity = Entity.new()
        return repo.saveEntity(entity).onFailure {
            // some error handling here
        }
    }
}
</code></pre>
<h1>Result型を使う上での便利な自作関数の紹介</h1>
<h2><code>andThen</code></h2>
<p>Result型を使っているとそのResultが成功だったときにその値を使って別のResult型を返す処理をしたいことが多々あります。例えば特定のエンティティーに対してステータスの更新をする場合は以下のようになるかと思います。</p>
<pre><code class="language-kotlin">fun UseCaseImpl.updataStatus(id: Id) : Result&lt;Entity&gt; {
  val entity = repository.fetchEntityById(id).getOrElse {
    return Result.failure(it)
  }
  val updatedEntity = entity.updateStatus().getOrElse {
    return Result.failure(it)
  }
  return repository.save(updatedEntity)
}
</code></pre>
<p>このような場合はmethodチェーンで処理を繋げられると書きやすくなります。<code>kotlin-result</code>では<code>andThen</code>という関数が用意されていますが、kotlin公式のResult型にはありません。そこで本Groupでは以下のようなmethodを定義して使っています。</p>
<pre><code class="language-kotlin">inline fun &lt;T, R&gt; Result&lt;T&gt;.andThen(transform: (T) -&gt; Result&lt;R&gt;): Result&lt;R&gt; {
    if (this.isSuccess) {
        return transform(getOrThrow())
    }
    return Result.failure(exceptionOrNull()!!)
}
</code></pre>
<p>これを使うことで上記の例は以下のように書き換えることができます。同じコードの記述が減ったので少しスマートになりました。</p>
<pre><code class="language-kotlin">fun UseCaseImpl.updataStatus(id: Id) : Result&lt;Entity&gt; {
  return repository.fetchEntityById(id).andThen { entity -&gt;
    entity.updateStatus()
  }.andThen { updatedEntity -&gt;
    repository.save(updatedEntity)
  }
}
</code></pre>
<h2><code>doInTransaction</code> for Exposed</h2>
<p>本GroupではORMapperとして<a href="https://github.com/JetBrains/Exposed">Exposed</a>を利用しています。Exposedでは <code>transaction</code>というラムダのスコープの中にDB処理を記述する必要があります。<code>transaction</code>ラムダはそのscopeの中でExceptionがthrowされたら自動的にrollbackしてくれます。Result型を利用するとExceptionをthrowすることがないため、Resultが失敗だったときに自動的にrollbackをする関数を作成しました。</p>
<pre><code class="language-kotlin">fun &lt;T&gt; doInTransaction(db: Database? = null, f: () -&gt; Result&lt;T&gt;): Result&lt;T&gt; {
    return transaction(db) {
        f().onFailure {
            rollback()
        }.onSuccess {
            commit()
        }
    }
}
</code></pre>
<p>先ほどの<code>UseCaseImpl</code>の例に当てはめると以下のように使用できます。</p>
<pre><code class="language-kotlin">fun UseCaseImpl.updataStatus(id: Id) : Result&lt;Entity&gt; {
  return doInTransaction {
    repository.fetchEntityByIdForUpdate(id).andThen { entity -&gt;
      entity.updateStatus()
    }.andThen { updatedEntity -&gt;
      repository.save(updatedEntity)
    }
  }
}
</code></pre>
<h2><code>respondResult</code> for Ktor</h2>
<p>本Groupは<a href="https://github.com/ktorio/ktor">Ktor</a>をWebフレームワークとして利用しています。UseCaseで返却されたResult型をそのままレスポンスに指定できるように <code>respondResult</code>という関数を作成しました。</p>
<pre><code class="language-kotlin">suspend inline fun &lt;reified T : Any&gt; ApplicationCall.respondResult(code: HttpStatusCode, result: Result&lt;T?&gt;) {
    result.onSuccess {
        when (it) {
            null, is Unit -&gt; respond(code)
            else -&gt; respond(code, it)
        }
    }.onFailure {
        // defined below
        respondError(it)
    }
}

suspend fun ApplicationCall.respondError(error: Throwable) {
    val response = error.toErrorResponse()
    val json = serializer.adapter(response.javaClass).toJson(response)
    logger.error(json, error)

    respondText(
        text = json,
        contentType = ContentType.Application.Json,
        status = e.errType.toHttpStatusCode(),
    )
}
</code></pre>
<p>単純ではありますが、この関数を使用することで <code>Result.getOrThrow</code>を呼ばなくても良くなるのでコードが少しスッキリします。</p>
<pre><code class="language-kotlin">fun Route.route(useCase: UseCase) {
  val result = useCase.run()
  call.respondResult(HttpStatusCode.OK, result.map {it.toViewModel()} )
}
</code></pre>
<p>ちなみに<code>respondError</code>はthrowableからエラーのレスポンスを返却する関数で、KtorのパイプラインでthrowされたExceptionもこの関数でレスポンスを返却するようにしています。Exceptionを処理するKtorのプラグインも自作しています。</p>
<pre><code class="language-kotlin">val ErrorHandler = createApplicationPlugin(&quot;ErrorHandler&quot;) {
    on(CallFailed) { call, cause -&gt;
        call.respondError(cause)
    }
}
</code></pre>
<h1>さいごに</h1>
<p>本Groupでのエラーハンドリングのやり方と、Result型の便利な自作関数を紹介しました。いろんな会社のtech blogをみていると <code>kotlin-result</code>を使っているところが多い印象でkotlin公式のResult型に関しては使っているという情報は少ない印象です。今のところKotlin公式のResult型でも十分にエラーハンドリングできているので皆様もぜひ使ってみてください。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Swift Observation:UI を更新するための、よりシンプルで効率的なアプローチ]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-19-swift-observation-cleaner-more-efficient-approach-to-updating-ui-en-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-19-swift-observation-cleaner-more-efficient-approach-to-updating-ui-en-ja/</guid>
            <pubDate>Wed, 09 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Observation を使用すると、Swift におけるオブザーバーデザインパターンを、堅牢で型安全かつ効率的に実装することができます。]]></description>
            <content:encoded><![CDATA[<p>本記事は、<a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2024</a>の19日目の投稿です。🎅🎄</p>
<h2>はじめに</h2>
<p>こんにちは。モバイルアプリ開発グループでiOSエンジニアをしている<strong>ラセル・ミア</strong>です。今日は、iOS 17で導入された新しい<a href="https://developer.apple.com/documentation/observation/observable()"><code>@Observable</code></a>マクロを使用して、SwiftUIのUIを更新するための改善されたアプローチを紹介します。その仕組み、解決する課題、そしてなぜこれを使うべきなのかを説明します。</p>
<h4>TL;DR</h4>
<blockquote>
<p><em>Observationを使用すると、Swiftにおけるオブザーバーデザインパターンを、堅牢で型安全かつ効率的に実装することができます。このパターンでは、Observableオブジェクトがオブザーバーのリストを保持し、特定または一般的な状態変化を通知することができます。これにより、オブジェクト同士を直接結びつけることなく、複数のオブザーバー間で暗黙的に更新を分配できるという利点があります。</em>
<a href="https://developer.apple.com/documentation/observation">Appleドキュメントより</a></p>
</blockquote>
<p>簡単に言えば、<code>Observation</code>はビューをデータの変更に応答させるための新しく簡単な方法です。</p>
<h2>Observationを使用しない場合の課題</h2>
<p><code>Observation</code>を使う前に、従来の方法でUIを更新する手法と、それに関連する課題を説明します。</p>
<p>簡単な例から見てみましょう。</p>
<pre><code class="language-swift">
import SwiftUI

class User:ObservableObject {
	@Published var name = &quot;&quot;
	let age = 20

	func setName() {
		name = &quot;KINTO Technologies&quot;
	}
}

struct ParentView:View {

	@StateObject private var user = User()

	var body: some View {
		let _ = print(&quot;ParentView.body&quot;)
		VStack {
			Button(&quot;Set Name&quot;) {
				user.setName()
			}
			ChildView(user: user)
		}
	}
}

struct ChildView:View {

	@ObservedObject var user:User

	var body: some View {
		let _ = print(&quot;ChildView.body&quot;)
		VStack {
			Text(&quot;Age is (user.age)&quot;)
		}
	}
}
</code></pre>
<p><em>User</em></p>
<ul>
<li><code>ObservableObject </code>プロトコルに準拠し、状態の監視を可能にしています。</li>
<li>プロパティnameに <code>@Published</code>を付与することで、ビューに変更を通知します。</li>
</ul>
<p><em>ParentView</em></p>
<ul>
<li><code>@StateObject</code>を使用して<code>User</code>クラスのインスタンスを管理します。</li>
</ul>
<p><em>ChildView</em></p>
<ul>
<li><code>@ObservedObject</code>を使用してUserオブジェクトを受け取ります。</li>
</ul>
<p>両方のビューで、<code>let _ = print(&quot;xxx.body&quot;)</code>を使用して、ビューの更新をデバッグログに出力します。</p>
<p>プロジェクトをビルドすると、初期状態では次のようなログが表示されます。</p>
<pre><code>ParentView.body
ChildView.Body
</code></pre>
<p>この時点では問題ありません。初期状態で両方のビューが描画されます。しかし、SetNameボタンを押すと、次のようなログが出力されます。</p>
<pre><code>ParentView.body
ChildView.Body
</code></pre>
<p><code>ParentView</code>と<code>ChildView</code>の両方が再描画されますが、<code>ParentView</code>は<code>User</code>のプロパティをまったく使用していなかったため、これは予想外です。さらに、<code>ChildView</code>ではUserモデルに依存しない定数プロパティを使用しているだけですが、それも再描画されています。このように、<code>ChildView</code>が静的なTextを返すだけの場合でも、<code>User</code>モデルへの参照を保持しているため、<code>User</code>モデルが変更されると再描画されてしまいます。これはパフォーマンス上の重大な課題を浮き彫りにしています。ここで、<code>Observation</code>フレームワークが私たちを救うために登場します。</p>
<h2>こんにちは、@Observable！</h2>
<p><code>@Observable</code>マクロは、WWDC 2023で <code>ObservableObject</code>とその<code>@Published</code>プロパティの代わりとして紹介されました。このマクロはプロパティを明示的にPublishedとマークする必要をなくし、SwiftUIビューが変更に応じて自動的に再描画されるようにします。</p>
<p><code>ObservableObject</code>から<code>@Observable</code>マクロに移行するには、観測できるようにしたいクラスに新しいSwiftマクロ<code>@Observable</code>を付けるだけです。また、すべてのプロパティから<code>@Published</code> 属性を削除してください。</p>
<pre><code class="language-swift">
@Observable
class User {
	var name = &quot;&quot;
	let age = 20

	func setName() {
		name = &quot;KINTO Technologies&quot;
	}
}
</code></pre>
<blockquote>
<p><strong>注意点:</strong> Structは<code>@Observable</code>マクロをサポートしていません。</p>
</blockquote>
<p>以上です！これで、UIはnameプロパティが変更されたときにのみ更新されるようになります。この動作を確認するには、以下のようにビューを修正します。</p>
<pre><code class="language-swift">
struct ParentView:View {
	// BEFORE
	// @StateObject private var user = User()
	
	// AFTER
	@State private var user = User()

	var body: some View {
		let _ = print(&quot;ParentView.body&quot;)
		VStack {
			Button(&quot;Set Name&quot;) {
				user.setName()
			}
			ChildView(user: user)
		}
	}
}

struct ChildView:View {
	// BEFORE
	// @ObservedObject var user:User

	// AFTER
	@Bindable var user:User

	var body: some View {
		let _ = print(&quot;ChildView.body&quot;)
		VStack {
			Text(&quot;Age is (user.age)&quot;)
		}
	}
}
</code></pre>
<p>修正後、カスタムのobservableモデルは<code>@State</code>を使用して宣言できます。この変更により、<code>@ObservedObject</code>、 <code>ObservableObject</code>、<code>@Published</code>、<code>@EnvironmentObject</code>が不要になります。</p>
<blockquote>
<p><em>@Bindable:Observableオブジェクトの変更可能なプロパティに対してバインディングを作成するためのプロパティラッパー型です。</em><a href="https://developer.apple.com/documentation/swiftui/bindable">Appleドキュメントより</a></p>
</blockquote>
<p>コードを実行すると、初期レンダリング時に以下の出力が表示されます。</p>
<pre><code>ParentView.body
ChildView.Body
</code></pre>
<p>しかし、<code>setName</code> ボタンを押しても、コンソールには何も表示されません。これは、<code>ParentView</code>がUserのプロパティを使用していないため、更新する必要がないからです。同様に、<code>ChildView</code>にも適用されます。</p>
<p><code>ParentView</code>に<code>Text(user.name)</code>を追加してプロジェクトをビルドします。その後、<code>setName</code>ボタンを押すと、以下の出力が得られます。</p>
<pre><code class="language-swift">
struct ParentView:View {

	@State private var user = User()

	var body: some View {
		let _ = print(&quot;ParentView.body&quot;)
		VStack {
			Button(&quot;Set Name&quot;) {
				user.setName()
			}
			Text(user.name) // &lt;--- 追加部分
			ChildView(user: user)
		}
	}
}

// 出力
ParentView.body 

// ChildViewで年齢を変更するコードを追加します。
@Observable
class User {
    var name = &quot;&quot;
    var age = 20
    
    func setName() {
        name = &quot;KINTO Technologies&quot;
    }
}

struct ParentView:View {
    
    @State private var user = User()
    
    var body: some View {
        let _ = print(&quot;ParentView.body&quot;)
        VStack {
            Button(&quot;Set Name&quot;) {
                user.setName()
            }
            ChildView(user: user)
        }
    }
}

struct ChildView:View {
    
    @Bindable var user:User
    
    var body: some View {
        let _ = print(&quot;ChildView.body&quot;)
        VStack {
            Button(&quot;Change Age&quot;) {
                user.age = 30
            }
            Text(&quot;Age is (user.age)&quot;)
        }
    }
}

// 出力
ChildView.Body 
</code></pre>
<p>この結果から、ビューが不要な再描画を行わず、正しく更新されていることが確認できます。これはパフォーマンスにおいて大きな改善を示しています。</p>
<h2>@Observableの仕組み</h2>
<p>@Observableの仕組みは一見すると魔法のように思えるかもしれません。モデルに<code>@Observable</code>マクロを付与しただけで、SwiftUIビューが問題なく更新されるようになります。しかし、その背後ではいくつかの重要な処理が行われています。<code>ObservableObject</code>プロトコルから <code>Observation.Observable</code>プロトコルへ移行しました。さらに、<code>@Published</code>プロパティラッパーの代わりに<code>@ObservationTracked</code>マクロを使用し、 <code>name</code>と<code>age</code>は <code>@ObservationTracked</code>に関連付けられています。このマクロを展開することで、その実装内容を確認することができます。以下は展開されたコードです。</p>
<pre><code class="language-swift">
@Observable
class User {
	@ObservationTracked
    var name = &quot;&quot;
	@ObservationTracked
    var age = 20
    
    func setName() {
        name = &quot;KINTO Technologies&quot;
    }

	@ObservationIgnored private let _$observationRegistrar = Observation.ObservationRegistrar()

	internal nonisolated func access&lt;Member&gt;(
		keyPath:KeyPath&lt;User, Member&gt;
	) {
		_$observationRegistrar.access(self, keyPath: keyPath)
	}

	internal nonisolated func withMutation&lt;Member, MutationResult&gt;(
		keyPath:KeyPath&lt;User, Member&gt;,
		_ mutation: () throws -&gt; MutationResult
	) rethrows -&gt; MutationResult {
		try _$observationRegistrar.withMutation(of: self, keyPath: keyPath, mutation)
	}
}
extension User:Observation.Observable {}
</code></pre>
<p>デフォルトでは、オブジェクトは観測オブジェクトにアクセス可能な、観測可能な型のすべてのプロパティを観測します。ただし、特定のプロパティが観測されないようにしたい場合は、そのプロパティに <code>@ObservationIgnored</code> マクロを付与することで制御できます。</p>
<pre><code class="language-swift">
@Observable
class User {
	var name = &quot;&quot;
	@ObservationIgnored var age = 20

	func setName() {
		name = &quot;KINTO Technologies&quot;
	}
}
</code></pre>
<p>これにより、ageプロパティの変更は追跡されなくなり、それに伴うUIの更新も発生しなくなります。 </p>
<h2>パフォーマンス分析</h2>
<p>以下は、Instrumentsツールを使用して記録されたビュー更新回数のレポートです。</p>
<p><img src="/assets/blog/authors/m-rasel/without_observation.png" alt="Observation未使用時"> <em>Observation未使用時</em> <img src="/assets/blog/authors/m-rasel/with_observation.png" alt="Observation使用時"> <em>Observation使用時</em></p>
<h2>記録結果からの考察</h2>
<h3>Observation未使用時(最初のイメージ)</h3>
<p>このInstrumentsセッションでは、SwiftUIアプリの最適化されていないパフォーマンスが記録されています。  </p>
<ul>
<li><strong>表示されたメトリクス</strong>:ビューの更新、プロパティの変更、タイミングの概要が表示されます。  </li>
<li><strong>ビューの再描画回数</strong>:初期レンダリングに加え、<code>Set Name</code>ボタンを３回押した際の再描画がそれぞれ発生し、合計で <strong>9</strong>回の再描画が行われました。  </li>
<li><strong>パフォーマンス</strong>:<ul>
<li><strong>合計再描画時間</strong>:<strong>377.71 マイクロ秒</strong>  </li>
<li><strong>平均再描画時間</strong>:<strong>41.97マイクロ秒/回</strong></li>
</ul>
</li>
</ul>
<h3>Observation使用時（2番目のイメージ）</h3>
<p>このセッションでは、Observationフレームワークを使用した最適化されたレンダリングが記録されています。  </p>
<ul>
<li><strong>表示されたメトリクス</strong>:最初のセッションと同じメトリクスが表示されますが、効率の向上が反映されています。  </li>
<li><strong>ビューの再描画回数</strong>:ボタンを複数回タップしても、初期レンダリングと状態変化時の再描画が1回のみの <strong>3</strong> 回の再描画で構成されます。  </li>
<li><strong>パフォーマンス</strong>:<ul>
<li><strong>合計再描画時間</strong>:<strong>235.58 マイクロ秒</strong>  </li>
<li><strong>平均再描画時間</strong>:<strong>78.53 マイクロ秒/回</strong></li>
</ul>
</li>
</ul>
<h3>定量的な比較</h3>
<ul>
<li><strong>ビューの再描画回数の削減</strong>:<ul>
<li>Observation未使用時:<strong>9回再描画</strong>。  </li>
<li>Observation使用時:<strong>3回の再描画</strong>（<strong>66.67％</strong> 削減）</li>
</ul>
</li>
<li><strong>合計再描画時間の短縮</strong>:<ul>
<li>Observation未使用時:<strong>377.71 マイクロ秒</strong>  </li>
<li>Observation使用時:<strong>235.58 マイクロ秒</strong> （<strong>37.65%</strong> 削減）</li>
</ul>
</li>
<li><strong>再描画の効率性</strong>:<ul>
<li>Observation未使用時:より頻繁で、平均<strong>41.97 マイクロ秒</strong></li>
<li>Observation使用時:再描画回数は少ないが最適化されており、平均<strong>78.53 マイクロ秒</strong></li>
</ul>
</li>
</ul>
<p>この比較から、再描画回数が少なくなった結果としてインスタンスごとの平均再描画時間がやや増加しているものの、<code>Observation</code>フレームワークが再描画回数の削減と全体的なレンダリングパフォーマンスの向上に大きく貢献していることが分かります。</p>
<h2>まとめ</h2>
<p>この記事では、iOS 17で導入された新しい <code>@Observable</code>マクロによるSwiftUIの改善点について解説しました。重要なポイントを簡単にまとめると、次のようになります。</p>
<h4>以前のアプローチの課題</h4>
<p>従来の<code>ObservableObject</code>と<code>@Published</code> プロパティを使用する方法では、不要な再描画が発生し、特に一部のビューが変更されたデータに依存しない場合に、パフォーマンスの問題が生じていました。</p>
<h4>@Observable の紹介</h4>
<p>この新しいマクロは状態監視プロセスを簡素化し、<code>@Published</code>、<code>@ObservableObject</code>、<code>@EnvironmentObject</code>の必要性を排除します。クラスに<code>@Observable</code>を付与し、ビューで <code>@Bindable</code>を使用することで、データの変更を自動的に追跡し、必要な場合にのみビューの更新をトリガーできます。</p>
<h4>パフォーマンスの向上</h4>
<p><code>@Observable</code>を使用することで、関連するデータが変更された場合にのみビューが更新され、不要な再描画が削減されるため、パフォーマンスが向上します。さらに、<code>@ObservationIgnored</code> マクロを活用することで、どのプロパティがUI更新をトリガーするかを開発者が細かく制御できるようになります。</p>
<h4>メリット</h4>
<ul>
<li>的確な更新によるパフォーマンスの向上。</li>
<li>ObservableObjectと@Publishedが不要になることでコードが簡潔に。</li>
<li>状態変更を型安全に管理可能。</li>
<li>ビューの更新をトリガーするプロパティを細かく制御できる柔軟性。</li>
</ul>
<p><code>@Observable</code>を使用することで、SwiftUIでのUI更新の管理がより簡単かつ効率的になり、エラーが減少します。その結果、開発者にとっては作業効率が向上し、エンドユーザーにとってはスムーズな体験が提供されます。</p>
<p>以上で本日の内容は終了です。楽しいコーディングを!</p>
<p><em>参照-</em></p>
<ul>
<li><a href="https://developer.apple.com/documentation/observation">Observation</a></li>
<li><a href="https://developer.apple.com/videos/play/wwdc2023/10149/">SwiftUIにおけるObservationを探る(WWDC23)</a></li>
</ul>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/m-rasel/swift-observation.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[Learning Roadside Station Podcast] Security & Privacy Study Session]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-17-学びの道の駅Podcast-セキュリティ&amp;プライバシー勉強会-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-17-学びの道の駅Podcast-セキュリティ&amp;プライバシー勉強会-en/</guid>
            <pubDate>Tue, 08 Apr 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for day 17 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<p>Hi, I’m Nakanishi from the Manabi-No-Michi-No-Eki (Learning Roadside Station) team. This year, the Learning Roadside Station project was officially launched and later established as a team. As part of this initiative, we also run an in-house podcast, and for this year&#39;s Advent Calendar, we’re excited to share some of its episodes with you.</p>
<h1>What is Manabi-No-Michi-No-Eki (Learning Roadside Station)</h1>
<p>The Learning Roadside Station is a project launched to enhance accessibility and effectiveness of the study sessions that are frequently held within the company. The aim is to promote knowledge sharing by supporting study sessions led by volunteers within the company.</p>
<h1>Security &amp; Privacy Study Session</h1>
<p>In our Podcast, we interview team members who hold in-house study sessions. It´s called “A Peek into the Study Session Next Door”. Today, we welcome Kuwahara-san, Morino-san, and Kasai-san, who are leading the Security and Privacy Study Sessions. Thank you all for joining us today. Could you start by introducing yourselves?</p>
<h1>Interview</h1>
<p><strong>Morino-san:</strong> I’m Morino from the Security &amp; Privacy Group. It’s a pleasure to be here.</p>
<p><strong>Kasai-san:</strong> My name is Kasai, and I’m part of the Data &amp; Privacy Governance Team. Thank you for having me.</p>
<p><strong>Kuwahara-san:</strong> I’m Kuwahara from the Security CoE Group. Pleasure to meet you.</p>
<p><strong>HOKA-san:</strong> First, could you tell us about your daily work and responsibilities?</p>
<p><strong>Morino-san:</strong> Our team is divided into three sub-teams. The Data &amp; Privacy Governance Team, the Information Security Team, and the Cybersecurity Defense Team. The Data &amp; Privacy Governance Team is responsible for establishing rules and implementing governance regarding data and privacy. The Information Security Team conducts assessments based on information security standards. The Cybersecurity Defense Team manages vulnerabilities and investigates threat intelligence.</p>
<p><strong>Kasai-san:</strong> My team, the Data &amp; Privacy Governance Team, establishes rules regarding data security management and personal information governance, conducts risk assessments, and reports the results to management.</p>
<p><strong>Kuwahara-san:</strong> The Security CoE Group is responsible for the security of cloud environments such as AWS, Azure, and Google Cloud. We set up guardrails to ensure safe cloud usage, monitor security risks, and provide support against potential threats.</p>
<p><strong>HOKA-san:</strong> Next, could you tell us more about the Security &amp; Privacy Study Session? What kind of study session is it?</p>
<p><strong>Kuwahara-san:</strong> We started this study session to spread security-related knowledge among employees. Last year, we created detailed security guidelines, and to ensure awareness and adoption, we began hosting these study sessions.</p>
<p><strong>Morino-san:</strong> Our goal is to establish Security by Design and Privacy by Design as standard considerations in all projects.</p>
<p><strong>Kasai-san:</strong> Ultimately, we want engineers to be able to focus on development, while we support them with privacy and security risk management.</p>
<p><strong>HOKA-san:</strong> How has the response been so far?</p>
<p><strong>Kuwahara-san:</strong> So far, we have hosted four sessions, and the response has been very positive. Many attendees have shared that they found the sessions informative, and we’ve also received valuable feedback for improvements.</p>
<p><strong>Morino-san:</strong> Some participants have mentioned that our explanations have become clearer, and we continue to refine our approach.</p>
<p><strong>HOKA-san:</strong> What changes do you hope to see at KTC through these study sessions?</p>
<p><strong>Morino-san:</strong> We aim to have security and privacy features implemented as standard practices.</p>
<p><strong>Kuwahara-san:</strong> Ideally, security should become so natural and ingrained that our roles become unnecessary.</p>
<p><strong>Kasai-san:</strong> I want to create an environment where engineers can focus on development with confidence, knowing that security is taken care of.</p>
<p><strong>HOKA-san:</strong> Finally, what inspired each of you to pursue this field?</p>
<p><strong>Morino-san:</strong> I started in software development and gradually developed a deep interest in security.</p>
<p><strong>Kasai-san:</strong> Initially, I wasn&#39;t particularly interested in security or privacy, but I became fascinated by governance and its impact.</p>
<p><strong>Kuwahara-san:</strong> I began my career as an infrastructure engineer, but after experiencing a security breach on a mail server I was managing, I shifted my focus to security.</p>
<p><strong>HOKA-san:</strong> Thank you all for your time today. We hope the Security and Privacy Study Session continues to be a valuable resource for everyone at KTC. Thank you everyone.</p>
<p>This time, we provided details about the Security and Privacy Study Session, the background to its operation, and future prospects. Please look forward to the next study session!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2024-12-podcast/podcast_security.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Behind the Scenes at Developers Summit 2024 KANSAI—Fresh Insights from Osaka Tech Lab ♪]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-16-DevelopersSummit2024KANSAI-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-16-DevelopersSummit2024KANSAI-en/</guid>
            <pubDate>Mon, 07 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I will be sharing a behind-the-scenes look at my experience at Developers Summit 2024 KANSAI, along with the latest updates from Osaka Tech Lab ♪]]></description>
            <content:encoded><![CDATA[<p>This article is the entry for day 16 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<h1>1. Introduction</h1>
<p>Hello. I&#39;m Okita from the Mobile App Development Group.</p>
<p>It&#39;s December! The end of the year is here!</p>
<p>Looking back, many events took place this year. One of the things that strongly stands out is:</p>
<p>My participation in Developers Summit 2024 KANSAI as a member of Osaka Tech Lab. </p>
<p>So, this time I&#39;ll be sharing the following:</p>
<ul>
<li>My participation and presentation at Developers Summit 2024 KANSAI</li>
<li>The latest news from Osaka Tech Lab</li>
</ul>
<h1>2. How I Felt Before the Presentation</h1>
<p>As a sponsor, our company had the opportunity to set up a booth and give a presentation at the summit.</p>
<blockquote>
<p>*For more details, please check out this article^^
<a href="https://blog.kinto-technologies.com/posts/2024-10-30-developers-summit-2024-kansai-reflection/">Reflecting on Developers Summit 2024 KANSAI</a></p>
</blockquote>
<p>We want to recruit people who will join us in envisioning the future! To that end, we want people to get to know KINTO Technologies Corporation and learn about Osaka Tech Lab!</p>
<p>With this goal in mind, our company applied to become a sponsor.</p>
<p>This is the first attempt of its kind for Osaka Tech Lab.</p>
<p>I was entrusted with the important role of delivering a presentation as a sponsor on behalf of our company.</p>
<p>At first, I was worried... I&#39;d never given a speech before, so I wondered if I could do it...</p>
<p>But it was a valuable opportunity so I decided to give it my best shot! </p>
<h1>3. The Day of My Presentation - a Moment of Tension and Emotion</h1>
<p>Amidst all this, the day arrived.</p>
<p>In fact, I spent the morning of the day refining my presentation and rehearsed until the very last minute.</p>
<p>And, overwhelmed with nerves, I arrived at the venue early and spent some time wandering around.</p>
<p>Meanwhile, the clock kept ticking, and before I knew it, it was my turn.</p>
<p>The theme of my presentation was <a href="https://event.shoeisha.jp/devsumi/20240918/session/5285">&quot;<strong>Challenges</strong>&quot;</a>.</p>
<p>&quot;There&#39;s no point in panicking now.&quot; &quot;Let&#39;s stay calm and speak slowly and carefully so that our message can reach as many people as possible.&quot;</p>
<p>With that in mind, I began to speak slowly.</p>
<p>As my speech neared its end, I looked up and saw people wearing black T-shirts.</p>
<p>To my surprise, they were colleagues from our company, warmly watching my presentation in their KINTO Technologies T-shirts.</p>
<p>I was deeply touched.</p>
<p>While the audience of other companies participated in bright-colored T-shirts like orange, yellow, and blue, my coworkers wore the less noticeable black.</p>
<p>That was so like the Osaka Tech Lab, which made me chuckle and relax.</p>
<ul>
<li><p>They quietly watched over me as I struggled to prepare for my presentation.</p>
</li>
<li><p>Team members from other offices in our company also supported me, someone with no prior presentation experience, by helping me compile my profile and create the presentation content.</p>
</li>
<li><p>A few days before the presentation, the managers and team leaders of the Mobile App Development Group suddenly found themselves scrambling to assist me, as I had reached out for their help.</p>
</li>
</ul>
<p>That&#39;s right; I just happened to be the one presenting, but the content was the result of the efforts and ideas of many others.</p>
<p>Thanks to everyone, 174 people attended my presentation.</p>
<p>I would like to take this opportunity to express my gratitude to the event management staff, our company members who supported me, and everyone who participated in the session.</p>
<h1>4. How I Felt After the Presentation</h1>
<p>I would like to share my thoughts as I reflect on my experience after the presentation.</p>
<ul>
<li><p>I began preparing more than a month before, but I found myself scrambling until the last minute. 
  -&gt; It turned out to be a great experience, but I spent too much time overthinking, so I now believe there were more efficient ways to approach it.</p>
</li>
<li><p>As it was my first presentation, I didn’t know how to prepare for it. 
  -&gt; In hindsight, I should have first clarified the purpose, goals, and key message of my speech before preparing the materials.</p>
</li>
<li><p>During my rehearsals, no matter how many times I practiced, my presentation always finished in just 20 minutes. But on the day of the presentation, I was able to speak for exactly 30 minutes.</p>
</li>
<li><p>I was holding the remote upside down, so even though I pressed the button, the next slide wouldn&#39;t show, and I got nervous right from the start.　</p>
</li>
<li><p>I prepared the materials in PowerPoint and then exported them as a PDF 
  -&gt; The font I had carefully chosen was not reflected in the PDF. It was disappointing, but it became a valuable lesson.</p>
</li>
</ul>
<p>As you can see on the list, the preparations were very challenging, but in the end, I was able to give a presentation that I have no regrets about.</p>
<p>I also made new connections with other companies that participated as sponsors, as well as people who contacted me through &#39;Ask the Speaker&#39;.</p>
<p>And to my surprise, the editorial department of CodeZine kindly offered to release a session report about me. <a href="https://codezine.jp/article/detail/20330">What is the appeal of working at Osaka Tech Lab for PM and mobile app engineers challenging themselves in the Toyota Group? (in Japanese)</a></p>
<p>Truly, I was deeply moved.</p>
<h1>Summary of my presentation - Learning and Growth</h1>
<p>This is a brief summary of my presentation.</p>
<h4>■ Good things and things I&#39;ve grown from</h4>
<ul>
<li>By delivering my presentation at the event, I was able to organize and verbalize my daily efforts.
As a result, the next steps for the project I belong to became clear. I was also able to level up myself.  
  <strong>-&gt; I was able to experience the significance of &quot;input and output&quot; and &quot;verbalization.&quot;</strong></li>
</ul>
<h4>■ Improvements</h4>
<ul>
<li><p>It was a bit heavy to have a 30-minute presentation slot for the first time in my life 
  <strong>-&gt; It is recommended to experience a presentation at an outside event in the 5 to 10-minute presentation slot first.♪</strong></p>
</li>
<li><p>While I was able to convey my thoughts, I regret that my presentation lacked concrete examples that went a step further. 
  <strong>-&gt; I would like to aim for a presentation from which the participants can take away at least one lesson.</strong></p>
</li>
<li><p>I should have clarified the main points of my presentation before preparing my presentation materials and story. 
  <strong>-&gt; If you create the core of your presentation first, it will be easier to create a consistent story and you will be able to prepare more efficiently.</strong></p>
</li>
</ul>
<pre><code>(Sample) Key points for a presentation

■ Purpose of presentation

■ Goal (what to achieve in the presentation)

■ Messages to convey
</code></pre>
<h1>6. Recent developments at Osaka Tech Lab</h1>
<p>Well, well.</p>
<p>Time flies. It&#39;s already December.</p>
<p>After the Developers Summit 2024 KANSAI, new developments have emerged at Osaka Tech Lab.</p>
<p>In other words, taking on a <strong>challenge</strong> of organizing external events.</p>
<p>The first of our challenges was:</p>
<p><a href="https://kinto-technologies.connpass.com/event/337002/">&quot;Kansai Front-End Year-End Party 2024 HACK.BAR × KINTO Technologies&quot;</a></p>
<p>In the future, I hope to organize the Mobile App Development Group’s events.</p>
<p>It would be fun to collaborate and hold events with other companies ♪</p>
<p>The list of things I want to try is overflowing, and it&#39;s becoming a bit of a problem, lol.</p>
<p>I want to take one step at a time (or maybe half a step at a time, lol).</p>
<h1>7. Conclusion</h1>
<p>How was it? I hope you found this article interesting. </p>
<p>Join us in shaping the future of Osaka Tech Lab as we step into an exciting new era!</p>
<p>We look forward to your application!</p>
<p><a href="https://www.kinto-technologies.com/recruit/">KINTO Technologies Corporation Recruitment Top Page</a><br><a href="https://www.wantedly.com/companies/company_7864825/about">Wantedly</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[SNSを使った集客に挑戦！4ヶ月のSNSマーケティングの振り返り]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-01_Prism_SNS/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-01_Prism_SNS/</guid>
            <pubDate>Mon, 07 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[SNSを使った集客に挑戦！4ヶ月のSNSマーケティングの振り返り]]></description>
            <content:encoded><![CDATA[<p>こんにちは！KINTOテクノロジーズで<a href="https://ppap.kinto-jp.com/">Prism Japan</a>という観光メディア(Web/iOS/Android)を運営している喜多です。
2024年11月からPrism JapanのSNS運用を担当しています。従来の広告配信に留まらず、SNSを用いることでサービスの認知拡大と新たな集客方法として確立することを目的としていました。
ここではその約4ヶ月間の振り返りをしてみたいと思います！</p>
<h2>できたこと</h2>
<p>まずはSNS運用をしていく上で「これはできたな」ということからまとめていきます。</p>
<ol>
<li><p><strong>ショート動画の作成</strong>
 TiktokおよびInstagram中心に、ショート動画は今のSNS環境においてキーとなる存在です。各媒体のアルゴリズムでは、投稿されたショート動画をフォロワー外のユーザーにも届け、その反応次第でさらに伸びるかどうかが決まります。
 SNSを用いた「認知拡大」という点においては必須科目だったため、類似したカテゴリにおいてバズっている閲覧数の多い動画を真似するところからスタートしました。
 そのイメージを元に動画編集ツールを触りながらスキルを習得しました。主に利用したツールは下記です。</p>
<table>
<thead>
<tr>
<th>ツール名</th>
<th>特徴</th>
</tr>
</thead>
<tbody><tr>
<td>Canva</td>
<td>基本的に無料で利用可能。画像、動画の編集が自由自在に可能であり、共同編集者とのシェアもしやすく管理が楽である。過去一度だけ作業中にサーバーが落ちてやり直しになった。</td>
</tr>
<tr>
<td>Adobe Express</td>
<td>基本的に無料で利用可能。Canvaと大きくは変わらないが、サーバーが安定していて作業途中で落ちることもなかったため、個人的にはこちらが好み。</td>
</tr>
<tr>
<td>Capcut</td>
<td>基本的に無料で利用可能。動画編集機能がメイン。文字の読み上げ機能や画像の3D化、SNSで「よく見る」あのフォントが利用できるなど、SNS向けに特化した機能が多い。<br>※無料版に付く「透かし」はInstagramにおいては評価されないため注意。</td>
</tr>
</tbody></table>
<p> 使う人や目的によって若干好みや使い方が分かれる部分もありますが、基本的にはどのツールを使っても問題はなく、無料の範囲でも充分なクオリティの動画が作成できると思います。</p>
</li>
<li><p><strong>インプレッションの獲得</strong>
 各媒体共通して多くのインプレッションを獲得することができました。
 ここでは運用を本格的に開始した2024年11月1日~2025年3月17日(執筆時点)の期間で前後比較してみます。
   <strong>X</strong>
 インプレッション数は293%増の62,500件となりました。
 <img src="/assets/blog/authors/kita/20250401/x_imp.png" alt="x_imp">
 <strong>Tiktok</strong>
 Tiktokは初めての運用だったため、比較対象がないのですが、全体を通して44万回再生されています。
 他のSNSと比較すると、後から再生数がじわじわと伸びてくるような特徴がありました。
 <img src="/assets/blog/authors/kita/20250401/tiktok_view.png" alt="tiktok_view">
 <strong>Instagram</strong>
 リーチ数が90%増の5万件となりました。
 ※閲覧数、インタラクションともに、2024年8月以前のデータが集計不可であったため正しい比較はできませんでした。
 <img src="/assets/blog/authors/kita/20250401/instagram_view.png" alt="instagram_view"></p>
<p> Tiktok,Instagramは特に顕著なのですが、年末年始のタイミングで山がグッと発生しています。
 多くの人がお休みであったタイミングと初詣シーズンにぴったりの神社を紹介したことが重なって伸びたのだと考えています。
 本格的なSNS運用前後で比較すると、投稿回数の増加やそもそも媒体を運用していなかったことも要因として挙げられますが、動画の形式や季節、話題性といったトレンドを押さえたこともあって、大幅な閲覧数の増加が見込めました。
 これらにより一定の「認知拡大」に貢献できたのではないかと思っております。</p>
</li>
<li><p><strong>ユーザーとのコミュニケーション</strong>
 殊にXにおいては新たなユーザーの発掘およびコミュニケーションを取ることができたと思います。
 従来の運用では、こちらからフォローを働きかけたりリプライするなどのアクションを行なっていませんでした。
 しかし、Xはエンゲージメント重視のアルゴリズムが採用されているため、積極的に観光系に興味を持つユーザーへアプローチしました。
 その結果、とあるユーザーさんが実際にアプリのインストールだけでなく、掲載しているスポットへお出かけレビューまで投稿してくださったことが判明しました！
 何より企画のアイディアなどもいただけて大変参考になりました。プロダクトやサービスを持っている方は積極的に動くことで結果的にサービス利用やフィードバックがもらえることもあるのでおすすめです。</p>
</li>
</ol>
<h2>できなかったこと</h2>
<p>続いてできなかったことを振り返りたいと思います。</p>
<ol>
<li><p><strong>アプリのインストールやWebサイトの流入には至らなかった</strong>
 上記に記載したように、「閲覧数」という観点においては増加するなどの結果であった反面、アプリのインストールやWebサイトへの流入は見込めない結果となりました。
 下記では各媒体の結果を分析したいと思います。評価期間はSNS運用を本格的に開始した2024年11月1日~2025年3月17日とします。
 以下はAppsFlyerで計測した期間中のアプリのインストール数になります。
 ※具体的なインストール数の公開は控えさせていただきます。
 <img src="/assets/blog/authors/kita/20250401/dl_daily.png" alt="dl_dailey"></p>
<p> <strong>Tiktok × アプリインストール数</strong>
 まずはTiktokの各指標とアプリのインストール数に相関関係がないか調査しました。</p>
<p> <strong>視聴数</strong>
 上記で既出の画像となりますが、視聴数とインストール数を照らし合わせてみました。
 <img src="/assets/blog/authors/kita/20250401/tiktok_view.png" alt="tiktok_view">
 ぱっと見のグラフの比較でお分かりかもしれませんが、相関関係はないと考えています。
 例えば、視聴数が盛り上がっている2025年1月2日のインストール数に変化がありませんでした。
 反対にインストール数が盛り上がっている2025年1月29日の視聴数には変化がありません。
 このことからもTiktokの視聴数とインストール数は相関関係にないと結論づけています。</p>
<p> <strong>プロフィールの表示回数</strong>
 <img src="/assets/blog/authors/kita/20250401/tiktok_profile_view.png" alt="tiktok_profile_view">
 続いてはプロフィールの表示回数です。プロフィールに来てくれている時点で一定の興味を持ってくれているユーザーであると考えていました。
 嬉しいことに増加傾向にあるものの、現状ではインストール数に大きな影響を与えているとは言えないと思います。</p>
<p> <strong>いいね</strong>
 <img src="/assets/blog/authors/kita/20250401/tiktok_like.png" alt="tiktok_like">
 いいねの付いた数はどうでしょうか。ぱっと見は視聴数と同じような推移だと思います。
 こちらも同様にいいねが最も付いた2025年1月2日のインストール数には変化がなく、2025年1月29日のインストール数の増加にも寄与しているとは言えませんでした。</p>
<p> <strong>コメント</strong>
 <img src="/assets/blog/authors/kita/20250401/tiktok_comment.png" alt="tiktok_comment">
 コメント数は全体通して81件であり、閲覧数を考慮すると少ない結果となりました。日によっては0件と連動しています。
 2025年2月17日のコメント数は4件とやや多めですが、インストール数に変化はなかったです。</p>
<p> <strong>シェア</strong>
 <img src="/assets/blog/authors/kita/20250401/tiktok_share.png" alt="tiktok_share">
 シェアも視聴数、いいね数と似たような推移となりました。
 いいねやシェアが多い動画は評価されて「おすすめ」に表示され、結果的に視聴数が伸びるというアルゴリズムを身を持って実感しました。</p>
<p> <strong>Instagram × アプリインストール数</strong>
 続いてはInstagramです。</p>
<p> <strong>閲覧数</strong>
 <img src="/assets/blog/authors/kita/20250401/instagram_view.png" alt="instagram_view">
 こちらも同様に年末年始にかけて大きく山ができています。
 そのタイミングでは上記の通りインストール数に影響はありません。
 一方で、複数日においてはやや相関関係にありました。全体を通して見ると相関関係は強くはないのですが、Tiktokと比較すると少しだけ関係がありそうです。
 また、リーチ数(いわゆるユニーク数)も上記と同様の傾向が見られたためここでは割愛します。</p>
<p> <strong>インタラクション</strong>
 <img src="/assets/blog/authors/kita/20250401/instagram_interaction.png" alt="instagram_interaction">
 最後にインタラクションです。Instagramにおいてはいいねや保存、コメント、シェアがこちらに該当します。
 こちらも同様にインストールとの相関は見られませんでした。</p>
<p> <strong>まとめ</strong>
 全体を通して、インストール数で見ると相関関係は見られず、SNSによって集客ができた、とは言えない状況だとわかりました。
 ただ、あくまで相関関係を見ただけなので、仮に視聴者が時間をおいてインストールした可能性もあるとは思いますが、正確な評価は正直なところ難しいと考えています。</p>
<p> <strong>Webサイトへの流入</strong>
 <img src="/assets/blog/authors/kita/20250401/web_referral.png" alt="web_referral">
 SEO以外の経路としてもSNSの集客が一定貢献することを想定していました。実際のところ、Instagramのストーリー機能およびXのポストから流入を獲得できましたが、総数で見ても約300件とわずかな送客しかできませんでした。
 これはXのフォロワー数が低いことによる拡散力の頼りなさ、Instagramのストーリー閲覧者を確保できなかったことが主な要因だと考えています。</p>
</li>
<li><p><strong>インプレッション数は伸びたが、大幅なフォロワー数の増加は見込めなかった</strong></p>
<p> <strong>X：425フォロワー</strong>
 Xにおいてはフォロー数やいいねなどのアクションがまだまだ足りていませんでした。
 他媒体との兼ね合いもあって、長い期間をかけてゆっくりと活動していたことが原因です。
 早期のうちに短期間でフォロー、いいねを行うことが好ましいかと思います。過度なフォローはシャドウバンや凍結にもつながるのでご注意ください。</p>
<p> <strong>Tiktok：155フォロワー</strong>
 視聴数の割に全くフォロワーがつきませんでした。理由は大きく二つかと思います。
 一つは汎用的な情報を流していたこと。このアカウントをフォローしないと見逃してしまう情報だ、というオリジナリティや独創性に欠けていたと思います。
 二つ目はターゲットが曖昧であったこと。Prism Japanの特性上、旅行したいと考える人という幅広い層がターゲットになるかと思います。
 その上で、Tiktokではどういう層(年代、性別)にするか曖昧であったため、結局間口が広いまま多数の人に情報を届けていました。</p>
<p> <strong>Instagram：6667フォロワー</strong>
 一見、多い印象を受けるのですが、広告の配信を停止して以降は下降傾向にありました。
 広告配信して獲得したフォロワーはエンゲージメントが低い傾向にあるのが一般的とも言われていることに加え、広告配信停止後の投稿は以前とガラリとニュアンスを変えていたため余計にユーザー離れが加速したように思います。</p>
</li>
</ol>
<h1><strong>各SNSの特徴と改善ポイント</strong></h1>
<p>ここでは実際に運用して感じた各媒体の強みと、投稿内容の分析をしていきます。</p>
<ol>
<li><p><strong>TikTok：フォロワー数が多くなくても内容次第でバズることができる</strong>
 最も伸びた投稿：<a href="https://www.tiktok.com/@prism_japan/video/7468578530721385736?is_from_webapp=1&sender_device=pc&web_id=7436024728442635793">東京で女子旅するのにぴったりの穴場スポット3選！</a>
 <img src="/assets/blog/authors/kita/20250401/tiktok_best_post.png" alt="tiktok_best_post">
 「東京で女子旅する人」というかなり狭めたターゲットを勝手に設定して投稿しました。
 内容としてはアプリの実際の画面上で穴場のスポットを探すという実際のフローを踏んでいます。
 扱っているスポットはまさに穴場で、「海外風、某アニメ、縁結び」をテーマに取り上げました。
 結果として、94%の人が検索から流入し、「東京 遊ぶ場所 女子2人」「東京観光」といったクエリで検索した結果流入してくれました。
 ターゲットを狭め、穴場として実際にリアルなスポットを取り上げられたことが要因の一つかと考えています。</p>
<p> 最も伸びなかった投稿：<a href="https://www.tiktok.com/@prism_japan/video/7446682449301490961?is_from_webapp=1&sender_device=pc&web_id=7436024728442635793">日比谷のイルミネーションが綺麗すぎた！</a>
 <img src="/assets/blog/authors/kita/20250401/tiktok_worst_post.png" alt="tiktok_worst_post">
 伸びなかった動画は日比谷のイルミネーションです。こちらは実際に現地に足を運んで撮影し、イルミネーションの鮮やかや雰囲気を伝えたかったのがポイントです。
 また冒頭部分はBGMとムービーが連動していて興味を引くような作り方をしたつもりでしたが、全く視聴されませんでした。
 「クリスマス」「イルミネーション」といったハイクオリティな動画を提供する競合がひしめくキーワード帯に突撃してしまったことが原因ではないかと考えています。</p>
<p> <strong>まとめ</strong>
 上記を比較すると、伸びた動画はターゲットが明確かつ内容がそのターゲットにとって有用であったことが挙げられるかと思います。
 結果、平均視聴数やフル視聴率といった各指標も高く、視聴数を大幅に上げられました。</p>
</li>
<li><p><strong>X：ユーザーとのコミュニケーションが強み</strong></p>
<p> 最も伸びた投稿：<a href="https://x.com/PrismJapan/status/1857725299581944025">神社仏閣×紅葉のおすすめ4選</a>
 <img src="/assets/blog/authors/kita/20250401/x_best_post.png" alt="x_best_post">
 紅葉と神社仏閣のコラボが見どころのスポットをまとめて紹介しました。各スポットの位置情報や見どころポイントとそれに付随した画像を載せています。</p>
<p> 最も伸びなかった投稿：<a href="https://x.com/PrismJapan/status/1871714799392083996">今日はクリスマスですね！</a>
 <img src="/assets/blog/authors/kita/20250401/x_worst_post.png" alt="x_worst_post">
 クリスマスに盛り上がっていた #MerryChristmas のタグとともに後述する生成AIを利用した動画を添えて投稿しました。
 目的としてはアカウントの「人間味」であったのとタグで若干のインプレッションを獲得したいところにありましたが、結果として盛り上がりに欠ける投稿となりました。</p>
<p> <strong>まとめ</strong>
 Xにおいては他の伸びた投稿を見ても時季やトレンドに関する投稿が伸びる傾向にありました。
 そして情報として有用か(まとまっているか、自分にとって価値があるか)が重要だと思いました。</p>
</li>
<li><p><strong>Instagram：ショート動画を用いてフォロワー外にリーチしやすくなった</strong></p>
<p> 最も伸びた投稿：<a href="https://www.instagram.com/p/DDzDc7IiFLr/?utm_source=ig_web_copy_link&igsh=MzRlODBiNWFlZA==">【24~25シーズン】都内から行ける！おすすめのスキー場4選</a>
 <img src="/assets/blog/authors/kita/20250401/instagram_best_post.png" alt="intagram_best_post">
 冬のスキーシーズン開始に伴って関東から行けるスキー場を画像で紹介しました。
 各スキー場の開場期間や時間、料金情報を記載しました。その結果、「保存」が10件と他投稿と比べると多くなりました。
 よって、55.4%のフォロワー外ユーザーへもリーチできています。私の感覚では、「リール動画」は保存数あまり関係なく半数はフォロワー外のユーザーへリーチできる一方、画像投稿は9割がフォロー内に留まる印象でした。
 振り返るとユーザーが「保存」したくなる投稿が何よりも重要であると考えています。</p>
<p> 最も伸びなかった投稿：<a href="https://www.instagram.com/reel/DFsE4igtOpy/?utm_source=ig_web_copy_link&igsh=MzRlODBiNWFlZA==">AIがあなたを診断！</a>
 <img src="/assets/blog/authors/kita/20250401/instagram_worst_post.png" alt="instagram_worst_post">
 こちらは上記とは打って変わってアプリの機能「気分で検索」をリール動画としてアピールしたものです。
 実際のアプリ画面を録画し、手順通り進めることでユーザーが使うシーンを想起してもらうことを意図していましたが、結果としては最も再生されない動画でした。
 ターゲットが不明瞭で誰にも刺さらない動画であったことと、ユーザーの便益が伝わりにくいものであったのではないかと思います。</p>
<p> <strong>まとめ</strong>
 リール動画、画像投稿によってフォロワー外へのリーチのしやすさ、に違いがあるものの、得てして「保存」されることで投稿が伸びるかどうかが決まっている印象を受けました。
 有益な情報であることはもちろん、保存を促したり、定期的にその分析を行うことが大事です。</p>
</li>
</ol>
<h2>全体のまとめと今後の方向性</h2>
<p>全体を通して、SNSが強く集客に貢献しているとは言えないことが分かりました。一方で必ずしも貢献していないとも断言はできないため、引き続き振り返りを経て改善したいと思います。
投稿において特に重要なことは下記2点です。</p>
<ol>
<li><strong>ターゲットを明確にすること</strong></li>
<li><strong>視聴者にとって有益な情報であること</strong>
当たり前と言えば当たり前かもしれないですが、この2点は媒体問わず共通して伸びる動画にとって必要であることだと実感しました。
また、Tiktokの内容をInstagramへもそのまま流用していましたが、それだと媒体でユーザー層が異なるため、反応が得にくい状態にありました。
とはいえ、媒体別に投稿することが望ましいのですが、リソースにも限界があるため、有意義な取り組みになっていませんでした。媒体ごとに特性やユーザー層が異なるため、目的に合わせて何がベストかを見極めることをおすすめします。</li>
</ol>
<h2>参考文献</h2>
<p><a href="https://www.amazon.co.jp/SNS%E3%83%9E%E3%83%BC%E3%82%B1%E3%83%86%E3%82%A3%E3%83%B3%E3%82%B07%E3%81%A4%E3%81%AE%E9%89%84%E5%89%87-%E9%A3%AF%E9%AB%99%E6%82%A0%E5%A4%AA/dp/4296113704">SNSマーケティング7つの鉄則</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AndroidでTensorFlowを使用したオブジェクト検出]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-09-android-object-detection-flow-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-09-android-object-detection-flow-ja/</guid>
            <pubDate>Fri, 04 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KTCは、ARおよびAI機能を使用して新しいスマートマニュアルを作成するために、Unlimitedアプリ内に新機能を導入しました。]]></description>
            <content:encoded><![CDATA[<p>みなさん、こんにちは。KINTOテクノロジーズのモバイル開発グループのマーティンです！このガイドでは、TFLite（TensorFlow Lite）モデルをゼロから構築する方法を簡単に説明します。それでは、早速始めましょう。</p>
<p>この記事は KINTOテクノロジーズアドベントカレンダー2024の9日目の記事です🎅🎄</p>
<h1><strong>準備</strong></h1>
<p>データセットを準備する方法は基本的に2通りあります。1つは、ローカルでアノテーションプロセスを行う方法、もう1つは、オンラインでデータセットにアノテーションを行い、チームメンバーと初期の工数をより効率的に共有しながら進める方法です。このガイドでは、Roboflow (1) の使用に重点を置いています。</p>
<p>Roboflow のモデルエクスポート機能を使用すると、トレーニング済みのモデルをさまざまな形式でエクスポートできるため、独自のアプリケーションに簡単に展開したり、さらに微調整したりすることができます。今回の場合、TFlite モデルをトレーニングしたいので、下の画像に示すように TFRecord 形式にエクスポートする必要があります。<img src="/assets/blog/authors/beckmartin/roboflow.png" alt="デモ"></p>
<ul>
<li>ただし、Roboflowのようなサードパーティのオンラインアノテーションツールを用いずに、ローカルで画像にアノテーションを付けたい場合は、無料のPythonライブラリlabelImgを試してみてください： <a href="https://github.com/HumanSignal/labelImg">https://github.com/HumanSignal/labelImg</a><ul>
<li>一般的に、ローカルでもオンラインでも、まず画像のデータセットを収集し、それらにラベルを付けて、対応する境界ボックス分類メタデータ (xml) ファイルを取得する必要があります。（この場合は、VOC [Visual Object Classes] Pascal メタデータを作成）</li>
<li>Pascal VOCの詳細情報は、こちらで確認できます：<a href="https://roboflow.com/formats/pascal-voc-xml">https://roboflow.com/formats/pascal-voc-xml</a></li>
</ul>
</li>
<li>Google Cloud Platform の標準ランタイムインスタンスを作成したら、それを Colab ノートブックに接続する必要があります。基本的に、Google Colab は、組織内のデータサイエンスチームと機械学習チームに、安全でスケーラブルな共同作業プラットフォームを提供します。それが完了したら、まずTensorFlow（テンソルフロー）を動作させるために必要なライブラリをインポートする必要があります（ステップ1）</li>
</ul>
<p>GCP標準インスタンスの作成: <img src="/assets/blog/authors/beckmartin/runtime.png" alt="デモ"></p>
<p>Colab Enterprise(<a href="https://cloud.google.com/colab/docs">https://cloud.google.com/colab/docs</a>) ノートブックの作成:
<img src="/assets/blog/authors/beckmartin/notebook.png" alt="デモ"></p>
<ul>
<li>Google Cloud バケットを接続する（ステップ 4）</li>
</ul>
<h1><strong>実行</strong></h1>
<ul>
<li>TensorFlow オブジェクト検出 API をインストールします（このガイドのステップ 5）</li>
<li>トレーニングに必要な TFRecord ファイルを生成します。（これには、csv ファイルを生成するための generate_tfrecord.pyスクリプトが必要です）</li>
<li>モデルパイプライン構成ファイルを編集し、事前にトレーニングされたモデルのチェックポイントをダウンロードします。</li>
<li>モデルのトレーニングと評価</li>
<li>モデルをエクスポートして、TFlite（TensorFlow Lite）形式に変換します。</li>
</ul>
<h1><strong>展開</strong></h1>
<ul>
<li>TFlite モデルを Android / iOS / IoT デバイスに展開します。</li>
</ul>
<h1><strong>それでは、始めましょう</strong></h1>
<p>Colab Enterprise ノートブック内で実行すべき手順を詳しく説明します：</p>
<h1><strong>1) ライブラリをインポートします</strong></h1>
<pre><code>!pip install tensorflow==2.13.0

import os
import glob
import xml.etree.ElementTree as ET
import pandas as pd
import tensorflow as tf
print(tf.__version__)
</code></pre>
<h1><strong>2) GCPクラウドストレージバケットに*<code>customTF2</code><em>、</em><code>training</code><em>、</em><code>data</code>*フォルダを作成します（初回のみ必要）</strong></h1>
<ul>
<li>GCPクラウドストレージバケットに<em><strong>customTF2</strong></em>という名前のフォルダを作成します。</li>
<li><em><strong>customTF2</strong></em> フォルダ内に <em><strong>training</strong></em> と <em><strong>data</strong></em> という2つのサブフォルダを作成します（<em><strong>training</strong></em> フォルダはトレーニング中にチェックポイントが保存される場所です）</li>
</ul>
<p>GCSバケット内のフォルダ構造の作成: <img src="/assets/blog/authors/beckmartin/gcp.png" alt="デモ"></p>
<h1><strong>3) 以下を <em><code>generate_tfrecord.py</code></em> ファイルとしてダウンロードして保存し、バケットの <em><code>CustomTF2</code></em> フォルダにアップロードします。(初回のみ必要)</strong></h1>
<pre><code>from __future__ import division
from __future__ import print_function
from __future__ import absolute_import

import os
import io
import pandas as pd
import tensorflow as tf
import argparse

from PIL import Image
from tqdm import tqdm
from object_detection.utils import dataset_util
from collections import namedtuple, OrderedDict


def __split(df, group):
   data = namedtuple(&#39;data&#39;, [&#39;filename&#39;, &#39;object&#39;])
   gb = df.groupby(group)
   return [data(filename, gb.get_group(x)) for filename, x in zip(gb.groups.keys(), gb.groups)]


def create_tf_example(group, path, class_dict):
   with tf.io.gfile.GFile(os.path.join(path, &#39;{}&#39;.format(group.filename)), &#39;rb&#39;) as fid:
      encoded_jpg = fid.read()
   encoded_jpg_io = io.BytesIO(encoded_jpg)
   image = Image.open(encoded_jpg_io)
   幅、高さ = 画像.サイズ

   filename = group.filename.encode(&#39;utf8&#39;)
   image_format = b&#39;jpg&#39;
   xmins = []
   xmaxs = []
   ymins = []
   ymaxs = []
   classes_text = []
   classes = []

   for index, row in group.object.iterrows():
      if set([&#39;xmin_rel&#39;, &#39;xmax_rel&#39;, &#39;ymin_rel&#39;, &#39;ymax_rel&#39;]).issubset(set(row.index)):
         xmin = row[&#39;xmin_rel&#39;]
         xmax = row[&#39;xmax_rel&#39;]
         ymin = row[&#39;ymin_rel&#39;]
         ymax = row[&#39;ymax_rel&#39;]

      elif set([&#39;xmin&#39;, &#39;xmax&#39;, &#39;ymin&#39;, &#39;ymax&#39;]).issubset(set(row.index)):
         xmin = row[&#39;xmin&#39;] / width
         xmax = row[&#39;xmax&#39;] / width
         ymin = row[&#39;ymin&#39;] / height
         ymax = row[&#39;ymax&#39;] / height

      xmins.append(xmin)
      xmaxs.append(xmax)
      ymins.append(ymin)
      ymaxs.append(ymax)
      classes_text.append(str(row[&#39;class&#39;]).encode(&#39;utf8&#39;))
      classes.append(class_dict[str(row[&#39;class&#39;])])

   tf_example = tf.train.Example(features=tf.train.Features(
       feature={
           &#39;image/height&#39;: dataset_util.int64_feature(height),
           &#39;image/width&#39;: dataset_util.int64_feature(width),
           &#39;image/filename&#39;: dataset_util.bytes_feature(filename),
           &#39;image/source_id&#39;: dataset_util.bytes_feature(filename),
           &#39;image/encoded&#39;: dataset_util.bytes_feature(encoded_jpg),
           &#39;image/format&#39;: dataset_util.bytes_feature(image_format),
           &#39;image/object/bbox/xmin&#39;: dataset_util.float_list_feature(xmins),
           &#39;image/object/bbox/xmax&#39;: dataset_util.float_list_feature(xmaxs),
           &#39;image/object/bbox/ymin&#39;: dataset_util.float_list_feature(ymins),
           &#39;image/object/bbox/ymax&#39;: dataset_util.float_list_feature(ymaxs),
           &#39;image/object/class/text&#39;: dataset_util.bytes_list_feature(classes_text),
           &#39;image/object/class/label&#39;: dataset_util.int64_list_feature(classes), }))
   return tf_example


def class_dict_from_pbtxt(pbtxt_path):
   # open file, strip \n, trim lines and keep only
   # lines beginning with id or display_name

   with open(pbtxt_path, &#39;r&#39;, encoding=&#39;utf-8-sig&#39;) as f:
      data = f.readlines()

   name_key = None
   if any(&#39;display_name:&#39; in s for s in data):
      name_key = &#39;display_name:&#39;
   elif any(&#39;name:&#39; in s for s in data):
      name_key = &#39;name:&#39;

   if name_key is None:
      raise ValueError(
          &quot;label map does not have class names, provided by values with the &#39;display_name&#39; or &#39;name&#39; keys in the contents of the file&quot;
      )

   data = [l.rstrip(&#39;\n&#39;).strip() for l in data if &#39;id:&#39; in l or name_key in l]

   ids = [int(l.replace(&#39;id:&#39;, &#39;&#39;)) for l in data if l.startswith(&#39;id&#39;)]
   names = [
       l.replace(name_key, &#39;&#39;).replace(&#39;&quot;&#39;, &#39;&#39;).replace(&quot;&#39;&quot;, &#39;&#39;).strip() for l in data
       if l.startswith(name_key)]

   # id と display_names を1つの辞書に結合します
   class_dict = {}
   for i in range(len(ids)):
      class_dict[names[i]] = ids[i]

   return class_dict


if __name__ == &#39;__main__&#39;:
   parser = argparse.ArgumentParser(
       description=&#39;Create a TFRecord file for use with the TensorFlow Object Detection API.&#39;,
       formatter_class=argparse.RawDescriptionHelpFormatter)
   parser.add_argument(&#39;csv_input&#39;, metavar=&#39;csv_input&#39;, type=str, help=&#39;Path to the CSV input&#39;)
   parser.add_argument(&#39;pbtxt_input&#39;,
                       metavar=&#39;pbtxt_input&#39;,
                       type=str,
                       help=&#39;Path to a pbtxt file containing class ids and display names&#39;)
   parser.add_argument(&#39;image_dir&#39;,
                       metavar=&#39;image_dir&#39;,
                       type=str,
                       help=&#39;Path to the directory containing all images&#39;)
   parser.add_argument(&#39;output_path&#39;,
                       metavar=&#39;output_path&#39;,
                       type=str,
                       help=&#39;Path to output TFRecord&#39;)

   args = parser.parse_args()

   class_dict = class_dict_from_pbtxt(args.pbtxt_input)

   writer = tf.compat.v1.python_io.TFRecordWriter(args.output_path)
   path = os.path.join(args.image_dir)
   examples = pd.read_csv(args.csv_input)
   grouped = __split(examples, &#39;filename&#39;)

   for group in tqdm(grouped, desc=&#39;groups&#39;):
      tf_example = create_tf_example(group, path, class_dict)
      writer.write(tf_example.SerializeToString())

   writer.close()
   output_path = os.path.join(os.getcwd(), args.output_path)
   print(&#39;Successfully created the TFRecords: {}&#39;.format(output_path))
</code></pre>
<h1><strong>4) GCSバケットをマウントし、GCSFUSEをインストールしてフォルダをリンクします。</strong></h1>
<pre><code>from google.colab import auth
auth.authenticate_user()

!echo「deb https://packages.cloud.google.com/apt gcsfuse-bionic main」&gt; /etc/apt/sources.list.d/gcsfuse.list
!curl https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
!apt -qq update
!apt -qq install gcsfuse

!gsutil ls -r gs://your-cloud-storage-bucket-name

!mkdir customTF2
!gcsfuse --implicit-dirs your-cloud-storage-bucket-name customTF2
</code></pre>
<h1><strong>5) TensorFlow モデルのGitレポジトリをクローンし、TensroFlow オブジェクト検出APIをインストールします。</strong></h1>
<pre><code>%cd /content

# colab クラウドvmに tensorflowモデルをクローンする
!git clone --q https://github.com/tensorflow/models.git

# protosをコンパイルするために/models/research フォルダにナビゲートします。
%cd models/research

# protosをコンパイルします。
!protoc object_detection/protos/*.proto --python_out=.

# TensorFlowオブジェクト検出 API をインストールします。
!cp object_detection/packages/tf2/setup.py .
!python -m pip install .
</code></pre>
<h1><strong>6) モデルビルダーをテストします (推奨)</strong></h1>
<pre><code>%cd /content/models/research
# モデルビルダーのテスト

!pip install &#39;tf-models-official &gt;=2.5.1, &lt;2.16.0&#39;

!python object_detection/builders/model_builder_tf2_test.py
</code></pre>
<h1><strong>7）事前にトレーニングされたモデルチェックポイントをダウンロードします（初めての場合のみ必要）</strong></h1>
<p>現在の作業ディレクトリは /content/customTF2/customTF2/data/</p>
<p><strong>ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.tar.gz</strong> を　<em><strong>データ</strong></em>　フォルダにダウンロードして解凍します。</p>
<p>その他の Tensorflow 2.x の検出チェックポイントのリストは<a href="https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_detection_zoo.md">こちら</a>。</p>
<pre><code>%cd /content/customTF2/customTF2/data/

#事前トレーニング済みモデル ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.tar.gz をダウンロードしてデータフォルダに入れて解凍します。

!wget http://download.tensorflow.org/models/object_detection/tf2/20200711/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.tar.gz
!tar -xzvf ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.tar.gz
</code></pre>
<h1>*<em>8）モデルパイプライン構成ファイルを入手して変更を加え、<em>データ</em>フォルダに入れます（クラス番号の量を変更するたびに必要）</em>*</h1>
<p>現在の作業ディレクトリは /content/customTF2/customTF2/data/</p>
<p><strong>ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config</strong>を <em><strong>/content/models/research/object_detection/configs/tf2</strong></em>からダウンロードします。必要な変更を加え、 <em><strong>/content/customTF2/customTF2/data/</strong></em> フォルダーにアップロードします。</p>
<p><strong>または</strong></p>
<p>colab内 で <em><strong>/content/models/research/object_detection/configs/tf2</strong></em> からの構成ファイルを編集し、編集済みの構成ファイルを <em><strong>/content/customTF2/customTF2/data</strong></em> フォルダにコピーします。</p>
<p>パイプライン構成ファイルは、前のステップでダウンロードしたモデルチェックポイントフォルダ内にもあります。</p>
<p><strong>次の変更を行う必要があります。</strong></p>
<ul>
<li><em><strong>num_classes</strong></em> を自分のクラス数に変更します。</li>
<li><em><strong>test.record</strong></em> パス、<em><strong>train.record</strong></em> パス、<em><strong>labelmap</strong></em> パスを、これらのファイルを作成した場所のパスに変更します（トレーニング中は現在の作業ディレクトリに対する相対パスである必要があります）</li>
<li><em><strong>fine_tune_checkpoint</strong></em>を、ステップ 12 でダウンロードしたチェックポイントがあるディレクトリのパスに変更します。</li>
<li><em><strong>fine_tune_checkpoint_type</strong></em> を、分類タイプに応じて数値 <strong>classification</strong> または <strong>detection</strong> に変更します。</li>
<li><em><strong>batch_size</strong></em> を、使用する GPU の能力に応じて 8 の倍数に変更します（例：24、128、...、512） - 通常、標準的な colab enterprise インスタンスでは、小さなデータセットには 24、大きなデータセットには 32 が適しています。</li>
<li><em><strong>num_steps</strong></em> を、検出器にトレーニングさせたいステップ数に変更します。</li>
</ul>
<pre><code>#編集した構成ファイルを configs/tf2 ディレクトリから GCP ストレージの data/ フォルダにコピーします。

!cp /content/models/research/object_detection/configs/tf2/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config /content/customTF2/customTF2/data
</code></pre>
<p>次のステップでは、公式の TensorBoard ツールを使用して、実行状況やグラフを可視化し、学習と分類の損失を時間経過とともに確認したいと思います。グラフの読み方やツールの使い方に関する詳細情報は、こちらをご覧ください: <a href="https://www.tensorflow.org/tensorboard/get_started#:~:text=TensorBoard%20is%20a%20tool%20for,during%20the%20machine%20learning%20workflow%E3%80%82">https://www.tensorflow.org/tensorboard/get_started#:~:text=TensorBoard%20is%20a%20tool%20for,during%20the%20machine%20learning%20workflow。</a></p>
<h1><strong>9) TensorBoardをロードします (推奨)</strong></h1>
<pre><code># cload tensorboard

%cd /content/customTF2/customTF2/training

# !pip install tensorboard
# tensorboard --inspect --logdir /content/customTF2/customTF2/training

# !gcloud init
# !gcloud auth application-default login

%reload_ext tensorboard
%tensorboard --logdir &#39;/content/customTF2/customTF2/training&#39;
</code></pre>
<h1><strong>10) モデルをトレーニングします</strong></h1>
<h2>colab vm の <em><strong>object_detection</strong></em> フォルダに移動します</h2>
<pre><code>%cd /content/models/research/object_detection
</code></pre>
<h2>10 (a) model_main_tf2.py を使用したトレーニング（推奨方法）</h2>
<p>ここで、<strong>PIPELINE_CONFIG_PATH</strong> はパイプライン構成ファイルを指し、<strong>MODEL_DIR</strong> はトレーニングのチェックポイントとイベントが書き込まれるディレクトリを指します。</p>
<p>最良の結果を得るためには、損失が0.1未満になった時点でトレーニングを停止してください。それが難しい場合は、損失がしばらくの間、顕著な変化を示さなくなるまでモデルをトレーニングしてください。理想的な損失は0.05未満です（モデルを過学習させずに、損失をできるだけ低く抑えるようにしてください）。モデルがすでに収束している場合（例えば、損失がそれ以上顕著に減少せず、減少に時間がかかる場合）、損失を下げようとしてトレーニングステップを高くしすぎないようにしてください。</p>
<pre><code>!pip install tensorflow==2.13.0

以下のコマンドをcontent/models/research/object_detection ディレクトリから実行してください。
&quot;&quot;&quot;
PIPELINE_CONFIG_PATH=path/to/pipeline.config
MODEL_DIR=path to training checkpoints directory
NUM_TRAIN_STEPS=50000
SAMPLE_1_OF_N_EVAL_EXAMPLES=1

python model_main_tf2.py -- \
  --model_dir=$MODEL_DIR --num_train_steps=$NUM_TRAIN_STEPS \
  --sample_1_of_n_eval_examples=$SAMPLE_1_OF_N_EVAL_EXAMPLES \
  --pipeline_config_path=$PIPELINE_CONFIG_PATH \
  --alsologtostderr
&quot;&quot;&quot;

!python model_main_tf2.py --pipeline_config_path=/content/customTF2/customTF2/data/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config --model_dir=/content/customTF2/customTF2/training --alsologtostderr
</code></pre>
<h2>10 (b) Evaluation using model_main_tf2.py (Optional, just if you want more customization)</h2>
<p>これを並行して実行するには、別の Colab ノートブックを開き、上記のトレーニングコマンドと同時にこのコマンドを実行します（その際、gcpストレージをマウントし、TF gitリポジトリをクローンし、TF2オブジェクト検出 API をインストールするのを忘れないでください）。これにより、検証損失、mAP などが表示され、モデルのパフォーマンスがどのようになっているかをより良く把握できます。</p>
<p>ここで、<strong>{CHECKPOINT_DIR}</strong> はトレーニングジョブによって生成されたチェックポイントが格納されているディレクトリを指します。評価イベントは <strong>{MODEL_DIR/eval}</strong> に書き込まれます。</p>
<pre><code>以下のコマンドをcontent/models/research/object_detection ディレクトリから実行してください。
&quot;&quot;&quot;
PIPELINE_CONFIG_PATH=path/to/pipeline.config
MODEL_DIR=path to training checkpoints directory
CHECKPOINT_DIR=${MODEL_DIR}
NUM_TRAIN_STEPS=50000
SAMPLE_1_OF_N_EVAL_EXAMPLES=1

python model_main_tf2.py -- \
  --model_dir=$MODEL_DIR --num_train_steps=$NUM_TRAIN_STEPS \
  --checkpoint_dir=${CHECKPOINT_DIR} \
  --sample_1_of_n_eval_examples=$SAMPLE_1_OF_N_EVAL_EXAMPLES \
  --pipeline_config_path=$PIPELINE_CONFIG_PATH \
  --alsologtostderr
&quot;&quot;&quot;

!python model_main_tf2.py --pipeline_config_path=/content/customTF2/customTF2/data/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config --model_dir=/content/customTF2/customTF2/training/ --checkpoint_dir=/content/customTF2/customTF2/training/ --alsologtostderr
</code></pre>
<h2>モデルの再トレーニング (接続が切れた場合)</h2>
<p>接続が切れたり、colab vmのセッションが失われた場合、トレーニングは中断した場所から再開できます。チェックポイントは<em><strong>training</strong></em>フォルダ内のクラウドストレージに保存されています。トレーニングを再開するには、<strong>ステップ 1, 4, 5, 6, 9, 10</strong> を実行してください。</p>
<p>トレーニングに必要なすべてのファイル（記録ファイル、編集済みpipeline configファイル、label_mapファイル、モデルチェックポイントフォルダなど）が揃っているため、これらを再作成する必要はありません。</p>
<p><strong>model_main_tf2.pyスクリプトは、1000ステップごとにチェックポイントを保存します。</strong> トレーニングは、最後に保存されたチェックポイントから自動的に再開されます。</p>
<p>ただし、最後のチェックポイントからトレーニングが再開されない場合は、パイプライン構成ファイルに1つの変更を加えることができます。<strong>fine_tune_checkpoint</strong> を、最新のトレーニング済みチェックポイントが書き込まれている場所に変更し、最新のチェックポイントを指すように以下のように設定します。</p>
<pre><code>fine_tune_checkpoint: &quot;/content/customTF2/customTF2/training/ckpt-X&quot; (where ckpt-X is the latest checkpoint)
</code></pre>
<h1><strong>11) トレーニング済みモデルをテストします</strong></h1>
<h2>推論グラフをエクスポート</h2>
<p>現在の作業ディレクトリは /content/models/research/object_detection検出です</p>
<pre><code>%cd /content/models/research/object_detection

!pip install tensorflow==2.13.0

# #推論グラフをエクスポート
!python exporter_main_v2.py --trained_checkpoint_dir=/content/customTF2/customTF2/training --pipeline_config_path=/content/customTF2/customTF2/data/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config --output_directory /content/customTF2/customTF2/data/inference_graph
</code></pre>
<h2>訓練済みのオブジェクト検出モデルを画像でテストします（好みのテスト画像を提供して image_path を調整します）</h2>
<p>現在の作業ディレクトリは /content/models/research/object_detection検出です</p>
<pre><code>%cd /content/models/research/object_detection

# ラベルテキストの異なるフォントタイプ。（このステップはオプション）
!wget https://www.freefontspro.com/d/14454/arial.zip
!unzip arial.zip -d .

%cd utils/
!sed-i「s/font = ImageFont.TrueType (&#39;arial.ttf&#39;, 24) /font = ImageFont.TrueType (&#39;arial.ttf&#39;, 50)/」visualization_utils.py
%cd ..
</code></pre>
<pre><code>%cd /content/models/research/object_detection

!pip install tensorflow==&quot;2.12.0&quot;

#保存された_モデルのローディング
import tensorflow as tf
import time
import numpy as np
import warnings
warnings.filterwarnings(&#39;ignore&#39;)
from PIL import Image
from google.colab.patches import cv2_imshow
from object_detection.utils import label_map_util
from object_detection.utils import visualization_utils as viz_utils

IMAGE_SIZE = (12, 8) # Output display size as you want
import matplotlib.pyplot as plt
PATH_TO_SAVED_MODEL=&quot;/content/customTF2/customTF2/data/inference_graph/saved_model&quot;
print(&#39;Loading model...&#39;, end=&#39;&#39;)

# 保存したモデルを読み込み、検出機能を構築します。
detect_fn=tf.saved_model.load(PATH_TO_SAVED_MODEL)
print(&#39;Done!&#39;)

#ラベル_マップのローディング
category_index=label_map_util.create_category_index_from_labelmap(&quot;/content/customTF2/customTF2/data/label_map.pbtxt&quot;,use_display_name=True)

def load_image_into_numpy_array(path):

    return np.array(Image.open(path))

# テストイメージに置き換えます
image_path = &quot;/content/customTF2/customTF2/data/images/your_test.jpg&quot;
#print(&#39;Running inference for {}... &#39;.format(image_path), end=&#39;&#39;)

image_np = load_image_into_numpy_array(image_path)

# 入力はテンソルでなければなりません。`tf.convert_to_tensor` を使用して変換します。
input_tensor = tf.convert_to_tensor(image_np)
# モデルは画像のバッチを想定しているため、`tf.newaxis` で軸を追加してください。
input_tensor = input_tensor[tf.newaxis, ...]

detections = detect_fn(input_tensor)

# すべての出力はバッチテンソルです。
# numpy 配列に変換し、インデックス [0] を使用してバッチディメンションを削除します。
# 対象とするのは最初の num_detections だけです。
num_detections = int(detections.pop(&#39;num_detections&#39;))
detections = {key: value[0, :num_detections].numpy()
              for key, value in detections.items()}
detections[&#39;num_detections&#39;] = num_detections

# 検出_クラスは整数でなければなりません。
detections[&#39;detection_classes&#39;] = detections[&#39;detection_classes&#39;].astype(np.int64)

image_np_with_detections = image_np.copy()

viz_utils.visualize_boxes_and_labels_on_image_array(
      image_np_with_detections,
      detections[&#39;detection_boxes&#39;],
      detections[&#39;detection_classes&#39;],
      detections[&#39;detection_scores&#39;],
      category_index,
      use_normalized_coordinates=True,
      max_boxes_to_draw=200,
      min_score_thresh=.8, # この値を調整して True として分類される最小確率ボックスを設定します
      agnostic_mode=False)
%matplotlib inline
plt.figure(figsize=IMAGE_SIZE, dpi=200)
plt.axis(&quot;off&quot;)
plt.imshow(image_np_with_detections)
plt.show()
</code></pre>
<h1>トレーニング済みの SSD (シングルショット検出器) モデルから TFLite モデルへの変換</h1>
<h1><strong>12) tf-nightlyをインストールします</strong></h1>
<p>TFLiteコンバーターは、tf-nightlyでうまく機能します。</p>
<pre><code>%cd /content/models/research/object_detection

!pip install tensorflow==&quot;2.12.0&quot;
!pip install numpy==1.26.4
!pip install tf-nightly
</code></pre>
<h1><strong>13) SSDTFLiteグラフをエクスポートします</strong></h1>
<p>現在の作業ディレクトリは /content/models/research/object_detection検出です</p>
<pre><code># !pip3 uninstall keras
# !pip3 install keras==2.14.0

!pip3 install --upgrade tensorflow keras

!pip3 install tensorflow==&quot;2.12.0&quot;

# !pip3 install --upgrade tensorflow keras
# !pip3 install tensorflow==&quot;2.13.1&quot;
# !pip3 install numpy --upgrade
# !pip3 uninstall numpy
# !pip3 install numpy==&quot;1.22.0&quot;
# !pip3 install tensorflow --upgrade
#!python --version



%cd /content/models/research/object_detection

!python export_tflite_graph_tf2.py --pipeline_config_path /content/customTF2/customTF2/data/ssd_mobilenet_v2_fpnlite_320x320_coco17_tpu-8.config --trained_checkpoint_dir /content/customTF2/customTF2/training --output_directory /content/customTF2/customTF2/data/tflite
</code></pre>
<h1><strong>14) TF 保存モデルを TFLite モデルに変換</strong></h1>
<p>現在の作業ディレクトリは /mydrive/customTF2/data/ です</p>
<pre><code>%cd /content/customTF2/customTF2/data/
</code></pre>
<h2>入力テンソル名と出力テンソル名を確認</h2>
<pre><code>!saved_model_cli show--dir /content/customTF2/customTF2/data/tflite/saved_model--tag_set serve—all
</code></pre>
<h2>TFLite への変換:</h2>
<p>方法 (a) または方法 (b) のいずれかを使用します。 </p>
<h4>方法 (a) コマンドラインツール <code>tflite_convert</code> を使用-(基本モデル変換)</h4>
<pre><code># デフォルトの推論タイプは浮動小数点です。
%cd /content/customTF2/customTF2/data/

!tflite_convert --saved_model_dir=tflite/saved_model --output_file=tflite/detect.tflite
</code></pre>
<h4>方法 (b) Python API を使用-(最適化などによる高度なモデル変換用)</h4>
<pre><code>%cd /mydrive/customTF2/data/

#&#39;&#39;&#39;********************************
# 浮動小数点推論用
#*********************************&#39;&#39;&#39;

#import tensorflow as tf

saved_model_dir = &#39;/content/customTF2/customTF2/data/tflite/saved_model&#39;

#converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
#tflite_model = converter.convert()
#open(&quot;/content/customTF2/customTF2/data/tflite/detect.tflite&quot;, &quot;wb&quot;).write(tflite_model)


#&#39;&#39;&#39;**************************************************
# 最適化による浮動小数点推論用
#***************************************************&#39;&#39;&#39;

import tensorflow as tf
converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir,signature_keys=[&#39;serving_default&#39;])
converter.optimizations = [tf.lite.Optimize.DEFAULT]
converter.experimental_new_converter = True
converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS,
                                       tf.lite.OpsSet.SELECT_TF_OPS]
tflite_model = converter.convert()

with tf.io.gfile.GFile(&#39;/mydrive/customTF2/data/tflite/detect.tflite&#39;, &#39;wb&#39;) as f:
   f.write(tflite_model)


#&#39;&#39;&#39;**********************************
# ダイナミックレンジ量子化用
#*************************************
# このモデルは重みが量子化され、少し小さくなりましたが、他の変数データはまだfloat形式です。&#39;&#39;&#39;

# import tensorflow as tf

# converter = tf.lite.TFLiteConverter.from_saved_model(&#39;/content/customTF2/customTF2/data/tflite/saved_model&#39;,signature_keys=[&#39;serving_default&#39;])
# converter.optimizations = [tf.lite.Optimize.DEFAULT]
# tflite_quant_model = converter.convert()

# with tf.io.gfile.GFile(&#39;/content/customTF2/customTF2/data/tflite/detect.tflite&#39;, &#39;wb&#39;) as f:
#   f.write(tflite_quant_model)


# &#39;&#39;&#39;***********************************************************************
# 整数と浮動小数点のフォールバック量子化（デフォルトの最適化を使用）
# **************************************************************************
# これで、すべての重みと変数データが量子化され、元のTensorFlow Liteモデルと比較してモデルが大幅に小さくなりました。
# ただし、float形式のモデル入力および出力テンソルを従来使用しているアプリケーションとの互換性を維持するために、
# TensorFlow Lite コンバーターはモデルの入力テンソルと出力テンソルをフロートのままにします&#39;&#39;&#39;

# import tensorflow as tf
# import numpy as np

# saved_model_dir = &#39;/content/customTF2/customTF2/data/tflite/saved_model&#39;

# def representative_dataset():
#     for _ in range(100):
#       data = np.random.rand(1, 320, 320, 3)
#       yield [data.astype(np.float32)]

# converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
# converter.optimizations = [tf.lite.Optimize.DEFAULT]
# converter.representative_dataset = representative_dataset
# tflite_quant_model = converter.convert()

# with open(&#39;/content/customTF2/customTF2/data/tflite/detect.tflite&#39;, &#39;wb&#39;) as f:
#   f.write(tflite_quant_model)


# &#39;&#39;&#39;*********************************
# 全整数量子化用
# ************************************
# 内部量子化は前の浮動小数点フォールバック量子化方法と同じままです。
# しかし、入力と出力のテンソルは、ここでも整数形式になっていることがわかります。&#39;&#39;&#39;

# import tensorflow as tf
# import numpy as np

# saved_model_dir = &#39;/content/customTF2/customTF2/data/tflite/saved_model&#39;

# def representative_dataset():
#     for _ in range(100):
#       data = np.random.rand(1, 320, 320, 3)
#       yield [data.astype(np.float32)]

# converter = tf.lite.TFLiteConverter.from_saved_model(saved_model_dir)
# converter.optimizations = [tf.lite.Optimize.DEFAULT]
# converter.representative_dataset = representative_dataset
# converter.target_spec.supported_ops = [tf.lite.OpsSet.TFLITE_BUILTINS_INT8]
# converter.inference_input_type = tf.uint8
# converter.inference_output_type = tf.uint8
# tflite_quant_model_full_int = converter.convert()

# with open(&#39;/content/customTF2/customTF2/data/tflite/detect.tflite&#39;, &#39;wb&#39;) as f:
#   f.write(tflite_quant_model_full_int)
</code></pre>
<p>トレーニング後の量子化の詳細は、 <a href="https://ai.google.dev/edge/litert/models/post_training_quantization">こちら</a>をご覧ください。これらについては、<a href="https://colab.research.google.com/github/tensorflow/tensorflow/blob/master/tensorflow/lite/g3doc/performance/post_training_integer_quant.ipynb#scrollTo=wYd6NxD03yjB">こちら</a>のColabノートブックでも読むことができます。</p>
<h1><strong>15) TFLite メタデータを作成します</strong></h1>
<pre><code>!pip install tflite_support_nightly
</code></pre>
<pre><code>%cd /content/customTF2/customTF2/data/
%d tflite/
!mkdir tflite_with_metadata
%cd ..
</code></pre>
<p><em><strong>データ</strong></em>フォルダ内の各行にクラスの名前が書き込まれた<em><strong>labelmap.txt</strong></em> ファイルを作成します。</p>
<p>最後に、次のセルを実行して、メタデータが添付された<em><strong>detect.tflite</strong></em>モデルを作成します。</p>
<p>現在の作業ディレクトリは /content/customTF2/customTF2/data/</p>
<pre><code>%cd /content/customTF2/customTF2/data/
!pip uninstall tensorflow
!pip install tensorflow==&quot;2.13.1&quot;


# TFLite にメタデータを添付
from tflite_support.metadata_writers import object_detector
from tflite_support.metadata_writers import writer_utils
import flatbuffers
import platform

from tensorflow_lite_support.metadata import metadata_schema_py_generated
from tensorflow_lite_support.metadata import schema_py_generated
from tensorflow_lite_support.metadata.python import metadata
from tensorflow_lite_support.metadata.python import metadata_writers

import flatbuffers
import os
from tensorflow_lite_support.metadata import metadata_schema_py_generated as _metadata_fb
from tensorflow_lite_support.metadata.python import metadata as _metadata
from tensorflow_lite_support.metadata.python.metadata_writers import metadata_info
from tensorflow_lite_support.metadata.python.metadata_writers import metadata_writer
from tensorflow_lite_support.metadata.python.metadata_writers import writer_utils

ObjectDetectorWriter = object_detector.MetadataWriter

_MODEL_PATH = &quot;/content/customTF2/customTF2/data/tflite/detect.tflite&quot;
_LABEL_FILE = &quot;/content/customTF2/customTF2/data/labelmap.txt&quot;
_SAVE_TO_PATH = &quot;/content/customTF2/customTF2/data/tflite/tflite_with_metadata/detect.tflite&quot;

writer = ObjectDetectorWriter.create_for_inference(
    writer_utils.load_file(_MODEL_PATH), [127.5], [127.5], [_LABEL_FILE])
writer_utils.save_file(writer.populate(), _SAVE_TO_PATH)

# 入力されたメタデータと関連ファイルを確認します。
displayer = metadata.MetadataDisplayer.with_model_file(_SAVE_TO_PATH)
print(&quot;Metadata populated:&quot;)
print(displayer.get_metadata_json())
print(&quot;Associated file(s) populated:&quot;)
print(displayer.get_packed_associated_file_list())

model_meta = _metadata_fb.ModelMetadataT()
model_meta.name = &quot;SSD_Detector&quot;
model_meta.description = (
    &quot;Identify which of a known set of objects might be present and provide &quot;
    &quot;information about their positions within the given image or a video &quot;
    &quot;stream.&quot;)

# 入力情報を作成します。
input_meta = _metadata_fb.TensorMetadataT()
input_meta.name = &quot;image&quot;
input_meta.content = _metadata_fb.ContentT()
input_meta.content.contentProperties = _metadata_fb.ImagePropertiesT()
input_meta.content.contentProperties.colorSpace = (
    _metadata_fb.ColorSpaceType.RGB)
input_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.ImageProperties)
input_normalization = _metadata_fb.ProcessUnitT()
input_normalization.optionsType = (
    _metadata_fb.ProcessUnitOptions.NormalizationOptions)
input_normalization.options = _metadata_fb.NormalizationOptionsT()
input_normalization.options.mean = [127.5]
input_normalization.options.std = [127.5]
input_meta.processUnits = [input_normalization]
input_stats = _metadata_fb.StatsT()
input_stats.max = [255]
input_stats.min = [0]
input_meta.stats = input_stats

# 出力情報を作成します。
output_location_meta = _metadata_fb.TensorMetadataT()
output_location_meta.name = &quot;location&quot;
output_location_meta.description = &quot;The locations of the detected boxes.&quot;
output_location_meta.content = _metadata_fb.ContentT()
output_location_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.BoundingBoxProperties)
output_location_meta.content.contentProperties = (
    _metadata_fb.BoundingBoxPropertiesT())
output_location_meta.content.contentProperties.index = [1, 0, 3, 2]
output_location_meta.content.contentProperties.type = (
    _metadata_fb.BoundingBoxType.BOUNDARIES)
output_location_meta.content.contentProperties.coordinateType = (
    _metadata_fb.CoordinateType.RATIO)
output_location_meta.content.range = _metadata_fb.ValueRangeT()
output_location_meta.content.range.min = 2
output_location_meta.content.range.max = 2

output_class_meta = _metadata_fb.TensorMetadataT()
output_class_meta.name = &quot;category&quot;
output_class_meta.description = &quot;The categories of the detected boxes.&quot;
output_class_meta.content = _metadata_fb.ContentT()
output_class_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.FeatureProperties)
output_class_meta.content.contentProperties = (
    _metadata_fb.FeaturePropertiesT())
output_class_meta.content.range = _metadata_fb.ValueRangeT()
output_class_meta.content.range.min = 2
output_class_meta.content.range.max = 2
label_file = _metadata_fb.AssociatedFileT()
label_file.name = os.path.basename(&quot;labelmap.txt&quot;)
label_file.description = &quot;Label of objects that this model can recognize.&quot;
label_file.type = _metadata_fb.AssociatedFileType.TENSOR_VALUE_LABELS
output_class_meta.associatedFiles = [label_file]

output_score_meta = _metadata_fb.TensorMetadataT()
output_score_meta.name = &quot;score&quot;
output_score_meta.description = &quot;The scores of the detected boxes.&quot;
output_score_meta.content = _metadata_fb.ContentT()
output_score_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.FeatureProperties)
output_score_meta.content.contentProperties = (
    _metadata_fb.FeaturePropertiesT())
output_score_meta.content.range = _metadata_fb.ValueRangeT()
output_score_meta.content.range.min = 2
output_score_meta.content.range.max = 2

output_number_meta = _metadata_fb.TensorMetadataT()
output_number_meta.name = &quot;number of detections&quot;
output_number_meta.description = &quot;The number of the detected boxes.&quot;
output_number_meta.content = _metadata_fb.ContentT()
output_number_meta.content.contentPropertiesType = (
    _metadata_fb.ContentProperties.FeatureProperties)
output_number_meta.content.contentProperties = (
    _metadata_fb.FeaturePropertiesT())

# サブグラフ情報を作成します。
group = _metadata_fb.TensorGroupT()
group.name = &quot;detection result&quot;
group.tensorNames = [
    output_location_meta.name, output_class_meta.name,
    output_score_meta.name
]
subgraph = _metadata_fb.SubGraphMetadataT()
subgraph.inputTensorMetadata = [input_meta]
subgraph.outputTensorMetadata = [
    output_location_meta, output_class_meta, output_score_meta,
    output_number_meta
]
subgraph.outputTensorGroups = [group]
model_meta.subgraphMetadata = [subgraph]

b = flatbuffers.Builder(0)
b.Finish(
    model_meta.Pack(b),
    _metadata.MetadataPopulator.METADATA_FILE_IDENTIFIER)
metadata_buf = b.Output()
</code></pre>
<p>質問されたら、&#39;Y&#39;で進みます</p>
<h1><strong>16) TFLite モデルをダウンロードします</strong></h1>
<p>お疲れ様でした。これで完了です！</p>
<h1><strong>まとめ</strong></h1>
<p>Google Colab Enterpriseは、機械学習のための強力なクラウドベースのプラットフォームであり、TensorFlow Liteモデルを構築する上で理想的な環境です。このプラットフォームを1年以上使用してみて、最も時間がかかるのはデータ準備と初期の試行錯誤段階であることが分かりました。この段階では、データセットの特定の部分を認識する際の課題を特定し、画像が誤って分類される誤検知に対処するために、著しい反復とテストが必要です。</p>
<p>*Android ロボットのヘッダー画像は、Google が作成および共有した画像から複製または変更されたものであり、クリエイティブ コモンズ 3.0 帰属ライセンスに記載されている条件に従って使用されています。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/beckmartin/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Processing S3 events while taking care of data conflicts ~With Rust Lambda DynamoDB~]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-06-データ競合を気にしながらS3イベントを処理してみた-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-06-データ競合を気にしながらS3イベントを処理してみた-en/</guid>
            <pubDate>Thu, 03 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I tried processing S3 events while taking care of data conflicts with sample codes using Rust Lambda DynamoDB.]]></description>
            <content:encoded><![CDATA[<p>This article is part of day 6 of <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>.</p>
<hr>
<h1>Introduction</h1>
<p>Hello. I am Uehara (<a href="https://twitter.com/penpen_77777">@penpen_77777</a>), and I am part of the KINTO FACTORY Development Group. I joined in July 2024, and I was assigned to the backend development of KINTO FACTORY.</p>
<p>This time, using sample code, I will introduce the data conflicts that you should be cautious of when processing S3 events, and how to resolve them.</p>
<h1>Intended Readers</h1>
<ul>
<li>Those who are concerned with AWS S3 event notifications being duplicated or the order of notifications being changed.</li>
<li>Those with basic knowledge of Rust, S3, DynamoDB, Lambda, and Terraform.<ul>
<li>Having this knowledge will make it easier to understand when reading the sample code.</li>
</ul>
</li>
</ul>
<h1>S3 Event Overview</h1>
<p>An S3 event^[AWS document regarding S3 event notifications <a href="https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/EventNotifications.html%5D">https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/EventNotifications.html]</a> is an event that occurs when an operation such as uploading or deleting an object to S3 is triggered. By detecting S3 events using Lambda functions or SNS, various processes concerning S3 can be automated.</p>
<h1>Issues with S3 Events</h1>
<p>Something you should be cautious of when processing S3 events is that the event notifications could be duplicated or the order could be changed.</p>
<p>For example, let’s think about the process of creating an object after deleting an object for the same object key. In this case, first you will be notified of the object delete event, then you can expect to be notified of the create event (remove the same object =&gt; see the diagram of the expected S3 event reception order when created). However, there are times when the S3 event order isn’t secured, and the create event notification comes before the delete event notification (remove the same object =&gt; see the diagram of the order of reception of possible S3 events when created). As a result, the processing result by the event that deletes the object will be the latest, and the issue of data consistency not being guaranteed depending on the processing content occurs.</p>
<pre><code class="language-mermaid">gantt
    title Remove the same object =&gt; Expected S3 event reception order when created
    dateFormat  HH:mm:ss
    axisFormat %H:%M:%S
    section Object deletion
    Object deletion :done, cre1, 00:00:01, 1s
    Receive delete event / Process :done, cre2, 00:00:03, 1s
    section Upload object
    Upload object :done, cre1, 00:00:02, 1s
    Receive create event / Process :active, cre2, 00:00:04, 1s
</code></pre>
<pre><code class="language-mermaid">gantt
    title Remove the same object =&gt; Expected S3 event reception order when created
    dateFormat  HH:mm:ss
    axisFormat %H:%M:%S
    section Object deletion
    Object deletion :done, cre1, 00:00:01, 1s
    Receive delete event / Process :active, cre2, 00:00:04, 1s
    section Upload object
    Upload object :done, cre1, 00:00:02, 1s
    Receive create event / Process :done, cre2, 00:00:03, 1s
</code></pre>
<p>As a solution to this problem, there is a way to guarantee the order of events using the sequencer key included in S3 events^[document regarding S3 event structure.
<a href="https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/notification-content-structure.html%5D">https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/notification-content-structure.html]</a></p>
<blockquote>
<p>One way to determine the sequence of events is to use the sequencer key. There is no guarantee that event notifications will be delivered in the order in which the event occurred. However, notifications and delete objects from events that create objects (PUT) include the sequencer. This can be used to determine the order of events for a particular object key.</p>
<p>When comparing the sequencer strings of two event notifications for the same object key, you can see that the event notification with the larger 16 hexadecimal value sequencer is the event that occurred later. When maintaining a separate database or index of Amazon S3 objects using event notifications, I recommend that you compare and save the sequencer value each time you process an event notification.</p>
<p>Please note the following:</p>
<p>The sequencer cannot be used to determine the order of events for multiple object keys.</p>
<p>The length of the sequencer may be different. To compare these values, first input 0 on the right of the shorter value, and then perform a lexicographic comparison.</p>
</blockquote>
<p>To Summarize:</p>
<ul>
<li>The sequencer is a value included in an object&#39;s PUT or DELETE event, and can be used to determine the order of events.</li>
<li>Lexicographically compare the sequencers. The larger the value, the later the event that occurred.<ul>
<li>If the lengths differ, compare after inputting 0 on the right side of the shorter value.</li>
</ul>
</li>
<li>It cannot be used to determine the order of S3 events between multiple objects.<ul>
<li>It is used to determine the order of PUT and DELETE events for the same object.</li>
</ul>
</li>
</ul>
<p>For example, if you want to implement the S3 event sequencer comparison in Rust, you can implement it as follows:</p>
<p>Define the fields and constructor of the structure <code>S3Sequencer</code> to express the nature of the S3 sequencer.</p>
<pre><code class="language-rust">// 1. Define the structure S3Sequencer
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct S3Sequencer {
    bucket_name: String,
    object_key: String,
    sequencer: String,
}

// 2. Define the S3Sequencer constructor
// Take the bucket name, object key, and sequencer as argument
impl S3Sequencer {
    pub fn new(bucket_name: &amp;str, objcet_key: &amp;str, sequencer: &amp;str) -&gt; Self {
        Self {
            bucket_name: bucket_name.to_owned(),
            object_key: objcet_key.to_owned(),
            sequencer: sequencer.to_owned(),
        }
    }
}
</code></pre>
<p>Next, to determine the order of events by comparing the size of the S3Sequencer, use the <code>PartialOrd</code>^[Mathematically speaking, it can represent a partially ordered set. There is also a full ordered set that implements the ‘Ord’ trait. It is interesting that traits are intentionally divided to implement comparison methods. I will implement <a href="https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html">https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html</a> <a href="https://ja.wikipedia.org/wiki/%E9%A0%86%E5%BA%8F%E9%9B%86%E5%90%88#%E5%8D%8A%E9%A0%86%E5%BA%8F%E9%9B%86%E5%90%88%5D">https://ja.wikipedia.org/wiki/%E9%A0%86%E5%BA%8F%E9%9B%86%E5%90%88#%E5%8D%8A%E9%A0%86%E5%BA%8F%E9%9B%86%E5%90%88]</a> and <code>PartialEq</code> trait^[PartialEq trait definition <a href="https://doc.rust-lang.org/std/cmp/trait.PartialEq.html%5D">https://doc.rust-lang.org/std/cmp/trait.PartialEq.html]</a> By implementing these two, you can compare the size of the sequencers using comparison operators such as <code>==</code>, <code>&lt;</code>, or <code>&gt;</code> as shown below.</p>
<pre><code class="language-rust">let seq1 = S3Sequencer::new(&quot;bucket1&quot;, &quot;object1&quot;, &quot;abc123&quot;);
let seq2 = S3Sequencer::new(&quot;bucket1&quot;, &quot;object1&quot;, &quot;abc124&quot;);
if seq1 &lt; seq2 {
    println!(&quot;seq1 is an older event than seq2&quot;);
} else if seq1 == seq2 {
    println!(&quot;seq1 and seq2&quot;);
} else {
    println!(&quot;seq1 is a newer event than seq2&quot;);
}
</code></pre>
<p>Implement the partial_cmp method required to implement the PartialOrd trait as shown below.</p>
<pre><code class="language-rust">impl PartialOrd for S3Sequencer {
    fn partial_cmp(&amp;self, other: &amp;Self) -&gt; Option&lt;std::cmp::Ordering&gt; {
        // Sequencers with different bucket names cannot be compared
        if self.bucket_name != other.bucket_name {
            return None;
        }

        // Sequencers with different object keys cannot be compared
        if self.object_key != other.object_key {
            return None;
        }

        // Compare by adding 0 to the end of the shorter one to match with the longer one
        let max_len = std::cmp::max(self.sequencer.len(), other.sequencer.len());
        let self_sequencer = self
            .sequencer
            .chars()
            .chain(std::iter::repeat(&#39;0&#39;))
            .take(max_len);
        let other_sequencer = other
            .sequencer
            .chars()
            .chain(std::iter::repeat(&#39;0&#39;))
            .take(max_len);

        Some(self_sequencer.cmp(other_sequencer))
    }
</code></pre>
<p>Since there is no meaning in comparing sequencers with different bucket names and object keys, return None using early return if they are different. You can compare the sequencers once you confirm that the bucket names and object keys are the same, but the process will be in the following order.</p>
<ol>
<li>Compare the length of the sequencers, and store the longer one in <code>max_len</code></li>
<li>Add 0 to the end of the shorter sequencer to match <code>max_len</code></li>
<li>Compare the sequencers created in 2 in lexicographic order and return the size</li>
</ol>
<p>The PartialEq trait is implemented as follows:</p>
<pre><code class="language-rust">impl PartialEq for S3Sequencer {
    fn eq(&amp;self, other: &amp;Self) -&gt; bool {
        self.partial_cmp(other)
            .map_or(false, |o| o == std::cmp::Ordering::Equal)
    }
}
</code></pre>
<p>Determine if the sequencer comparison results are equal using the partial_cmp method of the PartialOrd trait.</p>
<p>With the above implementations, it is now possible to compare sequencers for S3 events.</p>
<h1>Example of S3 Event Processing Implementation Considering Data Consistency</h1>
<h2>Architecture Diagram</h2>
<p>Now, I will introduce how to guarantee the order of S3 events using sequencers, along with sample code The sample code converts the image file uploaded to the S3 bucket to grayscale on Lambda and saves it to the S3 bucket for output. Below is an architecture diagram.</p>
<p>![sample code architecture diagram. Convert the image file uploaded to the S3 bucket to grayscale on Lambda and save it to the S3 bucket for output. Use DynamoDB to implement lock process.](/assets/blog/authors/uehara/2024-12-02-how-to-gurantee-s3-event-order/architecture.svg =600x)</p>
<p>When an image file is uploaded to the image input bucket, the Lambda function is triggered through an S3 event. The launched Lambda function checks DynamoDB to confirm that it is not processing, then sets the processing flag and processes the image file. Once processing has finished, it clears the processing flag and waits for the next image file to be processed.</p>
<p>If the order of the create and delete event notifications is reversed, problems such as accidently deleting images that should exist could occur. For example, let’s assume that the process is implemented as follows.</p>
<ol>
<li>Image file A is deleted from the input bucket</li>
<li>Image file A is re-uploaded to the input bucket</li>
<li>Lambda receives a delete event (corresponding to 1) and deletes image file A from the output bucket</li>
<li>Lambda receives a create event (corresponding to 2), processes image file A, converts it to grayscale, and saves it in the output bucket</li>
</ol>
<pre><code class="language-mermaid">gantt
    title Remove the same object =&gt; Expected S3 event reception order when created
    dateFormat  HH:mm:ss
    axisFormat %H:%M:%S
    section Object deletion
    (1) Object deletion :done, cre1, 00:00:01, 1s
    (3) Event reception, Object deletion :done, cre2, 00:00:03, 1s
    section Upload object
    (2) Upload object :done, cre1, 00:00:02, 1s
    (4) Event reception, convert to grayscale :active, cre2, 00:00:04, 1s
</code></pre>
<p>However, there is a possibility of the notification order for 3 and 4 being reversed in S3 events, so the following flow may occur.</p>
<ol>
<li>Image file A is deleted from the input bucket</li>
<li>Image file A is re-uploaded to the input bucket</li>
<li>Lambda receives a create event (corresponding to 2), processes image file A, converts it to grayscale, and saves it in the output bucket</li>
<li>Lambda receives a delete event (corresponding to 1) and deletes image file A from the output bucket</li>
</ol>
<pre><code class="language-mermaid">gantt
    title Remove the same object =&gt; Expected S3 event reception order when created
    dateFormat  HH:mm:ss
    axisFormat %H:%M:%S
    section Object deletion
    (1) Object deletion :done, cre1, 00:00:01, 1s
    (4) Event reception, Object deletion :active, cre2, 00:00:04, 1s
    section Upload object
    (2) Upload object :done, cre1, 00:00:02, 1s
    (3) Event reception, convert to grayscale :done, cre2, 00:00:03, 1s
</code></pre>
<p>In this case, the problem occurs that even though image file A exists in the input bucket, the grayscaled image file A&#39; does not exist in the output bucket.</p>
<p>In order to prevent problems such as this, use the S3 event sequencer to implement exclusive processing. Additionally, it is also necessary to implement exclusive processing to manage image processing status by DynamoDB. Use DynamoDB conditional write to set an in-progress flag in order to prevent multiple Lambda functions from processing the same image file at the same time.</p>
<p>The sample code is published on GITHUB. Take a look using the link below. (You will need to build an AWS infrastructure to run it, but it has been made easy to try using the terraform code.) </p>
<p><a href="https://github.com/kinto-technologies/techblog-s3-sequencer-example">https://github.com/kinto-technologies/techblog-s3-sequencer-example</a></p>
<h2>Implementing Sample Code with Rust</h2>
<p>This time, I will implement a Lambda function using Rust. It is convenient to use cargo-lambda to implement Lambda functions.</p>
<p><a href="https://www.cargo-lambda.info/">https://www.cargo-lambda.info/</a></p>
<p>I will omit the details of how to use cargo-lambda.</p>
<h3>Creating an Entry Point</h3>
<p>When you run the initialization command on cargo-lambda, main.rs is automatically generated as shown below.</p>
<pre><code class="language-bash">cargo lambda init
</code></pre>
<pre><code class="language-rust:lambda/src/main.rs">use lambda_runtime::{
    run, service_fn,
    tracing::{self},
    Error,
};
mod handler;
mod image_task;
mod lock;
mod s3_sequencer;

#[tokio::main]
async fn main() -&gt; Result&lt;(), Error&gt; {
    tracing::init_default_subscriber();

    run(service_fn(handler::function_handler)).await
}
</code></pre>
<p>Since <code>handler:function_handler</code> is specified as the entry point for the Lambda function, I will write the implementation in <code>handler.rs</code></p>
<p>The <code>function_handler</code> is implemented as follows.</p>
<pre><code class="language-rust:lambda/src/handler.rs">use crate::image_task::ImageTask;
use aws_lambda_events::event::s3::S3Event;
use lambda_runtime::{tracing::info, Error, LambdaEvent};

pub async fn function_handler(event: LambdaEvent&lt;S3Event&gt;) -&gt; Result&lt;(), Error&gt; {
    // Convert S3 events to ImageTask
    let tasks: Vec&lt;_&gt; = event
        .payload
        .records
        .into_iter()
        .map(ImageTask::try_from)
        .collect::&lt;Result&lt;Vec&lt;_&gt;, _&gt;&gt;()?;

    // Use futures::future::join_all to create the task to be executed
    let execute_tasks = tasks.iter().map(|task| task.execute());

    // Use join_all to execute or wait all tasks
    // Store the execution results in ret
    let ret = futures::future::join_all(execute_tasks).await;

    // Output the execution results to log
    for (t, r) in tasks.iter().zip(&amp;ret) {
        info!(&quot;object_key: {}, Result: {:?}&quot;, t.object_key, r);
    }

    // Return error is there is an error
    if ret.iter().any(|r| r.is_err()) {
        return Err(&quot;Some tasks failed&quot;.into());
    }

    // Successful termination
    Ok(())
}
</code></pre>
<ol>
<li>Convert the S3 event vector to an ImageTask structure vector. Since the conversion method implements the TryFrom trait, you just have to call the try_form method.</li>
<li>Create an image processing task based on the ImageTask structure vector.</li>
<li>Use the tokio crate join_all function to execute all tasks in parallel.</li>
<li>Output the results returned by join_all in 3 to the log.</li>
<li>If there is an error, return the error and terminate the Lambda function abnormally.</li>
<li>If there are no errors it will successfully terminate.</li>
</ol>
<h3>Image Processing Implementation</h3>
<p>The ImageTask structure used in 1 is defined as below and holds the information necessary to execute Lambda.</p>
<pre><code class="language-rust:lambda/src/image_task.rs">#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(tag = &quot;type&quot;)]
pub enum TaskType {
    Grayscale,
    Delete,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ImageTask {
    pub bucket_name: String,
    #[serde(rename = &quot;id&quot;)]
    pub object_key: String,
    pub sequencer: S3Sequencer,
    pub task_type: TaskType,
    pub processing: bool,
}
</code></pre>
<table>
<thead>
<tr>
<th>Field name</th>
<th>Description</th>
</tr>
</thead>
<tbody><tr>
<td>bucket_name</td>
<td>S3 bucket name</td>
</tr>
<tr>
<td>object_key</td>
<td>Object key</td>
</tr>
<tr>
<td>sequencer</td>
<td>S3 event sequencer</td>
</tr>
<tr>
<td>task_type</td>
<td>Enumeration indicating the type of task (Grayscale, Delete)</td>
</tr>
<tr>
<td>processing</td>
<td>Processing flag</td>
</tr>
</tbody></table>
<p>Implement specific image processing within the execute method of the ImageTask structure.</p>
<pre><code class="language-rust:lambda/src/image_task.rs">impl ImageTask {
    pub async fn execute(&amp;self) -&gt; Result&lt;(), Error&gt; {
        // 1. Acquire lock
        let lock = S3Lock::new(&amp;self).await?;

        // 2. Process according to the task type
        match self.task_type {
            TaskType::Grayscale =&gt; {
                // Convert image to grayscale and save in output bucket
                let body = lock.read_input_object().await?;
                let format = image::ImageFormat::from_path(&amp;self.object_key)?;
                let img = image::load_from_memory_with_format(&amp;body, format)?;
                let img = img.grayscale();
                let mut buf = Vec::new();
                img.write_to(&amp;mut Cursor::new(&amp;mut buf), format)?;
                lock.write_output_object(buf).await?;
            }
            // Delete image from output bucket
            TaskType::Delete =&gt; lock.delete_output_object().await?,
        }

        // 3. Release lock
        lock.free().await?;

        Ok(())
    }
}
</code></pre>
<ol>
<li>Apply exclusive processing to prevent S3 object data inconsistencies</li>
<li>Process according to the task type</li>
</ol>
<ul>
<li>If a file was added to a previous bucket, convert the image to grayscale and save it in the output bucket</li>
<li>If a file was deleted, delete the file from the output bucket</li>
</ul>
<ol start="3">
<li>Release lock when processing has finished</li>
</ol>
<h2>Implement Lock Processing</h2>
<ul>
<li>Define the S3Lock structure to implement lock processing.</li>
</ul>
<pre><code class="language-rust:lambda/src/lock.rs">pub struct S3Lock {
    dynamodb_client: aws_sdk_dynamodb::Client,
    table_name: String,

    s3_client: aws_sdk_s3::Client,
    input_bucket_name: String,
    input_object_key: String,
    output_bucket_name: String,
    output_object_key: String,
}
</code></pre>
<p>The specific lock acquisition process is implemented in the constructor. The code is a bit long, but roughly speaking, it is as follows.</p>
<ul>
<li>If writing to DynamoDB is successful, the lock is considered to have been acquired.</li>
<li>If writing fails, try again every 2 seconds.</li>
<li>If the lock cannot be acquired within 30 seconds, a timeout occurs.</li>
</ul>
<p>The lock processing sequence diagram is shown below.</p>
<pre><code class="language-mermaid">sequenceDiagram
    participant ImageTask
    participant S3Lock
    participant DynamoDB

    ImageTask-&gt;&gt;S3Lock: Acquire lock
    loop
        alt timeout (30 seconds)
            S3Lock-&gt;&gt;ImageTask: Return error (timeout)
        end
        S3Lock-&gt;&gt;DynamoDB: Acquire processing status
        DynamoDB-&gt;&gt;S3Lock: Return results

        alt record exists
            S3Lock-&gt;&gt;S3Lock: Sequencer comparison
            alt is old
                S3Lock-&gt;&gt;ImageTask: Return error (skip)
            else is new
                S3Lock-&gt;&gt;S3Lock: Retry acquiring lock
            end
        else
            S3Lock-&gt;&gt;DynamoDB: Acquire lock with conditional write
            DynamoDB-&gt;&gt;S3Lock: Write return result
            alt write successful
                S3Lock-&gt;&gt;ImageTask: Lock acquisition successful
            else failure
                S3Lock-&gt;&gt;S3Lock: retry
            end
        end
    end
</code></pre>
<p>The code in the constructor is as follows:</p>
<pre><code class="language-rust:lambda/src/lock.rs">impl S3Lock {
    pub async fn new(task: &amp;ImageTask) -&gt; Result&lt;Self, Error&gt; {
        let table_name = std::env::var(&quot;DYNAMODB_TABLE_NAME&quot;).unwrap();
        let output_bucket_name = std::env::var(&quot;OUTPUT_BUCKET_NAME&quot;).unwrap();
        let require_lock_timeout = Duration::from_secs(
            std::env::var(&quot;REQUIRE_LOCK_TIMEOUT&quot;)
                .unwrap_or_else(|_| &quot;30&quot;.to_string())
                .parse::&lt;u64&gt;()
                .unwrap(),
        );
        let interval_retry_time = Duration::from_secs(
            std::env::var(&quot;RETRY_INTERVAL&quot;)
                .unwrap_or_else(|_| &quot;2&quot;.to_string())
                .parse::&lt;u64&gt;()
                .unwrap(),
        );

        let config = aws_config::load_defaults(aws_config::BehaviorVersion::v2024_03_28()).await;
        let s3_client = aws_sdk_s3::Client::new(&amp;config);
        let dynamodb_client = aws_sdk_dynamodb::Client::new(&amp;config);

        // Acquire lock
        Measure execution time 
        let start = Instant::now();
        loop {
            // It will time out if the lock cannot be acquired for more than 30 seconds.
            if start.elapsed() &gt; require_lock_timeout {
                return Err(&quot;Failed to acquire lock, timeout&quot;.into());
            }

            Acquire sequencer from DynamoDB with strong read consistency
            let item = dynamodb_client
                .get_item()
                .table_name(table_name.clone())
                .key(&quot;id&quot;, AttributeValue::S(task.object_key.clone()))
                .consistent_read(true)
                .send()
                .await?;

            Compare sequencer if acquired item exists
            if let Some(item) = item.item {
                let item: ImageTask = from_item(item)?;
                if task.sequencer &lt;= item.sequencer {
                    If the sequencer itself is old, there is no need to process it, so skip it
                    return Err(&quot;Old sequencer&quot;.into());
                }

                If the sequencer itself is new, wait until other processing has finished
                if item.processing {
                    warn!(
                        &quot;Waiting for other process to finish task, retrying, remaining time: {:?}&quot;,
                        require_lock_timeout - start.elapsed()
                    );
                    thread::sleep(interval_retry_time);
                    continue;
                }
            }

            // Acquire lock with conditional write to DynamoDB
            // If the record exists at that time, only write if the processing flag is false
            let resp = dynamodb_client
                .put_item()
                .table_name(table_name.clone())
                .set_item(Some(to_item(&amp;task).unwrap()))
                .condition_expression(&quot;attribute_not_exists(id) OR processing = :false&quot;)
                .expression_attribute_values(&quot;:false&quot;, AttributeValue::Bool(false))
                .send()
                .await;

            Once acquired, exit the loop and continue processing
            If it could not be acquired, continue trying until the lock can be acquired.
            match resp {
                Ok(_) =&gt; break,
                Err(SdkError::ServiceError(e)) =&gt; match e.err() {
                    PutItemError::ConditionalCheckFailedException(_) =&gt; {
                        warn!(
                            &quot;Failed to acquire lock, retrying, remaining time: {:?}&quot;,
                            require_lock_timeout - start.elapsed()
                        );
                        thread::sleep(Duration::from_secs(2));
                        continue;
                    }
                    _ =&gt; return Err(format!(&quot;{:?}&quot;, e).into()),
                },
                Err(e) =&gt; return Err(e.into()),
            }
        }

        return Ok(Self {
            dynamodb_client,
            output_bucket_name,
            s3_client,
            table_name,
            input_bucket_name: task.bucket_name.clone(),
            input_object_key: task.object_key.clone(),
            output_object_key: task.object_key.clone(),
        });
    }
}
</code></pre>
<p>The process to release the lock is implemented as shown below, and the lock is released by updating the processing flag to false.</p>
<pre><code class="language-rust:lambda/src/lock.rs">impl S3Lock {
    pub async fn free(self) -&gt; Result&lt;(), Error&gt; {
        // Release DynamoDB lock
        // Only update the processing flag
        self.dynamodb_client
            .update_item()
            .table_name(self.table_name)
            .key(&quot;id&quot;, AttributeValue::S(self.input_object_key))
            .update_expression(&quot;SET processing = :false&quot;)
            .expression_attribute_values(&quot;:false&quot;, AttributeValue::Bool(false))
            .send()
            .await?;
        Ok(())
    }
}
</code></pre>
<p>Since I want to force a lock when touching an S3 object, I have created a method for manipulating S3 objects in S3Lock^[I feel that reusability will be lower if I create it in S3Lock, but for simplicity I will define it in the same structure. Though there seems to be a better way...]</p>
<pre><code class="language-rust:lambda/src/lock.rs">impl S3Lock {
    pub async fn read_input_object(&amp;self) -&gt; Result&lt;Vec&lt;u8&gt;, Error&gt; {
        // Acquire S3 object
        let object = self
            .s3_client
            .get_object()
            .bucket(&amp;self.input_bucket_name)
            .key(&amp;self.input_object_key)
            .send()
            .await?;
        let body = object.body.collect().await?.to_vec();
        Ok(body)
    }

    pub async fn write_output_object(&amp;self, buf: Vec&lt;u8&gt;) -&gt; Result&lt;(), Error&gt; {
        // Save S3 object
        let byte_stream = ByteStream::from(buf);
        self.s3_client
            .put_object()
            .bucket(&amp;self.output_bucket_name)
            .key(&amp;self.output_object_key)
            .body(byte_stream)
            .send()
            .await?;
        Ok(())
    }

    pub async fn delete_output_object(&amp;self) -&gt; Result&lt;(), Error&gt; {
        // Delete S3 object
        self.s3_client
            .delete_object()
            .bucket(&amp;self.output_bucket_name)
            .key(&amp;self.output_object_key)
            .send()
            .await?;
        Ok(())
    }
}
</code></pre>
<h2>Trying it Out</h2>
<p>Let’s try  using the sample code.</p>
<p>I need to prepare an image that I want to convert to grayscale, so this time I will use Hyogo Prefectural Park Awaji Hanasajiki^[a beautiful flower garden on Awaji Island <a href="https://awajihanasajiki.jp/about/%5D">https://awajihanasajiki.jp/about/]</a>. I took the photo. I will use the image from <a href="https://awajihanasajiki.jp/about/">https://awajihanasajiki.jp/about/</a>.</p>
<p><img src="/assets/blog/authors/uehara/2024-12-02-how-to-gurantee-s3-event-order/sample_img.webp" alt="Photo of the flower garden at Hyogo Prefectural Park Awaji Hanasajiki"></p>
<h3>Infrastructure</h3>
<p>First, use terraform apply to build the AWS infrastructure. Then, clone the GitHub repository and implement the following command.</p>
<pre><code class="language-bash">cd terraform
# Modify variables.tf and provider.tf appropriately.
terraform init
terraform apply
</code></pre>
<p>![sample code architecture diagram. Convert the image file uploaded to the S3 bucket to grayscale on Lambda and save it to the S3 bucket for output. Use DynamoDB to implement lock process.](/assets/blog/authors/uehara/2024-12-02-how-to-gurantee-s3-event-order/architecture.svg =600x)</p>
<h3>Upload the Image File to the S3 Bucket</h3>
<p>If infrastructure is completed, upload the image file to the input S3 bucket.</p>
<p><img src="/assets/blog/authors/uehara/2024-12-02-how-to-gurantee-s3-event-order/upload_sample_img.png" alt="The image is added to the bucket for input to S3."></p>
<p>The Lambda function process begins when the upload finishes, and the item is added to the DynamoDB table.  The image file is saved in the output S3 bucket when the process finishes, and the processing flag of the DynamoDB item is set to false.</p>
<p><img src="/assets/blog/authors/uehara/2024-12-02-how-to-gurantee-s3-event-order/processed_dynamodb.png" alt="DynamoDB item when Lambda completes image processing."></p>
<p>You can see the image added to the output S3 bucket and converted to grayscale.</p>
<p><img src="/assets/blog/authors/uehara/2024-12-02-how-to-gurantee-s3-event-order/grayed_sample_img.webp" alt="Image converted into grayscale."></p>
<h3>Delete the Image File from the S3 Bucket</h3>
<p>When an object is deleted from the input bucket, it is also deleted from the output bucket. <img src="/assets/blog/authors/uehara/2024-12-02-how-to-gurantee-s3-event-order/deleted_sample_img_from_s3.png" alt="Image converted into grayscale."></p>
<h3>Confirm that Exclusive Processing is Working</h3>
<p>When the <code>processing</code> flag of an item added to DynamoDB is set to true, processing will wait even if an S3 event that should be processed arrives. To confirm this behavior, let&#39;s intentionally set the DynamoDB <code>processing</code> flag to true and upload a file with the same name. <img src="/assets/blog/authors/uehara/2024-12-02-how-to-gurantee-s3-event-order/change_processing_flg.png" alt="The DynamoDB item processing flag is true."></p>
<p>If you look at CloudWatch Logs, you can see that it waits for other processes to complete without processing newly generated S3 events. The log message will indicate that Lambda is waiting.</p>
<p>Setting the DynamoDB processing flag back to false will restart processing. When the DynamoDB item&#39;s processing flag is set back to false, the log will indicate that processing is completed.</p>
<p>Thanks to exclusive processing, the order of processing is guaranteed even if delete and upload events occur at approximately the same time.</p>
<h1>Conclusion</h1>
<p>This time, I introduced sample code for image processing that uses a sequencer to guarantee the order of S3 events. To guarantee the order of S3 events, you need to utilize a sequencer to compare the order of events. I implemented the sample code in Rust as my own hobby, but it should be possible to implement something similar in other languages. Please feel free to use this as a reference.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/uehara/2024-12-02-how-to-gurantee-s3-event-order/thumbs.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Getting Started with Bedrock Knowledge Base Using Terraform]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-20-Getting-Started-with-Bedrock-Knowledge-Base-using-Terraform-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-20-Getting-Started-with-Bedrock-Knowledge-Base-using-Terraform-en/</guid>
            <pubDate>Wed, 02 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[In this article, I will share my experience setting up an Amazon Bedrock Knowledge Base using Terraform.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I&#39;m Shimakawa, a member of the Cloud Infrastructure Group. The Cloud Infrastructure Group is responsible for everything from designing to operating the company&#39;s entire infrastructure, including AWS. As generative AI adoption grows across various products in our company, the Cloud Infrastructure Group has been actively supporting these initiatives.</p>
<p>In this article, I’ll share my experience building an Amazon Bedrock Knowledge Base using Terraform. I’ll also touch on the <a href="https://aws.amazon.com/jp/about-aws/whats-new/2024/12/amazon-bedrock-knowledge-bases-rag-evaluation-preview/">RAG Evaluation</a> announced at re:Invent 2024.</p>
<h1>Configuration</h1>
<p>Here is the architecture we will be building. <img src="/assets/blog/authors/j.shimakawa/bedrock_knowledge_base_image.png" alt="bedrock_knowledge_base_image"></p>
<p>We will use OpenSearch Serverless as the Vector store for Amazon Bedrock Knowledge Base, and specify S3 as the data source.</p>
<h1>Building with Terraform</h1>
<p>The directory structure is as follows. I will explain each file’s content in detail. The Terraform version used in this setup is <code>1.7.5</code>.</p>
<pre><code class="language-bash">$ tree
.
├── aoss.tf # OpenSearch Serverless
├── bedrock.tf # Bedrock resources
├── iam.tf # iam
├── s3.tf # S3 for bedrock
├── locals.tf # variable definitions
├── provider.tf # provider definitions
└── terraform.tf # Backend settings, etc.
</code></pre>
<p>This section defines the variables.</p>
<pre><code class="language-hcl:locals.tf">locals {
  env = {
    environment  = &quot;dev&quot;
    region_name  = &quot;us-west-2&quot;
    sid          = &quot;test&quot;
  }
  aoss = {
    vector_index     = &quot;vector_index&quot;
    vector_field     = &quot;vector_field&quot;
    text_field       = &quot;text_field&quot;
    metadata_field   = &quot;metadata_field&quot;
    vector_dimension = 1024
  }
}
</code></pre>
<p>We specify the AWS provider, the OpenSearch provider version, and the S3 backend to store the tfstate. The S3 bucket used here was created manually and is not included in this Terraform code.</p>
<pre><code class="language-hcl:terraform.tf">terraform {
   required_providers {
    # https://registry.terraform.io/providers/hashicorp/aws/
    aws = {
      source  = &quot;hashicorp/aws&quot;
      version = &quot;~&gt; 5.0&quot;
    }
    opensearch = {
      source  = &quot;opensearch-project/opensearch&quot;
      version = &quot;2.2.0&quot;
    }
  }

  backend &quot;s3&quot; {
    bucket  = &quot;***-common-bucket&quot;
    region  = &quot;ap-northeast-1&quot;
    key     = &quot;hogehoge-terraform.tfstate&quot;
    encrypt = true
  }
}
</code></pre>
<p>This defines the AWS and OpenSearch providers. The OpenSearch provider is used to create and manage indexes.</p>
<pre><code class="language-hcl:provider.tf">provider &quot;aws&quot; {
  region = local.env.region_name

  default_tags {
    tags = {
      SID         = local.env.sid
      Environment = local.env.environment
    }
  }
}

provider &quot;opensearch&quot; {
  url        = aws_opensearchserverless_collection.collection.collection_endpoint
  aws_region = local.env.region_name

  healthcheck = false
}
</code></pre>
<p>Create OpenSearch Serverless resources and an index. I referred to <a href="https://aws.amazon.com/jp/blogs/big-data/deploy-amazon-opensearch-serverless-with-terraform/">Deploy Amazon OpenSearch Serverless with Terraform</a>.</p>
<p>For this setup, the security policy is set to public, but ideally, access should be restricted using a VPC endpoint.</p>
<pre><code class="language-hcl:aoss.tf">
data &quot;aws_caller_identity&quot; &quot;current&quot; {}

# Creates a collection
resource &quot;aws_opensearchserverless_collection&quot; &quot;collection&quot; {
  name             = &quot;${local.env.sid}-collection&quot;
  type             = &quot;VECTORSEARCH&quot;
  standby_replicas = &quot;DISABLED&quot;

  depends_on = [aws_opensearchserverless_security_policy.encryption_policy]
}

# Creates an encryption security policy
resource &quot;aws_opensearchserverless_security_policy&quot; &quot;encryption_policy&quot; {
  name        = &quot;${local.env.sid}-encryption-policy&quot;
  type        = &quot;encryption&quot;
  description = &quot;encryption policy for ${local.env.sid}-collection&quot;
  policy = jsonencode({
    Rules = [
      {
        Resource = [
          &quot;collection/${local.env.sid}-collection&quot;
        ],
        ResourceType = &quot;collection&quot;
      }
    ],
    AWSOwnedKey = true
  })
}

# Creates a network security policy
resource &quot;aws_opensearchserverless_security_policy&quot; &quot;network_policy&quot; {
  name        = &quot;${local.env.sid}-network-policy&quot;
  type        = &quot;network&quot;
  description = &quot;public access for dashboard, VPC access for collection endpoint&quot;
  policy = jsonencode([
    ###References for using VPC endpoints
    # {
    #   Description = &quot;VPC access for collection endpoint&quot;,
    #   Rules = [
    #     {
    #       ResourceType = &quot;collection&quot;,
    #       Resource = [
    #         &quot;collection/${local.env.sid}-collection}&quot;
    #       ]
    #     }
    #   ],
    #   AllowFromPublic = false,
    #   SourceVPCEs = [
    #     aws_opensearchserverless_vpc_endpoint.vpc_endpoint.id
    #   ]
    # },
    {
      Description = &quot;Public access for dashboards and collection&quot;,
      Rules = [
        {
          ResourceType = &quot;collection&quot;,
          Resource = [
            &quot;collection/${local.env.sid}-collection&quot;
          ]
        },
        {
          ResourceType = &quot;dashboard&quot;
          Resource = [
            &quot;collection/${local.env.sid}-collection&quot;
          ]
        }
      ],
      AllowFromPublic = true
    }
  ])
}

# Creates a data access policy
resource &quot;aws_opensearchserverless_access_policy&quot; &quot;data_access_policy&quot; {
  name        = &quot;${local.env.sid}-data-access-policy&quot;
  type        = &quot;data&quot;
  description = &quot;allow index and collection access&quot;
  policy = jsonencode([
    {
      Rules = [
        {
          ResourceType = &quot;index&quot;,
          Resource = [
            &quot;index/${local.env.sid}-collection/*&quot;
          ],
          Permission = [
            &quot;aoss:*&quot;
          ]
        },
        {
          ResourceType = &quot;collection&quot;,
          Resource = [
            &quot;collection/${local.env.sid}-collection&quot;
          ],
          Permission = [
            &quot;aoss:*&quot;
          ]
        }
      ],
      Principal = [
        data.aws_caller_identity.current.arn,
        iam_role.bedrock.arn,
      ]
    }
  ])
}

resource &quot;opensearch_index&quot; &quot;vector_index&quot; {
  name = local.aoss.vector_index

  mappings = jsonencode({
    properties = {
      &quot;${local.aoss.metadata_field}&quot; = {
        type  = &quot;text&quot;
        index = false
      }

      &quot;${local.aoss.text_field}&quot; = {
        type  = &quot;text&quot;
        index = true
      }

      &quot;${local.aoss.vector_field}&quot; = {
        type      = &quot;knn_vector&quot;
        dimension = &quot;${local.aoss.vector_dimension}&quot;
        method = {
          engine = &quot;faiss&quot;
          name   = &quot;hnsw&quot;
        }
      }
    }
  })

  depends_on = [aws_opensearchserverless_collection.collection]
}
</code></pre>
<p>Create the Knowledge Base and data source.</p>
<pre><code class="language-hcl:bedrock.tf">data &quot;aws_bedrock_foundation_model&quot; &quot;embedding&quot; {
  model_id = &quot;amazon.titan-embed-text-v2:0&quot;
}
resource &quot;aws_bedrockagent_knowledge_base&quot; &quot;this&quot; {
  name     = &quot;test-kb&quot;
  role_arn = iam_role.bedrock.arn

  knowledge_base_configuration {
    type = &quot;VECTOR&quot;
    vector_knowledge_base_configuration {
      embedding_model_arn = data.aws_bedrock_foundation_model.embedding.model_arn
    }
  }

  storage_configuration {
    type = &quot;OPENSEARCH_SERVERLESS&quot;
    opensearch_serverless_configuration {
      collection_arn    = aws_opensearchserverless_collection.collection.arn
      vector_index_name = local.aoss.vector_index
      field_mapping {
        vector_field   = local.aoss.vector_field
        text_field     = local.aoss.text_field
        metadata_field = local.aoss.metadata_field
      }
    }
  }

  depends_on = [iam_role.bedrock]
}

resource &quot;aws_bedrockagent_data_source&quot; &quot;this&quot; {
  knowledge_base_id = aws_bedrockagent_knowledge_base.this.id
  name              = &quot;test-s3-001&quot;
  data_source_configuration {
    type = &quot;S3&quot;
    s3_configuration {
      bucket_arn = &quot;arn:aws:s3:::****-dev-test-***&quot; ### Masked bucket name
    }
  }

  depends_on = [aws_bedrockagent_knowledge_base.this]
}
</code></pre>
<p>Set the service role that bedrock will use.</p>
<pre><code class="language-hcl:iam.tf">resource &quot;aws_iam_role&quot; &quot;bedrock&quot; {
  name                = &quot;bedrock-role&quot;
  managed_policy_arns = [aws_iam_policy.bedrock.arn]

  assume_role_policy = jsonencode({
    Version = &quot;2012-10-17&quot;
    Statement = [
      {
        Action = &quot;sts:AssumeRole&quot;
        Effect = &quot;Allow&quot;
        Sid    = &quot;&quot;
        Principal = {
          Service = &quot;bedrock.amazonaws.com&quot;
        }
      },
    ]
  })
}

resource &quot;aws_iam_policy&quot; &quot;bedrock&quot; {
  name = &quot;bedrock-policy&quot;

  policy = jsonencode({
    Version = &quot;2012-10-17&quot;
    Statement = [
      {
        Action   = [&quot;bedrock:InvokeModel&quot;]
        Effect   = &quot;Allow&quot;
        Resource = &quot;*&quot;
      },
      {
        Action = [
          &quot;s3:GetObject&quot;,
          &quot;s3:ListBucket&quot;,
        ]
        Effect   = &quot;Allow&quot;
        Resource = &quot;***-dev-test-***&quot; ### ARN of the S3 bucket that was
      },
      {
        Action = [
          &quot;aoss:APIAccessAll&quot;,
        ]
        Effect   = &quot;Allow&quot;
        Resource = &quot;arn:aws:aoss:us-west-2:12345678910:collection/*&quot;
      },

    ]
  })
}
</code></pre>
<p>Create an S3 bucket for use with Bedrock. Also, configure CORS. Below is a reference image of the error. <img src="/assets/blog/authors/j.shimakawa/error001.png" alt="error001"></p>
<pre><code class="language-hcl:s3.tf">resource &quot;aws_s3_bucket&quot; &quot;bedrock&quot; {
  bucket = &quot;***-dev-test-***&quot; ### Masked bucket name
}

resource &quot;aws_s3_bucket_cors_configuration&quot; &quot;this&quot; {
  bucket = aws_s3_bucket.bedrock.id

  cors_rule {
    allowed_headers = [&quot;*&quot;]
    allowed_methods = [
      &quot;GET&quot;,
      &quot;PUT&quot;,
      &quot;POST&quot;,
      &quot;DELETE&quot;
    ]
    allowed_origins = [&quot;*&quot;]
  }
}
</code></pre>
<h1>Execution</h1>
<p>Use <code>terraform apply</code> to create all resources at once.</p>
<h1>Verifying the Created Resources</h1>
<p>Check that the Knowledge Base has been created in Bedrock and that the data source is available. <img src="/assets/blog/authors/j.shimakawa/image001.png" alt="image001"></p>
<p>Next, verify that an OpenSearch collection has been created and that an index has been set. <img src="/assets/blog/authors/j.shimakawa/image003.png" alt="image003"></p>
<h1>Testing the Knowledge Base</h1>
<p>Upload some sample text to S3 to use as a data source.</p>
<pre><code class="language-text:test001.txt">Dogs like meat.
Cats like fish
</code></pre>
<pre><code>aws s3 cp ./test001.txt s3://[S3 Bucket Name]/test001.txt
</code></pre>
<p>Next, synchronize the data source. <img src="/assets/blog/authors/j.shimakawa/image002.png" alt="image003"></p>
<p>Now, let’s test it by asking a question. (Using Claude 3.5 Sonnet for the Prompt) <img src="/assets/blog/authors/j.shimakawa/image004.png" alt="image004"></p>
<p>The system correctly retrieves answers from the provided text while refraining from answering questions about information that is not present.</p>
<p>This was a brief overview of setting up a Knowledge Base and OpenSearch Serverless using Terraform.</p>
<h1>Trying out RAG Evaluation</h1>
<p>Next, we will test the <a href="https://aws.amazon.com/jp/about-aws/whats-new/2024/12/amazon-bedrock-knowledge-bases-rag-evaluation-preview/">RAG evaluation</a> announced at re:Invent 2024 on the Knowledge Base we created.</p>
<p>@<a href="https://aws.amazon.com/jp/about-aws/whats-new/2024/12/amazon-bedrock-knowledge-bases-rag-evaluation-preview/">card</a></p>
<h2>Preparation</h2>
<p>First, prepare a dataset file in JSONL format for evaluation. This file contains prompts with their expected answers.</p>
<pre><code class="language-jsonl:dataset001.jsonl">{&quot;conversationTurns&quot;:[{&quot;referenceResponses&quot;:[{&quot;content&quot;:[{&quot;text&quot;:&quot;Cats&#39; favorite food is fish.&quot;}]}],&quot;prompt&quot;:{&quot;content&quot;:[{&quot;text&quot;:&quot;What do cats like?
{&quot;conversationTurns&quot;:[{&quot;referenceResponses&quot;:[{&quot;content&quot;:[{&quot;text&quot;:&quot;Dogs’ favorite food is meat.&quot;}]}],&quot;prompt&quot;:{&quot;content&quot;:[{&quot;text&quot;:&quot;What do dogs like?
</code></pre>
<p>Since the evaluation references data from S3, upload the dataset file to an S3 bucket.</p>
<pre><code>aws s3 cp ./dataset001.txt s3://[S3 Bucket Name]/datasets/dataset001.txt
</code></pre>
<h2>Creating a Job</h2>
<p>Next, we will create a job. While this can be done through the management console, this time, I executed it using the CLI.
@<a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/knowledge-base-evaluation-create.html">card</a></p>
<pre><code class="language-text:sample">aws bedrock create-evaluation-job \
 --job-name &quot;rag-evaluation-complete-stereotype-docs-app&quot; \
 --job-description &quot;Evaluates Completeness and Stereotyping of RAG for docs application&quot; \
 --role-arn &quot;arn:aws::iam:&lt;region&gt;:&lt;account-id&gt;:role/AmazonBedrock-KnowledgeBases&quot; \
 --evaluation-context &quot;RAG&quot; \
 --evaluationConfig file://knowledge-base-evaluation-config.json \
 --inference-config file://knowledge-base-evaluation-inference-config.json \
 --output-data-config &#39;{&quot;s3Uri&quot;:&quot;s3://docs/kbevalresults/&quot;}&#39; 
</code></pre>
<p>You need to specify the JSONL file and the destination for saving the results in <code>knowledge-base-evaluation-config.json</code>.</p>
<h2>Checking the Job</h2>
<p>After waiting about 15 to 20 minutes, the job was completed, so I checked the results. I started by reviewing the summary. The responses were almost exactly as expected, so there wasn’t much surprise. However, the Correctness and Completeness scores were both 1, indicating that the system performed as expected. <img src="/assets/blog/authors/j.shimakawa/image005.png" alt="image005"></p>
<p>The only exception was the Helpfulness score, which was 0.83. When I checked the evaluation comments,</p>
<pre><code> it stated: “The answer is neither particularly interesting nor unexpected, but in this context, it doesn&#39;t need to be.”
</code></pre>
<p>I think the fact that this context does not have to be the case is what is causing the score to drop. <img src="/assets/blog/authors/j.shimakawa/image006.png" alt="image006"></p>
<h1>Final Thoughts</h1>
<p>Our company has been increasingly integrating generative AI, including Amazon Bedrock, and its real-world applications are expanding. Moving forward, I plan to explore more features and ensure we are well-prepared to meet project requirements. I hope this blog post serves as a useful reference. Thank you for reading!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AI時代の新しい開発手法「TDD × AI」を始めよう]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-04-02-tdd_x_ai/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-04-02-tdd_x_ai/</guid>
            <pubDate>Wed, 02 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[AIとTDDを組み合わせた新しい開発手法を紹介し、その効果と実践方法を解説します。]]></description>
            <content:encoded><![CDATA[<p>AIが急速に進化する2025年、エンジニアにとって「AIをどう活用するか」が重要なスキルとなってきました。</p>
<p>しかし、AIを効果的に活用するには、適切なプロンプト（指示の出し方）を理解する必要があり、そのためには 慣れや知見 が不可欠です。</p>
<p>そこで、AIとコーディングを行う最初のステップとして、今回のテーマである TDD(test-driven development: テスト駆動開発) と AI を活用した開発を紹介します。</p>
<h2>TDD × AIのメリット</h2>
<p>✅ <strong>実装コストが激減！エンジニアは&quot;テストを書くだけ&quot;でOK</strong>
テストを書くだけで、複雑な指示もプロンプトも不要。あとはAIが自動でコードを生成。</p>
<p>✅ <strong>開発速度が爆速化！細かな往復が劇的に減少</strong>
TDDのステップごとにAIが即座にコードを生成するので、開発効率が驚異的にアップ。</p>
<p>✅ <strong>圧倒的なコード品質！テストでしっかりAIの暴走をコントロール</strong>
明確なテストでAIの生成コードをしっかり制御。結果的にバグの少ないコードになる。</p>
<h2>TDDとは？</h2>
<p>大前提となるTDD（Test-Driven Development: テスト駆動開発）について簡単に説明します。</p>
<p><img src="/assets/blog/authors/HiroyaHinomori/2025_03_tdd_x_ai_01.png" alt="chart1"></p>
<p><a href="https://www.amazon.co.jp/dp/4274217884">https://www.amazon.co.jp/dp/4274217884</a></p>
<p>「TDD（Test-Driven Development: テスト駆動開発）」はKent Beck氏が👆の書籍で20年以上前に提唱した手法です。
上の図の「まずテストを書いて、次にテストを通過する実装をする、最後にリファクタリング」というシンプルなサイクルを繰り返すことで、品質の高いコードを生み出すことができます。
また、テストが実装の起点になるので、<strong>テスタブルな構造を担保しつつ、実装を進めることが出来ます。</strong></p>
<h2>TDDを実践する</h2>
<p>:::message
VSCodeの GitHub CopilotのAgentモードを使って実施しています。
:::
<a href="https://code.visualstudio.com/docs/copilot/copilot-edits#_use-agent-mode-preview">https://code.visualstudio.com/docs/copilot/copilot-edits#_use-agent-mode-preview</a></p>
<p>今回の方法では、エンジニアがテストを書き、生成AIが実装・リファクタリングを担うことで、コード品質を維持しつつ効率的に開発を進めます。</p>
<p><img src="/assets/blog/authors/HiroyaHinomori/2025_03_tdd_x_ai_03.png" alt="chart2"></p>
<p>今回は <strong>パスワードのバリデーション</strong> を例に、TDD × AIの流れを説明していきます。  </p>
<p>まずは、<strong>文字数のバリデーション</strong> から始めていきましょう。  </p>
<p>:::message alert<br><strong>最初からすべての仕様を詰め込まず、細かいステップで進めることが重要です！</strong><br>:::</p>
<h3>ステップ１: 文字数バリデーションテストの作成</h3>
<p>対象になるPasswordバリデーションのクラスとテストファイルを用意しましょう。
この段階ではどちらも空のファイルで問題ありません。</p>
<p>ファイルが作成できたらそのままPasswordバリデーションクラスを書きたくなるところですが、今回はTDDなのでテストを書いていきます。</p>
<pre><code class="language-swift:PasswordValidatorTests.swift">@Test(&quot;パスワードの文字数が8文字以上16文字以下でバリデーションが成功すること&quot;)
func testPasswordLengthValidation() async throws {
    let sut = PasswordValidator()
    let validPasswordShort = &quot;Valid123&quot;
    let validPasswordLong = &quot;ValidPassword123&quot;
    let invalidPasswordShort = &quot;Short01&quot;
    let invalidPasswordLong = &quot;ThisPasswordIsWay&quot;

    #expect(sut.validate(validPasswordShort) == true)
    #expect(sut.validate(validPasswordLong) == true)
    #expect(sut.validate(invalidPasswordShort) == false)
    #expect(sut.validate(invalidPasswordLong) == false)
}
</code></pre>
<p>この段階では PasswordValidator クラスや validate メソッドが未実装のため、当然エラーが発生します。</p>
<p>次に、AIにプロンプトを入力して実装を依頼します。</p>
<p>![input prompt](/assets/blog/authors/HiroyaHinomori/2025_03_tdd_x_ai_02.png =500x)</p>
<p>すると、数十秒後にAIが実装を生成してくれます。</p>
<pre><code class="language-swift:PasswordValidator.swift">struct PasswordValidator {
    // パスワードの長さの制約を定数として定義
    private let minimumPasswordLength = 8
    private let maximumPasswordLength = 16

    /// パスワードが有効かどうかを検証します
    /// - Parameter password: 検証するパスワード
    /// - Returns: パスワードが有効な場合はtrue、そうでない場合はfalse
    func validate(_ password: String) -&gt; Bool {
        let passwordLength = password.count
        return passwordLength &gt;= minimumPasswordLength &amp;&amp; passwordLength &lt;= maximumPasswordLength
    }
}
</code></pre>
<p>次に、数字・文字・記号のミックス判定を追加してみます。</p>
<h3>ステップ２: 数字、文字、記号の含有バリデーションの追加</h3>
<pre><code class="language-swift:PasswordValidatorTests.swift">@Test(&quot;パスワードの文字列の中に数字、文字、記号が含まれている場合にバリデーションが成功すること&quot;)
func testPasswordCharacterValidation() async throws {
    let sut = PasswordValidator()
    let validPassword = &quot;ValidPassword12!&quot;
    let invalidPasswordNoNumber = &quot;InvalidPassword!&quot;
    let invalidPasswordNoCharacter = &quot;1234567890!&quot;
    let invalidPasswordNoSymbol = &quot;InvalidPassword1&quot;

    #expect(sut.validate(validPassword) == true)
    #expect(sut.validate(invalidPasswordNoNumber) == false)
    #expect(sut.validate(invalidPasswordNoCharacter) == false)
    #expect(sut.validate(invalidPasswordNoSymbol) == false)
}
</code></pre>
<p>この段階でエラーが発生しますが、気にせずテストケースをAIに渡して実装を生成します。</p>
<pre><code class="language-swift:PasswordValidator.swift">struct PasswordValidator {
    // パスワードの長さの制約を定数として定義
    private let minimumPasswordLength = 8
    private let maximumPasswordLength = 16

    /// パスワードが有効かどうかを検証します
    /// - Parameter password: 検証するパスワード
    /// - Returns: パスワードが有効な場合はtrue、そうでない場合はfalse
    func validate(_ password: String) -&gt; Bool {
        let passwordLength = password.count
        guard passwordLength &gt;= minimumPasswordLength &amp;&amp; passwordLength &lt;= maximumPasswordLength
        else {
            return false
        }

        let hasNumber = password.rangeOfCharacter(from: .decimalDigits) != nil
        let hasLetter = password.rangeOfCharacter(from: .letters) != nil
        let hasSymbol =
            password.rangeOfCharacter(from: .symbols) != nil
            || password.rangeOfCharacter(from: .punctuationCharacters) != nil

        return hasNumber &amp;&amp; hasLetter &amp;&amp; hasSymbol
    }
}
</code></pre>
<p>ここまで<strong>10分もかからず</strong>実装できました。</p>
<p>通常のコーディングではプロンプトに色々な条件や仕様を伝える必要がありますが、この方法では <code>テスト条件を満たした実装をして</code> というお願いをするだけです。
実装内容に関してはTestに全て書かれているので、複雑なプロンプトの指定はほぼ不要になります。</p>
<h3>AIとのコミュニケーションをさらに効率化するには</h3>
<p>実装のルールや制約を事前に「copilot-instructions.md」に書いておけば、毎回AIに細かい指示を伝える必要もありません。</p>
<pre><code class="language-markdown:.github/copilot-instructions.md">日本語で返答してください。

### コーディングルール

- テストはswift-testingを使用してください。
- 実装には基本的にマジックナンバーは使わないこと
- DRYの原則に則って実装してください
- KISSの原則に則って実装してください
- YAGNIの原則に則って実装してください
</code></pre>
<h2>AI時代に活躍するエンジニアになるために</h2>
<p>AIは万能ではありません。しかし、だからといって諦めるのはとてももったいないです！
<strong>「AIが得意なこと」と「人間が担うべきこと」を冷静に見極めること</strong> が重要です。
「TDD × AI」で、AIコーディングの癖を理解し、次世代の開発スピードと品質を手に入れましょう🚀</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/HiroyaHinomori/2025_03_tdd_x_ai_03.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[First Steps When Migrating From Android View to Jetpack Compose]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-18-first-step-of-migration-from-android-view-to-compose-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-18-first-step-of-migration-from-android-view-to-compose-en/</guid>
            <pubDate>Tue, 01 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Helpful Tips for Transitioning from Android View to Jetpack Compose]]></description>
            <content:encoded><![CDATA[<p>This article is the entry for day 18 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>🎅🎄</p>
<hr>
<p>Nice to meet you, I’m Tsuyoshi Yamada, an Android engineer at KINTO Technologies. In this article, we’ll share some handy techniques to help you take the first step in migrating an app built with Android View to a <a href="https://developer.android.com/compose">Jetpack Compose</a>-based UI or gradually integrating Jetpack Compose into an existing Android View-based UI.</p>
<h2>1. Introduction</h2>
<p>Development of <a href="https://developer.android.com/compose">Jetpack Compose</a>, Android’s <strong>Declarative UI</strong>, was announced a few years ago, and version 1.0 was officially released in July 2021. Many Android developers quickly embraced the concept of Jetpack Compose, harnessing the flexibility and extensibility of <a href="https://kotlinlang.org/">Kotlin</a>, which had already become the second officially supported language for Android development after Java. It seems that the adoption of Jetpack Compose in Android app development has been gradually increasing. With the introduction of declarative UI, developers can write UI more intuitively with less code compared to traditional view-based UI, leading to improved development efficiency and productivity. With the recent release of <a href="https://www.jetbrains.com/compose-multiplatform/">Compose Multiplatform</a>, the scope of Jetpack Compose skills has expanded beyond Android. Moving forward, we might see more libraries being developed exclusively for Jetpack Compose.</p>
<p>To bring these benefits to our existing apps, we have started migrating some of our development projects from Android View-based UIs to Jetpack Compose-based UIs. However, traditional view-based UIs rely heavily on <em>procedural</em> elements, making it challenging to adopt Jetpack Compose’s <em>declarative</em> style seamlessly. This article introduces some practical techniques to compensate for elements that may have been lost or become less visible due to Jetpack Compose’s simplified approach. In particular, we’ll cover tracking Composable locations, checking debug information, and ensuring smooth interoperability between Views and Composables—all with the goal of making the transition from a View-based UI to a Jetpack Compose-based UI as seamless as possible.</p>
<h2>2. Aligning and Debugging Composables</h2>
<p>Replacing the existing View expressions with new Composables came with a big challenge—not just figuring out how to represent the same content, but also whether it could be positioned correctly. Even for someone familiar with Android Views, that was a major concern. To address this, we’ll first go over how to align a Composable’s position with a View’s and how to retrieve and check debugging information to assist in this process.</p>
<h3>2.1. Checking the position of Composable and View</h3>
<p>The <a href="https://developer.android.com/reference/android/view/View.html">View</a>, available since Android API level 1, is built on Java’s object-oriented principles. It not only represents rectangular screen elements but also effectively handles containment relationships between Views, interactions, and extending functionality through subclasses. For example, you can log the positions of a single View or multiple Views inside a <a href="https://developer.android.com/reference/android/view/ViewGroup">ViewGroup</a> using the following code:</p>
<pre><code class="language-kotlin">private fun Resources.findResourceName(@IdRes resId: Int): String = try {
    getResourceName(resId)
} catch (e: Resources.NotFoundException) {
    &quot;?&quot;
}

fun View.logCoordinators(logger: (String) -&gt; Unit, res: Resources, outLocation: IntArray, rect: Rect, prefix: String, density: Float) {
    getLocationInWindow(outLocation)
    rect.set(outLocation[0], outLocation[1], outLocation[0] + width, outLocation[1] + height)
    var log = &quot;$prefix${this::class.simpleName}(${res.findResourceName(id)})${rect.toShortString()}(${rect.width().toFloat() / density}dp, ${rect.height().toFloat() / density}dp)&quot;
    if (this is TextView) {
        log += &quot;{${if (text.length &lt;= 10) text else text.substring(0, 7) + &quot;...&quot;}}&quot;
    }
    logger(log)
    if (this is ViewGroup) {
        val nextPrefix = &quot;$prefix  &quot;
        repeat(childCount) {
            getChildAt(it).logCoordinators(logger, res, outLocation, rect, nextPrefix, density)
        }
    }
}

fun View.logCoordinators(logger: (String) -&gt; Unit = { Log.d(&quot;ViewLogUtil&quot;, it) }) =
    logCoordinators(logger, resources, IntArray(2), Rect(), &quot;&quot;, resources.displayMetrics.density)
</code></pre>
<p>Here, <a href="https://developer.android.com/reference/android/view/View.html#getLocationInWindow(int%5B%5D)">View$getLocationInWindow(IntArray)</a> is a function that retrieves the top-left coordinates of the Activity’s window where the View is located. If you’re familiar with Android Views, checking how this code works should be pretty straightforward. One thing to keep in mind is that calling these functions directly from <a href="https://developer.android.com/reference/android/app/Activity#onResume()">Activity$onResume()</a>, for example, won’t work as expected because the View layout isn’t fully set up yet, so you won’t be able to get meaningful information. In most cases, you’ll need to call them from a callback like <a href="https://developer.android.com/reference/android/view/View#addOnLayoutChangeListener(android.view.View.OnLayoutChangeListener)">View$addOnLayoutChangeListener(OnLayoutChangeListener)</a>.</p>
<p>A lot of developers might not be sure how to achieve the same thing in Jetpack Compose, and since it’s not easy to check if Composable behaves the same way, the migration process can feel like a hassle. In Jetpack Compose, you can use the <a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier">Modifier</a> extension function <a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/OnGloballyPositionedModifier#onGloballyPositioned(androidx.compose.ui.layout.LayoutCoordinates)">onGloballyPositioned((LayoutCoordinator) -&gt; Unit)</a> to get a Composable&#39;s position like this:</p>
<pre><code class="language-kotlin">@Composable
fun BoundsInWindowExample() {
    Text(
        modifier = Modifier
            .padding(16.dp)
            .background(Color.White)
            .onGloballyPositioned {
                Log.d(&quot;ComposeLog&quot;, &quot;Target boundsInWindow: ${it.boundsInWindow()}&quot;)
            },
        text = &quot;Hello, World!&quot;,
        style = TextStyle(
            color = MaterialTheme.colorScheme.onSecondary,
            fontSize = 24.sp
        )
    )
}
</code></pre>
<p>By passing a callback to <code>onGloballyPositioned(...)</code>, you can get the position coordinates every time the Composable’s position updates. In this case, <a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/layout/package-summary#(androidx.compose.ui.layout.LayoutCoordinates).boundsInWindow()">LayoutCoordinator.boundsInWindow()</a> is an extension function that retrieves the top, bottom, left, and right bounds of the Composable’s rectangle in the Activity coordinate system.</p>
<p>Getting the positions of all Composables inside a single Composable at once seems tricky for now, but grabbing the position of an individual Composable is easy. Plus, in many cases, you can get position data without worrying about complex lifecycles like Activities. <code>onGloballyPositioned(...)</code> There’s also <code>onPositioned(...)</code>, a callback similar to <code>onGloballyPositioned(...)</code>, which gets called after the relative position within the parent Composable is determined. Besides <code>boundsInWindow()</code>, there are also <code>boundsInRoot()</code> and <code>boundsInParent()</code>, which you can use depending on the situation, but we’ll spare you the details for now.</p>
<h3>2.2. Creating a Composable to Check Screen Display</h3>
<p>Now that we’ve figured out how to get the position in Composable using <code>boundsInWindow()</code>, which is compatible with <code>View$getLocationInWindow(IntArray)</code>, we can log the position changes during testing to check its behavior. By doing this while developing the Composable, we’ll gradually get used to Compose and be able to recreate something similar to View. This method is simple and effective, but since LogCat floods with text, it can be hard to read—especially when dealing with a lot of information. To make things easier, let’s try creating a separate Composable just for checking the screen display. If you create a debug display area in a part of your app just for testing purposes and keep it constantly updated, you won’t have to scramble to find key logs among the endless stream of LogCats.</p>
<p>...Of course, we’ve known this since the Android View days, but actually implementing it always felt like a hassle... I can almost hear the sighs. With Jetpack Compose, a <strong>declarative UI</strong>, creating such a debug area is much easier with minimal effort:</p>
<pre><code class="language-kotlin:MainActivity.kt">class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        WindowCompat.getInsetsController(window, window.decorView).isAppearanceLightStatusBars =
            resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK != Configuration.UI_MODE_NIGHT_YES
        setContentView(R.layout.activity_main)
        findViewById&lt;WebView&gt;(R.id.webView).let { webView -&gt;
            webView.loadUrl(&quot;https://blog.kinto-technologies.com/&quot;)
        }

        val targetRect = mutableStateOf(Rect.Zero) // androidx.compose.ui.geometry.Rect
        findViewById&lt;ComposeView&gt;(R.id.composeTargetContainer).let { containerComposeView -&gt;
            containerComposeView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            containerComposeView.setContent {
                KtcAdventCalendar2024Theme {
                    ScrollComposable(targetRect)
                }
            }
        }

        val visibleRect = mutableStateOf(Rect.Zero)
        val outLocation = IntArray(2)
        findViewById&lt;View&gt;(R.id.layoutMain).addOnLayoutChangeListener { v, left, top, right, bottom, _, _, _, _ -&gt;
            v.getLocationInWindow(outLocation)
            visibleRect.value = Rect(outLocation[0].toFloat(), outLocation[1].toFloat(), outLocation[0].toFloat() + (right - left), outLocation[1].toFloat() + (bottom - top))
        }

        findViewById&lt;ComposeView&gt;(R.id.composeTargetWatcher).let { watcherComposeView -&gt;
            watcherComposeView.setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
            watcherComposeView.setContent {
                KtcAdventCalendar2024Theme {
                    TargetWatcher(visibleRect.value, targetRect.value)
                }
            }
        }
    }
}
</code></pre>
<p>The app in the sample code is in the middle of being converted to Jetpack Compose. Right now, it’s still using a View with <a href="https://developer.android.com/reference/android/app/Activity#setContentView(int)">Activity$setContentView(Int)</a>, and we’re introducing Composable within that View by using <a href="%5B%60ComposeView%60%5D(https://developer.android.com/reference/kotlin/androidx/compose/ui/platform/ComposeView)">ComposeView</a>. Here, <a href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#mutableStateOf(kotlin.Any,androidx.compose.runtime.SnapshotMutationPolicy)">mutableStateOf(...)</a> is used to share the rectangular position information between View and Composable, allowing us to observe how it behaves on the screen. The screen layout looks like this: We’re working on making the HorizontalScrollView part composable. To help with that, the bottom part of the screen will be used to display debug information:</p>
<p>![Screen composition](/assets/blog/authors/tsuyoshi_yamada/advent-calendar_sample_screen-area.png =252x)</p>
<p>The layout XML file for MainActivity as follows:</p>
<pre><code class="language-xml:activity_main.xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;LinearLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    android:orientation=&quot;vertical&quot;
    android:layout_marginVertical=&quot;48dp&quot;&gt;

    &lt;androidx.constraintlayout.widget.ConstraintLayout
        android:id=&quot;@+id/layoutMain&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;0dp&quot;
        android:layout_weight=&quot;1&quot;&gt;

        &lt;WebView
            android:id=&quot;@+id/webView&quot;
            android:layout_width=&quot;0dp&quot;
            android:layout_height=&quot;match_parent&quot;
            android:layout_marginHorizontal=&quot;16dp&quot;
            app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
            app:layout_constraintEnd_toStartOf=&quot;@id/scrollView&quot;
            app:layout_constraintStart_toStartOf=&quot;parent&quot;
            app:layout_constraintTop_toTopOf=&quot;parent&quot;
            tools:ignore=&quot;NestedWeights&quot; /&gt;

        &lt;HorizontalScrollView
            android:id=&quot;@+id/scrollView&quot;
            android:layout_width=&quot;0dp&quot;
            android:layout_height=&quot;match_parent&quot;
            app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
            app:layout_constraintEnd_toEndOf=&quot;parent&quot;
            app:layout_constraintStart_toEndOf=&quot;@id/webView&quot;
            app:layout_constraintTop_toTopOf=&quot;parent&quot;&gt;
            &lt;androidx.compose.ui.platform.ComposeView
                android:id=&quot;@+id/composeTargetContainer&quot;
                android:layout_width=&quot;wrap_content&quot;
                android:layout_height=&quot;match_parent&quot; /&gt;
        &lt;/HorizontalScrollView&gt;

    &lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;

    &lt;androidx.compose.ui.platform.ComposeView
        android:id=&quot;@+id/composeTargetWatcher&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;300dp&quot;
        android:paddingTop=&quot;16dp&quot; /&gt;

&lt;/LinearLayout&gt;
</code></pre>
<p>MainActivity.kt and activity_main.xml are in the middle of being converted to Jetpack Compose, so they currently contain a mix of Views and Composables. As of this writing, <a href="https://developer.android.com/reference/android/webkit/WebView">WebView</a> and some other elements don’t yet have Composable equivalents, so for now, a hybrid approach is necessary. [^1] In activity_main.xml, <code>ComposeView</code> inside <code>HorizontalScrollView</code> is intended to replace an existing View with a Composable, while the other <code>ComposeView</code> is placed to track the position of the Composable above.</p>
<p>The Composable that replaces the View is structured as follows and implements <code>onGloballyPositioned(...)</code> to check the position of the part labeled &quot;Target&quot;. </p>
<pre><code class="language-kotlin:ScrollComposable.kt">@Composable
fun ScrollComposable(targetRect: MutableState&lt;Rect&gt;) {
    val textStyle = TextStyle(
        textAlign = TextAlign.Center,
        color = MaterialTheme.colorScheme.onSecondary,
        fontSize = 24.sp,
        fontWeight = FontWeight.W600
    )

    Row(
        modifier = Modifier
            .fillMaxSize(),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Box(
            modifier = Modifier
                .padding(16.dp)
                .width(100.dp)
                .fillMaxHeight()
                .background(Color.Red),
            contentAlignment = Alignment.Center
        ) {
            Text(&quot;1&quot;, style = textStyle)
        }

        Box(
            modifier = Modifier
                .padding(16.dp)
                .width(100.dp)
                .fillMaxHeight()
                .background(Color.Magenta),
            contentAlignment = Alignment.Center
        ) {
            Text(&quot;2&quot;, style = textStyle)
        }

        Box(
            modifier = Modifier
                .padding(16.dp)
                .width(100.dp)
                .fillMaxHeight()
                .background(MaterialTheme.colorScheme.primary)
                .onGloballyPositioned { targetRect.value = it.boundsInWindow() },
            contentAlignment = Alignment.Center
        ) {
            Text(&quot;Target&quot;, style = textStyle)
        }

        Box(
            modifier = Modifier
                .padding(16.dp)
                .width(100.dp)
                .fillMaxHeight()
                .background(Color.Cyan),
            contentAlignment = Alignment.Center
        ) {
            Text(&quot;4&quot;, style = textStyle)
        }
    }
}
</code></pre>
<p>Here&#39;s a Composable that keeps an eye on this: Here, the Composable itself uses <code>onGloballyPositioned(...)</code> to get its own size.</p>
<pre><code class="language-kotlin:TargetWatcher.kt">@Composable
fun TargetWatcher(visibleRect: Rect, targetRect: Rect) {
    if (visibleRect.width &lt;= 0f || visibleRect.height &lt;= 0f) return
    val rootAspectRatio = visibleRect.width / visibleRect.height
    val density = LocalDensity.current // For calculating toDp()
    val targetColor = MaterialTheme.colorScheme.primary.copy(alpha = 0.25F)
    var size by remember { mutableStateOf(IntSize.Zero) }
    Box(
        modifier = Modifier
            .fillMaxSize()
            .onGloballyPositioned { coordinates -&gt; size = coordinates.size }
    ) {
        if (size.width &lt;= 0F || size.height &lt;= 0F) return@Box
        val watchAspectRatio = size.width.toFloat() / size.height
        val (paddingH: Float, paddingV: Float) = if (rootAspectRatio &lt; watchAspectRatio) {
            (size.width - size.height * rootAspectRatio) / 2 to 0F
        } else {
            0F to (size.height - size.width / rootAspectRatio) / 2
        }

        with(density) {
            Box(
                modifier = Modifier
                    .padding(horizontal = paddingH.toDp(), vertical = paddingV.toDp())
                    .fillMaxSize()
                    .background(Color.Gray)
            )
        }

        if (targetRect.width &lt;= 0f || targetRect.height &lt;= 0f) return@Box
        with(density) {
            Box(
                modifier = Modifier
                    .padding( // Caution: exception is thrown if padding is negative
                        start = max(
                            0F, marginOf(
                                size.width,
                                paddingH,
                                visibleRect.left,
                                visibleRect.right,
                                targetRect.left
                            )
                        ).toDp(),
                        end = max(
                            0F, size.width - marginOf(
                                size.width,
                                paddingH,
                                visibleRect.left,
                                visibleRect.right,
                                targetRect.right
                            )
                        ).toDp(),
                        top = max(
                            0F, marginOf(
                                size.height,
                                paddingV,
                                visibleRect.top,
                                visibleRect.bottom,
                                targetRect.top
                            )
                        ).toDp(),
                        bottom = max(
                            0F, size.height - marginOf(
                                size.height,
                                paddingV,
                                visibleRect.top,
                                visibleRect.bottom,
                                targetRect.bottom
                            )
                        ).toDp()
                    )
                    .fillMaxSize()
                    .background(targetColor)
            )
        }
    }
}

private fun marginOf(
    sizePx: Int,
    halfPx: Float,
    visibleFrom: Float,
    visibleTo: Float,
    px: Float
): Float {
    val alpha = (px - visibleFrom) / (visibleTo - visibleFrom)
    return (1 - alpha) * halfPx + alpha * (sizePx - halfPx)
}
</code></pre>
<p>This Composable function illustrates the relationship between the Activity screen and the Composable labeled as &quot;Target&quot;. Setting the padding is a bit tricky, but it’s not rocket science. Composable is technically a function, but it maintains UI state by continuously holding values with <code>MutableState</code> and <code>remember</code>. At the same time, automatically recompose when persistent information changes, reducing the need for tedious event handling and allowing for a more declarative coding style.</p>
<p>When you scroll the HorizontalScrollView in the upper right corner of the screen left and right, the Composable at the bottom follows along and updates its position. By retrieving the position of a View or Composable inside a ScrollView using <code>View$getLocationInWindow(IntArray)</code> or <code>LayoutCoordinator.boundsInWindow()</code>, you can also get off-screen coordinates. This allows you to check whether elements can move properly in and out of the visible screen. 　 (However, this does not necessarily apply when scrolling using <code>Modifier.horizontalScroll(...)</code>, <code>Modifier.verticalScroll(...)</code>, or similar methods in a Composable.)</p>
<p>In a conventional View, displaying this kind of debug information required modifying both the layout XML file and the Java/Kotlin code. Since these changes wouldn’t be reflected in the released app, it felt like a bit of a hassle. Data binding was also introduced, allowing layout XML files to track variable updates, but it wasn’t the most intuitive approach. Jetpack Compose lets you to design screens as if you were writing information directly into the design, making it easy to see your implementation results almost instantly. Displaying everything graphically isn’t always the best approach, but having more options for expression can make it easier to dive into coding.</p>
<p>[^1]: <a href="https://google.github.io/accompanist/web/">A wrapper</a> that uses WebView functionality as a Composable is available in the <a href="https://google.github.io/accompanist/">accompanist</a> library, but it is currently <em>deprecated</em>. Even in the official implementation, making a View fully Composable doesn’t seem easy. Since it’s common for Views and Composables to coexist for a long time in general app development, there’s no need to worry—go ahead and embrace Composables with confidence.</p>
<h3>2.3. Writing Debug Information Declaratively</h3>
<p>Displaying debug information with Composables allows you to add details even more easily with fewer steps: </p>
<pre><code class="language-kotlin:TargetWatcher.kt">@Composable
fun TargetWatcher(visibleRect: Rect, targetRect: Rect) {

    // ...
    
        // ...

        Text(
            text = when {
                targetRect in visibleRect -&gt; &quot;Target: inside screen&quot;
                visibleRect.overlaps(targetRect) -&gt; &quot;Target: crossing edge of screen&quot;
                else -&gt; &quot;Target: outside screen&quot;
            },
            modifier = Modifier.align(Alignment.TopStart),
            style = TextStyle(
                textAlign = TextAlign.Center,
                color = MaterialTheme.colorScheme.onSurface,
                fontSize = 24.sp,
                fontWeight = FontWeight.W600
            )
        )
    }
}

/**
 * &quot;operator fun receiver.contains&quot; defines in and !in (syntax: other in(!in) receiver)
 */
private operator fun Rect.contains(other: Rect) =
    left &lt;= other.left &amp;&amp; top &lt;= other.top &amp;&amp; right &gt;= other.right &amp;&amp; bottom &gt;= other.bottom
</code></pre>
<p>The above uses <code>Text(...)</code> to add debug information.   The  <code>style</code> argument in <code>Text(...)</code> 
can be omitted if the default settings are sufficient.   You can display information with almost the same amount of typing as <code>Log.d(...)</code> or <code>println(...)</code>. Unlike those methods, the information doesn’t disappear as you scroll. One of the advantages of <strong>Declarative UI</strong> is that it makes building a UI as effortless as &quot;debugging with print statements&quot;.</p>
<p>Build and run the app, then scroll horizontally in the top-right scroll view to see the app in action at the bottom of the screen, like this:</p>
<table>
<thead>
<tr>
<th>Off-screen display</th>
<th>Partially over screen borders</th>
<th>On-screen display</th>
</tr>
</thead>
<tbody><tr>
<td><img src="/assets/blog/authors/tsuyoshi_yamada/advent-calendar_sample_outside-screen.png" alt="Off-screen display"></td>
<td><img src="/assets/blog/authors/tsuyoshi_yamada/advent-calendar_sample_crossing-edge.png" alt="Partially over screen borders"></td>
<td><img src="/assets/blog/authors/tsuyoshi_yamada/advent-calendar_sample_inside-screen.png" alt="On-screen display"></td>
</tr>
</tbody></table>
<h2>3. Procedural Processing with LaunchedEffect</h2>
<p>So far, we’ve discussed the significance of <strong>Declarative UI</strong>, but when it comes to handling events triggered by state changes or adding animations to show those changes, some <em>Procedural</em> processing is also necessary. If you can’t write procedural code like event handling, which has traditionally been done in View, moving to Jetpack Compose won&#39;t be possible. In Jetpack Compose, <a href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#LaunchedEffect(kotlin.Function0,androidx.compose.runtime.SideEffect)">LaunchedEffect</a> is commonly used to handle actions based on state changes.</p>
<pre><code class="language-kotlin:TargetWatcher.kt">@Composable
fun TargetWatcher(visibleRect: Rect, targetRect: Rect) {

    // ...
    
        // ...

        var currentState by remember { mutableStateOf(TargetState.INSIDE) }
        var nextState by remember { mutableStateOf(TargetState.INSIDE) }
        var nextState by remember { mutableStateOf(TargetState.INSIDE) }
        var stateText by remember { mutableStateOf(&quot;&quot;) }
        var isTextVisible by remember { mutableStateOf(true) }
            nextState = when {
            visibleRect.overlaps(targetRect) -&gt; TargetState.CROSSING
            else -&gt; TargetState.OUTSIDE
        }

        LaunchedEffect(key1 = nextState) {
            if (stateText.isNotEmpty()) {
                if (currentState == nextState) return@LaunchedEffect
                stateText = when (nextState) {
                    TargetState.INSIDE -&gt; &quot;Target: entered screen&quot;
                    TargetState.OUTSIDE -&gt; &quot;Target: exited screen&quot;
                    TargetState.CROSSING -&gt; if (currentState == TargetState.INSIDE)
                        &quot;Target: exiting screen&quot;
                    else
                        &quot;Target: entering screen&quot;
                }
                currentState = nextState
                repeat(3) {
                    isTextVisible = true
                    delay(250)
                    isTextVisible = false
                    delay(250)
                }
            }
            stateText = when (nextState) {
                TargetState.INSIDE -&gt; &quot;Target: inside screen&quot;
                TargetState.CROSSING -&gt; &quot;Target: crossing edge of screen&quot;
                TargetState.OUTSIDE -&gt; &quot;Target: outside screen&quot;
            }
            isTextVisible = true
        }

        if (isTextVisible) {
            Text(
                text = stateText,
                modifier = Modifier.align(Alignment.TopStart),
            )
        }
    }
}

enum class TargetState { INSIDE, CROSSING, OUTSIDE }
</code></pre>
<p>You can set multiple keys for <code>LaunchedEffect</code> . If you only want to run the process the first time the Composable is called, you can use <code>LaunchedEffect(Unit) { ... }</code> . Whenever the nextState specified as a key changes, the corresponding process will be executed accordingly. The code above will make the text flash for 1.5 seconds after a state change, showing the current state compared to the previous one, and then display it statically. You can handle events by specifying a state variable as the key of <code>LaunchedEffect</code> and writing the processing inside the block to run when the state changes. Inside the <code>LaunchedEffect</code> block,  you can write time-consuming processes using <strong>suspendfunctions</strong> like <code>delay(...)</code>. If a state change happens before the block’s processing finishes, the current processing is canceled, and the new state’s processing starts from the beginning in response to the change.</p>
<p>The <code>LaunchedEffect</code> block handles <em>procedural</em> processing, while the <code>Text(...)</code> follows a <em>Declarative</em> approach based on the values of the variables provided procedurally. For handling changes in the UI, it’s best to use <code>LaunchedEffect</code> . Besides, there are other effects suited for different situations, such as <a href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#SideEffect(kotlin.Function0)">SideEffect</a> , as well as <a href="https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary#DisposableEffect(kotlin.Function1)">DisposableEffect</a>, which handles processing according to the lifecycle of Activity and Fragment. On the other hand, it’s also important not to overuse these processes to keep the code from becoming unnecessarily complex.  For example, it’s recommended to handle event processing triggered by responses from the Internet or inputs from sensors like NFC in the ViewModel, while keeping procedural code in Composable limited to UI-related elements.</p>
<h2>4. Interoperability between ComposeView and AndroidView</h2>
<p>When using a Composable in an Activity or Fragment, you can display it with <code>ComposeView</code>, as shown above. Conversely, if you want to use something like the aforementioned <code>WebView</code> inside a Composable, you can embed a View into a Composable using <a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidView(kotlin.Function1,androidx.compose.ui.Modifier,kotlin.Function1)">AndroidView</a> or <a href="https://developer.android.com/reference/kotlin/androidx/compose/ui/viewinterop/package-summary#AndroidViewBinding(kotlin.Function3,androidx.compose.ui.Modifier,kotlin.Function1)">AndroidViewBinding</a>. For instructions, please refer to <a href="https://developer.android.com/develop/ui/compose/migrate/interoperability-apis/views-in-compose">Here (Using views in Compose)</a> . This article won’t go into details, but thanks to the <code>AndroidView</code> Composable, even if you’ve made progress in converting your app to Composable but find that replacing certain Views is challenging or time-consuming, you can still continue development by integrating both Compose and View. This interoperability is extremely powerful—you can call <code>AndroidView</code> inside a Composable invoked by <code>ComposeView</code>, then nest another <code>ComposeView</code> inside it to call a Composable again, and even embed <code>AndroidView</code> within that, creating a layered structure that can continue further.  By keeping the option to use Views within Composables, you can minimize the risk of wasted effort if the transition to Composable doesn’t progress as expected or if replacing Views takes significantly longer than a development sprint.</p>
<h2>5. Preview Function</h2>
<p>So far, we’ve focused on the flexibility of Composable, which allows you to reflect on the development process. It supports techniques for alignment and debugging at a level comparable to Views, enables procedural processing, and offers the option to revert parts to Views through powerful interoperability when facing challenges in the transition to Composable. Here, we’ll talk about one of the key benefits of Composable—the simple yet powerful preview capabilities of the Preview function.</p>
<h3>5.1. Create a Preview function</h3>
<p>Android Studio’s preview feature allowed you to visualize Views in layout XML files, but Jetpack Compose takes it a step further with even more powerful preview functions. Just create a function with the <code>@Preview</code> annotation, and your Composable will be displayed in the preview:</p>
<pre><code class="language-kotlin:TargetWatcher.kt">// ...

private class TargetWatcherParameterProvider :
    PreviewParameterProvider&lt;TargetWatcherParameterProvider.TargetWatcherParameter&gt; {
    class TargetWatcherParameter(
        val visibleRect: Rect,
        val targetRect: Rect
    )

    override val values: Sequence&lt;TargetWatcherParameter&gt; = sequenceOf(
        TargetWatcherParameter(
            visibleRect = Rect(0f, 0f, 100f, 300f),
            targetRect = Rect(90f, 80f, 110f, 120f)
        ),
        TargetWatcherParameter(
            visibleRect = Rect(0f, 0f, 300f, 100f),
            targetRect = Rect(80f, 90f, 120f, 110f)
        )
    )
}

@Preview
@Composable
private fun PreviewTargetWatcher(
    @PreviewParameter(TargetWatcherParameterProvider::class)
    params: TargetWatcherParameterProvider.TargetWatcherParameter
) {
    KtcAdventCalendar2024Theme {
        TargetWatcher(params.visibleRect, params.targetRect)
    }
}
</code></pre>
<p>The above example demonstrates how to use <code>PreviewParameterProvider</code> to supply multiple parameters to a single preview function and display them in the preview. In the layout XML file of the Android View,   you can’t achieve this using the <code>tools:???</code> attribute to configure the preview. However, can display a preview without using <code>PreviewParameterProvider</code> —just call a Composable function inside a function annotated with <code>@Preview</code> and <code>@Composable</code>.</p>
<p>My recommendation is to create a preview function as soon as you start working on a new Composable. Just the advantage of having powerful preview capabilities from the start when creating a new Composable is reason enough to switch to Jetpack Compose. Being able to check the display of debug information with the preview function is another advantage of using Composable for debugging. Recently, using preview functions for UI testing with libraries like roborazzi has been gaining attention. From a testing efficiency standpoint, creating preview functions is definitely worthwhile.</p>
<h3>5.2. Try running the Preview function</h3>
<p><a href="https://developer.android.com/develop/ui/compose/tooling/previews#run-preview"> As explained here (Running the Preview)</a>, you can run the Preview function on an actual Android device or emulator by clicking the <strong>Run &#39;...&#39;</strong> icon on the left side of Android Studio. This is similar to the previous functionality for executing a specific Activity, but the Preview function is more powerful. It is easier to write and it can run in a simplified environment without needing elements like intents. Callbacks for UI actions, such as button taps, are also executed, allowing you to use the Preview function for testing as if it were a simple app.</p>
<p>However, it’s not recommended to overload a Composable with too many features just to make it more testable in the Preview function. To keep Composable purely as a declarative UI, it’s best to separate business logic into other classes or functions, such as a ViewModel or a Presenter in Circuit, and focus on writing only UI-related code.</p>
<h2>6. Conclusion</h2>
<p>I hope this article will encourage more developers to take the leap from Android View to Jetpack Compose. Transitioning to a UI system with a different approach isn’t always straightforward, and the fear of setbacks is understandable. My hope is that your first steps feel as smooth as possible and that the risk of wasted effort is kept to a minimum.</p>
<h2>7. References</h2>
<ul>
<li><a href="https://developer.android.com/reference">Android API reference</a></li>
<li><a href="https://qiita.com/kaleidot725/items/681ff4ef557f9ee831f7"> [Android] Functions for Retrieving a View’s Position </a></li>
<li><a href="https://speakerdeck.com/wiroha/jetpack-compose-modifier">In-Depth Guide to Jetpack Compose Modifier</a></li>
<li><a href="https://proandroiddev.com/what-can-advanced-lesser-known-modifiers-do-for-your-ui-a-comprehensive-exploration-in-jetpack-1b3b3b3b1b3b">What can Advanced / Lesser Known Modifiers do for your UI? — A Comprehensive Exploration in Jetpack Compose: A Journey Through Advanced and </a></li>
</ul>
<h2>Notes</h2>
<p>The Android robot is reproduced or modified from work created and shared by Google and used under the terms of the Creative Commons Attribution 3.0 License. <a href="https://developer.android.com/distribute/marketing-tools/brand-guidelines#android_robot">^2</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/tsuyoshi_yamada/CoverMigrationFromAndroidViewToJetpackCompose.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[AWS認定12冠-資格から始まるクラウド生活]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-11-AWS-All-Certification-to-start-cloud-life/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-11-AWS-All-Certification-to-start-cloud-life/</guid>
            <pubDate>Tue, 01 Apr 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[AWS認定12冠を取った感想と回顧]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h2>1.start-(概要)</h2>
<p>初めまして！
KINTOテクノロジーズのCloud Infrastructure GでInfrastructure Architectを担当している劉(YOU)です。</p>
<p>今年の1月に入社して、techblogには初執筆なのでこれからよろしくお願いします！
AWSの認定は23年10月のSAAを始じめ、25年2月のMLAを最後に、1年4ヶ月でやっとAWS認定12冠を取りました。せっかくなので、AWS認定12冠を達成しながら感じた個人的な意見と情報を共有します。</p>
<p><img src="/assets/blog/authors/you/01-01.jpeg" alt="All Certi"></p>
<p>先に話しますと、AWS認定12冠とは、AWS社が主催する資格認定を全て取得したことを指します。その基準は毎年変更されるんですけど、</p>
<p><a href="https://aws.amazon.com/jp/blogs/psa/2024-japan-aws-all-certifications-engineers/">https://aws.amazon.com/jp/blogs/psa/2024-japan-aws-all-certifications-engineers/</a> </p>
<p><a href="https://aws.amazon.com/jp/blogs/psa/">AWS JAPAN APN ブログ</a>からその詳細を前もって発表して、選出された方々を表彰します。2024年は1,222名が正式に「AWS All Certifications Engineers」として認められています。公式記事では、</p>
<blockquote>
<p>”AWS 認定資格を全て取得し・維持する事” は AWS のテクノロジーを正しく理解し、お客様に信頼性の高い最新の技術をご案内できる基盤をお持ちだという証になります。</p>
</blockquote>
<p>と述べています。</p>
<p>AWS以外でも、AzureとかGCPとかクラウドサービスを提供している会社は多いですが、クラウドサービスの量と質、高い占有率からできる汎用性、圧倒的なアップデートスピード、どう考えてもAWSは クラウド業界の標準だと言い切れます。</p>
<p>そして、最近AIの注目が高くなることと伴い、クラウドの重要性も上がっています。「クラウドとかAIとか、私と関係ないだろう」と思う方がいらっしゃるかも知れませんが、現時点でほぼ全ての業務にパソコンを使うようになった事と同じく、もうじきAIを日常で使うことになる時代がやって来ると思います。</p>
<p>AIその物になるモデルとモデルを動かすためのコンピュータ、それが簡単に提供されるプラットフォームがクラウドであり、時代を追いつくためにもクラウドを習得する必要があると言うことです。それで、AWS及びクラウドを勉強するために資格はなぜ必要なのか？</p>
<p>その答えを次から説明して行きます。</p>
<h2>2.status-(現状)</h2>
<p>残念ながら、資格はあってもなくてもクラウド活用に大きい影響を与えません。例を挙げると、今からクラウドを英語だと見なして考えてください。英語を活用するためにTOEICを準備して、高得点をしました。それが本当の意味で英語を上達することだと思いますか？</p>
<p>どれだけ試験のテクニックが良くても、単語と文法などを暗記しても、実際に英語を要求される時に使いこなせなかったら無駄です。
しかし、TOEICが英語の実力向上に役立たないって言ったらそれは違うと断言できます。意味が無かったら数々の大学・企業からTOEICの点数を基準として評価する訳がないです。ビジネス英語力を点数に換算する試験がTOEICだからこそ、ただの点数ではなくて能力として認められてることになります。</p>
<p>そう言う意味で、AWS認定12冠はクラウドに対して分かりやすい天井です。
実体化されていない知識の塊を資格という形で見えるようにします。こう言う見える化によって得られる効果を整理しますと、</p>
<blockquote>
<ul>
<li>明確な目標設定(Goal)：AWS社が証明してくれるロードマップを従った認定なので、階段式でスケジューリング可能</li>
<li>努力の源(Motivation)：受験日を決めて勉強することで、頑張れる環境が作られる</li>
<li>知識の保証(Knowledge)：資格を取るための最低限の知識が保証される</li>
<li>振り返り(Remind)：そもそもクラウドに詳しい人であっても、資格で要求される知識を点検することができる</li>
<li>キャッチアップ(Discovery)：試験はアップデートを沿って変わっていくので、触れる機会がない情報を勉強することができる</li>
</ul>
</blockquote>
<p>になります。</p>
<p>逆に英語で置換して見ても、違和感なく受け入れられる内容ではないでしょうか？結局、資格を取ったらクラウド力が上がる、クラウドやりたいから資格取ろうではなく、鍛えること、それ自体に意味があると思います。</p>
<h3>AWS認定のこれから</h3>
<p>続いて、私が一年ちょっと超える時間をAWS認定準備しながら感じた「これからAWS認定はどうなる？」を突っ込んで行きます。</p>
<p>:::message
あくまでも、<strong>根拠のない自分勝手の推測なので、AWSで公式の何かがあることではありません。</strong> 引用の時にはご注意お願いします。
:::</p>
<p>私がAWS認定の準備し始めた時期は、22年ChatGPTが流行ってAIに対する関心が大きくなってる状況でした。AWSもAIを中心にするサービスをどんどん出して、24年から資格の構成を大きく変更させました。</p>
<p>既存のスペシャル資格三つを24年4月で削除し、</p>
<blockquote>
<p>AWS Certified Data Analytics – Specialty (DAS)
AWS Certified Database – Specialty (DBS)
AWS Certified: SAP on AWS – Specialty (PAS)</p>
</blockquote>
<p>DASとDBSを代行するために資格が24年3月に登場、</p>
<blockquote>
<p>AWS Certified Data Engineer – Associate (DEA)</p>
</blockquote>
<p>その後、Amazon QとかAmazon Bedrockなど新しく出たAIサービスと、Amazon Sagemaker周りの強化されたAIサービスのロードマップを提示するために24年10月、</p>
<blockquote>
<p>AWS Certified AI Practitioner (AIF)
AWS Certified Machine Learning Engineer – Associate (MLA)</p>
</blockquote>
<p>結構、大変革だったので試験を準備している個人の立場でもちょっと困りました。勉強してた内容が大幅に変更されることと同然だったので、先に計画してた試験日程も変えるしか無かったです。これからも、今後の技術トレンドであるAIを中心に変わっていくことは確かです。</p>
<p>あくまでも推測で過ぎませんが、変更が起きる可能性が高い資格は</p>
<blockquote>
<p>AWS Certified Machine Learning Engineer – Specialty (MLS)</p>
</blockquote>
<p>だと思います。</p>
<p>MLSが最後にアップデートされたのは22年7月のなのでAIF, MLAに比べたら内容が古くなっています。現状のSpecialtyのままアップデートされることもありますが、新しいprofessional資格に改編される可能性が高いです。
その理由としては、既存のパスがPractitioner→Associate→Professionalに繋がる仕組みだからです。</p>
<p><img src="/assets/blog/authors/you/01-02.jpeg" alt="path"><a href="https://aws.amazon.com/jp/blogs/big-data/upgrade-your-resume-with-the-aws-certified-big-data-specialty-certification/">^1</a></p>
<p>同様にAIF → MLA資格の次に来るProfessional資格が必要になります。単純にスペシャリティがプロフェッショナルになって最新化される…ことはAWS側が決める所ですが、そうなったらDEAの上位資格も想定するしかないです。</p>
<blockquote>
<p>(仮)AWS Certified Machine Learning Engineer – Professional (MLP)
(仮)AWS Certified Data Engineer – Professional (DEP)</p>
</blockquote>
<p>これが普通に考えられる予測ですが、これはこれで問題が生じます。AWS社の認定は12冠を象徴として維持してるそうなので、二つが増えれば資格の数が13個を超えてしまうことです。</p>
<p>それを回避する方法があって、まずは上記の資格が増やされる分、曖昧になったSpecialtyを減らすことです。</p>
<blockquote>
<p><del>(仮)AWS Certified Security – Specialty (SCS)</del>
AWS Certified Advanced Networking – Specialty (ANS)</p>
</blockquote>
<p>SCSとANSはもう消えている他のスペシャリティとは違って、プロフェッショナルから携わった知識を深く振り込む内容で構成された認定です。両方とも出る内容が60%以上がプロフェッショナルと重複しつつ、SCSは組織全体のセキュリティを重視し、ANSはオンプレミスとのネットワーキングが主になっています。その中、現状のままではやむを得ない欠陥があります。</p>
<p>SCSはAIのアップデートされなかったので、AIに対するセキュリティの内容が含まれていません。AIの発展が早くなっていながら、AIに対するセキュリティーやコンプライアンスも大事にされてるので、SCSにAIの内容が入れるか、各プロフェッショナルに溶かすかの問題です。すでにAIのトレンドに合わせて統廃合されたスペシャリティ認定が多いので後者がなり得ると思ってます。
ANSの場合、SCSと似ているポジションですが、ネットワークはAIができてもAWS内部に限ってはそんなに差がありません。OpenAIを使うにはAzureを、Geminiを使うにはGCPを、その他のクラウドベンダが運営しているAIを使うにはマルチクラウドが確かに必要になりますが、自社製品じゃないとAWS社が積極的に支援しないスタンスなので、マルチクラウドに関係する認定のアップデートは今の所はないです。代わりに、アンチクラウドの流れからハイブリッドクラウドが浮上していますので、ANS認定の仕組みは維持すると見込んでいます。</p>
<p>とにかく、資格の数を減らしたら12冠は担保できるのでこう言うやり方もあることと、二つのプロフェッショナルを増やさずにDevOps Engineerみたいに一つに納める事もあります。</p>
<blockquote>
<p>(仮)AWS Certified MLOps Engineer – Professional (MOP)</p>
</blockquote>
<p>AWSで紹介している<a href="https://aws.amazon.com/jp/what-is/mlops/">MLOps</a> は、「ML アプリケーション開発 (Dev) と ML システムのデプロイおよび運用 (Ops) を統合する ML カルチャーとプラクティスです」だと記述してます。</p>
<p><img src="/assets/blog/authors/you/01-03.jpeg" alt="MLOps"><a href="https://aws.amazon.com/what-is/mlops/">^2</a></p>
<p>これはMLに関する全体作業を意味してるので、まさにDEAで扱うデータエンジニアリング、データ分析を通して、AIF→MLA→MLSで使われる機械学習の全体を活用できますので、新しいプロフェッショナルがただ一つだけ必要だったらこれで通用できる仕方だと思ってます。</p>
<h3>AWS認定の類型</h3>
<p>そして、認定の種類だけではなく、試験の類型にも変化が起きています。</p>
<p>既存の試験はSOAのラボ試験が中止になってから、選択肢で正解を選ぶ形式のみが評価方式でした。客観的に定量評価ができる要素は長所ですが、実装とは関係が深くない知識になることも否定できません。AWS社もこれを意識しているかなと思いまして、AIFとMLAから新しい出題形式が出ました。</p>
<p><a href="https://d1.awsstatic.com/ja_JP/training-and-certification/docs-ai-practitioner/AWS-Certified-AI-Practitioner_Exam-Guide.pdf">AIF試験ガイド</a>を見ると、</p>
<blockquote>
<ul>
<li>並べ替え: 指定されたタスクを完了することを目的とした 3～5 つの答えのリストが提示される。設問に対する点数を得るには、正解を選択し、正しい順序に並べる必要がある。</li>
<li>内容一致: 3～7 つのプロンプトのリストと一致する答えのリストが提示される。設問に対する点数を得るは、すべてのペアを正しく一致させる必要がある。</li>
<li>ケーススタディ : 1 つのシナリオに、そのシナリオに関する設問が 2 つ以上含まれている。ケーススタディの各設問のシナリオは同じである。ケーススタディの各設問は個別に採点される。ケーススタディでは正解した設問ごとに点数が得られる。</li>
</ul>
</blockquote>
<p>上記の三つの類型は私の試験にも多い数ではありませんが、試験ガイドで述べている事と同じく出題されました。問題のレベルとしては選択肢の問題と同様で、AWS試験の秘密保証のため問題の詳しい形式は言えませんが、私が感じた出題形式の評価はこうなります。
並べ替えと内容一致は、選択肢の類似性から正解を類推することが出来なくなりました。本当に実施するべきの手順だったり、提示される単語や説明を結び付く、問題から要求する内容を熟知しないと解けない形です。</p>
<p>ケーススタディの基本は設問＝選択肢なんですが、単一のケースで複数の問題を提示するやり方です。ここはケースを多角的に接近することもできるし、長問・多問からは知識の応用より読解力を求められる状況があるから、それが解消されます。我らの現実世界もケースがあったら一問一答で絶えず、ケースごとにシミュレーションすることが一般的なので、このケーススタディは受験者としていい類型だと思います。</p>
<p>AWS社は認定に関してこれからも出題形式もそうですし、SOAのラボみたいに「本当に実装できますか？」を目指して変化して行くと考えられます。こういう変更は単発で起きることではなく、連続的に他の認定にも反映されるので、AWS試験を準備していらっしやる方々はキャッチアップして備えて行きましょう！</p>
<h2>3.stance-(心構え)</h2>
<p>私も周りの人達と話す時に職種不問でよく出る話題なんですが、</p>
<blockquote>
<p>「AWSとは関係ない仕事してるけど、これ勉強して本当に使える？」
「AWS資格取るとしたらどこから始まる？」
「何をネタに勉強してる？」</p>
</blockquote>
<p>と質問を貰います。私はクラウドエンジニアとして資格を取っていまして、クラウドで業務をするための知識が元々必要になります。実務で使っているからこそ、クラウドに関わる頻度が他の人と比べて非常に多いです。その為、資格を取得したらすぐにクラウドに関わる業務ができるようになるとは言えません。</p>
<p>今まで全然使っていなかったのに、資格を取ったらすぐ使えるようになることはあんまりないです。資格は言わば、何らかのクーポンのような物です。１万円以上に限って10％割引をしてくれるガソリンスタンドのクーポンができたとしても、車がそもそもないから無用、クーポンの店が遠いから行けない、10％割引額を達成するお金がない、クーポンは色んな理由で使えきれないケースがいっぱいです。こう見たらクーポンを使うための条件は明確です。</p>
<blockquote>
<ol>
<li>自分・知り合いが車を持っていたり、車を持つ計画がある。</li>
<li>クーポンを使えるガソリンスタンドとの距離が近い。</li>
<li>クーポンを使える余力がある。</li>
</ol>
</blockquote>
<p>自分自身にそのクーポンが欲しい理由が整えているかを確認してください。</p>
<p>言い換えますと、「資格を活かせるように動けますか？」という意味です。クーポンを手に入れたとして、車が自動で生成されたり、ガソリンスタンドが勝手に家の前にできたり、クーポンを使えるお金が急に現れたり、そんな出来事は現実では起こらないですよね？クラウドもAWSも同じです。クラウドが己の業務と当てはまらない方々、例えば</p>
<ul>
<li>ITと関係ないビジネス系</li>
<li>インフラは専門ではない開発系</li>
<li>オンプレに特化したインフラ系</li>
</ul>
<p>が挙げられます。</p>
<p>車は高いから買えない人には何を推奨しますか？カーリース・サブスクは月額払いだけできれば車を使えることができます。これがITではクラウドです。私は「技術を借りる」がクラウドの本質だと思ってます。技術を教わることが高かったら借りればいい話です。領域によって詳しくは違うかも知れませんが、知っている事だけで技術の視界が覆ると確信してます。</p>
<p>ガソリンスタンドが遠くて行けないって言ったら、それはそれで十分です。無理矢理に行くことまではないです。ただし、通勤途中で寄り道に行けそうな距離だったらどうしますか？開発系の方にとって、クラウドはそんなに遠い所にある訳ではありません。むしろ、視線を少し横に移すだけで活用できることが溢れるているかもです。</p>
<p>最後に、どれだけお得になるクーポンを持っていても、使わずにいたらない事と同然です。車をすでに持っていて、ガソリンスタンドがすぐ前に出来ても、いつも通っていたガソリンスタンドだけ利用してたらクーポンは使えません。１万円を前払いできないとか、他社のガソリンカードがあるとか、検証できない店は行きたくないとか、それぞれ抱えた理由は千差万別だと思ってます。しかし、否定できない事実はインフラ系の方々は、他の誰よりもクラウドを始めることに特化していることです。オンプレだけやってたら、クラウドはIaaS(Infra as a Service)とかPaaS(Platform as a Service)として提供していて馴染みがないと思われます。それでも、基本構造はインフラの知識の上で作られているから、ビジネス系と開発系に比べれば極めて簡単です。「クラウドまでやれる余力がない」ではなく、「クラウドまでやる余力を作ってみよう」はどうですか？</p>
<p>私もキャリアを開発系でスタートしていましたが、個人的に勉強したクラウドの知識があってクラウド業務も任されました。その後は資格を取りながらクラウド職に転向もできました。多分、私がやろうとしてる事とやっている仕事に限って集中していたらできなかったと思います。AWS認定12冠の達成もチャンスを増やす感覚であります。資格取得で得た知識のうち、KTCに合流してから実践で使える知識は50％前後程度です。しかし、使ってない50％の知識もこれから活かせるように頑張っています。KTCはAIファーストを今年の目標に決めているので、私もAI活動を盛り上げて行くつもりです。</p>
<p>AIファーストと繋ぐKTCの今年目標に興味がある方は、弊社副社長の景山が記事を掲載しているのでぜひ読んでみてください。</p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/">https://blog.kinto-technologies.com/posts/2024-12-25-LookBack2024/</a> </p>
<p>公式から推薦する<a href="https://d1.awsstatic.com/ja_JP/training-and-certification/docs/AWS_certification_paths.pdf">AWS認定パス</a>もありますので、ここもご参考お願いします！</p>
<h2>4.strategy-(攻略法)</h2>
<p>勉強法は私以外にもたくさんの方が推薦してくれていると思いますので、観点を変えてAWS認定を効率よく攻略する戦法をテーマにお話しします。</p>
<h3>正攻法</h3>
<p>真面目に勉強する方法は極簡単です。前述してた<a href="https://d1.awsstatic.com/ja_JP/training-and-certification/docs-ai-practitioner/AWS-Certified-AI-Practitioner_Exam-Guide.pdf">AIF試験ガイド</a>で出ている内容をゼロから習得することです。ここはベース知識がなくて誠実に勉強して行きたい方や、受験日程に焦らずにやりたい方にお勧めします。
全部５段階でやってます。</p>
<ol>
<li><p>情報収集：検索、SNS、YouTube、Blogなどを参考して己が好むソースを探す。</p>
</li>
<li><p>ソース決定：下記のソースの中で、最も自分に適することを決めます。</p>
</li>
</ol>
<ul>
<li><p>AWS公式
 AWS社で提供してくれるドキュメントは最新反映されてて、信用度も高いし、内容も上品です。私も他のやり方で勉強しても公式はいつも参照します。一部無料のAWS Traning Centerの活用もすごく助けになりますので、ぜひ活用してください。有料の物は使っていませんが、下で紹介する外部学習サイトと似てる効果だと感じます。</p>
</li>
<li><p>YouTube
  無料情報の量は一番多いですが、アップローダーさんによって質も量もバラバラですし、最新化も望めない短所があります。でも、映像や音声で学習できることと言語の縛りさえなければ短所も薄くなります。聞いてみていつでもお気軽にやめる事もできることが嬉しいです。</p>
</li>
<li><p>書籍
  アナログ勉強法が好きだったら推しです。ターゲットを絞って情報の質を保証することが本の魅力です。買う前にある程度内容把握もできるし、一冊で求める内容が集約されていることが長所です。しかし、情報の最新化には弱いことは変化が早いAWS認定には向いてないことを注意してください。即時、勉強に入って認定の更新前に試験を受ける事じゃなかったら避けた方がいいです。</p>
</li>
<li><p>外部学習サイト
  Udemyみたいに有料で提供するソースを記述します。お金を払う分、YouTubeを超える質を持ちながら最新化も早いです。端的に言うと、YouTubeと書籍の長所を合体させた感じなので、個人的に愛用しています。気を付ける所はAWS認定の場合、資格同士に被る内容が結構ありますので目次をよく見て購入してください。</p>
</li>
</ul>
<ol start="3">
<li><p>学習開始：試験によって学習時間は違いますが、2-3ヶ月を想定して勉強することが最適でした。</p>
</li>
<li><p>検証：AWS公式とかでやってる模擬試験で自己評価</p>
</li>
<li><p>試験：日程を予め取って受験します。オンラインでもオフラインでも受験できますが、一貫的な試験環境のためにもオフラインを推奨します。オフラインでの試験で何かの問題が起こったら、試験主催側が調整してくれるので安心ですし、試験結果もオフライン試験の方が早く届くのでお得です。</p>
</li>
</ol>
<h3>速攻法</h3>
<p>逆に「私はAWS知っているから、初めから勉強するまでもない。知らない所だけ勉強したい！」と考えてる方や、「資格を早く取って勉強してもいいから、最速で取りたい！」と思ってる方もいます。私も幾つかの試験はそうだったので、コスパ最高の攻略法を話します。</p>
<p>:::message
生成AI：私はPerplexityを使ってましたが、どんな生成AIでも大丈夫です！そして、紹介するプロンプトは記事用の例であって、個人が向いてるプロンプトやり方でしてもOKです。
:::</p>
<h4>キーワード整理</h4>
<p>サービスの内容を要約してピンポイントだけ知ることができます。</p>
<p><img src="/assets/blog/authors/you/01-04.jpeg" alt="Input Prompt"></p>
<p>:::details 回答</p>
<p>以下に、AWSの監視サービスであるCloudWatchを中心に、関連するサービス（CloudTrail、Config、WAF）との比較を整理します。また、これらに含まれない他の監視ツールも簡単に触れます。</p>
<p><strong>CloudWatchの整理</strong></p>
<p><strong>概要</strong></p>
<p>Amazon CloudWatchは、AWSリソースやアプリケーションをリアルタイムで監視するためのサービスです。主に次の機能を提供します：</p>
<ul>
<li><p>メトリクス収集：CPU使用率、ディスクI/O、ネットワークトラフィックなどのリソースデータを収集。</p>
</li>
<li><p>ログ監視：アプリケーションログやシステムログを収集し、分析可能。</p>
</li>
<li><p>アラーム設定：特定の条件（しきい値）に基づいて通知や自動アクションを実行。</p>
</li>
<li><p>イベント管理：特定のイベントをトリガーとして処理を自動化（例：インスタンス起動時の通知）。</p>
</li>
</ul>
<p><strong>主なユースケース</strong></p>
<ul>
<li><p>リソースのパフォーマンス監視</p>
</li>
<li><p>異常検知とアラート通知</p>
</li>
<li><p>システム全体の稼働状況の可視化</p>
</li>
</ul>
<p><strong>CloudWatchと関連サービスの比較</strong></p>
<table>
<thead>
<tr>
<th>サービス名</th>
<th>主な目的</th>
<th>特徴</th>
<th>主なユースケース</th>
</tr>
</thead>
<tbody><tr>
<td>CloudWatch</td>
<td>メトリクス＆ログ監視</td>
<td>AWSリソースやアプリケーションの稼働状況をリアルタイムで監視可能。<br/>ほぼリアルタイムで通知。</td>
<td>リソース監視、パフォーマンス最適化、異常検知</td>
</tr>
<tr>
<td>CloudTrail</td>
<td>操作履歴の記録</td>
<td>誰がいつ何をしたか（APIコール）の履歴を記録。<br/>コンプライアンスやセキュリティ監査向け。</td>
<td>セキュリティ監査、不正操作検出</td>
</tr>
<tr>
<td>AWS Config</td>
<td>設定変更の追跡＆評価</td>
<td>リソース構成変更を記録し、ルール違反を検出可能。<br/>設定状態をスナップショットで保存。</td>
<td>設定管理、コンプライアンス遵守、トラブルシューティング</td>
</tr>
<tr>
<td>AWS WAF</td>
<td>Webアプリケーション層のセキュリティ対策</td>
<td>SQLインジェクションやDDoS攻撃などからWebアプリケーションを保護。<br/>CloudFrontと連携可能。</td>
<td>セキュリティ強化、攻撃遮断</td>
</tr>
</tbody></table>
<p><strong>比較ポイント</strong></p>
<ol>
<li>目的と対象範囲：</li>
</ol>
<ul>
<li><p>CloudWatchはシステム全体の稼働状況を監視。</p>
</li>
<li><p>CloudTrailは操作履歴に特化。</p>
</li>
<li><p>Configは設定変更とルール評価。</p>
</li>
<li><p>WAFはセキュリティ対策専用。</p>
</li>
</ul>
<ol start="2">
<li>リアルタイム性：</li>
</ol>
<ul>
<li><p>CloudWatchはほぼリアルタイムで通知可能。</p>
</li>
<li><p>CloudTrailは遅延が発生する場合あり。</p>
</li>
</ul>
<ol start="3">
<li>ユースケース重複：</li>
</ol>
<ul>
<li>udTrailやConfigで記録したデータをCloudWatch Logsに送信し、統合的に管理することも可能。</li>
</ul>
<hr>
<p><strong>他のAWS外部監視ツールとの比較</strong></p>
<p>AWS以外でも利用可能な監視ツールには以下があります：</p>
<table>
<thead>
<tr>
<th>ツール名</th>
<th>特徴</th>
<th>メリット</th>
<th>デメリット</th>
</tr>
</thead>
<tbody><tr>
<td>Zabbix</td>
<td>オープンソース。<br/>高度なカスタマイズ性</td>
<td>無料で利用可能。<br/>幅広い環境に対応</td>
<td>導入・運用が複雑</td>
</tr>
<tr>
<td>Mackerel</td>
<td>SaaS型日本製ツール</td>
<td>簡単導入。<br/>複数環境一元管理が可能</td>
<td>AWS専用機能が少ない</td>
</tr>
<tr>
<td>Site24x7</td>
<td>マルチクラウド対応</td>
<td>ハイブリッドクラウド環境にも対応</td>
<td>AWS専用ツールより細かい設定が難しい</td>
</tr>
</tbody></table>
<p><strong>選択基準</strong></p>
<p>AWS専用ならCloudWatchが最適。</p>
<p>マルチクラウド環境ではSite24x7やMackerelが有効。</p>
<p>高度なカスタマイズが必要ならZabbix。</p>
<hr>
<p><strong>試験準備に役立つポイント</strong></p>
<ol>
<li><p>各サービスの役割とユースケースを明確に理解する。</p>
</li>
<li><p>CloudWatchと他サービス（CloudTrail, Config, WAF）の連携例を把握する。</p>
</li>
<li><p>コスト面（従量課金制）やリアルタイム性なども試験問題に出る可能性あり。</p>
</li>
</ol>
<p>必要なら追加質問してください！
:::</p>
<h4>試験問題生成</h4>
<p>キーワードの概念を分かったら、簡単な問題を生成して自己評価します。</p>
<p><img src="/assets/blog/authors/you/01-05.jpeg" alt="Input Prompt"></p>
<p>:::details 回答 </p>
<blockquote>
<p>問題 1: あなたは、EC2インスタンスのCPU使用率が特定のしきい値を超えた場合に通知を受け取りたいと考えています。この目的を達成するためにどのAWSサービスを使用するべきですか？
選択肢:
	•	A. AWS CloudTrail
	•	B. Amazon CloudWatch
	•	C. AWS Config
	•	D. AWS WAF</p>
</blockquote>
<blockquote>
<p>問題 2: セキュリティチームが、過去3か月間に発生したAWSリソースへのAPIコールを追跡し、特定のユーザーアクティビティを調査したいと考えています。この目的に最適なサービスはどれですか？
選択肢:
	•	A. Amazon CloudWatch
	•	B. AWS CloudTrail
	•	C. AWS Config
	•	D. Amazon Athena</p>
</blockquote>
<blockquote>
<p>問題 3: ある企業が、AWSリソースの設定変更を追跡し、コンプライアンス違反を検出したいと考えています。この目的に最適なサービスはどれですか？
選択肢:
	•	A. AWS CloudTrail
	•	B. Amazon CloudWatch Logs
	•	C. AWS Config
	•	D. AWS WAF</p>
</blockquote>
<blockquote>
<p>問題 4:あなたは、WebアプリケーションをSQLインジェクションやクロスサイトスクリプティング（XSS）攻撃から保護したいと考えています。この目的に最適なAWSサービスはどれですか？
選択肢:
	•	A. Amazon CloudWatch Logs
	•	B. AWS WAF
	•	C. AWS Shield
	•	D. AWS Config
:::</p>
</blockquote>
<p>速攻法は私が主に使ってる使用法二つだけ説明できなかったんですけど、使用者によって無限にカスタマイズできる生成AI学習法です。紹介した方法以外にも、細かい質問を数十個を投げてリマインドすることも結構やっています。生成AIが参考できる資料をInputに入れたりしたら、もっと豊かなキーワード整理や試験問題を生成してくれます。
私もAIFとかMLAは速攻方を70%割合で使って、1-2周くらいで取ったんで効果は保証します！</p>
<h2>(1st-art).最初から始まる美学</h2>
<p>AWS認定12冠達成して、私が感じた色々を呟いてみました。</p>
<p>気づいてるか知れませんが、実はタイトルにも本文にもトリックを入れました。最初に戻ってみたらすぐ分かると思います。</p>
<blockquote>
<p>1.start-(概要) → (1st-art).最初から始まる美学</p>
</blockquote>
<p>こんな面倒臭い仕業を入れた理由は、私の1年4ヶ月の12冠の挑戦はスタート(アート)したから得られた一つの絵だと話かったです。どんな結果物が誕生するか、絵を描く時には知らないと思います。小学生の頃、「私の未来を描いて下さい」って聞いて、私が描いた未来は消防士でした。そして、中学生の頃は小説家でした。現在はクラウドエンジニアで全然違う仕事をしています。</p>
<p>そうだとして、私の幼い頃の絵が意味がなかったことでしょうか。私はその絵を描きながら、自分の夢に向き合ったことに意味があったと信じます。私は今、「AWS認定12冠」と言う絵を完成しました。次も新しい絵を描いて行くつもりです。ここtechblogに書いたこの記事も一つの絵になりますし、KTCでの仕事も別の絵になれると思ってます。</p>
<p>記事を読んでいただきありがとうございました！</p>
<hr>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[Learning Roadside Station Podcast] Figma Study Session]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-18-学びの道の駅Podcast-Figma勉強会-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-18-学びの道の駅Podcast-Figma勉強会-en/</guid>
            <pubDate>Mon, 31 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for day 18 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<p>I’m Nakanishi from the Manabi-no-Michi-no-Eki (Learning Roadside Station) team. This year, the Learning Roadside Station Project was officially launched and later established as a team. As part of this initiative, we are also running an in-house podcast, and for this year’s Advent Calendar, we’re excited to share some of its episodes with you.</p>
<h1>What is the &quot;Learning Roadside Station&quot;?</h1>
<p>&quot;Learning Roadside Station&quot; is a project launched to enhance the accessibility and effectiveness of the study sessions that are frequently held within the company. This initiative aims to promote knowledge sharing by supporting study sessions led by dedicated volunteers within the company.</p>
<h1>Figma Study Session</h1>
<p>The KTC Learning Roadside Station Podcast features interviews with people who organize study sessions within the company. This segment is called “A Peek into the Study Session Next Door”. Today&#39;s podcast guests are T.M. and M.K., who are leading the Figma Study Session. First of all, could you tell us about the work you both do? Let&#39;s start with T.M.-san.</p>
<h1>Interview</h1>
<p><strong>T.M.-san:</strong> I am a member of the Retailer Digital Transformation (DX) Planning Team of the DX Planning Promotion Group in the Mobility Product Development Division. As a designer, I primarily work on developing products related to digital transformation (DX) for our company&#39;s retail stores. Lately I’ve been focusing on designing a mobile app, which we plan to release in the fall. Since our team was formed only about a year ago, we are now entering the phase of fully commercializing the project.</p>
<p><strong>Akeda-san:</strong> Thank you. Now, let’s hear from M.K.-san.</p>
<p><strong>M.K.-san:</strong> I work as a designer in the Marketing Planning Division. My main responsibilities include visual design and UI/UX design, and recently, I have been working on the website design for a new vehicle subscription service.</p>
<p><strong>Akeda-san:</strong> Thank you. Now, regarding the Figma study session held last Friday, what was the reason for holding it?</p>
<p><strong>T.M.-san:</strong> It all started when Nakanishi-san casually dropped by and said, “Why don’t you host a Figma study session?” However, even before that, I had felt that there was room for improvement in how we were using Figma at KTC, so it was perfect timing. So, together with M.K., we discussed the best format, and held our first session last Friday.</p>
<p><strong>Akeda-san:</strong> Nakanishi-san, what made you reach out and suggest the study session?</p>
<p><strong>Nakanishi:</strong> Since T.M. joined the company, I felt that he had a strong desire to improve the design.  We had already talked about how it would be great to hold a Figma study session, so I felt that now was the right time and reached out.</p>
<p><strong>Akeda-san:</strong> Thank you. What was the reaction of the participants when you actually held the study session?</p>
<p><strong>T.M.-san:</strong> The response was quite positive. It seemed like many of the participants wanted to learn more about Figma. There were a lot of questions during the Q&amp;A session, and it was clear that everyone was interested.</p>
<p><strong>M.K.-san:</strong> I felt the same way. It was a great opportunity to see that many people are interested in Figma and improving UI/UX.</p>
<p><strong>Akeda-san:</strong> What kind of questions were asked?</p>
<p><strong>T.M.-san:</strong> The questions covered a wide range of topics, from technical aspects to discussions about UI/UX initiatives at KTC. There were especially many questions from engineers, which showed that they, too, have a strong interest in UX.</p>
<p><strong>Akeda-san:</strong> Please tell us about future plans for the study group.</p>
<p><strong>T.M.-san:</strong> In the short term, we aim to improve productivity and efficiency by providing the most relevant themes and content.  In the long term, I want to use Figma as a way to increase the presence of designers and contribute to the success of businesses.</p>
<p><strong>M.K.-san:</strong> I completely agree. We also want to create more opportunities for engineers and designers to develop a common language and communicate with each other.</p>
<p><strong>Akeda-san:</strong> Thank you. Finally, do you have a message for everyone listening to this podcast?</p>
<p><strong>M.K.-san:</strong> Design is not just about visuals. It&#39;s important for everyone to have an opinion. Let’s communicate freely and openly.</p>
<p><strong>T.M.-san:</strong> To create a great product, everyone should think like a designer. Please feel free to reach out to us anytime!</p>
<p><strong>Akeda-san:</strong> Thank you very much. That wraps up today&#39;s podcast. T.M.-san and M.K.-san, thank you for joining us. Your passion for the Figma study session and design really came through. I’m excited to see how KTC&#39;s design continues to evolve.</p>
<p>This time, we have provided details about the Figma study group, the background to its operation, and future prospects. Stay tuned for the next study session!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2024-12-podcast/podcast_figma.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Appium: Creating Automation Source Code Based on Test Specifications]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-18-how-to-write-test-documents-based-on-Appium-source-code-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-18-how-to-write-test-documents-based-on-Appium-source-code-en/</guid>
            <pubDate>Fri, 28 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[How to create Appium automation test source code based on test specifications]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hi, my name is Pann Nu Wai, and I am part of the QA Group at KINTO Technologies. As a Test Automation Specialist for the App team in the QA Group, I am responsible for building and maintaining the test automation environment for the <a href="https://kinto-jp.com/entry_app/">KINTO Kantan Moushikomi App</a>, as well as writing test specifications and test scripts.</p>
<p>Previously, I wrote a technical article about <a href="https://blog.kinto-technologies.com/posts/2022-12-15-DarkModeChangeInAppium/">DarkMode automation testing using Appium</a>.</p>
<p>I have been working with automated testing using Appium for three years. In my team, we follow a process where test specifications are defined first before writing the automated test source code. So, in this post, I’d like to share an approach to creating test specifications that has proven highly useful when writing Java source code for Appium.</p>
<p>Before diving into the details, I’ll first explain why this method of creating test specifications is essential.</p>
<h1>Benefits of Creating Test Specifications</h1>
<ul>
<li>Saves time when writing automated test source code.</li>
<li>Easy to understand even for non-automation testers.</li>
<li>Improves readability by organizing specifications by screen, source code class, and operation function.</li>
</ul>
<p>In this article, we will use the login scenario specification for the KINTO Kantan Moushikomi  App as a sample test specification.</p>
<h3>Login steps</h3>
<p>Here, we will explain each step of logging in to the KINTO Kantan Moushikomi App.</p>
<h5>Step 1</h5>
<p><img src="/assets/blog/authors/pannnu.wai/20241218_1.png" alt="MainPage"></p>
<h5>Step 2</h5>
<p><img src="/assets/blog/authors/pannnu.wai/20241218_2.png" alt="MyPagingPage"></p>
<h5>Step 3</h5>
<p><img src="/assets/blog/authors/pannnu.wai/20241218_3.jpeg" alt="LoginPage"></p>
<h1>Collecting Test Data for Login</h1>
<p>As the first step in creating a login test specification, it is essential to gather test data, including the test environment, test account, password, report name (automatically generated after test execution), and test execution file. Now, let’s begin with the iOS login scenario specification.</p>
<table>
<thead>
<tr>
<th>Data Name</th>
<th>Data Information</th>
</tr>
</thead>
<tbody><tr>
<td>Environment</td>
<td>Stg4</td>
</tr>
<tr>
<td>Test Account</td>
<td>******@gmail.com</td>
</tr>
<tr>
<td>Password</td>
<td>********</td>
</tr>
<tr>
<td>Report Name</td>
<td>For iOS<br/>  iOS.poc.login<br/> For Android<br/>android.poc.login</td>
</tr>
<tr>
<td>Executable Source File</td>
<td>For iOS<br/>  iOS.poc.login.xml<br/> For Android<br/> android.poc.login.xml</td>
</tr>
</tbody></table>
<h1>Login Scenario Specification for iOS</h1>
<table>
<thead>
<tr>
<th>Confirmation Function</th>
<th>Screen</th>
<th>Operation</th>
<th>Points to Check</th>
<th>Executable File (xml)</th>
<th>Source File</th>
<th>Method</th>
</tr>
</thead>
<tbody><tr>
<td>Log in</td>
<td>Main Screen</td>
<td>Click on the My Page logo</td>
<td>The login button can be pressed</td>
<td>iOS.poc.login.xml</td>
<td>iOS.MainPage</td>
<td>clickMyPagingLogo</td>
</tr>
<tr>
<td></td>
<td>My Page Screen</td>
<td>Click on &quot;Application Details&quot;<br/>  Press &quot;Log in here!&quot;</td>
<td></td>
<td></td>
<td>iOS.MyPagingPage</td>
<td>clickApplyTab<br/>  clickLoginHereButton</td>
</tr>
<tr>
<td></td>
<td>Login Screen</td>
<td>Enter the above test account in the &quot;Email address (KINTO ID)&quot; field.<br/>  Enter the above password in the &quot;Login Password&quot; field.<br/>  Click &quot;Log in to My KINTO&quot;</td>
<td></td>
<td></td>
<td>iOS.LoginPage</td>
<td>fillMailAddress<br/>  fillPassword<br/>  clickToMyKintoLoginButton</td>
</tr>
</tbody></table>
<p>Now let’s create a Java class for each screen in the scenario specification. First, click the My Page logo on the main screen (MainPage.java).</p>
<h3>MainPage.java</h3>
<pre><code class="language-java">public class MainPage extends Base {
    public static final String MY_PAGING_LOGO = 
        &quot;//XCUIElementTypeButton[@name=&quot;My Page&quot;]&quot;;
    
    /**
     * Test method for clicking the &quot;My Page&quot; logo on the main screen
     *
     * This method uses XPath to locate the &quot;My Page&quot; logo on the main page
     * and performs a click action.
     * 
     */
    @Test(groups = &quot;MainPage&quot;)
    public void clickMyPagingLogo() {
        driver.findElementByXPath(MY_PAGING_LOGO).click();
    }
}
</code></pre>
<p>Step 2: Click &quot;Application Details&quot; on the My Page screen, then click &quot;Log in here.&quot;</p>
<h3>MyPagingPage.java</h3>
<pre><code class="language-java">public class MyPagingPage extends Base　{
    public static final String APPLY_TAB = 
        &quot;//XCUIElementTypeButton[@name=&quot;Application Details&quot;]&quot;;
    public static final String LOGIN_HERE_BUTTON = 
        &quot;//XCUIElementTypeButton[@name=&quot;Log in to My KINTO&quot;]&quot;;

    /**
     * Test method to click &quot;Application Details&quot; on the My Page screen
     *
     * This method uses XPath to identify the &quot;Application Details&quot; on the My Page screen,
     * and performs a click action.
     * 
     */
    @Test(groups = &quot;MyPagingPage&quot;, dependsOnGroups = &quot;MainPage&quot;)
    public void clickApplyTab() {
        driver.findElementByXPath(APPLY_TAB).click();
    }

    /**
    * A test method to click &quot;Log in here&quot; on the My Page screen
    *
    * This method uses XPath to identify &quot;Log in here&quot; on the My Page screen,
    * and performs a click action.
    * 
    */
    @Test(groups = &quot;MyPagingPage&quot;, dependsOnMethods = &quot;clickApplyTab&quot;)
    public void clickLoginHereButton() {
        driver.findElementByXPath(LOGIN_HERE_BUTTON).click();
    }
}
</code></pre>
<p>Next, in step 3, enter your email address and password on the Login Screen, then press the Login button.</p>
<h3>LoginPage.java</h3>
<pre><code class="language-java">public class LoginPage extends Base {
    public static final String EMAIL_TEXT_FIELD = 
        &quot;//XCUIElementTypeApplication[@name=&quot;KINTO Easy Application&quot;]/XCUIElementTypeOther[2]/XCUIElementTypeTextField&quot;;
    public static final String PASSWORD_TEXT_FIELD = 
        &quot;//XCUIElementTypeApplication[@name=&quot;KINTO Easy Application&quot;]/XCUIElementTypeOther[3]/XCUIElementTypeSecureTextField&quot;;
    public static final String ENTER_KEY = 
        &quot;//XCUIElementTypeButton[@name=&quot;Return&quot;]&quot;;
    public static final String TO_MY_KINTO_LOGIN_BUTTON = 
        &quot;//XCUIElementTypeButton[@name=&quot;Log in to My KINTO&quot;]&quot;;

    /**
    * A test method to enter a test account in the &quot;Email address (KINTO ID)&quot; field on the login screen
    *
    * This method uses XPath to search for the &quot;email address&quot; on the login screen
    * and enter the test account (Parameter) in the xml file.
    * 
    */
    @Parameters(&quot;email&quot;)
    @Test(groups= &quot;LoginPage&quot;, dependsOnGroups = &quot;MyPagingPage&quot;)
    public void fillMailAddress(String email) {
        driver.findElementByXPath(EMAIL_TEXT_FIELD).click();
        driver.getKeyboard().sendKeys(email);
    }

　　/**
    * A test method to enter a password in the &quot;Login Password&quot; field on the login screen.
    *
    * This method uses XPath to get the login password from the login screen.
    * and enter the password (Parameter) for the xml file.
    * 
    */
    @Parameters(&quot;password&quot;)
    @Test(groups= &quot;LoginPage&quot;, dependsOnGroups = &quot;MyPagingPage&quot;)
    public void fillPassword(String password) {
        driver.findElementByXPath(PASSWORD_TEXT_FIELD).click();
        driver.getKeyboard().sendKeys(password);
        driver.findElementByXPath(ENTER_KEY).click();
    }

    /**
    * A test method to click &quot;Log in to My KINTO&quot; on the login screen
    *
    * This method uses XPath to identify &quot;Log in to My KINTO&quot; on the login screen,
    * and performs a click action.
    * 
    */
    @Test(groups= &quot;LoginPage&quot;, dependsOnGroups = &quot;MyPagingPage&quot;)
    public void clickToMyKintoLoginButton() {
        driver.findElementByXPath(TO_MY_KINTO_LOGIN_BUTTON).click();
    }
}
</code></pre>
<p>The xml file below is the test execution file for automated testing. Each function is written in sequence based on the test specification.</p>
<pre><code class="language-xml">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE suite SYSTEM &quot;http://testng.org/testng-1.0.dtd&quot;&gt;
&lt;suite name=&quot;iOS.poc.login&quot;&gt;
    &lt;test verbose=&quot;2&quot; name=&quot;iOS.poc.login&quot;&gt;
        &lt;classes&gt;
            &lt;class name=&quot;iOS.MainPage&quot;&gt;
                &lt;methods&gt;
                    &lt;include name=&quot;clickMyPagingLogo&quot;/&gt;
                &lt;/methods&gt;
            &lt;/class&gt;
            &lt;class name=&quot;iOS.MyPagingPage&quot;&gt;
                &lt;methods&gt;
                    &lt;include name=&quot;clickApplyTab&quot;/&gt;
                    &lt;include name=&quot;clickLoginHereButton&quot;/&gt;
                &lt;/methods&gt;
            &lt;/class&gt;
            &lt;class name=&quot;iOS.LoginPage&quot;&gt;
                &lt;methods&gt;
                    &lt;parameter name=&quot;email&quot; value=&quot;******.gmail.com&quot;/&gt;
                    &lt;parameter name=&quot;password&quot; value=&quot;*********&quot;/&gt;
                    &lt;include name=&quot;fillMailAddress&quot;/&gt;
                    &lt;include name=&quot;fillPassword&quot;/&gt;
                    &lt;include name=&quot;clickToMyKintoLoginButton&quot;/&gt;
                &lt;/methods&gt;
            &lt;/class&gt;
        &lt;/classes&gt;
    &lt;/test&gt;
&lt;/suite&gt;
</code></pre>
<h2>Summary</h2>
<p>In this article, we&#39;ve outlined how to create test specifications for Appium Java source code. However, we believe this approach will be valuable not only for those working with Appium but also for developing automated test source code across different frameworks.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/pannnu.wai/appium_coverimage.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Beyond KPT: Choosing the Right Retrospective at the Right Time]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-17_retrospective-methods-other-than-KPT-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-17_retrospective-methods-other-than-KPT-en/</guid>
            <pubDate>Thu, 27 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Beyond KPT: Choosing the Right Retrospective at the Right Time]]></description>
            <content:encoded><![CDATA[<h1>Self-Introduction</h1>
<p>Hello. This is Koyama (<a href="https://x.com/_koyasoo">@_koyasoo</a>) from KINTO Technologies. Since the beginning of this year, I&#39;ve been dedicated to promoting Agile practices and working full-time as a Scrum Master, supporting my team in growing stronger every day. Today, I’d like to share some of the things we’ve been working on.</p>
<h1>Speaking of retrospectives...</h1>
<p>Do you hold them regularly?</p>
<p>When it comes to retrospectives, the KPT (Keep/Problem/Try) format often comes to mind. It’s practically synonymous with the practice. KPTs are widely used in many workplaces, but is it actually effective? </p>
<p>Back in June, I attended Scrum Fest Osaka, and out of all the sessions I joined, one left a particularly strong impression on me.</p>
<p><strong>OODA!!!!!!</strong> (If you know, you know)</p>
<p>Yep, it’s Ikuo (<a href="https://x.com/dora_e_m">@dora_e_m</a>) san. Ikuo san’s session on retrospectives really left an impression on me. Until then, I had only used KPT for retrospectives, so when I heard the words, <strong>&quot;To keep retrospectives meaningful, we need to avoid falling into a rut,&quot;</strong> they really hit home. That made me think, &quot;I do retrospectives all the time, I should be able to apply this right away.&quot; Or so I thought.</p>
<p>@<a href="https://speakerdeck.com/kakehashi/retrospectives-with-ooda">card</a></p>
<p>As mentioned on page 22 of the session deck, it was eye-opening to see how simply switching from KPT to YWT (which is quite similar) led to a flood of opinions. It’s amazing how just shifting your perspective can bring out so many ideas...</p>
<p><a href="https://speakerdeck.com/kakehashi/retrospectives-with-ooda?slide=22"><img src="/assets/blog/authors/tkoyama/retro/ikuo-22.png" alt="Quoted from page 22 of Ikuo san’s document"></a><br><em>Quoted from page 22 of Ikuo san’s document</em></p>
<p>So in this article, I’ll be sharing five retrospective techniques that I’ve actually tried out since then.</p>
<h1>Summary of How to Choose the Right Retrospective Method</h1>
<p>As Ikuo san mentioned in his session, switching up between different methods to fit the situation can really help a team grow and improve. Below is a summary of each method’s key features; feel free to use it as a reference.</p>
<table>
<thead>
<tr>
<th></th>
<th>When to Use It!</th>
<th>Things to Keep in Mind</th>
</tr>
</thead>
<tbody><tr>
<td>KPT</td>
<td>A versatile method that works anytime.</td>
<td>Just be careful not to rely on it too much.</td>
</tr>
<tr>
<td>Hot Air Balloon</td>
<td>Great for thinking about your team’s future.</td>
<td>Puts more focus on current challenges rather than reflecting on the past.</td>
</tr>
<tr>
<td>LeanCoffee</td>
<td>Great for discussing various topics, not just retrospectives.</td>
<td>Can be a bit tiring since discussions are held under time pressure.</td>
</tr>
<tr>
<td>Celebration Grid</td>
<td>Ideal for fact-based discussions.</td>
<td>Hard to generate opinions when there’s little actual implementation and more personal impressions.</td>
</tr>
<tr>
<td>FunDoneLearn</td>
<td>When you want to reflect on the positives.</td>
<td>Negatives often get overlooked.</td>
</tr>
<tr>
<td>Elephants, dead fish, and vomit</td>
<td>When the team seems to be building up frustration.</td>
<td>Facilitating to keep the team from falling apart.</td>
</tr>
</tbody></table>
<p>Next, let’s dive into each retrospective method in detail.</p>
<h1>Let’s explore retrospectives!</h1>
<p>I hope this article encourages you (especially if you’ve only used KPT so far) to <strong>take the first step in trying a different approach!</strong> With that in mind, I’ll walk you through how to put it into practice with as much detail as possible. Feel free to use whatever works best for you. Just give it a try, you might be surprised. It’s actually not that different from KPTs!</p>
<p>By the way, the examples in this article mainly use the online whiteboard tool <a href="https://miro.com/blog/">Miro</a>.</p>
<p>@<a href="https://miro.com/blog/">card</a></p>
<h2>1. Hot Air Balloon</h2>
<p>This reflection method involves replacing the &quot;hot air balloon&quot; in the center with your own product and thinking about what kind of &quot;baggage&quot; it carried, what &quot;updrafts&quot; helped it rise, and what &quot;clouds&quot; might become obstacles in the future.</p>
<p>All you need is an image of a hot air balloon and three types of sticky notes to differentiate the categories. Here’s the hot air balloon our team came up with.</p>
<p><img src="/assets/blog/authors/tkoyama/retro/netsukikyu.png" alt="Diagram of a hot air balloon"><br><em>Diagram of a hot air balloon</em></p>
<p>Here’s how it goes:</p>
<ol>
<li>First, we spent 5 minutes writing about &quot;updrafts&quot;, followed by an 8-minute discussion.</li>
<li>Next, we did the same with &quot;luggage&quot;—writing for 5 minutes, then followed by an 8-minute discussion.</li>
<li>Then, we repeated the process with &quot;clouds&quot;—5 minutes of writing, followed by an 8-minute discussion.</li>
<li>Finally, we wrapped up with a 10-minute discussion on the question: &quot;What’s important for making a hot air balloon fly higher?&quot; The discussion took place from this perspective (10 minutes).</li>
</ol>
<p>This method naturally encourages discussions that reflect on the present and envision the future. Compared to KPT, it breaks down problems into current issues and anticipated challenges, making discussions more focused and effective.</p>
<h2>2. LeanCoffee</h2>
<p>Lean Coffee is a method that starts with gathering topics, then breaks them into short time-boxed discussions to carry out various conversations about them.</p>
<p>You can make this work by setting up an area where people can add and edit sticky notes for topics and another area to process selected notes one by one for discussion. Miro had a template for this, so I gave it a try.</p>
<p><img src="/assets/blog/authors/tkoyama/retro/leancoffee.png" alt="LeanCoffee Diagram"><br><em>LeanCoffee Diagram</em></p>
<p>Here’s how it works.</p>
<ol>
<li>First, participants will come up with a topic (8 minutes). Giving them a suggested theme can help spark ideas and make it easier to share opinions.</li>
<li>Use features like polling to find out which topics interest the group the most.</li>
<li>Discussions will follow the cycle below, starting with the topic that gets the most votes.<ul>
<li>Each discussion begins with 5 minutes, including time to introduce the topic.</li>
<li>After 5 minutes, the conversation will pause. At that point, ask participants if they’d like to continue discussing the topic. You can use a poll to decide.</li>
<li>If they want to continue, add 3 more minutes. If not, move on to the next topic.</li>
<li>After those 3 minutes, pause again and check if they’d like to keep going.</li>
<li>If they want to continue, add 1 more minute. If not, move on to the next topic.</li>
<li>Once the final minute is up, that topic wraps up. If they want to keep the conversation going, set aside extra time for it and wrap up the discussion within that time.</li>
</ul>
</li>
</ol>
<p>It also gives your insight into the current interests and trends among team members. You can also see trends in the interests of your team members at any given time. Plus, it might help reinforce awareness of timeboxing among the team.</p>
<p>Facilitating discussions can make it tricky to step in and stop them, so try using a timer or pausing at natural breaks in the conversation. Be mindful that this method relies on sticking to the timebox—if not, it could fall apart.</p>
<h2>3. Celebration Grid</h2>
<p>This method involves discussing completed actions by dividing them into six quadrants based on two axes: one for &quot;success&quot; and &quot;failure&quot;, and another for &quot;wrong ways&quot;, &quot;experimental ways&quot;, and &quot;known ways&quot;. As the name suggests, the focus is on keeping a positive mindset—celebrating every outcome, whether it’s a success or a failure.</p>
<p>It seems that people often use the diagrams from this site as a guide.</p>
<p>@<a href="https://management30.com/practice/celebration-grids/">card</a></p>
<p><img src="/assets/blog/authors/tkoyama/retro/celebration-grid-completed.jpg" alt="CelebrationGrid template"><br><em>CelebrationGrid template</em></p>
<p>As shown in the diagram, each area varies in size based on the likelihood of an event occurring, and they carry the following meanings: The discussion followed these categories.</p>
<table>
<thead>
<tr>
<th></th>
<th>Wrong way</th>
<th>Experimental way</th>
<th>Known Way</th>
</tr>
</thead>
<tbody><tr>
<td>Success</td>
<td>Lucky!</td>
<td>It was a great experience!</td>
<td>You did the right thing!</td>
</tr>
<tr>
<td>Failure</td>
<td>It was inevitable.</td>
<td>It’s ok, there was a lesson in it.</td>
<td>Unlucky</td>
</tr>
</tbody></table>
<p><img src="/assets/blog/authors/tkoyama/retro/celebrationgrid.png" alt="Celebration Grid in action"><br><em>Celebration Grid in action</em></p>
<p>Here’s how it works.</p>
<ol>
<li>Ask participants to specify a time period and list &quot;what they have done&quot; (5 minutes). Guide them to consider where each item belongs as they list them.</li>
<li>Take a deeper dive into each one.</li>
<li>Wrap up by celebrating the many insights gained.</li>
</ol>
<p>At first glance, this method may seem complicated, but it&#39;s actually quite simple. Since discussions are based on actual “events and facts&quot;, participants can stay grounded and discuss things without personal biases. However, because participants must first list &quot;facts&quot;, some may find it harder to express their opinions. To make the process smoother, it’s best to include people who have been actively involved in the work.</p>
<h2>4. FunDoneLearn</h2>
<p>As the name suggests, this method involves listing Fun (what was enjoyable), Done (what was accomplished), and Learn (what was learned.) Write down the elements that fit into each category where the circles overlap.</p>
<p>You can use a template like a Venn diagram with overlapping circles to make organizing easier. Making the overlapping areas larger will give more space for sticky notes and make them easier to place.</p>
<p><img src="/assets/blog/authors/tkoyama/retro/fundonelearn.png" alt="FunDoneLearn Diagram"><br><em>FunDoneLearn Diagram</em></p>
<p>It’s not something that needs a detailed explanation, but here’s how to do it:</p>
<ol>
<li>Set a time limit and have participants put sticky notes (5 minutes).</li>
<li>Discuss with each one.</li>
</ol>
<p>This method keeps the focus positive, incorporating an element of Fun. This approach helps create a generally positive and happy atmosphere for the review. On the other hand, since it focuses less on negative aspects like Problem, it may not be the best fit if there are many issues to address.</p>
<h2>5. Elephants, dead fish and vomit</h2>
<p>This method helps identify issues from three different angles. Members are encouraged to openly discuss things they might normally hesitate to say, categorized as follows:</p>
<ul>
<li><strong>Elephants</strong> – Issues that everyone is aware of.</li>
<li><strong>Dead Fish</strong> – Issues that could cause trouble if left unaddressed.</li>
<li><strong>Vomit</strong> – Issues that are on one&#39;s mind.</li>
</ul>
<p>You can facilitate this exercise using a simple diagram with an elephant, a fish, and vomit. However, to prevent personal conflicts among members, it’s a good idea to clearly outline the ground rules in a visible way.</p>
<p><img src="/assets/blog/authors/tkoyama/retro/zoushindasakanaouto.png" alt="Drawing of an elephant, dead fish, and vomit"><br><em>Drawing of an elephant, dead fish, and vomit</em></p>
<p>Here’s how it works.</p>
<ol>
<li>First, start by explaining the rules. Make it clear that the goal of this method is not to create conflict within the team, but to come up with ways to address existing issues. This is a key point to keep in mind when using this method.</li>
<li>Ask participants to write thoughts with opinions on sticky notes that fit each category (8 minutes). In my team, to keep the discussion from becoming too negative, we encouraged participants to add a pink sticky note if they wanted to reframe an issue into a positive perspective.</li>
<li>With this method, seeing other’s notes while brainstorming might sometimes cause discomfort. To prevent this, we make sure that sticky notes in progress are not visible to others. If you are using Miro, enabling Private Mode is a good idea.</li>
<li>Once ready, disable Private Mode and start discussing each topic.</li>
</ol>
<p>Since this method involves addressing negative aspects, it requires a bit more sensitivity compared to other approaches. That said, the atmosphere doesn’t tend to feel too negative. Comments like &quot;Oh, so that’s what you were worried about!&quot; or &quot;I was thinking the same thing!&quot; are likely to come up. This method is highly effective in aligning the team’s approach to problem-solving.</p>
<h1>What Applies to Any Retrospective</h1>
<p>Having conducted six retrospectives, including KPT, I’ve noticed that there are more commonalities than you might expect.</p>
<ul>
<li><strong>The ultimate goal is always to &quot;agree on the next action as a team&quot;.</strong></li>
<li>Writing the rules in large letters helps prevent confusion.</li>
<li>It’s totally fine to share opinions during the session! Time guidelines: 5 minutes for basic topics, 8–10 minutes for deeper discussions.</li>
<li>Discussion flow: First, explain the sticky notes, then either &quot;share your own thoughts&quot; or &quot;randomly ask someone who might have an opinion&quot;. (Keeping it casual makes it easier for everyone to speak up. Haha!)</li>
<li>If you’re also sharing your own thoughts, preparing your sticky notes in advance helps you stay focused on facilitating.</li>
</ul>
<p>Most importantly, as long as everyone is aligned on &quot;agreeing on the next action&quot;, any method will work. Once that’s decided, the retrospective will be worthwhile. You can almost forget about everything else.</p>
<h1>Feedback from Participants</h1>
<p>I always feel a bit anxious after implementing a new retrospective method with new members. Would KPT have been just fine...? Did I give too many instructions, leaving little time for actual reflection...?</p>
<p>If you ever feel this way (like I do), don’t hesitate to ask your team for feedback! You’ll probably hear nothing but positive responses.</p>
<ul>
<li>It was refreshing to do a different kind of retrospective. It was fun. (Hot Air Balloon)</li>
<li>We were able to talk while being conscious of the time box, so we were able to talk about a variety of topics, which is good as we usually end up talking about the same thing. The issues became clear. (LeanCoffee)</li>
<li>We made a lot of mistakes, but this helped us distinguish between good mistakes and bad ones. (Celebration Grid)</li>
<li>It was great to understand what makes my team members enjoy their work. I liked that we could simply share fun experiences. (FunDoneLearn)</li>
<li>I’m glad that the issues I had in mind became a shared understanding within the team. I appreciate how direct and open the discussion was. (Elephants, dead fish, vomit)</li>
</ul>
<h1>Summary</h1>
<p>Rather than sticking to just one method, teams can grow stronger by choosing and applying the retrospective format that best fits the situation. I’ve only tried six so far, but I&#39;m excited to explore even more!</p>
<p>As mentioned at the beginning, if you’ve only used KPT, I highly encourage you to try others too. Start by following the steps outlined in this article. Once you’re comfortable, why not tweak and adapt approaches to better fit your team?</p>
<p>I’d be happy if this article helps Scrum Masters who are looking for better ways to run retrospectives.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/tkoyama/retro/brands-people-RPPdQVp-nds-unsplash.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[KINTOテクノロジーズの「一体感」を作るノベルティ制作の舞台裏]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025_03_21_Novelty-Design/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025_03_21_Novelty-Design/</guid>
            <pubDate>Thu, 27 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[社員のコミュニケーションを促進するノベルティ制作の舞台裏を公開！]]></description>
            <content:encoded><![CDATA[<p>こんにちは！KINTOテクノロジーズのクリエイティブ室でデザイナーをしているmayuです。
私は普段、アプリのUI/UXデザインをメインで担当していますが、今回は会社のイベントで社員に配布するノベルティの制作を手掛けました。
この記事では、ノベルティ制作の企画からデザインまでの舞台裏をお話しします。
ノベルティ制作に携わる方々のヒントになれば嬉しいです。</p>
<h1>ノベルティの選定</h1>
<h4><strong>テーマは「一体感を感じられるもの」</strong></h4>
<p>今回のイベントでは「一体感を感じられるもの」を大前提としたノベルティを目指しました。
そこで、以下のような条件をもとにアイデアを出しました。</p>
<ul>
<li>普段話さない人とコミュニケーションを取るきっかけになる</li>
<li>団結意識を高める</li>
<li>イノベーションを促進する</li>
<li>年齢・性別を問わず、誰でも嬉しいもの</li>
<li>複数人のニーズがある</li>
<li>誰でもすぐ利用できる手軽さ</li>
<li>予算は数百円〜千円程度/人</li>
<li>長く価値が続く</li>
</ul>
<p>さまざまな案が出ましたが、最終的に「<strong>マグネットカードスタンド</strong>」と「<strong>オリジナルネームカード</strong>」を制作することに決定しました。</p>
<h1>「マグネットカードスタンド」と「ネームカード」を選んだ理由</h1>
<h4><strong>マグネットカードスタンド:</strong></h4>
<ul>
<li>デスクに置いて使うことで、自然と声をかけやすくなりコミュニケーションの促進につながる。</li>
<li>KINTOテクノロジーズのロゴや車の形を取り入れることで、会社への愛着やモチベーション向上が期待できる。</li>
<li>シンプルなデザインで、日常的に使いやすく、誰でも活用できる。</li>
</ul>
<h4><strong>ネームカード:</strong></h4>
<ul>
<li>社員一人一人に名前を入れたネームカードを作ることで、初対面でも話しかけやすく、社内の交流を促進。</li>
<li>「見切れKTCデザイン」によって、会社全体としての一体感をデザインで表現。</li>
<li>当日は名札として、イベント後はデスクに置いて使い続けられる。</li>
</ul>
<h1>マグネットカードスタンドの制作</h1>
<h4><strong>1. 業者選定と依頼</strong></h4>
<p>マグネットカードスタンドの制作は、オリジナルグッズ専門サイト「<a href="https://novelty-moku.com/product/jb278/">MOKU</a>」に依頼しました。
MOKUはカスタマイズ性が高く、デザインデータを入稿するだけでオリジナルのマグネットカードスタンドが作れることが決め手でした。</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/moku.jpg" alt="moku">
<br></p>
<h4><strong>2. プロトタイピング</strong></h4>
<p>紙を使って簡易的なプロトタイプを試作し、サイズ感や使い勝手を確認しました。
実際のデスクに置いてみて、視認性や実用性を検証します。</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/prototyping.jpg" alt="prototyping">
<br></p>
<h4><strong>3. マグネットカードスタンドのデザイン作成</strong></h4>
<p>Adobe Illustratorを使って、規定のテンプレートにロゴを配置したデザインデータを作成。
KINTOテクノロジーズのロゴが際立つシンプルなデザインに仕上げました。</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/stand_design-1.jpg" alt="stand_design-1">
<br></p>
<h4><strong>4. 取扱説明書のデザイン作成</strong></h4>
<p>使い方が分かりやすいように、オリジナルの取扱説明書も作成しました。
こちらもAdobe Illustratorを使って、規定のテンプレートに沿ってデザインデータを作成。</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/stand_design-2.jpg" alt="stand_design-2">
<br></p>
<h4><strong>5. データ入稿＆納品</strong></h4>
<p>デザインデータを入稿し、約3週間で納品されました！(数量は500個で発注)</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/card-stand.jpg" alt="card-stand"></p>
<h1>ネームカードの制作</h1>
<h4><strong>1. ネームカードのデザイン作成</strong></h4>
<p>Figmaで名前、部署、そしてSlackで使っているアイコンを配置したオリジナルデザインを作成。
マグネットカードスタンドとサイズが合うよう、プロトタイプを作って確認しました。</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/card_design-1.jpg" alt="card_design-1">
<br></p>
<p>こだわりポイントは、この「<strong>見切れKTC</strong>」。KTCは「KINTOテクノロジーズ」の略称です。
並んでいる小さな四角は社員を表現しており、「社員一人一人が集まってKTCになる」という意味を込めました。
黒を基調としたシンプルでスタイリッシュなデザインで、テックカンパニーらしさも演出しました。</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/card_design-2.jpg" alt="card_design-2">
<br></p>
<h4><strong>2. データの自動生成</strong></h4>
<p>全員分のデータを手作業で作るのは大変なので、社内のエンジニアさんに協力してもらい HTMLでデータを自動生成しました。
CSVファイルから社員情報を取り込み、テンプレートに自動反映させる仕組みを構築しました。</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/html.jpg" alt="html">
<br></p>
<h4><strong>3. 印刷・カット作業</strong></h4>
<p>会社のプリンターで印刷し、ひたすら手作業でカット。
大変でしたが、その分かなりのコストカットに成功しました！笑</p>
<p><img src="/assets/blog/authors/mayu_jibu/250321/cutting.jpg" alt="cutting"></p>
<h1>プロジェクトの結果と学び</h1>
<p>ノベルティ配布後、社員の皆さんからは以下のような嬉しいフィードバックをいただきました。</p>
<ul>
<li>「声をかけやすくなった！」</li>
<li>「デザインが可愛い！」</li>
<li>「Slackのアイコンが入っているので愛着がわく！」</li>
</ul>
<p>ノベルティを通じて一体感が生まれ、私自身も大きなやりがいを感じました。
また、「ただデザインするだけではなく、どう使われるか？」を考えながら制作する大切さを改めて実感しました。
目的に沿ったデザインの力 を発揮できたと思います。</p>
<h1>さいごに</h1>
<p>今回のプロジェクトを通じて得た学びを、これからのデザイン業務にも活かしていきたいと思います。
もし「KINTOテクノロジーズって楽しそう！」と感じていただけたら、ぜひ<a href="https://www.kinto-technologies.com/recruit/">採用ページ</a>もご覧ください！お話しできるのを楽しみにしています。
ご覧いただき、ありがとうございました！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/mayu_jibu/250321/main.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Integration of Flutter Apps with Native Features – Our Approach to Adding an Android-Specific Camera Analysis Library]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-17-flutter-platform-view-android-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-17-flutter-platform-view-android-en/</guid>
            <pubDate>Wed, 26 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This story is about our experience integrating Android native UI and features into a Flutter app.]]></description>
            <content:encoded><![CDATA[<h1>Integrating Native Features into Flutter Apps – Our Approach to Adding an Android-Specific Camera Analysis Library</h1>
<p>Hello. My name is Osugi, and I’m part of the Toyota Woven City Payment development group.</p>
<p>Our team develops the payment system used in <a href="https://woven.toyota/en">Woven by Toyota</a>’s <a href="https://www.woven-city.global/">Toyota Woven City</a>, covering a wide range of payment-related functions, from backend to Web frontend and mobile applications.</p>
<p>So far, we’ve been using Flutter to develop a mobile app for Proof of Concept (PoC).  In this article, we have summarized the trial and error we went through to overcome the challenges we faced when developing new functions by incorporating a new camera analysis library that is only available natively on Android/iOS into the PoC app.</p>
<h2>Introduction</h2>
<p>Integrating native functions into a Flutter app doesn’t just add to the development workload—it also increases maintenance costs, making development more challenging.</p>
<p>In our project, considering the development timeline and available resources, we chose not to integrate native functions directly into the Flutter app. Instead, we developed a separate PoC app and a native app for camera analysis, linking them together to carry out the PoC. After completing the PoC, when we considered integrating the Flutter app with the camera analysis app, we found that the information on design guidelines and implementation methods for Flutter&#39;s native linking function was fragmented, and we felt that there were few systematic guidelines, especially for Android&#39;s complex UI configuration.</p>
<p>In this article, we’ll focus on Android and share design principles and practical methods for incorporating native UI into a Flutter app.</p>
<p>Hopefully, this will be helpful for engineers facing similar challenges.</p>
<p>:::message</p>
<p>At the time of writing, the sample code was created using <code>Flutter v3.24.3 / Dart v3.5.3</code></p>
<p>:::</p>
<h2>App Overview</h2>
<p>For the purposes of this article, we’ve simplified the app developed during the actual PoC. The app follows these specifications:</p>
<p><strong>Specifications</strong></p>
<ul>
<li>When you press the start button, the camera preview will be displayed.</li>
<li>The camera analysis function runs on the preview image, and the analysis results are sent as notifications.</li>
</ul>
<p><img src="/assets/blog/authors/osugi/20241217/camera_preview.gif" alt="Demo showing camera preview and analysis notification in a flutter app "></p>
<p>In this article, I would like to talk about this app.</p>
<h2>Data Integration Between Flutter and Native Android</h2>
<p>We implemented data exchange between Flutter and Android native using <a href="https://api.flutter.dev/flutter/services/MethodChannel-class.html">MethodChannel</a> and <a href="https://api.flutter.dev/flutter/services/EventChannel-class.html">EventChannel</a>, enabling camera control from Flutter and analysis result notifications from Android native.</p>
<p><code>MethodChannel</code> is used for commands like starting and stopping the camera, while <code>EventChannel</code> is used for sending analysis result notifications.</p>
<p>The sequence diagram below illustrates this process:</p>
<pre><code class="language-mermaid">
sequenceDiagram
  actor u as User
  participant f as Flutter
  
  participant mc as MethodChannel
  participant ec as EventChannel

  participant an as Android Native

  u -&gt;&gt; f: press start button
  activate f
  f -&gt;&gt; mc: start camera
  mc -&gt;&gt; an: set up camera
  an --&gt;&gt; mc: 
  mc --&gt;&gt; f: result
  deactivate f

  loop 
    an -&gt;&gt; ec: analyzed result
    ec -&gt;&gt; f: send analyzed data
    f -&gt;&gt; f: show data
  end

  u -&gt;&gt; f: press stop button
  activate f
  f -&gt;&gt; mc: stop camera
  mc -&gt;&gt; an: reset camera
  an --&gt;&gt; mc: 
  mc --&gt;&gt; f: result
  deactivate f
</code></pre>
<p>Next, I would like to talk about how to display the Android native camera preview UI on the Flutter side.</p>
<h2>How to display native Android UI in a Flutter app</h2>
<p>There are three main ways to display native Android UI in a Flutter app:</p>
<ol>
<li><p><a href="https://api.flutter.dev/flutter/widgets/Texture-class.html">Texture</a> widget – Displays an image rendered on an Android native Surface within the Flutter Widget tree.</p>
</li>
<li><p><a href="https://docs.flutter.dev/platform-integration/android/platform-views">PlatformView</a> – Embeds, displays, and controls Android native UI inside the Flutter widget tree.</p>
</li>
<li><p><a href="https://developer.android.com/guide/components/intents-common">Intent</a> – Launches a new Activity.</p>
</li>
</ol>
<p>We’ll go over the characteristics of each method and how to implement them.</p>
<h3>Texture Widget</h3>
<p>The <code>Texture</code> widget displays an image rendered on an Android native Surface within the Flutter Widget tree. In other words, it allows Flutter to draw native UI images directly to the GPU.</p>
<p>This approach works well for use cases where latency isn’t a major concern, such as camera previews and video playback. However, for UI animations requiring real-time performance, adjustments must be made on the native side. This means a solid understanding of both Flutter and Android native development is necessary.</p>
<p>Additionally, the <code>Texture</code> widget itself does not detect user interactions like touch events, so this must be handled on the Flutter side using <code>GestureDetector</code> or similar.</p>
<p>That said, if it aligns with your requirements, it can be implemented relatively easily using the approach shown below.</p>
<h4>Implementation Steps</h4>
<p>First, obtain <code>TextureRegistry</code>. For Flutter apps, use <a href="https://api.flutter.dev/javadoc/io/flutter/embedding/engine/renderer/FlutterRenderer.html">FlutterEngine.FlutterRenderer</a> ,which implements TextureRegistry . For Flutter plugins, retrieve it from FlutterPluginBinding.</p>
<pre><code class="language-kotlin">
// For Flutter apps
val textureRegistry = this.flutterEngine.renderer

// For Flutter plugin
val textureRegistry = this.flutterPluginBinding.textureRegistry
</code></pre>
<p>Next, create a <code>textureEntry</code>, which is a <a href="https://developer.android.com/reference/android/graphics/SurfaceTexture">SurfaceTexture</a>, from the <code>textureRegistry</code>, then set up a <code>SurfaceProvider</code> to provide a <code>Surface</code> to the CameraX preview instance. Once this is done, you’re all set. This <code>Surface</code> acts as the drawing buffer mentioned earlier.</p>
<pre><code class="language-kotlin">
val textureEntry = textureRegistry.createSurfaceTexture()
val surfaceProvider = Preview.SurfaceProvider { request -&gt;
    val texture = textureEntry?.surfaceTexture()
    texture?.setDefaultBufferSize(
        request.resolution.width,
        request.resolution.height
    )

    val surface = Surface(texture)
    request.provideSurface(surface, cameraExecutor) { }
}

val preview = Preview.Builder().build().apply {
    setSurfaceProvider(surfaceProvider)
}

// To meet the requirements for camera analysis mentioned at the beginning of the article,
// this can be achieved by setting up a cameraProvider and configuring the Preview and analysis processing for the camera.
try {
    camera = cameraProvider?.bindToLifecycle(
        this,
        CameraSelector.DEFAULT_BACK_CAMERA,
        preview,
        analysis, // Set the camera image analysis process here
    )
} catch(e: Exception) {
    Log.e(TAG, &quot;Exception!!!&quot;, e)
}
</code></pre>
<p>Then, simply return the ID of the <code>TextureEntry</code> associated with the <code>Surface</code> to Flutter as the return value of <code>MethodChannel</code>.</p>
<pre><code class="language-kotlin">
fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
    when(call.method) {
        &quot;startCamera&quot; -&gt; {
            result.success(textureEntry.id())
        }
        &quot;stopCamera&quot; -&gt; {
            stopCamera()
        }
        else -&gt; result.notImplemented()
    }
}
</code></pre>
<p>To render a native <code>SurfaceTexture</code> on the Flutter side, simply set the textureId obtained from <code>MethodChannel</code> to the <code>Texture</code> widget, and the camera preview will appear in the Flutter app.</p>
<pre><code class="language-dart">
static const platform =
      MethodChannel(&#39;com.example.camera_preview_texture/method&#39;);

int? _textureId;

Future&lt;void&gt; onPressed() async {
  try {
    final result = await platform.invokeMethod&lt;int&gt;(&#39;startCamera&#39;);
    if (result != null) {
      setState(() {
        _textureId = result;
      });
    }
  } on PlatformException catch (e) {
    print(e.message);
  }
}

Widget build(BuildContext context) {
  if (_textureId == null) {
    return const SizedBox();
  }

  return SizedBox.fromSize(
    size: MediaQuery.of(context).size,
    child: Texture(
      textureId: _textureId!,
    ),
  );
}
</code></pre>
<p>For an implementation using the <code>Texture</code> widget, the <a href="https://github.com/juliansteenbakker/mobile_scanner">mobile_scanner</a> serves as a great reference.</p>
<h3>PlatformView</h3>
<p><code>PlatformView</code> allows embedding Android native UI into Flutter’s widget tree, making it possible to display and control it.</p>
<p>There are three rendering modes for <code>PlatformView</code>: <strong>Virtual Display</strong> (<code>VD</code>), <strong>Hybrid Composition</strong> (<code>HC</code>), and <strong>TextureLayerHybridComposition</strong> (<code>TLHC</code>)[^1]. When using the <code>PlatformView</code> API, <code>TLHC</code> is selected by default. However, if the Android native UI tree contains <code>SurfaceView</code>, it will fall back to <code>VD</code> or <code>HC</code>[^2].</p>
<p>In addition, <code>Texture</code> improves frame rate synchronization between Flutter and Android native, which was not possible with the Texture widget. It also allows user interaction control and supports displaying UI elements beyond just camera previews and videos.</p>
<h4>Implementation Steps</h4>
<p>In this sample code using <code>PlatformView</code>, the camera preview screen is implemented with Jetpack Compose.</p>
<p>To use Jetpack Compose in a Flutter app, add the following dependencies and configuration to <code>app/build.gradle</code>:</p>
<pre><code class="language-kotlin:build.gradle">
android {
    
    ~
    ~

    buildFeatures {
        compose true
    }

    composeOptions {
        kotlinCompilerExtensionVersion = &quot;1.4.8&quot;
    }
}

dependencies {
    implementation(&quot;androidx.activity:activity-compose:1.9.3&quot;)
    implementation(platform(&quot;androidx.compose:compose-bom:2024.04.01&quot;))
    implementation(&quot;androidx.compose.material3:material3&quot;)
}
</code></pre>
<p>Now, let’s dive into the details of the implementation.</p>
<p>Implementing <code>PlatformView</code> requires the following three steps:</p>
<ol>
<li>Implement NativeView that inherits <code>PlatformView</code></li>
<li>Implement NativeViewFactory that inherits <code>PlatformViewFactory</code></li>
<li>Register <code>PlatformViewFactory</code> to <code>FlutterEngine</code></li>
</ol>
<p>1. Implementing NativeView For a general implementation, please refer to <a href="https://docs.flutter.dev/platform-integration/android/platform-views">Official</a>.</p>
<p>One key difference from the official approach is that this implementation uses Jetpack Compose. Here, the <code>CameraPreview</code> (built with Jetpack Compose) is embedded into the Android native View tree using <code>ComposeView</code>.</p>
<pre><code class="language-kotlin:NativeView.kt">
class NativeView(context: Context, id: Int, creationParams: Map&lt;String?, Any?&gt;?, methodChannel: MethodChannel, eventChannel: EventChannel) : PlatformView {
    private var nativeView: ComposeView? = null

    override fun getView(): View {
        return nativeView!!
    }

    override fun dispose() {}

    init {
        nativeView = ComposeView(context).apply {
            setContent {
                CameraPreview(methodChannel, eventChannel)
            }
        }
    }
}
</code></pre>
<p>In the Jetpack Compose implementation, <code>PreviewView</code>  from CameraX, which is a <code>View</code> , is Composed using <code>AndroidView</code>. As a side note, <code>AndroidView</code> can also be used for <code>Fragment</code>.</p>
<pre><code class="language-kotlin:CameraPreview.kt">
@Composable
fun CameraPreview(methodChannel: MethodChannel, eventChannel: EventChannel) {
    val context = LocalContext.current

    val preview = Preview.Builder().build()
    val previewView = remember {
        PreviewView(context)
    }

    suspend fun startCamera(context: Context) {
        val cameraProvider = context.getCameraProvider()
        cameraProvider.unbindAll()

        // To meet the requirements for camera analysis mentioned at the beginning of the article,
        // this can be achieved by setting up a cameraProvider and configuring the Preview and analysis processing for the camera.
        cameraProvider.bindToLifecycle(
          LocalLifecycleOwner.current, 
          CameraSelector.Builder().requireLensFacing(CameraSelector.LENS_FACING_BACK).build(), 
          preview,
          analysis, // Set the camera image analysis process here
        )

        preview.surfaceProvider = previewView.surfaceProvider
    }

    suspend fun stopCamera(context: Context) {
        val cameraProvider = context.getCameraProvider()
        cameraProvider.unbindAll()
    }

    LaunchedEffect(Unit) {
        fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
            when(call.method) {
                &quot;startCamera&quot; -&gt; {
                    runBlocking {
                        CoroutineScope(Dispatchers.Default).launch {
                            withContext(Dispatchers.Main) {
                                startCamera(context)
                            }
                        }
                    }
                    result.success(&quot;ok&quot;)
                }
                &quot;stopCamera&quot; -&gt; {
                    runBlocking {
                        CoroutineScope(Dispatchers.Default).launch {
                            withContext(Dispatchers.Main) {
                                stopCamera(context)
                            }
                        }
                    }
                }
                else -&gt; result.notImplemented()
            }
        }

        methodChannel.setMethodCallHandler(::onMethodCall)
    }

    AndroidView(factory = { previewView }, modifier = Modifier.fillMaxSize())
}
</code></pre>
<p>Next, 2. implement NativeViewFactory and 3. register it to FlutterEngine as follows.</p>
<pre><code class="language-kotlin:">
class MainActivity: FlutterFragmentActivity() {
  
  ~
  ~

  override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)

    val methodChannel = MethodChannel(
        flutterEngine.dartExecutor.binaryMessenger,
        METHOD_CHANNEL
    )

    val eventChannel = EventChannel(
        flutterEngine.dartExecutor.binaryMessenger,
        EVENT_CHANNEL
    )

    flutterEngine
        .platformViewsController
        .registry
        .registerViewFactory(VIEW_TYPE, NativeViewFactory(methodChannel, eventChannel))
    }
}

class NativeViewFactory(
    private val methodChannel: MethodChannel,
    private val eventChannel: EventChannel
) : PlatformViewFactory(StandardMessageCodec.INSTANCE) {
    override fun create(context: Context, viewId: Int, args: Any?): PlatformView {
        val creationParams = args as Map&lt;String?, Any?&gt;?
        return NativeView(
            context,
            viewId,
            creationParams,
            methodChannel,
            eventChannel
        )
    }
}
</code></pre>
<p>Finally, here is the implementation on the Flutter side. </p>
<p><code>PlatformViewsService.initSurfaceAndroidView()</code> is an API for using either <code>TLHC</code> or <code>HC</code>. <code>PlatformViewsService.initAndroidView()</code> allows you to use either <code>TLHC</code> or <code>VD</code>.<code>PlatformViewsService.initExpensiveAndroidView()</code> forces the use of <code>HC</code>.</p>
<pre><code class="language-dart:camera_preview_view.dart">
class CameraPreviewView extends StatelessWidget {
  final String viewType = &#39;camera_preview_compose&#39;;
  final Map&lt;String, dynamic&gt; creationParams = &lt;String, dynamic&gt;{};

  CameraPreviewView({super.key});

  @override
  Widget build(BuildContext context) {
    return PlatformViewLink(
      viewType: viewType,
      surfaceFactory: (context, controller) {
        return AndroidViewSurface(
          controller: controller as AndroidViewController,
          hitTestBehavior: PlatformViewHitTestBehavior.opaque,
          gestureRecognizers: const &lt;Factory&lt;OneSequenceGestureRecognizer&gt;&gt;{},
        );
      },
      onCreatePlatformView: (params) {
        return PlatformViewsService.initSurfaceAndroidView(
          id: params.id,
          viewType: viewType,
          layoutDirection: TextDirection.ltr,
          creationParams: creationParams,
          creationParamsCodec: const StandardMessageCodec(),
          onFocus: () {
            params.onFocusChanged(true);
          },
        )
          ..addOnPlatformViewCreatedListener(params.onPlatformViewCreated)
          ..create();
      },
    );
  }
}
</code></pre>
<p>By using <code>PlatformView</code> this way, you can integrate Android native UI into your Flutter app.</p>
<h3>Intent</h3>
<p><code>Intent</code> is an Android feature (not specific to Flutter) that allows launching an Activity separate from the MainActivity where Flutter runs. With Intent, you can navigate to another screen within your app, launch external apps, and exchange data between Activities.</p>
<p>The two methods mentioned above (Texture widget and PlatformView) have been reported to have performance issues [^3]. To resolve these issues, a deep understanding of both Flutter and Android native is essential. In some cases, building a separate Android app might actually help keep development costs down.</p>
<p>However, this poses a different challenge.</p>
<ul>
<li><p>If your team only has Flutter engineers, you will need to catch up on Android development.</p>
</li>
<li><p>If the app is developed as an external application, the interface between apps must include security measures and be designed with lifecycle considerations in mind.</p>
<p>For instance, the following measures may be necessary:</p>
<ul>
<li>Validate the data exchanged between activities.</li>
<li>Restrict access so that only a specific app can call it.</li>
<li>Ensure the called app functions correctly even if the calling app has been killed.</li>
</ul>
</li>
</ul>
<p>Now let’s take a look at how to use <code>Intent</code> in Flutter. First, we’ll go over how to call another Activity from a Flutter app.</p>
<p><strong>Calling Activity (MainActivity where the Flutter app runs)</strong></p>
<pre><code class="language-kotlin:">
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
    if (call.method!!.contentEquals(&quot;startCamera&quot;)) {
      val dummyData = call.argument&lt;String&gt;(&quot;dummy_data&quot;) ?: return result.error(
        &quot;ERROR&quot;,
        &quot;data is invalid&quot;,
        null
      )

      // In case of screen transition
      val intent = Intent(this, SubActivity::class.java)

      // For external apps
      val packageName = &quot;com.example.camera_preview_intent&quot;
      val intent = activity.packageManager.getLaunchIntentForPackage(packageName) ?: return result.error(
        &quot;ERROR&quot;,
        &quot;unexpected error&quot;,
        null
      )
      intent.setClassName(packageName, &quot;.SubActivity&quot;)

      // Store the sending data
      intent.putExtra(&quot;EXTRA_DUMMY_DATA&quot;, dummyData)

      intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
      activity.startActivityForResult(intent, REQUEST_CODE)
    }
}

override fun onListen(arguments: Any?, sink: EventChannel.EventSink?) {
    eventSink = sink
}

override fun onCancel(arguments: Any?) {
    eventSink = null
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {
    if (requestCode == REQUEST_CODE &amp;&amp; resultCode == Activity.RESULT_OK &amp;&amp; data != null) {
        val result = data.getStringExtra(&quot;RESULT_DATA&quot;) ?: &quot;&quot;,
        eventSink?.success(result)

        return true
    }

    return false
}
</code></pre>
<p>Next, let’s implement the Activity that gets called from the Flutter app. Once a specific operation is completed, you can use <code>Intent</code> to return data, as shown below.</p>
<p><strong>Target Activity</strong></p>
<pre><code class="language-kotlin">
val intent = Intent()
intent.putExtra(&quot;RESULT_DATA&quot;, resultData)
activity.setResult(Activity.RESULT_OK, intent)

finish()
</code></pre>
<p>By using <code>Intent</code> this way, you can avoid dealing with complex UI control on both the Flutter and native Android sides while enabling data exchange between Flutter and native Android Activities.</p>
<p>However, security and data integrity must be carefully considered in this approach.</p>
<h2>Summary</h2>
<p>In this article, we&#39;ve discussed how to incorporate native functionality into Flutter apps, with a focus on Android.</p>
<ul>
<li>Data communication between Flutter and Android native was achieved using <code>MethodChannel</code> and <code>EventChannel</code>.</li>
<li>Here’s how to incorporate Android native UI into Flutter:<ul>
<li><code>Texture</code> widget<ul>
<li>Great for camera previews and video displays, and relatively easy to implement.</li>
<li>However, it requires handling user interactions and may have some performance issues.</li>
</ul>
</li>
<li><code>PlatformView</code><ul>
<li>Lets you integrate native UI into Flutter’s widget tree while enabling user interaction control.<ul>
<li>Supports embedding View, Fragment, and Jetpack Compose.</li>
</ul>
</li>
<li>Performance can also be an issue.</li>
</ul>
</li>
<li><code>Intent</code><ul>
<li>Allows seamless screen transitions and launching of other apps, making it possible to directly display Android’s UI and exchange data.</li>
<li>However, security and data handling require careful attention.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>As mentioned above, each method comes with its own strengths and limitations when integrating Android native features into a Flutter app. The best choice depends on your project’s specific needs.</p>
<h1>Notes</h1>
<p>The thumbnail of the Droid is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons Attribution 3.0 License.</p>
<p>[^1]: <a href="https://docs.flutter.dev/platform-integration/android/platform-views">Hosting native Android views in your Flutter app with Platform Views</a>
[^2]: <a href="https://github.com/flutter/flutter/blob/master/docs/platforms/android/Android-Platform-Views.md#selecting-a-mode">Android Platform Views</a>
[^3]: <a href="https://docs.flutter.dev/platform-integration/android/platform-views#performance">Performance</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/osugi/20241217/thumbnail.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[[Learning Roadside Station Podcast] The Google 10X Program]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-16-学びの道の駅Podcast-Google10xプログラム-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-16-学びの道の駅Podcast-Google10xプログラム-en/</guid>
            <pubDate>Tue, 25 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for day 16 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<p>Hi, I’m Nakanishi from the Manabi-no-Michi-no-Eki (Learning Roadside Station) team. This year, the Manabi-no-Michi-no-Eki project was officially launched and later established as a team. As part of this initiative, we&#39;re also hosting an in-house podcast, and for this year’s Advent Calendar, we’d love to share more episodes with you.</p>
<h1>What is the Manabi-no-Michi-no-Eki (Learning Roadside Station)?</h1>
<p>It’s a project aimed at making the frequently held in-house study sessions more accessible and effective. The initiative is led by passionate volunteers within the company, with the goal of supporting study sessions and fostering a culture of knowledge sharing across the organization.</p>
<h1>10X Innovation Culture Program</h1>
<p>The Learning Roadside Station Podcast features interviews with employees who organize study sessions within the company. This segment is called “A Peek into the Study Session Next Door”. For today’s podcast, we’re joined by Awata-san and HOKA-san, who are working on the 10X Innovation Culture Program being provided by Google. Usually, HOKA-san conducts the interviews, but today, Akeda-san and I will be taking on that role. So, without further delay, let&#39;s jump right into the interview.</p>
<h1>Interview</h1>
<p><strong>Awata-san:</strong> Thank you for having me. My regular work focuses on platform engineering, ensuring that database-based operations are accessible to everyone. Besides that, I’m interested in corporate culture, so I’m doing a variety of activities.</p>
<p><strong>HOKA-san:</strong> I usually work in the Human Resources Group’s Organizational Human Resources Team. We plan and conduct training by identifying needs and challenges through interviews with both new and existing employees.</p>
<p><strong>Akeda-san:</strong> Please tell us what prompted you to hold these study sessions.</p>
<p><strong>Awata-san:</strong> As part of our Google Cloud Enterprise User Group (<a href="https://jaguer.jp/">Jagu’e’r</a>) I took part in a subcommittee for thinking about corporate culture and innovation. As part of our activities, we decided to try the 10X Innovation Culture Program, gathered around 15 volunteers, and went ahead with it. HOKA-san was also among them, and things took off from there.</p>
<p><strong>HOKA-san:</strong> Yes, that’s right. When we held our first event at the Google office in Shibuya, the reaction from the participants was extremely good. Collaborating in a workshop with people we usually had no interaction with opened up new opportunities for communication.</p>
<p><strong>Akeda-san:</strong> Next, please tell us some details about the events. How did they expand after the initial session?</p>
<p><strong>Awata-san:</strong> We initially held them at the Google office, then subsequently shifted to holding them in-house. The in-house events also drew a large number of participants and got an extremely positive reaction.</p>
<p><strong>HOKA-san:</strong> Seeing KTC employees engage so positively, the Google team also expressed high praise. We hope to go on spreading this program further both inside and outside the company.</p>
<p><strong>Akeda-san:</strong> What are the prospects for the future?</p>
<p><strong>Awata-san:</strong> In the future, we’d like to become certified facilitators, and get to spread 10X to other companies as well.</p>
<p><strong>HOKA-san:</strong> First, we plan to roll it out to other in-house groups and cultivate a culture of innovation across KTC.</p>
<p><strong>Akeda-san:</strong> What kind of organization do you envision for KTC?</p>
<p><strong>Awata-san:</strong> I want to make it a vibrant hive of thinking outside the box, flexible communication, and collaboration.</p>
<p><strong>HOKA-san:</strong> I want to create a culture where people can take on challenges without fearing failure. To achieve this, I plan to utilize the 10X methods.</p>
<p><strong>Akeda-san:</strong> Finally, could you share a message with all our listeners?</p>
<p><strong>Awata-san:</strong> Culture isn’t something that can be imposed; it naturally emerges from people&#39;s actions. If you&#39;re interested, we’d love for you to join us.</p>
<p><strong>HOKA-san:</strong> If you&#39;re interested, feel free to start by just taking a look—don’t hesitate to reach out. Through the 10X Innovation Culture Program, we aim to make KTC a more collaborative and supportive organization to work in. If you’re interested, please contact Awata-san or HOKA-san.</p>
<p>In this article, we shared insights into the 10X Innovation Culture Program, its background, and what the future may hold for it. We hope you’re looking forward to the next study session as well!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2024-12-podcast/podcast_10x.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[[Learning Roadside Station Podcast] Osaka Information Sharing Meeting]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-15-学びの道の駅Podcast-大阪情報共有会-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-15-学びの道の駅Podcast-大阪情報共有会-en/</guid>
            <pubDate>Mon, 24 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is part of Day 15 of the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<p>Hi, I’m Nakanishi from Learning Roadside Station. This year, the Learning Roadside Station project was officially launched and structured as an organization. As part of our initiatives, we also run an in-house podcast, and for this year&#39;s Advent Calendar, we’d like to share more about it.</p>
<h1>What is &quot;Learning Roadside Station&quot;?</h1>
<p>&quot;Learning Roadside Station&quot; is a project launched to make the frequent study sessions held within the company more convenient and effective. The aim is to support the holding of study sessions, mainly by volunteers within the company, and to promote knowledge sharing within the company.</p>
<h1>Osaka Tech Lab Information Sharing Meeting</h1>
<p>In the KTC Learning Roadside Station Podcast, we interview people who hold in-house study sessions. This segment is called &quot;Surprise! Our Neighbor’s Study Group.&quot; Today&#39;s podcast guests are Okita-san and Fukuda-san, who are leading the Osaka Tech Lab Information Sharing Meeting. Could you start by introducing yourselves?</p>
<h1>Interview</h1>
<p><strong>Okita-san:</strong> Yes, my name is Okita. I belong to the mobile app development group, and as a development PM, I am responsible for connecting the mobile development team with other groups. I look forward to our discussion today.</p>
<p><strong>Fukuda-san:</strong> My name is Fukuda. I joined KTC (formerly KINTO Corporation) in July 2020 and worked in the Production Group. I took 10 months of parental leave, and in February 2024, I returned to work. I am now part of the Creative Division, where I manage the operation and renewal of KTC&#39;s corporate website. I look forward to our discussion today.</p>
<p><strong>Hoka-san:</strong> Thank you. Could you tell us about what inspired you to start the Osaka Tech Lab Information Sharing Meeting?</p>
<p><strong>Fukuda-san:</strong> Osaka Tech Lab was launched in April 2022. At first, it was just Tomonaga-san from the Analysis Group running it alone. As more members joined, we started hearing comments like, &quot;I don&#39;t even know what the person sitting next to me is working on.&quot; That’s when we decided to start an information-sharing meeting to improve communication.</p>
<p><strong>Okita-san:</strong> Fukuda-san was the founder of the Osaka Tech Lab Information Sharing Meeting.</p>
<p>At first, we started with self-introductions, and by sharing our hobbies, we aimed to find common ground and build connections among members.</p>
<p><strong>Hoka-san:</strong> I see. So, Okita-san, you have been involved since the very first session and helped promote the initiative. How has it evolved over time?</p>
<p><strong>Okita-san:</strong> In the beginning, there were only a few members, so we could just gather and share ideas casually. Now, in our 17th session, the number of members has grown, and naturally, the format of our meetings has evolved as well.</p>
<p><strong>Hoka-san:</strong> What does it mean that Osaka Tech Lab is the main player in this event?</p>
<p><strong>Okita-san:</strong> The purpose is to stimulate communication within Osaka Tech Lab. We encourage members to share their work and initiatives, fostering stronger horizontal connections across teams. Additionally, our discussions often lead to tech blog content, helping us document and share insights more effectively.</p>
<p><strong>Hoka-san:</strong> That sounds great! It feels like the initiative is truly taking shape. Have you noticed any changes in the reactions of the participants or the atmosphere?</p>
<p><strong>Fukuda-san:</strong> At first, the meetings were casual and conversational, but over time, they have become a space for discussing challenges. We now also use these sessions to talk about how to improve our office environment.</p>
<p><strong>Okita-san:</strong> For example, we didn’t have a clock in the office, so we installed one, and also added bookshelves. It’s a continuous collaborative effort where we share ideas and implement improvements together.</p>
<p><strong>Hoka-san:</strong> What are your plans for the future of the Osaka Tech Lab Information Sharing Meeting?</p>
<p><strong>Okita-san:</strong> As our organization grows, I want to maintain the friendly and open atmosphere we have built while continuing to prioritize communication.</p>
<p><strong>Fukuda-san:</strong> I want to promote KTC from Osaka. We’ll consider whether to continue the information-sharing meetings in their current form or evolve into a new format.</p>
<p><strong>Hoka-san:</strong> What if other department members want to participate?</p>
<p><strong>Okita-san:</strong> We are recruiting LT speakers every month, so we’d love for more people to participate!</p>
<p>If you’re interested, feel free to reach out to any Osaka Tech Lab member.</p>
<p><strong>Fukuda-san:</strong> After the information-sharing meeting, we also hold a beer bash, so we hope everyone uses it as an opportunity to connect and communicate in a relaxed setting.</p>
<p><strong>Hoka-san:</strong> Lastly, do you have a message for our audience?</p>
<p><strong>Okita-san:</strong> Please visit Osaka! We’d love to have you here.</p>
<p><strong>Fukuda-san:</strong> Bring an LT (Lightning Talk) and join our information-sharing meetings! We’d love to have you here.</p>
<p><strong>Hoka-san:</strong> Thank you both for your time today. I can really see how your efforts in Osaka are creating a positive impact across the company. Thank you Okita-san and Fukuda-san. That&#39;s the summary of the interview. This article conveys the importance and role of the Osaka Tech Lab Information Sharing Meeting to our readers.</p>
<p>This time, we have provided details about the Osaka Tech Lab information sharing session, the background to its operation, and future prospects. Please look forward to the next study session!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2024-12-podcast/podcast_osaka.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Introducing Redis Pub/Sub for System Date Changes in Testing]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-15-redis-pub-sub-for-system-datetime-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-15-redis-pub-sub-for-system-datetime-en/</guid>
            <pubDate>Fri, 21 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[In this article, I will demonstrate how to update a system date in real-time using Redis Pub/Sub with Spring Boot.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello! My name is Yoo, and I am a member of the New Car Subscription Development Group at KINTO Technologies.</p>
<p>While our approach may not be perfect, we continuously strive to improve challenges step by step. In this article, I’d like to share how we implemented Redis Pub/Sub in Spring Boot to dynamically change the system date.</p>
<h1>Background and Motivation</h1>
<p>When conducting QA and testing, there are many cases where it is necessary to change the system date to verify specific behaviors. This is especially important for subscription-based services, where business logic often depends on specific dates. For example, testing requires validation of processes related to start and end dates of the period, monthly fees, settlement charges for mid-term cancellations, maintenance inspections, and vehicle inspections. Previously, the system date was defined in the configuration file, meaning that every time the date needed to be changed, the container had to be redeployed. As a result, each test or QA cycle required more than five minutes just for redeployment, significantly impacting efficiency. In this article, I will introduce how we solved this issue and improved our workflow.</p>
<h1>Benefits of introducing Redis Pub/Sub</h1>
<p>By implementing Redis Pub/Sub, we optimized system date changes in test environments, making them more efficient and responsive. As a result, we have successfully reduced the workload for testing and QA, leading to improved operational efficiency. Specifically, container redeployment is no longer required. Instead, by simply sending a message (the desired setting value) to the corresponding setting item (topic), each container can instantly receive the update and apply the changes in real time. Even in multi-container environments, all subscribers receive the message simultaneously, allowing system settings to be updated across multiple containers without requiring a restart.</p>
<p>Additionally, system date changes are now logged, making it possible to track and review change history when needed. Furthermore, with Spring Boot Profile settings, this feature can be enabled exclusively in designated test environments, preventing accidental application to production or other environments. *For more details on Profiles, see <a href="https://spring.pleiades.io/spring-boot/reference/features/profiles.html">here</a>.</p>
<h1>What is Redis Pub/Sub</h1>
<p>Redis Pub/Sub is one of the messaging patterns used in message queuing systems. Message Queuing is a method of asynchronous communication commonly used in serverless and microservices architectures to enable real-time event notification in distributed systems. This mechanism is widely used not only as a database and cache but also as a message broker, as it supports scalable and stable communication between different software modules.</p>
<h3>Main components</h3>
<ul>
<li>Topic: The subject or category that subscribers listen to.</li>
<li>Publisher: Sends messages related to a specific topic.</li>
<li>Subscriber: Receives messages from publishers for subscribed topics.</li>
</ul>
<h3>Keyspace Notifications</h3>
<p>Redis can monitor real-time changes to keys and values by receiving events that impact the Redis dataset in various ways.</p>
<h1>How is it implemented?</h1>
<h3>System date change mechanism</h3>
<p>We implemented an API as a publisher to send messages to designated topics. When an event occurs for a subscribed topic (key), multiple containers (subscribers) receive the message and update the settings in real time. <img src="/assets/blog/authors/yoo/architecture.png" alt="System Date Change Architecture"></p>
<h3>System configuration</h3>
<p>It is built using Java and Spring Boot. Applications are containerized and run in a cloud environment.</p>
<h3>Adding the necessary library to build.gradle</h3>
<pre><code class="language-promql">implementation &#39;org.springframework.data:spring-data-redis&#39;
</code></pre>
<h3>Implementing the RedisConfig class</h3>
<pre><code class="language-java">@AllArgsConstructor
@Configuration
public class RedisTemplateConfig {
    private final RedissonClient redissonClient;

    @Bean
    public RedisTemplate&lt;String, String&gt; redisTemplate() {
        RedisTemplate&lt;String, String&gt; template = new RedisTemplate&lt;&gt;();
        template.setConnectionFactory(new RedissonConnectionFactory(redissonClient));
        template.setDefaultSerializer(new StringRedisSerializer());
        return template;
    }

    @Bean
    public RedisMessageListenerContainer redisContainer() {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(new RedissonConnectionFactory(redissonClient));
        return container;
    }
}
</code></pre>
<h3>Implementing the Publisher</h3>
<p>Implement an API that sends messages to a designated topic.</p>
<pre><code class="language-java">@RestController
public class SystemTimeController {
    private final SomeService service;

    @PostMapping(&quot;/update&quot;)
    public void updateSystemTime(@RequestParam String specifiedDateTime) {
        service.publish(specifiedDateTime);
    }
}

@Service
@RequiredArgsConstructor
public class SomeService {
    private final RedisTemplate&lt;String, String&gt; redisTemplate;
    // Define the topic key
    private static final String FOO_TOPIC = &quot;foo-key&quot;;

    public void publish(String specifiedDateTime) {
        // Send a message to the specified topic
        redisTemplate.opsForValue().set(FOO_TOPIC, specifiedDateTime);
    }
}
</code></pre>
<h3>Implementing the Subscriber</h3>
<p>The subscriber receives messages when an event occurs for a subscribed topic (key).</p>
<pre><code class="language-java">@Slf4j
@Component
@Profile(&quot;developer1, developer2&quot;) // Only enabled for the specified test environment profiles
public class FooKeyspaceEventMessageListener extends KeyspaceEventMessageListener {
    private final RedisMessageListenerContainer listenerContainer;
    private final RedisTemplate&lt;String, String&gt; redisTemplate;
    private static final String FOO_TOPIC = &quot;foo-key&quot;;

    @Override
    public void init() {
        doRegister(listenerContainer);
    }

    public FooKeyspaceEventMessageListener(
            RedisMessageListenerContainer listenerContainer,
            RedisTemplate&lt;String, String&gt; redisTemplate) {
        super(listenerContainer);
        this.listenerContainer = listenerContainer;
        this.redisTemplate = redisTemplate;
    }

    @Override
    protected void doHandleMessage(Message message) {
        // Retrieve the system date from Redis
        String systemTime = updateSystemTimeConfig(redisTemplate.opsForValue().get(FOO_TOPIC));
        // Create and invoke the method to update the system date
        updateSystemTimeConfig(systemTime);
        log.info(&quot;Receive a message about FOO_TOPIC: {}&quot;, message);
    }
}
</code></pre>
<h1>Finally</h1>
<p>Thank you for reading this article to the end. There are still many areas for improvement, but we strive to address challenges step by step and grow with each iteration. While the structure and implementation may not be perfect, I believe that gradual progress in the right direction is what truly matters. We will continue to learn, ensuring that these small advancements accumulate and lead to even better outcomes. I hope we can continue this journey of growth together. Thank you very much.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[AGSLでAndroid UIを変換するCustom Shaders を簡単に]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-15-AGSL-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-15-AGSL-ja/</guid>
            <pubDate>Wed, 19 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[魅力的なUIエフェクトやダイナミックなグラフィックの作成をAGSLがどうサポートしているのか、実際のシェーダー例で探ってみましょう。]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは！Yao Xieです。 KINTO Technologies のモバイルアプリ開発グループで、<a href="https://kinto-jp.com/entry_app/">KINTO
かんたん申し込みアプリ</a>のAndroidアプリを開発しています。本記事では、AGSL(Android Graphics Shading Language)を活用してカスタムUIコンポーネントを向上したり、Androidアプリで高度な画像処理をする方法を紹介します。</p>
<h2>AGSLとは</h2>
<p>AGSL (Android Graphics Shading Language)は、Android 向けに設計されたGPUベースのシェーディング言語のことです。Skia Shading Language (SKSL)をベースにしたAGSLは、高度なグラフィックエフェクトを生みだせるAndroid特有の最適性を備えています。AGSLはAndroidのレンダリングパイプラインと完全に統合しているので、複雑なビジュアルエフェクトを効率良くスムーズに実装できます。</p>
<h3>GLSLからSKSLへ、そしてAGSLへ</h3>
<p>グラフィックスシェーディング言語は、現代のアプリで求められている、高品質なグラフィックへの需要に応えるために大きく進化してきました。簡単にまとめると:</p>
<ul>
<li>GLSL（OpenGLシェーディング言語）：<ul>
<li>リジナルのシェーディング言語で、2D・3DグラフィックのレンダリングにOpenGLと併用されます。GLSLのおかげで、GPU上で動作するカスタムシェーダーを書き出すことができます。</li>
</ul>
</li>
<li>SKSL（Skiaシェーディング言語）：<ul>
<li>Skiaグラフィックスライブラリの一部として導入されています。SKSL は 2DグラフィックをレンダリングするためにAndroidなどいろいろなプラットフォームで使われています。</li>
</ul>
</li>
<li>AGSL（Android Graphics Shading Language）：<ul>
<li>Android用に特化してデザインしてあるシェーディング言語です。SKSLの機能をベースに、Androidのレンダリングパイプラインとスムーズに統合できるように調整してあります。</li>
</ul>
</li>
</ul>
<h3>GLSL、SKSL、AGSLの主な違い</h3>
<p>AGSLはモバイルデバイス向けに最適化されていて、GLSLよりもパフォーマンスが高く、消費電力が低いです。Androidレンダリングパイプラインと統合していることで、より効率良くグラフィックをレンダリングすることができます。</p>
<ul>
<li>GLSL：<ul>
<li>OpenGL用の、C言語に似た構文です。</li>
<li>クロスプラットフォーム対応ではあるものの、OpenGL ESのバリエーションの影響でAndroidでは制限があります。</li>
</ul>
</li>
<li>SKSL：<ul>
<li>GLSLに似ていますが、Skiaの2Dグラフィック向けに最適化してあります。</li>
<li>主にSkia内部で使用されていて、Androidの直接的な開発ではあまり利用できません。</li>
</ul>
</li>
<li>AGSL：<ul>
<li>SKSLをベースにしつつ、 Android特有の向上性を備えています。</li>
<li>Androidのグラフィックパイプラインと完全に統合していて、最適なパフォーマンスを発揮します。</li>
</ul>
</li>
</ul>
<h2>AGSLの仕組みは?</h2>
<p>下の図は、AGSLシェーダー文字列がAndroidのグラフィックレンダリングシステムやデータフロープロセスの中でどの位置にあるかを示す階層図（上から下への順序）です。（概念的な図なので、正確なシステムアーキテクチャではありません。）</p>
<p><img src="/assets/blog/authors/yao.xie/agsl_layers.svg" alt="agsl_get_started"></p>
<h2>はじめに</h2>
<h3>Step 1:グラデーションシェーダを定義する</h3>
<p>AGSLを使用して、テキストにのみ滑らかなグラデーションエフェクトを加えるシェーダーファイルを作成します。コンポーザブルインプットによって、グラデーションがテキストのアルファマスクにしっかりと適切に反映できます。</p>
<pre><code class="language-kotlin">@Language(&quot;AGSL&quot;)
val gradientTextShader = &quot;&quot;&quot;
  uniform float2 resolution; // Text size
  uniform float time;        // Time for animation
  uniform shader composable; // Input composable (text mask)

  half4 main(float2 coord) {
      // Normalize coordinates to [0, 1]
      float2 uv = coord / resolution;

      // Hardcoded gradient colors
      half4 startColor = half4(1.0, 0.65, 0.15, 1.0); // Orange
      half4 endColor = half4(0.26, 0.65, 0.96, 1.0);  // Blue

      // Linear gradient from startColor to endColor
      half4 gradientColor = mix(startColor, endColor, uv.x);

      // Optional: Add a subtle animation (gradient shifting)
      float shift = 0.5 + 0.5 * sin(time * 2.0);
      gradientColor = mix(startColor, endColor, uv.x + shift * 0.1);

      // Use the alpha from the input composable mask
      half4 textAlpha = composable.eval(coord);

      // Combine the gradient color with the composable alpha
      return gradientColor * textAlpha.a;
  }
&quot;&quot;&quot;.trimIndent()
</code></pre>
<h3>Step 2:シェーダ用Modifierを作成する</h3>
<p>テキストにグラデーションシェーダを適用するカスタム Modifierを定義します。このシェーダは、ダイナミックなタイムパラメータを活用して、グラデーションをアニメーション化します。</p>
<pre><code class="language-kotlin">fun Modifier.gradientTextEffect(): Modifier = composed {
    val shader = remember { RuntimeShader(gradientTextShader) }
    var time by remember { mutableStateOf(0f) }

    // Increment animation time
    LaunchedEffect(Unit) {
        while (true) {
            time += 0.016f // Simulate 60 FPS
            delay(16)
        }
    }

    this.graphicsLayer {
        shader.setFloatUniform(&quot;resolution&quot;, size.width, size.height)
        shader.setFloatUniform(&quot;time&quot;, time)

        renderEffect = RenderEffect
            .createRuntimeShaderEffect(shader, &quot;composable&quot;)
            .asComposeRenderEffect()
    }
}
</code></pre>
<h3>Step 3:シェーダをテキストコンポーネントに適用する</h3>
<p>UIでModifier.gradientTextEffectを使用して、グラデーションエフェクトを適用します。</p>
<pre><code class="language-kotlin">@Composable
fun GradientTextDemo() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        contentAlignment = Alignment.Center
    ) {
        Text(
            text = &quot;Gradient Text&quot;,
            fontSize = 36.sp,
            fontWeight = FontWeight.Bold,
            color = Color.White,
            modifier = Modifier.gradientTextEffect()
        )
    }
}
</code></pre>
<h3>結果</h3>
<p><img src="/assets/blog/authors/yao.xie/agsl_get_started.png" alt="agsl_get_started"></p>
<h2>AGSLで他にできることってこれだけ？</h2>
<p>AGSLの機能は基本をはるかに超えて、ダイナミックで魅力的で、高パフォーマンスなアプリ体験を作り上げるのをサポートしてくれます。ここからは、AGSLでアプリをさらにレベルアップする方法を、実例をもとに探ってみましょう。</p>
<h3>1.UIコンポーネントを強化</h3>
<p>AGSLを使えば、ユーザーの心を捉えて、アプリの目的を際立たせる魅力的なUI要素を作成することができます。</p>
<ul>
<li><strong>アニメーションボーダー:</strong> カード、ボタン、または画像の周囲にマーキーエフェクトや点滅エフェクトを作成します。</li>
<li><strong>カスタムグラデーション:</strong> ダイナミックに流れる、アニメーション化したGPUアクセラレーショングラデーションを実装すします。</li>
<li><strong>Dynamic Glow エフェクト:</strong> ボタンやスライダーに、光るハイライト後光を追加します。</li>
</ul>
<h4>例：運転スキルのトレーニングアプリ</h4>
<p>運転スキルのトレーニングアプリを開発していると想像してみてください。目標は、インターフェイスを視覚的に惹きつけるものにして、ユーザーが「トレーニング開始」ボタンのように大切な要素を操作できるようにすることです。AGSLがDynamic Glowエフェクトをどのように実現するのかをご紹介します。</p>
<h5>AGSL シェーダコード：</h5>
<pre><code class="language-kotlin">@Language(&quot;AGSL&quot;)
val glowButtonShader = &quot;&quot;&quot;
  // Shader for a glowing rounded rectangle button

  uniform shader button;              // Input texture or color for the button
  uniform float2 size;                // Button size
  uniform float cornerRadius;         // Corner radius of the button
  uniform float glowRadius;           // Radius of the glow effect
  uniform float glowIntensity;        // Intensity of the glow
  layout(color) uniform half4 glowColor; // Color of the glow

  // Signed Distance Function (SDF) for a rounded rectangle
  float calculateRoundedRectSDF(vec2 position, vec2 rectSize, float radius) {
      vec2 adjustedPosition = abs(position) - rectSize + radius; // Adjust for rounded corners
      return min(max(adjustedPosition.x, adjustedPosition.y), 0.0) 
             + length(max(adjustedPosition, 0.0)) - radius;
  }

  // Function to calculate glow intensity based on distance
  float calculateGlow(float distance, float radius, float intensity) {
      return pow(radius / distance, intensity); // Glow falls off as distance increases
  }

  half4 main(float2 coord) {
      // Normalize coordinates and aspect ratio
      float aspectRatio = size.y / size.x;
      float2 normalizedPosition = coord.xy / size;
      normalizedPosition.y *= aspectRatio;

      // Define normalized rectangle size and center
      float2 normalizedRect = float2(1.0, aspectRatio);
      float2 normalizedRectCenter = normalizedRect / 2.0;
      normalizedPosition -= normalizedRectCenter;

      // Calculate normalized corner radius and distance
      float normalizedRadius = aspectRatio / 2.0;
      float distanceToRect = calculateRoundedRectSDF(normalizedPosition, normalizedRectCenter, normalizedRadius);

      // Get the button&#39;s color
      half4 buttonColor = button.eval(coord);

      // Inside the rounded rectangle, return the button&#39;s original color
      if (distanceToRect &lt; 0.0) {
        return buttonColor;
      }

      // Outside the rectangle, calculate glow effect
      float glow = calculateGlow(distanceToRect, glowRadius, glowIntensity);
      half4 glowEffect = glow * glowColor;

      // Apply tone mapping to the glow for a natural look
      glowEffect = 1.0 - exp(-glowEffect);

      return glowEffect;
  }
&quot;&quot;&quot;.trimIndent()
</code></pre>
<h5>結果</h5>
<p>ボタンが明滅する後光を出して、関心を引き付けながら車のライトのような雰囲気を作り出せます。</p>
<p><a href="https://youtube.com/shorts/CW1yBgJyDo4?rel=0">https://youtube.com/shorts/CW1yBgJyDo4?rel=0</a></p>
<h3>2.高度な画像処理を実行する</h3>
<p>AGSLはリアルタイムの画像操作に優れていて、ダイナミックでインタラクティブなエフェクトを作ることができます。AGSL を使用すれば、GPUアクセラレーターを活かした高速な画像処理エフェクトを作成できます。</p>
<ul>
<li><strong>カスタムフィルタ:</strong> セピア、ピクセル化、ビネットなどアート風なエフェクトを追加します。</li>
<li><strong>ダイナミックブラー:</strong> モーションブラーや被写界深度エフェクトなど、リアルタイムでぼかしを適用します。</li>
<li><strong>カラー調整:</strong> UI上で明るさ、コントラスト、彩度をダイナミックに調整します。</li>
</ul>
<h4>例：画像の波紋エフェクト</h4>
<p>アプリに月の画像があると想像してみてください。月が水面に映るような波紋エフェクトを追加して、もっとインタラクティブで関心を引くインターフェースにしたいなと思っているとします。</p>
<h5>AGSL シェーダコード：</h5>
<pre><code class="language-kotlin">@Language(&quot;AGSL&quot;)
val rippleShader = &quot;&quot;&quot;
    // Uniform variables: inputs provided from the outside
    uniform float2 size;       // The size of the canvas in pixels (width, height)
    uniform float time;        // The elapsed time for animating the ripple effect
    uniform shader composable; // The shader applied to the composable content being rendered
    
    // Main function: calculates the final color at a given fragment (pixel) coordinate
    half4 main(float2 fragCoord) {
        // Scale factor based on the canvas width for normalization
        float scale = 1 / size.x;
        
        // Normalize fragment coordinates
        float2 scaledCoord = fragCoord * scale;
        
        // Calculate the center of the canvas in normalized coordinates
        float2 center = size * 0.5 * scale;
        
        // Calculate the distance from the current fragment to the center
        float dist = distance(scaledCoord, center);
        
        // Calculate the direction vector from the center to the fragment
        float2 dir = scaledCoord - center;
        
        // Apply a sinusoidal wave based on the distance and time
        float sin = sin(dist * 70 - time * 6.28);
        
        // Offset coordinates by applying the wave effect in the direction of the fragment
        float2 offset = dir * sin;
        
        // Calculate the texture coordinates with the ripple effect applied
        float2 textCoord = scaledCoord + offset / 30;
        
        // Sample the composable shader using the adjusted texture coordinates
        return composable.eval(textCoord / scale);
    }
&quot;&quot;&quot;.trimIndent()
</code></pre>
<h5>結果</h5>
<p>このシェーダーがあれば、最小限のパフォーマンスコストでアプリの画像に深みやエレガントさを加えることができます。</p>
<p><a href="https://www.youtube.com/shorts/80QOTzNUHLg?rel=0">https://www.youtube.com/shorts/80QOTzNUHLg?rel=0</a></p>
<h3>3. プロシージャルグラフィックを有効にする</h3>
<p>プロシージャルグラフィックスは、視覚に訴えるようなインターフェースを静的なアセットに頼ることなく作成するのにピッタリです。</p>
<ul>
<li><strong>パターンの生成:</strong> ストライプ、グリッド、ノイズなどのプロシージャルテクスチャを作成します。</li>
<li><strong>シェイプアニメーション：</strong> モーフィングシェイプや移動パターンをデザインします。</li>
<li><strong>3D風エフェクト:</strong> 奥行きや遠近感を、実際の3Dレンダリングなしで表現でき ます。</li>
</ul>
<h4>例：アニメーションローディング画面</h4>
<p>ローディング画面は単調になりがちですが、AGSLを使うとダイナミックな芸術作品のように変身します。例えば、アプリの読み込み中にきらきらなアニメーション球体を表示して、ユーザーの目を引く演出が作成できます。</p>
<h5>AGSL シェーダコード：</h5>
<pre><code class="language-kotlin">@Language(&quot;AGSL&quot;)
val lightBallShader = &quot;&quot;&quot;
    uniform float2 size;       // The size of the canvas in pixels (width, height)
    uniform float time;        // The elapsed time for animating the light effect
    uniform shader composable; // Shader for the composable content
    
    half4 main(float2 fragCoord){
        // Initialize output color
        float4 o = float4(0.0);
        
        // Normalize coordinates relative to the canvas center
        float2 u = fragCoord.xy * 2.0 - size.xy;
        float2 s = u / size.y;

        //ライトボールエフェクトを計算するループ
        for (float i = 0.0; i &lt; 180.0; i++) {
            float a = i / 90.0 - 1.0;                       // Calculate a normalized angle
            float sqrtTerm = sqrt(1.0 - a * a);            // Circular boundary constraint
            float2 p = cos(i * 2.4 + time + float2(0.0, 11.0)) * sqrtTerm; // Oscillation term
            
            // Compute position and adjust with distortion
            float2 c = s + float2(p.x, a) / (p.y + 2.0);
            
            // Calculate the distance factor (denominator)
            float denom = dot(c, c);
            
            // Add light intensity with color variation
            float4 cosTerm = cos(i + float4(0.0, 2.0, 4.0, 0.0)) + 1.0;
            o += cosTerm / denom * (1.0 - p.y) / 30000.0;
        }

        // Return final color with an alpha of 1.0
        return half4(o.rgb, 1.0);
    }
&quot;&quot;&quot;.trimIndent()
</code></pre>
<h5>結果</h5>
<p>このシェーダーでアプリのローディング画面が未来的でスタイリッシュになり、待ち時間が短く感じられてより楽しめるものになります。</p>
<p><a href="https://youtube.com/shorts/pUTU0KRmFek?rel=0">https://youtube.com/shorts/pUTU0KRmFek?rel=0</a></p>
<h3>4. アプリのパフォーマンスを向上させる</h3>
<p>AGSLはパフォーマンス重視な場面で力を発揮し、レンダリングタスクをGPUに任せることで、スムーズで効率的なアニメーションを実現します。</p>
<ul>
<li><strong>効率的なアニメーション:</strong> 複雑なリアルタイムエフェクトをスムーズに処理します。</li>
<li><strong>バッテリー最適化</strong> 消費電力を最小限に抑えつつ、目を見張るようなエフェクトを実現します。</li>
</ul>
<h4>例：マップビューでの天気アニメーション</h4>
<p>プロダクトマネージャーから、マップビューに天気アニメーションのオーバーレイを追加するように頼まれたとします。従来の方法はパフォーマンス集約型ですが、GSLはCPUオーバーヘッドを最小限にしつつ、Androidの最適化したレンダリングパイプラインを活かして、効率よくGPUレンダリングができます。</p>
<h5>雨のAGSLシェーダーコード：</h5>
<pre><code class="language-kotlin">@Language(&quot;AGSL&quot;)
val rainShader = &quot;&quot;&quot;
    uniform float time;        // The elapsed time for animating the rain
    uniform float2 size;       // The size of the canvas in pixels (width, height)
    uniform shader composable; // Shader for the composable content

    // Generate a pseudo-random number based on input
    float random(float st) {
        return fract(sin(st * 12.9898) * 43758.5453123);
    }

    half4 main(float2 fragCoord) {
        // Normalize fragment coordinates to the [0, 1] range
        float2 uv = fragCoord / size;

        // Rain parameters
        float speed = 1.0;             // Speed of raindrops
        float t = time * speed;        // Time-adjusted factor for animation
        float density = 200.0;         // Number of rain &quot;drops&quot; per unit area
        float length = 0.1;            // Length of a raindrop
        float angle = radians(30.0);   // Angle of the rain (in degrees)
        float slope = tan(angle);      // Slope of the rain&#39;s trajectory

        // Compute grid position and animated raindrop position
        float gridPosX = floor(uv.x * density);
        float2 pos = -float2(uv.x * density + t * slope, fract(uv.y - t));

        // Calculate the raindrop visibility at this fragment
        float drop = smoothstep(length, 0.0, fract(pos.y + random(gridPosX)));

        // Background and rain colors
        half4 bgColor = half4(0.0, 0.0, 0.0, 0.0);  // Black transparent background
        half4 rainColor = half4(0.8, 0.8, 1.0, 1.0); // Light blue raindrop color

        // Blend the background and raindrop color based on drop visibility
        half4 color = mix(bgColor, rainColor, drop);

        return color; // Output the final color for the fragment
    }
&quot;&quot;&quot;.trimIndent()
</code></pre>
<h5>結果</h5>
<p>このシェーダーは雨をリアルに再現できる上に、雲や雪にも対応でき（後者2つのコードはここでは省略します）、ローエンドのデバイスでもスムーズに動作します。</p>
<p><a href="https://youtube.com/shorts/l63i3mQ_n2Y?rel=0">https://youtube.com/shorts/l63i3mQ_n2Y?rel=0</a></p>
<h2>さいごに</h2>
<p>見た目が素晴らしく、高いインタラクティブ性と最適なパフォーマンスを備えたエフェクトがAndroid アプリで作成できる。AGSLはそんな多機能なツールです。UIコンポーネントの強化、高度な画像処理、プロシージャルグラフィックスの生成、アニメーションが多い場面でのパフォーマンス向上など、AGSLを使えばアプリが一段と際立ちます。</p>
<p>AGSLがあれば、可能性は創造力次第です。さっそく試して、アプリに命を吹き込みましょう!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yao.xie/agsl_cover_image.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[Learning Roadside Station Podcast] General Managers MoM Reading Session]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-14-学びの道の駅Podcast-部長会議事メモを読む会-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-14-学びの道の駅Podcast-部長会議事メモを読む会-en/</guid>
            <pubDate>Tue, 18 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for day 14 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>🎅🎄</p>
<p>Hi, I’m Nakanishi from the Manabi-No-Michi-No-Eki (Learning Roadside Station) team. This year, the Learning Roadside Station project was officially initiated and subsequently reorganized into a team. As part of our initiatives, we also run an in-house podcast, and for this year’s Advent Calendar, we’d like to share more about it.</p>
<h1>What is the Manabi-No-Michi-No-Eki (Learning Roadside Station)?</h1>
<p>The Manabi-No-Michi-No-Eki (Learning Roadside Station) project aims to enhance the accessibility and effectiveness of the frequently held in-house study sessions. The initiative is driven by dedicated volunteers within the company, aiming to support study sessions and promote a culture of knowledge-sharing throughout the organization.</p>
<h1>Reading Session to understand the General Managers’ Meeting Minutes</h1>
<p>The Learning Roadside Station Podcast showcases interviews with individuals who organize study groups at KTC. This segment is called &quot;A Peek into the Study Session Next Door&quot;. This time, we will be speaking with Omori-san and Takagi-san, the hosts of the &quot;Reading Session for General Manager’s Meeting Minutes.&quot;</p>
<h1>Interview</h1>
<p><strong>Hoka-san:</strong> Could you start by introducing yourselves?</p>
<p><strong>Omori-san:</strong> Yes, I’m Omori from Corporate IT. I usually handle PC kitting at the Muromachi 16th Floor Center. I am a member of the Asset Platform Team, responsible for managing work devices and SaaS account licenses. I am also in charge of preparing and collecting devices for new employees.</p>
<p><strong>Takagi-san:</strong> Yes, I’m Takagi, also part of the Corporate IT team. I work in the Tech Service Team and commute between Jimbocho and Muromachi. As a member of the Service Desk, I manage internal inquiries and offer problem resolution. Specifically, I am responsible for managing Self-Service Management (GSM) and OPIT Management.</p>
<p><strong>Hoka-san:</strong> Thank you. Could you share how the &quot;Reading Session for General Managers’ Meeting Minutes&quot; began?</p>
<p><strong>Omori-san:</strong> It all began with Kinchan in Nagoya, who initially proposed the idea. In Corporate IT, we rarely have direct access to frontline business information. Therefore, we started this session to enhance productivity by sharing the General Managers’ Meeting minutes, discussing them, and learning from one another.</p>
<p><strong>Takagi-san:</strong> I feel the same way too. By reviewing the meeting minutes, we can anticipate business trends and apply that insight to our work. For example, we can proactively prepare before official requests are made, enhancing work efficiency.</p>
<p><strong>Hoka-san:</strong> What kind of impact has this session had so far?</p>
<p><strong>Omori-san:</strong> Although it may not always directly relate to our daily tasks, reviewing the meeting minutes helps us understand the background of projects, allowing us to make better and more informed proposals. This improves the quality of operations.</p>
<p><strong>Takagi-san:</strong> I completely agree. Reading the meeting minutes allows us to grasp business movements and respond more efficiently to unexpected requests. The insights gained from the meeting minutes are invaluable for making informed business decisions and proposals.</p>
<p><strong>Hoka-san:</strong> What are your future plans for this session?</p>
<p><strong>Takagi-san:</strong> I’d like this session to also serve as a platform for facilitators to challenge themselves and enhance their skills. We aim to encourage more new participants, creating a lively and engaging learning environment.</p>
<p><strong>Omori-san:</strong> I completely agree. To deepen business understanding, I’d like to continue reading and discussing the meeting minutes, enabling all participants to apply this knowledge to their work. We also aim to gather and organize information, allowing people to catch up later if needed.</p>
<p><strong>Hoka-san:</strong> Finally, is there any message you’d like to share with the listeners?</p>
<p><strong>Omori-san:</strong> This session is open to everyone. If you’re interested, please feel free to join us! Let&#39;s enhance our business knowledge and elevate work quality together.</p>
<p><strong>Takagi-san:</strong> We’re planning to create a Slack channel to share announcements. Participating in this session will help deepen your understanding of the business and enhance your work.</p>
<p><strong>Hoka-san:</strong> Thank you both for your time today.</p>
<p>This time, we explored the details of the Reading Session for the General Managers&#39; Meeting Minutes, including its operational background and future prospects. We hope you look forward to the next study session!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2024-12-podcast/minutes.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[Appium Meetup Tokyoの登壇レポート]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-18-Appium-Meetup-Tokyo-report/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-18-Appium-Meetup-Tokyo-report/</guid>
            <pubDate>Tue, 18 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Appium Meetup Tokyoの登壇レポート]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1>登壇レポート　岡（okapi）</h1>
<p>2025年2月20日にAppiumの勉強会「<a href="https://autifyjapan.connpass.com/event/342867/">Appium Meetup Tokyo</a>」に登壇してきました。</p>
<p>発表したスライドは、「効率的なアプリ自動化のためのガイドラインと実践方法」(<a href="https://speakerdeck.com/kintotechdev/xiao-lu-de-naapurizi-dong-hua-notamenogaidoraintoshi-jian-fang-fa">https://speakerdeck.com/kintotechdev/xiao-lu-de-naapurizi-dong-hua-notamenogaidoraintoshi-jian-fang-fa</a>) で確認できますので、ぜひご覧ください。</p>
<h1>Appium Meetup Tokyoを行った背景</h1>
<p>弊社の新規開発アプリの不具合発生率は、Webに比べると高い傾向（10倍近いことも）</p>
<p>↓</p>
<p>テスト負荷が高いので、アプリのテスト自動化をしなくては</p>
<p>↓</p>
<p>Appiumで自動化作業開始</p>
<p>↓</p>
<p>情報を探してもAppiumについて学ぶ場所が見つからない</p>
<p>↓</p>
<p>であれば自分達で開催してしまおう！！</p>
<p>ということで登壇してきました。</p>
<p><img src="/assets/blog/authors/pannnu.wai/Presentation.jpg" alt="Presentation"></p>
<h1>発表資料で意識した点</h1>
<p>①　
弊社では、QA業務に理解のある開発エンジニアがとても多いので、前向きに作業を進められやすく、いっしょに品質を高めて、作り上げられるといった利点があります。
その点をアピールするため、QAと開発チームで協力して「自動化しやすいアプリの作成」をしている点を主として説明しました。</p>
<p><img src="/assets/blog/authors/pannnu.wai/oka_img1.png" alt="開発チームとやり取り"></p>
<p>②　
質問もしやすいように、Appiumが分からない人でも理解できる資料を意識して作成していたため、
オンラインで7件、オフラインで7件の計14件の質問をいただき、盛り上がって嬉しかったです。</p>
<h3>質問例</h3>
<p>こちらにKINTOかんたん申し込みアプリログインの各ステップについて教えます。</p>
<h5>Q1 :</h5>
<p>開発とQAと協力している点で、IDを振る際に同じような要素がたくさんあるページではどのようにIDを振っていますか？
例えば、先ほどスライドに映っていたページで車種がたくさん表示される画面ではどのようにしていますか？</p>
<h5>A1：</h5>
<p>IDはオブジェクトや画面名ごとに定義しており、必ず一意になるように設定しています。
車種が表示されるページでは、開発チームで使用している車種情報の仕様書に沿ってIDを振っています。</p>
<h5>Q2：</h5>
<p>IDは、iOSとAndroidで別で定義することもあるのでしょうか？</p>
<h5>A2：</h5>
<p>Appiumで使用するIDについては、全てiOSとAndroidで共通のIDを設定しています。</p>
<h1>登壇した感想</h1>
<p>外部での発表は初めてでしたが、事前に社内の合同勉強会で練習を行い、本発表前にも十分な準備をしたため、問題なく登壇することができました。私たちのように外部での発表経験がない方も、まずは身近な人や社内で練習を重ね、その後に外部で発表すると、発表が苦手な方でも取り組みやすいので、おすすめです。</p>
<p>練習風景です</p>
<p>↓</p>
<p><img src="/assets/blog/authors/pannnu.wai/oka_img2.jpg" alt="岡さんの練習"></p>
<h1>今後に向けた意気込み</h1>
<p>日本国内にはAppiumを学ぶ場所がほとんど存在しないため、我々のAppiumに関する知見やノウハウを引き続き「Appium Meetup Tokyo」(<a href="https://autifyjapan.connpass.com/event/342867/">https://autifyjapan.connpass.com/event/342867/</a>) で発信し、良いコミュニティを築けるように頑張ります！</p>
<h1>登壇レポート　パンヌウェイ (Pann Nu Wai)</h1>
<p>今回はAppiumの取り組みについてお話ししたいと思います。</p>
<h1>Appium Meetup Tokyoを行った背景</h1>
<p>社内でテスト自動化を主に行っているパンヌウェイです。</p>
<p>Appium の取り組みを広げるために様々な活動を行っていますが、その一環として社内の合同勉強会で話をさせてもらいました。</p>
<p>この機会を通じて、私たちのチームがどのようにしてテスト自動化を進めているのかを共有しました。そして合同勉強会での発表内容をブラッシュアップし、今回はAppium Meetup Tokyoで発表することができました。</p>
<h1>発表資料で意識した点</h1>
<p>チーム内でAppium を利用して、モバイルアプリ自動テストできたことを４つのステップとして説明しました。</p>
<p>自動化テストエンジニアではない方向けにも、テスト仕様書を読めばソースコードも把握できるように、仕様書作成から自動化テストのパフォーマンスまで含めて記述しています。
<img src="/assets/blog/authors/pannnu.wai/pannnu_img1.png" alt="自動化テスト"></p>
<h1>登壇した感想</h1>
<p>外国人として日本語の発音は少し苦手ですが、登壇に向けて、何度も練習を重ねた結果、当日の登壇は無事に成功しました。
多くの方々からフィードバックをいただき、大変有意義な時間を過ごすことができました。</p>
<p>練習風景です</p>
<p>↓</p>
<p><img src="/assets/blog/authors/pannnu.wai/pannnu_img2.jpg" alt="パンヌの練習"></p>
<h1>今後に向けた意気込み</h1>
<p>また、私は外部にもAppiumに関する情報を発信していきたいと考えています。</p>
<p>社内の取り組みを外部に広めることで、多くの人々と知識や経験を共有し、さらにテスト自動化の分野を発展させていきたいと思っています。</p>
<p>次回の登壇に向けて、さらに内容を充実させ、より多くの価値を提供できるように頑張ります。これからもどうぞよろしくお願いいたします。</p>
<h1>最後に</h1>
<p>本記事は「Appium Meetup Tokyo」の登壇レポートですが、
第1回イベントの「開催レポート」(<a href="https://blog.kinto-technologies.com/posts/2025-02-20-Appium-Meetup-Tokyo-%E9%96%8B%E5%82%AC%E3%83%AC%E3%83%9D%E3%83%BC%E3%83%88/">https://blog.kinto-technologies.com/posts/2025-02-20-Appium-Meetup-Tokyo-開催レポート/</a>) も執筆していますので、併せてお読みいただけると嬉しいです。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/pannnu.wai/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[A Day in the Life of a KTC Cloud Security Engineer]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-14-CloudSecurityEngineer-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-14-CloudSecurityEngineer-en/</guid>
            <pubDate>Mon, 17 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Let’s take a look at the work of a Cloud Security Engineer at KTC.]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello, I&#39;m Kuwahara from the SCoE Group at the Osaka Tech Lab in KINTO Technologies (KTC).<br />
SCoE stands for Security Center of Excellence, a term that might not be widely recognized yet. In April 2024, KTC restructured the CCoE team into the SCoE group. To learn more about the SCoE Group, check out the article <a href="https://blog.kinto-technologies.com/posts/2024-05-13-SCoE/">SCoE Group: Leading the Evolution of Cloud Security</a>.<br />
For more details about the Osaka Tech Lab, KTC&#39;s Kansai base, visit <a href="https://blog.kinto-technologies.com/posts/2023-06-01-OsakaTechLab%E7%B4%B9%E4%BB%8B/">Introduction to Osaka Tech Lab</a>.<br /></p>
<p>The mission of the SCoE Group is to &quot;implement real-time guardrail monitoring and improvement activities&quot; across AWS, Google Cloud, and Azure environments. These activities focus on three key areas:<br /></p>
<ul>
<li>Preventing security risks</li>
<li>Continuously monitoring and analyzing security risks</li>
<li>Responding promptly when a security risk arises</li>
</ul>
<p>In this post, I’ll provide a closer look at the work of KTC’s cloud security engineers. </p>
<h1>A Day in the Life of a Cloud Security Engineer</h1>
<p>To provide a clearer picture, I’d like to walk you through a typical day for a cloud security engineer (please note that due to the sensitive nature of the field, some aspects cannot be shared in detail.)</p>
<h2>Checking alerts</h2>
<p>The first thing we do in the morning is check whether there are any high-risk alerts. We use CSPM (Cloud Security Posture Management) and threat detection services to understand the security status of the entire cloud environment and check whether there are any alerts that require immediate action. KTC uses services such as <a href="https://aws.amazon.com/jp/security-hub/">AWS Security Hub</a>, <a href="https://aws.amazon.com/jp/guardduty/">Amazon GuardDuty</a>, and <a href="https://docs.sysdig.com/en/docs/sysdig-secure/">Sysdig Secure</a> for CSPM and threat detection services. <img src="/assets/blog/authors/DKuwahara/CloudSecurityEngineer/sysdig.png" alt=""></p>
<p>In checking alerts, the following are considered:</p>
<ul>
<li><strong>Alert prioritization</strong>: Alerts are classified and prioritized based on their severity and scope of impact.</li>
<li><strong>Alert Triage</strong>: Identify the cause of an alert and take necessary action.</li>
<li><strong>Management of false positives (”over-detection”)</strong>: Security tools can sometimes produce false positives. This may cause activities that are actually not problematic to be reported as alerts. A cloud security engineer also manages these as part of alert handling.</li>
<li><strong>Identification of operations required for work</strong>: This is related to managing false positives, but some alerts may be triggered by operations required for work. For example, this includes maintenance tasks regularly performed by the person in charge of each product. A cloud security engineer identifies these activities and responds to them appropriately.</li>
</ul>
<p>This allows you to understand the security status of your entire cloud environment and check for any alerts that require immediate action.</p>
<h2>Information Gathering and Catch-up</h2>
<p>Next, a cloud security engineer catches up on cybersecurity trends and the latest information on cloud services such as AWS. The following are used as information sources.</p>
<ul>
<li><strong>X (formerly Twitter)</strong>: Cloud security engineers follow cybersecurity experts and industry leaders on X (formerly Twitter). They share the latest threat information and countermeasures, allowing for real-time information gathering.</li>
<li><strong>Official news and blogs from AWS and Google Cloud</strong>: Official information from cloud service providers is an important source of information about new feature releases and security updates. This helps cloud security engineers stay informed about new service launches, the latest technological trends, and best practices.</li>
<li><strong>Other news sites</strong>: By regularly checking news sites and blogs focused on cybersecurity, cloud security engineers can understand trends across the industry and catch up with the latest threats and attack methods.</li>
</ul>
<h2>Threat Detection with SIEM</h2>
<p>KTC uses <a href="https://www.splunk.com/ja_jp/products/splunk-cloud-platform.html">Splunk Cloud Platform</a> as its SIEM (Security Information and Event Management). It aggregates security-related logs in Splunk and provides an environment for cross-sectional analysis and monitoring of logs.</p>
<p>That day, I discovered a suspicious log on the Splunk dashboard. The log stated: &quot;Attempted to create a resource for a service restricted by Google Cloud&#39;s organizational policy, but the operation failed.&quot;<img src="/assets/blog/authors/DKuwahara/CloudSecurityEngineer/siem.png" alt=""></p>
<p>We were able to determine the general activity from the information in the dashboard for Google Cloud Audit logs that we created using Splunk, but we will investigate in more detail.</p>
<p>First, we identify users who are repeatedly retrying to create resources for services restricted by Google Cloud organizational policies. User information is masked before being logged in Google Cloud&#39;s audit log (audicy_denied), so users cannot be identified from this log alone.  We identify users by analyzing cross-sectional logs together with terminal logs, etc. We created a query to use for this analysis and identified the users in question.</p>
<p>Next, we create queries to further analyze the behavior of the identified users and analyze the logs.
 <img src="/assets/blog/authors/DKuwahara/CloudSecurityEngineer/siem2.png" alt=""></p>
<p>It appears that the identified user is attempting to use the AI/ML service, <a href="https://cloud.google.com/vertex-ai">Vertex AI</a>. Since no requests for the use of Compute services were made in the relevant project, the use of Compute services is restricted by the organizational policy. When using Notebook with Vertex AI, a Compute Engine (GCE) instance is launched.  Therefore, this is a violation of the organizational policy.</p>
<p>Ultimately, we determined that this was a harmless activity, citing a omission of information about the services to be used when applying for a new Google Cloud project.</p>
<h2>Cost optimization for Cloud Vendor-Native Security Services</h2>
<p>Security services provided by cloud vendors are charged on a pay-as-you-go billing basis, so as cloud resources increase, the security service charge also increases.</p>
<p>Our idea of ​​&quot;security&quot; is <strong>&quot;security for business,&quot; and &quot;security that hinders business&quot; is unacceptable.</strong> Therefore, the &quot;balance between security and cost&quot; is also an important point, and cost optimization of security services is also included in the SCoE Group&#39;s mission.</p>
<p>On that day, I investigated the potential for cost optimization of several security services that accounted for a high proportion of the overall cost. <img src="/assets/blog/authors/DKuwahara/CloudSecurityEngineer/cost.png" alt=""></p>
<p>The graph above shows the services that were targeted in the analysis this time. Among them, I paid particular attention to <a href="https://aws.amazon.com/jp/config/">AWS Config</a>. (Specific item names have been masked.)</p>
<p>AWS Config is a service for auditing, evaluating, and recording the configuration of AWS resources. Until November 2023, the only recording method for AWS Config was “a method that records every time a change in resource configuration has occurred.&quot; This method is called &quot;recording frequency: continuous recording.&quot; In other words, if the frequency of resource changes is high, the number of records in AWS Config increases, and the usage fee increases proportionally.</p>
<p>As an example, let&#39;s look at network-related events. The data below is a graph showing the number of VPC and network-related configuration changes in AWS account over a one-week period. <img src="/assets/blog/authors/DKuwahara/CloudSecurityEngineer/ENI.png" alt=""></p>
<p>You can see that <code>CreateNetworkInterface</code> and <code>DeleteNetworkInterface</code>, which correspond to the creation and deletion of Elastic Network Interfaces (ENIs), occur approximately 17,000 times per day. KTC is using Fargate, an Amazon Elastic Container Service (ECS). For this reason, the creation/ deletion of ENI occur each time an ECS task (container) starts/stops. Under these circumstances, if you have AWS Config set to “Recording frequency: Continuous recording,&quot; the number of AWS Config records associated with these changes will be huge, and the amount you are charged will increase accordingly.</p>
<p>However, starting in November 2023, a new feature that allows you to select &quot;Recording Frequency: Daily Recording&quot; was added to AWS Config. This new feature allows us to adjust the recording frequency for each resource type, providing the flexibility in balancing security and cost. In general, this setting is believed to help optimize the cost of using AWS Config. <img src="/assets/blog/authors/DKuwahara/CloudSecurityEngineer/aws_config_setting.png" alt=""></p>
<p>However, this is only the case if you are not using <a href="https://aws.amazon.com/jp/controltower/">AWS Control Tower</a>. AWS Control Tower is a service for centrally managing the governance of multiple AWS accounts.</p>
<p>If you use AWS Control Tower to manage AWS Config in your AWS account, check <a href="https://docs.aws.amazon.com/controltower/latest/userguide/getting-started-guidance.html">Guidance for creating and modifying AWS Control Tower resources</a>.</p>
<p>Please pay attention to the following sentence at the beginning of the guidance:</p>
<pre><code>Do not modify or delete any resources created by AWS Control Tower, including resources in the management account, in the shared accounts, and in member accounts. If you modify these resources, you may be required to update your landing zone or re-register an OU, and modification can result in inaccurate compliance reporting.
</code></pre>
<p>As this statement indicates, <strong>ｍodifying or deleting resources created by AWS Control Tower by any means other than AWS Control Tower is not recommended</strong>.</p>
<p>Specifically, as of December 2024, AWS Control Tower does not provide the feature to modify the frequency of AWS Config recording. Therefore, changing the recording frequency of AWS Config under AWS Control Tower management is not recommended, and the official documentation also states that it may cause problems.</p>
<p>Taking into account the content of the official documentation, I also contacted AWS Support just to be sure and received the same opinion.</p>
<p>In this way, when “a setting itself is possible but poses the risk of problems or is not recommended,” it becomes difficult to maintain stable cloud security and governance. The result could be <strong>&quot;security that hinders business&quot;</strong>.</p>
<p>In light of the above, we decided to postpone changing the recording frequency of AWS Config for now and submitted an improvement request to AWS Support. I believe that proposing such an improvement request to enhance the convenience of cloud services is a modest yet very important initiative.</p>
<h2>Preparation for a Security Study Session</h2>
<p>Finally, I created presentation materials for our regularly held in-house security and privacy study sessions.</p>
<p>The SCoE Group has formulated &quot;Cloud Security Guidelines&quot; that summarize the key points of cloud security for&quot;requirements definition,&quot; &quot;design,&quot; and &quot;development&quot; phases of product development, and has made them available in-house. This set of guidelines is an important resource for ensuring compliance with the security policies of the group companies to which KTC belongs, minimizing security risks, and supporting efficient development.</p>
<p>I host study sessions to raise awareness and enhance understanding of the Cloud Security Guidelines. In the study sessions, I provide detailed explanations of each item in the guidelines, while also incorporating specific cases and practical advice.</p>
<p>On that day, I created presentation materials on IAM (Identity and Access Management) best practices, ensuring the materials were concise enough to fit within a 20-minute timeframe. <img src="/assets/blog/authors/DKuwahara/CloudSecurityEngineer/SecurityStudySession.png" alt=""></p>
<h1>Conclusion</h1>
<p>I’ve shared a glimpse into a day in the life of a cloud security engineer at KTC. While this is just a snapshot, I hope it helped you gain a better understanding of what the role entails.</p>
<p>The SCoE Group is currently looking for new team members. Whether you have hands-on experience in cloud security or are simply passionate about the field, we’d love to hear from you. Feel free to reach out to us.</p>
<p>For more information, please <a href="https://hrmos.co/pages/kinto-technologies/jobs/1811937538128224276">check here</a>.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[Learning Roadside Station Podcast] KINTO Factory Automotive Study Session]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-13-学びの道の駅Podcast-Factory自動車勉強会-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-13-学びの道の駅Podcast-Factory自動車勉強会-en/</guid>
            <pubDate>Fri, 14 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is part of Day 13 of the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a></p>
<p>Hi, I’m Nakanishi from Learning Roadside Station. This year, the Learning Roadside Station project was officially launched and structured as an organization. As part of our initiatives, we also run an in-house podcast, and for this year’s Advent Calendar, we’d like to share more about it.</p>
<h1>What is &quot;Learning Roadside Station&quot;?</h1>
<p>“Learning Roadside Station” is a project aimed at making in-house study sessions, which are frequently held, more accessible and effective. The initiative is led by passionate volunteers within the company, with the goal of supporting study sessions and fostering a culture of knowledge sharing across the organization.</p>
<h1>The KINTO Factory team&#39;s Automotive Study Group</h1>
<p>The Learning Roadside Station Podcast features interviews with employees who organize study groups within the company. This segment is called &quot;A Peek into the Study Session Next Door&quot;. This time, we interviewed Miura-san from the KINTO Factory Team.</p>
<h1>Interview</h1>
<p><strong>Hoka-san:</strong> Thank you, Miura-san. Could you introduce yourself and tell us about your role?</p>
<p><strong>Miura-san:</strong> Thank you very much. Officially, I am the Team Leader of the KINTO Factory Team, part of the Project Promotion Group in the Project Development Division. The KINTO Factory Team develops products not only with KTC but also in collaboration with KINTO&#39;s General Planning Department.</p>
<p><strong>Hoka-san:</strong> For this interview, we’d like to focus on the fact that you are studying automobiles within the Factory Team. Could you tell us more about that?</p>
<p><strong>Miura-san:</strong> First of all, I want everyone to enjoy their work. I believe that understanding the products we sell makes the work more engaging and rewarding. Since we handle automobile-related products, having technical knowledge about cars makes the development process even more enjoyable. Having a background in the automotive industry, I felt that sharing my knowledge would help us make better proposals, which is why I started the study group.</p>
<p><strong>Hoka-san:</strong> How did you decide to share that knowledge?</p>
<p><strong>Miura-san:</strong> We hold online study sessions. At first, session was for an hour, but now we&#39;ve reduced it to 30 minutes and hold it once a month. Recently, we’ve been discussing the evolution of in-car networks and navigation systems.</p>
<p><strong>Hoka-san:</strong> How have the participants responded to the study sessions?</p>
<p><strong>Miura-san:</strong> Based on survey feedback, many participants found the information fresh and valuable since they wouldn’t normally come across such details in their daily work. The number of participants has not decreased, and they are listening with interest.</p>
<p><strong>Hoka-san:</strong> How long have you been running these sessions?</p>
<p><strong>Miura-san:</strong> At first, the sessions were one hour long, but now it’s 30 minutes and we hold them once a month.</p>
<p><strong>Hoka-san:</strong> What topics have you covered in your study sessions so far?</p>
<p><strong>Miura-san:</strong> Recently, we talked about the evolution of in-car networks and navigation systems. We also discussed how the automotive industry is evolving based on insights from CES in Las Vegas.</p>
<p><strong>Hoka-san:</strong> Did you attend the CES in Las Vegas?</p>
<p><strong>Miura-san:</strong> I wasn’t able to attend in person, but I shared my own thoughts based on the exhibition content that are open to the public on the Internet.</p>
<p><strong>Hoka-san:</strong> Do you have any upcoming study sessions planned?</p>
<p><strong>Miura-san:</strong> Next, we’ll be discussing the vehicle installation process. Using dealer manuals, we’ll explore how different parts are assembled and installed within vehicles.</p>
<p><strong>Hoka-san:</strong> What should I do if I want to join the study sessions?</p>
<p><strong>Miura-san:</strong> Our sessions are conducted online, so anyone interested is free to participate. We’re also working on creating a system to visualize and organize study session information for easier access.</p>
<p><strong>Hoka-san:</strong> What inspired you to start the study group?</p>
<p><strong>Miura-san:</strong> It started as a team-building initiative. Our group transitioned from a project team to a formal team, and I wanted to ensure that everyone had a deeper understanding of automobiles.</p>
<p><strong>Hoka-san:</strong> Lastly, do you have a message for your colleagues?</p>
<p><strong>Miura-san:</strong> Since we work in the automotive industry, I believe that deepening our knowledge on vehicles makes our work more enjoyable and meaningful. If you’re interested, I encourage you to join our study sessions!</p>
<p><strong>Hoka-san:</strong> Thank you very much, Miura-san. I hope you will continue to share the fascinating world of automobiles with your colleagues through these study sessions.</p>
<p>This time, we shared insights about the Factory Team, the background to its operations, and future prospects. Please look forward to the next study session!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2024-12-podcast/factory.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[3つの生成AIを使って、喋るオリジナルキャラクターを作ってみた（Adobe Firefly、TTSMaker、Runway）]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-07-creating_a_mascot_with_generative_AI/</guid>
            <pubDate>Fri, 14 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[3つの生成AI（Adobe Firefly、TTSMaker、Runway）を使用した喋るオリジナルキャラクターの作成方法]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは！KINTOテクノロジーズでデザイナーをしている桃井（<a href="https://x.com/momoitter">@momoitter</a>）です。</p>
<p>クリエイティブ室に所属しており、<a href="https://www.kinto-technologies.com/">コーポレートサイト</a>や<a href="https://corp.kinto-jp.com/mascot/">くもびぃ</a>（KINTO公式マスコットキャラクター）関連サイトなどの制作に、最先端のWEB表現を取り入れながら携わっています。</p>
<p>2024年11月に「超本部会」という会社のイベントが開催され、私はそのイベントのオープニングムービーの作成を担当しました。その冒頭のワンシーンで、3つの生成AIを使用し、イベントのスタートを宣言する女性のキャラクターを作成しました。</p>
<p>今回はこちらの喋るオリジナルキャラクター作成の工程や、作成時にどのようなことを考えたかをご紹介します。</p>
<ul>
<li>独自のキャラクターを作成して、言葉を喋らせたい</li>
<li>AIを取り入れて印象に残る映像を手軽に作成したい</li>
</ul>
<p>という方はぜひご覧ください！</p>
<h2>背景</h2>
<p>イベント全体のクリエイティブを監修するアートディレクターからのオーダーとしては、「<a href="https://www.kinto-technologies.com/">コーポレートサイト</a>のKVで使われている動画を再編集して1分のオープニングムービーを作る」というものでした。</p>
<p>ですが、ただ再編集するだけでは社員からすると既視感があるので、華やかにイベントのスタートを切れるように、会場の空気を惹きつける何かが必要と感じていました。</p>
<p>そこで目をつけたのが、弊社のSlack内にある社内向け生成AIツール。
AIを駆使し、サプライズとしてその社内向け生成AIツールを擬人化した映像を作れば、注目があつまるのではないかと考えました。</p>
<h2>使用したAI</h2>
<p>喋るオリジナルキャラクター作成にあたり、下記3つのAIを使用しました。</p>
<ol>
<li>Adobe Firefly（キャラ画像生成）</li>
<li>TTSMaker（テキスト読み上げ）</li>
<li>Runway（キャラを喋らせる）</li>
</ol>
<p>ここから先は、これらのAIを使用しどのように動画を生成したかをご紹介します。</p>
<h2>1.キャラ画像生成</h2>
<h3>Adobe Firefly</h3>
<p>Adobeが提供している画像生成AIツール。
Adobe Stockなど著作権フリーの画像を学習しているので、著作権の侵害の心配なく使用できます。</p>
<p>一般的に有名な画像生成AIでは、著作権フリーを謳っているものも多いですが、アニメのキャラに似たものが生成できてしまったり実際はグレーなものが多いものの、社内イベントであるとはいえ著作権はしっかりクリアしておきたかったため、そのような問題を気にせず使用できるこちらのAIをセレクトしました。</p>
<p><a href="https://www.adobe.com/jp/products/firefly.html">Adobe Firefly</a></p>
<h3>画面上の操作</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly02.jpg" alt="firefly02"></p>
<p>Fireflyを開くとこのような画面になっています。</p>
<p>大まかにいうと、下の入力エリアに画像を生成するためのプロンプトを打ち込み、左側のメニューで縦横比・構成・スタイル・トーンなどの調整を行います。</p>
<p>今回は試行錯誤の末、「3dのキャラクター、女性、ピンクの髪、背景は白、上半身、白くてシンプルでデジタルな服装、正面を向く」というプロンプトで生成していきました。</p>
<h3>量産</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly03.jpg" alt="firefly03"></p>
<p>プロンプトがある程度固まってくると、良い生成結果に出会うためには運次第でもあるので、100～200枚をひたすら生成しました。</p>
<h3>選定</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly04.jpg" alt="firefly04"></p>
<p>イベントの始まりをフレッシュにスタートさせたかったので、「AIオペレーター」的なオフィシャル感、安心感があるキャラクターが理想でした。</p>
<p>そのため</p>
<ul>
<li>幼すぎる</li>
<li>服が奇抜</li>
<li>顔が怖い</li>
</ul>
<p>など、イメージから遠いものは除外していきました。</p>
<h3>決定した画像</h3>
<p><img src="/assets/blog/authors/momoi/250226/01_firefly05.jpg" alt="firefly05"></p>
<p>細かい選定作業を経て、最終的にクールでありながら親しみも感じられるこちらの画像に決定しました。</p>
<h2>2.テキスト読み上げ</h2>
<h3>TTSMaker</h3>
<p>打ち込んだテキストを音声に変換するAI音声ジェネレーター。</p>
<p>このようなAI準拠のテキスト読み上げサービスは多数存在するのですが、有料だったり無料でもクレジット表記をしないといけないものが多かったので、無料かつクレジット表記無しで利用できるこちらのツールを使用しました。</p>
<p><a href="https://ttsmaker.com/ja">TTSMaker</a></p>
<h3>画面上の操作</h3>
<p><img src="/assets/blog/authors/momoi/250226/02_tts01.jpg" alt="tts01">
TTSMakerを開くとこのような画面になっています。</p>
<p>手順としては</p>
<ol>
<li>言語を選択</li>
<li>読み上げさせたいテキストを入力</li>
<li>サンプル音声を視聴しながら声色を選択</li>
<li>しゃべる速さ、声の高さなど、詳細の設定</li>
<li>変換</li>
</ol>
<p>になります。</p>
<p>今回はAIオペレーター的な、オフィシャル感、安心感がある声が理想だったので、サンプル音声を聴き比べながら、「406 - yuki つみゆき-🇯🇵 japanese female」の声色を選択し、「もちろんです。超本部会を始めます。」というテキストを読み上げてもらいました。</p>
<h3>実際の音声</h3>
<p><a href="https://www.youtube.com/watch?v=r4Zw2by669I">https://www.youtube.com/watch?v=r4Zw2by669I</a></p>
<h2>3.キャラを喋らせる</h2>
<h3>Runway</h3>
<p>AIを活用して簡単に高品質な動画を生成・編集できるツール。</p>
<p>「Lip Sync Video」という、人物やキャラクターの静止画を、音声に合わせて喋らせる機能があったため、このツールを使用しました。</p>
<p><a href="https://runwayml.com/">Runway</a></p>
<h3>画面上の操作</h3>
<h4>1.「Generative Audio」内、「Lip Sync Video」を開き、先ほど生成したキャラクターの画像をドラッグ＆ドロップ</h4>
<p><img src="/assets/blog/authors/momoi/250226/03_runway01.jpg" alt="runway01"></p>
<h4>2.キャラクターの画像の顔の範囲の認識が合っているか確認し、問題なければ「upload audio」をクリック</h4>
<p><img src="/assets/blog/authors/momoi/250226/03_runway02.jpg" alt="runway02"></p>
<h4>3.先程生成した音声をドラッグ＆ドロップし、「Generate」をクリック</h4>
<p><img src="/assets/blog/authors/momoi/250226/03_runway03.jpg" alt="runway03"></p>
<h3>生成された映像</h3>
<p><a href="https://www.youtube.com/watch?v=usY4yB9Z1YA">https://www.youtube.com/watch?v=usY4yB9Z1YA</a></p>
<p>音声に合わせて喋るキャラクターの映像が生成されました。</p>
<h3>応用編 その1</h3>
<p><a href="https://www.youtube.com/watch?v=raYsnhwZONo">https://www.youtube.com/watch?v=raYsnhwZONo</a></p>
<p>アップロードする音声を曲にすると、キャラクターに歌わせることもできます。</p>
<h3>応用編 その2</h3>
<p><a href="https://www.youtube.com/watch?v=3edVClgoLug">https://www.youtube.com/watch?v=3edVClgoLug</a></p>
<p>このように人物の静止画を喋らせることも可能です。</p>
<p>先日の社内勉強会（東京開催）で、急遽登壇者が大阪からの出張ができなくなってしまったため、静止画とボイスメモを用意してもらい、このようなAI生成映像で発表を行いました。</p>
<h2>まとめ</h2>
<p><img src="/assets/blog/authors/momoi/250226/05_matome01.jpg" alt="matome01">
<em>会場で投影された際の様子</em></p>
<p>クリエイティブ系の生成AIにはそれぞれ特性があり、できることには限りがあります。
それらの特性を理解し組み合わせることで、今回は単一のツールでは作成できないクオリティの映像を生み出すことができました。</p>
<p>イベント当日はこのキャラクターが大きいスクリーンで投影され、ありがたいことに</p>
<ul>
<li>あれすごかったですね、どうやって作ったんですか！？</li>
<li>クオリティ高くて、外注してるのかと思いました。</li>
</ul>
<p>など社員から声をかけていただくことも多く、狙ったインパクトを残すことができたかなと思います。</p>
<p>それぞれのAIツールの操作としてはとても簡単で、非クリエイターでも使用できるようなものです。
アイデアさえあればこのような印象に残る映像を作成できるので、この記事を見て気になった方はぜひ実践してみてください！</p>
<p>最後までご覧いただき、ありがとうございました。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/momoi/250226/00_main_visual.jpg" length="0" type="image/jpg"/>
        </item>
        <item>
            <title><![CDATA[VDP(Vulnerability Disclosure Program)カイゼン活動紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-14_VDP/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-14_VDP/</guid>
            <pubDate>Fri, 14 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズのVDP（脆弱性開示プログラム）改善活動と、その成果について紹介します。]]></description>
            <content:encoded><![CDATA[<!-- ↓マークダウン チートシート↓ -->

<h1>自己紹介</h1>
<p>KINTOテクノロジーズにて主にプロダクトセキュリティ、セキュリティガバナンス業務に携わっている森野です。
RB大宮アルディージャとちいかわが好きです。
ここ10年はサイバーセキュリティ、情報セキュリティに関する仕事に携わっています。それ以前はWebアプリケーションエンジニアとしてWeb効果測定システムやECサイトのフロントエンドシステムの開発、運用に長く携わっていました。
本記事では当社のVDP(Vulnerability Disclosure Program)カイゼン活動について紹介させて頂きます。</p>
<h1>VDP(Vulnerability Disclosure Program)とは</h1>
<p>企業や組織が外部のセキュリティ研究者やホワイトハッカーから脆弱性の報告を受け取るための制度です。
2023年10月に楽天グループが公開サーバーに「security.txt」を配置しVDPを開始した事で世間の認知度が向上しました。</p>
<p><a href="https://xtech.nikkei.com/atcl/nxt/column/18/00001/08552/">参考：楽天が公開サーバーにテキスト設置、セキュリティー向上に役立つ「security.txt」</a></p>
<h1>security.txtとは</h1>
<p>2022年4月に「RFC 9116：A File Format to Aid in Security Vulnerability Disclosure」として企業や組織が脆弱性の開示方法を説明しセキュリティ研究者などが発見した脆弱性を報告しやすくするために定義されたものです。
2023年11月に当社もsecurity.txtを配置しました。</p>
<p><img src="/assets/blog/authors/masamorino/security_txt.jpg" alt="security.txt"></p>
<h1>security.txt設置後の反応</h1>
<p>報奨金の有無を確認する問い合わせが殆どで（当社は報奨金制度は提供していない）、KINTO/KTCの脆弱性情報を持っているのか否か不明な報告が多く寄せられました。</p>
<p><img src="/assets/blog/authors/masamorino/email01.jpg" alt="電子メール1">
<img src="/assets/blog/authors/masamorino/email02.jpg" alt="電子メール2"></p>
<h1>ホワイトハッカーからの報告キタ━(ﾟ∀ﾟ)━!</h1>
<p>2024年8月に当社サービスに存在する脆弱性の報告がありました。私たちのグループで報告内容を検証した結果、事実であることが確認できたため開発グループに依頼して脆弱性を修正しました。</p>
<p><img src="/assets/blog/authors/masamorino/email03.jpg" alt="電子メール3"></p>
<h1>VDPのカイゼン</h1>
<p>VDPの意義を実感できた一方、下記の課題感があったため、Issue Hunt社が提供しているVDPサービスの活用を2024年11月から開始しました。</p>
<ul>
<li>懸賞金の有無などVDPガイドラインの提示</li>
<li>報告対象となるWebサービスやアプリケーションの提示</li>
<li>報告テンプレートの提示</li>
</ul>
<p>開始から2025年3月7日現在、6件の報告があり内2件は対応が必要な脆弱性と判断して修正を行いました。予想以上の成果に正直驚いています。</p>
<p>Issue Hunt社のサイトに導入事例として当社が紹介されているので宜しければそちらもご覧ください。</p>
<p><a href="https://issuehunt.jp/cases/kinto-technologies">セキュリティ向上の新常識！車サブスク業界におけるVDP導入の成功事例</a></p>
<h1>P3NFEST Bug Bounty 2025 Winterへのプログラム提供</h1>
<p>前述の通り、当社では報奨金制度は提供していません。
しかし、報奨金制度の効果検証および将来のインターネットの安全を担う学生を応援することを目的にIssue Hunt社主催の学生向けバグバウンティプログラムにプログラムを提供することにしました。</p>
<p>バグバウンティ対象のサービスは以下の通りです。</p>
<ul>
<li>KINTOテクノロジーズコーポレートサイト</li>
<li>KINTO Tech Blog（当サイト）</li>
</ul>
<p>開催期間は2025年2月17日(月)から2025年3月31日(月)までです。</p>
<p>詳細はイベント情報ページをご覧ください。学生の皆さんの挑戦をお待ちしております。
<a href="https://issuehunt.jp/events/2025/winter/p3nfestbugbounty">P3NFEST Bug Bounty 2025 Winter</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[ "Aiming to Be a Company Where Generative AI is Second Nature" – Culture Building and In-House Training Initiatives]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-11-ktc-generative-ai-seminar-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-11-ktc-generative-ai-seminar-en/</guid>
            <pubDate>Thu, 13 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[With the goal of making generative AI a natural part of everyday work, we introduce KINTO Technologies’ company-wide generative AI training, efforts to promote AI tool adoption, and initiatives to create new value through AI.]]></description>
            <content:encoded><![CDATA[<p>Hello!
I am Wada (<a href="https://twitter.com/cognac_n">@cognac_n</a>), a Generative AI Evangelist at KINTO Technologies (KTC) as part of the Generative AI Development Project.</p>
<p>At KTC, the adoption of generative AI is advancing across various areas as we share in the below articles:</p>
<p>@<a href="https://blog.kinto-technologies.com/posts/2024-12-12-schema-review-by-llm-en/">card</a></p>
<p>@<a href="https://blog.kinto-technologies.com/posts/torii-ai_tool_slack-en/">card</a></p>
<p>@<a href="https://blog.kinto-technologies.com/posts/2024-12-08-Using-AI-en/">card</a> </p>
<p>:::Other KTC Tech Blogs on generative AI
@<a href="https://blog.kinto-technologies.com/posts/2024-06-17-pr-agent-en/">card</a>
@<a href="https://blog.kinto-technologies.com/posts/2024-12-07-mvp-en/">card</a>
@<a href="https://blog.kinto-technologies.com/posts/2024-12-09-openai-for-java-en/">card</a>
@<a href="https://blog.kinto-technologies.com/posts/2024-08-14-%E6%9C%AC%E5%BD%93%E3%81%AB%E3%81%99%E3%81%94%E3%81%84%EF%BC%81%E9%9F%B3%E6%A5%BD%E7%94%9F%E6%88%90AI%E3%81%A7%E5%8B%9D%E6%89%8B%E3%81%AB%E7%A4%BE%E6%AD%8C%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%9F-en/">card</a>
@<a href="https://blog.kinto-technologies.com/posts/2024-08-20-prompty-review-en/">card</a>
:::</p>
<p>Both engineers and non-engineers are adopting generative AI in ways tailored to their roles and tasks. To support this, the Generative AI Development Project has been working toward a vision of making generative AI second nature across the company. In this article, we’d like to share what we&#39;ve been doing.</p>
<h2>1. Introduction of the Generative AI Development Project</h2>
<p>This initiative was launched in January 2024 to promote the use of generative AI within the company.<br>The Generative AI Development Project currently serves three main functions.</p>
<h3>Three key functions of the project</h3>
<p><img src="/assets/blog/authors/s.wada/20241211/image_4.webp" alt="Functions of the Generative AI Development Project"> <em>Functions of the Generative AI Development Project</em></p>
<p>These functions are not independent but are part of a continuous cycle aimed at accelerating the adoption of generative AI in various scenarios:</p>
<ol>
<li><strong>Generating innovative ideas</strong></li>
<li><strong>Assessing feasibility</strong></li>
<li><strong>Implementing and delivering solutions</strong></li>
<li><strong>Expanding through case study deployment</strong></li>
</ol>
<p>The goal is to speed up this cycle, ensuring generative AI is utilized effectively in every scenario.</p>
<p><img src="/assets/blog/authors/s.wada/20241211/image_1.webp" alt="Generate AI Development Project functions and cycles"> <em>Generate AI Development Project functions and cycles</em></p>
<p>In this article, we will introduce our activities with a focus on <strong>education and training</strong>.</p>
<h2>2. The Basic Concept of the Education and Training System</h2>
<p>To realize the vision of &quot;a company where everyone naturally utilizes generative AI&quot;, we have adopted three key principles:</p>
<ol>
<li><strong>Generative AI is not just for specialists</strong></li>
<li><strong>There are “optimal utilization levels” based on each role</strong></li>
<li><strong>Emphasis on step-by-step learning, from basics to advanced expertise</strong></li>
</ol>
<p><img src="/assets/blog/authors/s.wada/20241211/image_2.webp" alt="Basic framework of the training system"> <em>Basic idea of the training system</em></p>
<p>At KTC, multiple instructors conduct training sessions on a variety of topics. In addition, the training participants include both engineers and non-engineers, covering a wide range of roles and responsibilities. To ensure consistently high-quality training, even in such a diverse environment, we established a shared understanding of the fundamental principles. This approach wasn’t predetermined from the start; instead, it gradually developed through ongoing refinements driven by internal feedback.</p>
<h2>3. Training System Implementation</h2>
<p>Based on these three core principles, we have developed a structured, step-by-step training program.</p>
<table>
<thead>
<tr>
<th>Training Name</th>
<th>Target Audience</th>
<th>Content</th>
</tr>
</thead>
<tbody><tr>
<td>Beginner</td>
<td>All employees</td>
<td>Basic knowledge of Generative AI and Prompt Engineering. The foundational first step for everything</td>
</tr>
<tr>
<td>Case Study</td>
<td>All employees</td>
<td>Introduction of internal and external use cases. Develop the ability to take best practices and adapt them independently</td>
</tr>
<tr>
<td>Improved Office Productivity</td>
<td>Selected employees from each department (Ambassador system)</td>
<td>Master generative AI as a tool to create business value. Drive business process transformation with AI integration. Become an in-house advocate and evangelist for AI utilization.</td>
</tr>
<tr>
<td>Generalist</td>
<td>People involved in system development</td>
<td>Learn key aspects of system development using generative AI. Develop the ability to assess technology, create value, and validate outcomes</td>
</tr>
<tr>
<td>Engineering</td>
<td>The engineer responsible for implementation</td>
<td>Build practical implementation skills for system development using generative AI. Gain hands-on knowledge and experience to effectively deliver value.</td>
</tr>
</tbody></table>
<p><img src="/assets/blog/authors/s.wada/20241211/image_3.webp" alt="Role-Specific Journey"></p>
<p>Each journey uniquely defines the target level of generative AI utilization.</p>
<p><img src="/assets/blog/authors/s.wada/20241211/image_5.webp" alt="Utilization Level Definition by Training_1"> <img src="/assets/blog/authors/s.wada/20241211/image_7.webp" alt="Utilization Level Definition by Training_2 (Engineering)"></p>
<h2>4. Emerging Value</h2>
<h3>How engineers are changing</h3>
<ul>
<li>Proposing the addition of generative AI features to existing systems</li>
<li>Planning and pitching new services utilizing generative AI</li>
<li>Independently developing AI-powered tools to improve work efficiency</li>
<li>Advanced utilization of generative AI tools such as GitHub Copilot</li>
</ul>
<h3>How non-engineers are changing</h3>
<ul>
<li>Active use of generated AI in day-to-day operations</li>
<li>Improved technical communication with AI support</li>
<li>Taking on the challenge of developing simple tools</li>
</ul>
<p>As I introduced the blog at the beginning, employees who have undergone training are now actively leveraging generative AI within their roles and responsibilities. From daily tasks and communication to system development, generative AI is driving efficiency improvements and adding value across various scenarios. The types of inquiries we receive have also evolved—from &quot;I don’t know what’s possible&quot; to &quot;I tried it! How can I improve it further?&quot; or &quot;I believe we can achieve something like this, can we collaborate?&quot; With growing generative AI literacy, employees are no longer hesitant to &quot;try first&quot;, and they are developing an intuitive sense of &quot;this seems possible—and valuable&quot;. <img src="/assets/blog/authors/s.wada/20241211/image_6.webp" alt="Change"></p>
<h2>5. Future Prospects</h2>
<p>Generation AI technology is evolving rapidly. KTC/KINTO is beginning to achieve &quot;commonplace AI usage&quot;, but there is no definitive goal for what &quot;commonplace&quot; should be.</p>
<p><strong>&quot;Aiming to be a company where using generative AI is second nature for everyone&quot;.</strong></p>
<p>We will continue pushing forward with our initiatives!</p>
<h2>We Are Hiring!</h2>
<p>KINTO Technologies is looking for passionate individuals to help drive AI adoption in our business. We’re happy to start with a casual interview. If you’re even slightly interested, feel free to reach out via the link below or through <a href="https://twitter.com/cognac_n">X DMs</a>. We look forward to hearing from you! Great place to stay!</p>
<p>@<a href="https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303115">card</a></p>
<p>Thank you for reading all the way to the end!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/s.wada/20241211/cover.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[SwiftPMでのプライベートリポジトリの資格情報設定]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-10-credential-setup-for-private-repositories-in-swiftpm-ja/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-10-credential-setup-for-private-repositories-in-swiftpm-ja/</guid>
            <pubDate>Wed, 12 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[SwiftPMでプライベートリポジトリの資格情報を設定する方法を見てみましょう。このガイドでは、.netrcファイルのセットアップ、Keychainの設定、ローカル環境とCI環境の両方でのアクセス許可の処理など、基本的な手順について解説しますHTTPSかSSHのどちらを使用している場合も、GitHub Appsでトークンを安全に管理している場合も、開発ワークフローを効率化する実用的なソリューションを紹介します。プライベートな依存関係を扱うチームにぴったりな内容です！]]></description>
            <content:encoded><![CDATA[<p>この記事は<a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTOテクノロジーズアドベントカレンダー2024</a>の10日目の記事です🎅🎄</p>
<hr>
<h2>背景</h2>
<p><a href="https://kinto-jp.com/entry_app/">KINTOかんたん申し込みアプリ</a>の開発にあたっては、KMP (Kotlin Multiplatform)を利用して共有コードを実装し、Swift Packageとして公開しました。このアプローチではコードの重複を回避することで、プラットフォーム間でコードを効率的に共有できたり、開発プロセスをシンプルにすることができました。</p>
<p>当社のiOSチームは現在XcodeGenを使用して依存関係を管理しており、KMPコードのインポートは<code>project.yml</code>ファイルに修正を4行加えるだけで簡単に行えます。このような変更例は次のとおりです。</p>
<pre><code class="language-diff">packages:
+  Shared:
+    url: https://github.com/[your organization]/private-android-repository
+    minorVersion: 1.0.0
targets:
  App:
    dependencies:
+     - package: Shared
      - package: ...
</code></pre>
<p>ところが、コードがプライベートリポジトリにあるためにいくつかの設定を追加する必要があります。このブログではその手順をまとめて、どのようにプロセスを効率化したかを説明します。</p>
<h2>Package.swiftについて</h2>
<p>KMPコードをSwiftパッケージとして公開する方法を簡単に説明します:</p>
<ol>
<li>KMPコードを<code>.xcframework </code>にコンパイルする。</li>
<li><code>.xcframework </code>をzipファイルにパッケージ化し、チェックサムを計算する。</li>
<li>GitHubに新しいリリースページを作成し、リリースアセットの一部としてzipファイルをアップロードする。</li>
<li>リリースページからzipファイルのURLを取得する。</li>
<li>URLとチェックサムを基に<code>Package.swift</code>ファイルを生成する。</li>
<li><code>Package.swift</code>ファイルをコミットし、リリースをマークするgitタグを追加する。</li>
<li>そのgitタグをリリースページに関連付け、GitHubリリースを公式に公開する。</li>
</ol>
<p>結果として生成される<code>Package.swift</code>ファイルは次のようになります。</p>
<pre><code class="language-swift">// swift-tools-version: 5.10

import PackageDescription

let packageName = &quot;Shared&quot;

let package = Package(
    name: packageName,
    ...
    targets: [
        .binaryTarget(
            name: packageName,
            url: &quot;https://api.github.com/repos/[your organization]/private-android-repository/releases/assets/&lt;asset_id&gt;.zip&quot;,
            checksum: &quot;&lt;checksum&gt;&quot;
        )
    ]
)
</code></pre>
<h2>開発環境の権限設定</h2>
<p>URLはプライベートリポジトリに存在するため、権限設定を行わないと次のエラーが発生します。</p>
<p><img src="/assets/blog/authors/yonghui.chen/2024-12-10/binary-target-404.png" alt="badResponseStatusCode_404"></p>
<p>これを解決するために、2つのオプションを検討します。 1つ目は、<code>.netrc</code> ファイル、２つ目は Keychainを使います。</p>
<h3>オプション1： <code>.netrc</code>ファイルを使用する場合</h3>
<p>GitHubの認証情報を<code>.netrc</code>ファイルに保存すると、APIリクエストの認証を簡単に行うことができます。</p>
<pre><code class="language-bash">#例： echo &quot;machine api.github.com login username password ghp_AbCdEf1234567890&quot; &gt;&gt; ~/.netrc 
echo &quot;machine api.github.com login &lt;Your Github Username&gt; password &lt;Your Personal Access Token&gt;&quot; &gt;&gt; ~/.netrc
</code></pre>
<p>素早くできて効果的な方法ではあるものの、トークンがプレーンテキストで保存されるため、セキュリティリスクの恐れがあります。</p>
<h3>オプション2： Keychainを使用する</h3>
<p>トークンをプレーンテキストで保存したくない場合は、 Keychainを使用して資格情報を安全に保存することができます。</p>
<p><img src="/assets/blog/authors/yonghui.chen/2024-12-10/keychain-new-item.png" alt="keychain new item"></p>
<ol>
<li><code>Keychain Access.app</code>を開く。</li>
<li>①<code>ログイン</code> Keychainを選択する。</li>
<li>②を選択して、新しいPassword項目を作成する。</li>
</ol>
<p>ダイアログボックスで、次の情報を入力する。</p>
<ul>
<li><strong>Keychain項目名:</strong> <code>https://api.github.com</code></li>
<li><strong>アカウント名:</strong> GitHubユーザー名</li>
<li><strong>パスワード:</strong> パーソナルアクセストークン</li>
</ul>
<p>このアプローチはより安全で、macOS認証メカニズムと円滑に統合します。</p>
<h2>SSHユーザーの場合</h2>
<p>上記の手順では、<code>https</code>プロトコルを使用してiOSリポジトリをクローンしたことを想定しています。この場合、<code>github.com</code>に対して必要な権限はすでに設定済みです。</p>
<p>しかし、<code>ssh</code>プロトコルを使用してリポジトリのクローンした場合、<code>github.com</code>に対する権限が不足し、<code>resolveDependencies</code>フェーズで権限に関連するエラーが発生する恐れがあります。</p>
<p>これを解決するには、<code>.netrc</code>ファイルにドメイン<code>github.com</code>のエントリを追加します。</p>
<pre><code class="language-bash">#例： echo &quot;machine github.com login username password ghp_AbCdEf1234567890&quot; &gt;&gt; ~/.netrc 
echo &quot;machine github.com login &lt;Your Github Username&gt; password &lt;Your Personal Access Token&gt;&quot; &gt;&gt; ~/.netrc
</code></pre>
<p>または、<code>Keychain Access</code>を使用して、<code> https://github.com</code>という名前の項目を追加します。どちらの方法でも、システムに必要な権限があることをしっかりと設定できます。</p>
<h2>GitHubのアクション</h2>
<p>ローカル開発環境の課題を解決した後は、 CI環境の権限への課題にも対応してビルド中の自動化をスムーズにする必要があります。</p>
<h3>GitHubアクションでトークンを取得する</h3>
<h4>パーソナルトークンを使用する</h4>
<p>簡単なアプローチの1つは、プライベートリポジトリにアクセス可能なパーソナルアクセストークン(PAT)を作成し、Actionsシークレットを介してCI環境に渡すことです。効果的な方法ではありますが、欠点がいくつかあります。</p>
<ul>
<li><strong>トークンの有効期限</strong><ul>
<li>有効期限のあるトークンは定期的な更新が必要で、更新を忘れるとCIが失敗する恐れがあります。</li>
<li>有効期限のないトークンは、長期的なセキュリティリスクを引き起こします。</li>
</ul>
</li>
<li><strong>広範囲にわたる権限</strong><ul>
<li>通常、個人アカウントは複数のプライベートリポジトリにアクセスできるため、PATの権限を単一のリポジトリに制限することが困難となってしまいます。</li>
</ul>
</li>
<li><strong>属人化</strong><ul>
<li>アカウント所有者がロール異動によってプライベートリポジトリへのアクセスを失うと、CIワークフローが失敗してしまいます。</li>
</ul>
</li>
</ul>
<h4>GitHubアプリを使用する</h4>
<p>より堅牢なソリューションにはGitHub Appの使用があり、次のようないくつかのメリットがあります。</p>
<ul>
<li>リポジトリに対するきめ細かい権限</li>
<li>個々のアカウントに依存しない</li>
<li>セキュリティを強化する一時的なトークンが使用可能</li>
</ul>
<h3>GitHubアプリの設定</h3>
<p>最終的にはGitHub Appを使ってアクセス許可を設定しました。手順は次のとおりです。</p>
<ol>
<li>組織内にGitHubアプリを作成する。</li>
<li>iOSとAndroid両方のプロジェクトにアプリをインストールし、リポジトリへのアクセスを管理する。</li>
<li>iOSプロジェクトのActionsシークレットでアプリの<code>AppID</code>と<code>Private Key</code>を設定する。</li>
<li>ワークフローにコードを追加して一時的なアクセストークンを取得する。例を紹介します。</li>
</ol>
<pre><code class="language-yaml">steps:
    - name: create app token
      uses: actions/create-github-app-token@v1
      id: app-token
      with:
        app-id: ${{ secrets.APP_ID }}
        private-key: ${{ secrets.APP_PRIVATE_KEY }}
        owner: &quot;YourOrgName&quot;

    - name: set access token for private repository
      shell: bash
      env:
        ACCESS_TOKEN: ${{ steps.app-token.outputs.token }}
      run: |
        git config --global url.&quot;https://x-access-token:$ACCESS_TOKEN@github.com/&quot;.insteadOf &quot;https://github.com/&quot;
        touch ~/.netrc
        echo &quot;machine github.com login x-access-token password $ACCESS_TOKEN&quot; &gt;&gt; ~/.netrc
        echo &quot;machine api.github.com login x-access-token password $ACCESS_TOKEN&quot; &gt;&gt; ~/.netrc
</code></pre>
<p>GitHub Appを使用することで、CIワークフローの安全性と効率性を確保して、個々のユーザーアカウントへの依存が解消できます。このアプローチでリスクが最小限に抑えられ、チーム間の開発がスムーズになります。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/yonghui.chen/2024-12-10/wordcloud_coverimage@2x.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[Event Report] Mobility Night #1 – Exploring GPS and Location-Based Technologies]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-10-Mobility-Night-1-GPS-位置情報-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-10-Mobility-Night-1-GPS-位置情報-en/</guid>
            <pubDate>Tue, 11 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for day 10 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>🎅🎄</p>
<hr>
<p>Mobility Night is a series of study sessions where software engineers, business professionals, researchers, and product managers in the mobility industry can casually gather to share industry-specific insights and challenges. After hosting our initial closed session (#0), we were excited to open the doors for everyone to join in our first public session (#1).</p>
<p>This event focused on &#39;GPS and Location Information&#39; which are fundamental technologies for mobility services. From car navigation and map applications to on-demand transportation, autonomous driving, and smart city infrastructure, precise location tracking is essential for enabling these innovations.</p>
<p>During the event, five sessions were held. Each session explored challenges and possibilities from unique perspectives, focusing on GPS and location information technology.</p>
<p>This article is written by Nakanishi from the Development Relations Group, who is also involved in planning and organizing the Mobility Night.</p>
<hr>
<h2>1. Exploring New Google Places API</h2>
<p><strong>Speaker:</strong> Numa, KINTO Technologies</p>
<p>Google Places API is one of the core features of the mapping platform and is an important interface for efficiently performing nearby searches and retrieving place information. In this session, the latest improvements were introduced, such as the enhancements to the Autocomplete feature, which provides instant suggestions while typing, and the Fields parameter to filter necessary information.</p>
<p><strong>Key points:</strong></p>
<ul>
<li><strong>Performance and cost optimization:</strong> By specifying Fields, unnecessary data retrieval can be reduced, leading to lower API costs and faster response times.</li>
<li><strong>User experience improvement:</strong> Providing quick access to the information they want is a significant advantage for users on the move. The enhanced Autocomplete feature reduces search load, refining the overall UX.</li>
<li><strong>Future outlook:</strong> Currently, the focus is on location information retrieval, but in the future, personalized strategies integrating IoT sensors and behavioral analysis are also expected.</li>
</ul>
<p><a href="https://speakerdeck.com/kintotechdev/exploring-new-google-places-api">https://speakerdeck.com/kintotechdev/exploring-new-google-places-api</a></p>
<hr>
<h2>2. Location Data Products Created with Driving Data from AI Dashcam Services</h2>
<p><strong>Speaker:</strong> Shimpei Matsuura, GO Inc.</p>
<p>Dashcams are commonly perceived as devices for recording accidents, but in this session, they were reinterpreted as &quot;driving data = a platform for turning streets into sensors.&quot; AI analysis of video and GPS data highlighted the potential for dynamically updating maps with real-time information on road signs, traffic signals, and construction conditions.</p>
<p><strong>Key points:</strong></p>
<ul>
<li><strong>Dynamic map updates:</strong> Evolving static maps into a &quot;living information infrastructure&quot; by reflecting changes in road infrastructure almost in real time.</li>
<li><strong>Multiple vehicle data integration:</strong> By cross-referencing data from different vehicles, temporary signs and construction sites can be detected with high accuracy.</li>
<li><strong>Privacy measures:</strong> Ensuring that personal information captured in video data is properly anonymized while retaining essential road-related information is crucial for both technology and operations.</li>
<li><strong>Future applications:</strong> Potential for various business developments, including HD maps for autonomous driving, smart city planning, and the creation of new services.</li>
</ul>
<p><a href="https://speakerdeck.com/pemugi/aidorarekosabisunozou-xing-deta-dezuo-ruwei-zhi-qing-bao-detapurodakuto-wei-zhi-qing-bao-jing-du-xiang-shang-nogong-fu">https://speakerdeck.com/pemugi/aidorarekosabisunozou-xing-deta-dezuo-ruwei-zhi-qing-bao-detapurodakuto-wei-zhi-qing-bao-jing-du-xiang-shang-nogong-fu</a></p>
<hr>
<h2>3. An Overview of Satellite Positioning Technology: Lessons from Using GPS Modules</h2>
<p><strong>Speaker:</strong> Shinya Hiruta, VP of Engineering, Charichari, Inc.</p>
<p>Although GPS is widely taken for granted, but in urban environments, there are many practical issues such as radio wave reflection, poor visibility, and uneven number of satellites. In this session, we explored the fundamentals of satellite positioning technology, as well as potential accuracy improvements and countermeasures.</p>
<p><strong>Key points:</strong></p>
<ul>
<li><strong>Environment-dependent issues:</strong> Location-specific conditions, such as multipath interference in urban areas and satellite signal loss in tunnels, can significantly impact the accuracy.</li>
<li><strong>Multi-GNSS utilization:</strong> Instead of relying solely on GPS, combining multiple systems such as GLONASS, Galileo, BeiDou, and Michibiki (QZSS) enhances overall accuracy.</li>
<li><strong>Hybrid methods:</strong> Improved accuracy with complementary technologies such as accelerometers, gyroscopes, Wi-Fi/Bluetooth beacons, and map matching.</li>
<li><strong>Basic knowledge as a guiding principle:</strong> This understanding will serve as a guiding principle for future product design, quality assurance, and data analysis.</li>
</ul>
<hr>
<h2>4. Experimenting the Post-Processing Technique for Location Information Correction (Tentative)</h2>
<p><strong>Speaker:</strong> Kensuke Takahara, IoT Team, Luup, Inc.</p>
<p>When real-time positioning accuracy is challenging, there is an alternative called &quot;Post-Processing Kinematic (PPK),&quot; which improves accuracy later. PPK is a method of post-processing by combining acquired data with reference station data without using expensive RTK equipment or special communication infrastructure.</p>
<p><strong>Key points:</strong></p>
<ul>
<li><strong>Benefits of PPK:</strong> Accuracy can be improved at a later date, regardless of real time. In the end, centimeter-level accuracy is achieved while reducing initial investment.</li>
<li><strong>Cost Efficiency and Scalability:</strong> Flexibility to improve accuracy later as future demand grows. Useful for delivery robots, drones, and shared mobility services.</li>
<li><strong>Application Range:</strong> PPK proves highly valuable in areas focused on post-analysis, such as map maintenance, advanced driving log, and infrastructure inspection.</li>
</ul>
<p><a href="https://speakerdeck.com/kensuketakahara/hou-chu-li-dewei-zhi-qing-bao-wobu-zheng-suruji-shu-woshi-sitemita">https://speakerdeck.com/kensuketakahara/hou-chu-li-dewei-zhi-qing-bao-wobu-zheng-suruji-shu-woshi-sitemita</a></p>
<hr>
<h2>5. Construction of Simulation Logic Before Introduction of On-Demand Bus Service (Tentative)</h2>
<p><strong>Speaker:</strong> Halufy, New Technology Development Office, Advanced Planning Department, TOYOTA Connected Corporation</p>
<p>While on-demand transportation is attractive for flexibility, it is not easy to ensure profitability and sustainability. This session introduced pre-implementation simulations to enable precise demand forecasting and operational planning.</p>
<p><strong>Key points:</strong></p>
<ul>
<li><strong>Building a sustainable model:</strong> Data is used to verify optimal station placement, fleet size, and time-of-day settings without relying on subsidies.</li>
<li><strong>Strategic data utilization:</strong> By integrating location data with OD data and reservation requests, simulations are conducted to test demand forecasting, pricing strategies, and route optimization.</li>
<li><strong>Long-term vision:</strong> It will serve as a foundation for improving overall urban transportation efficiency and convenience by integrating with other mobility means and infrastructure.</li>
</ul>
<hr>
<h2>Future Topics and the Prospects for Mobility Night</h2>
<p>By focusing on GPS and location information, Mobility Night #1 has taken a deep dive into the mobility industry&#39;s &quot;current location awareness&quot; technology. Many participants commented, &quot;I never realized how deep the topic of location data could be!&quot; and &quot;It is valuable to hear about everything from the basics to advanced utilization.&quot;</p>
<p>However, the mobility industry encompasses far more than just GPS and location information. In the future, we also aim to explore areas such as</p>
<ul>
<li><strong>IoT device utilization:</strong> Real-time data collection and control from sensors</li>
<li><strong>Data analysis:</strong> Demand forecasting and advanced operational optimization</li>
<li><strong>Product design:</strong> Improving UX and maximizing user satisfaction</li>
<li><strong>Quality assurance:</strong> Ensuring reliability and compliance with safety standards</li>
</ul>
<p>to make it a place that encourages innovation throughout the industry.</p>
<p>Mobility Night is not just planned by the organizers, but also welcome speaker proposals and topic suggestions from participants. We aim to create a community where discussions and co-hosting opportunities can be easily facilitated through Discord, making it accessible and engaging for everyone.</p>
<p><a href="https://discord.com/invite/nn7QW5pn8B">https://discord.com/invite/nn7QW5pn8B</a></p>
<hr>
<h2>Summary</h2>
<p>Mobility Night #1 clarified the core technological challenges of mobility services by focusing on GPS and location-based technologies, highlighting the potential for new value creation through overcoming these challenges. A diverse range of approaches intermingled, including efforts to transform static maps into a dynamic information infrastructure, enhance environment-dependent GPS accuracy through advanced methods, and develop data-driven strategies for on-demand transportation planning.</p>
<p>These insights will be combined with future Mobility Night themes such as IoT, data analysis, product design, and quality assurance, further accelerating progress across the industry. Stay tuned to Mobility Night, and let&#39;s learn, interact, and create new value together!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Revitalizing Internal Communications: a Fresh Approach Using Slack Profiles]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-09-Slackプロフィールを活用した社内コミュニケーション活性化の新しいアプローチ-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-09-Slackプロフィールを活用した社内コミュニケーション活性化の新しいアプローチ-en/</guid>
            <pubDate>Mon, 10 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Sharing a new approach making use of Slack profiles to revitalize internal communication. This article will cover the need for talent search, its benefits, and future prospects.]]></description>
            <content:encoded><![CDATA[<p>This article the entry for day 9 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">2024 KINTO Technologies Advent Calendar</a>🎅🎄</p>
<hr>
<p>KINTO Technologies is an engineer-driven organization with a team of over 300 members. In an increasingly complex business environment, we continuously seek to balance organizational efficiency and creativity. With dispersed locations and increase in remote work, cross-departmental communication has become more limited, making it essential to address this challenge seriously.</p>
<p>This article, written by Nakanishi from the Development Relations Group, discusses the first step in revitalizing the organization by using Slack, our primary tool for daily communication and how we&#39;re building a talent search system based on Slack.</p>
<p><strong>Talent search is a system that allows you to search for data on employees&#39; skills.</strong></p>
<h2>Why Is Talent Search Needed?</h2>
<p>While working efficiently is important, innovation often comes from unexpected encounters, and even seemingly mundane conversations can play a crucial role In our organization, the intense focus on daily tasks we often tend to overlook “tacit knowledge” and “latent potential”.</p>
<p>For example, even if you think, &quot;Is there anyone with this skill?&quot;, you don&#39;t know who to consult. As our organization grows, this information asymmetry has become a serious challenge. To tackle this challenge directly, we decided to strategically utilize Slack profiles and take a proactive approach.</p>
<h2>Benefits of Utilizing Slack Profiles</h2>
<h3>From the Perspective of the Developer Relations (DevRel) Group</h3>
<ol>
<li><p><strong>Discovering previously unnoticed talent</strong><br>There are many individuals whose talents and potential remain unnoticed within the organization. Although members of the Technical PR Group communicate with employees daily, it is still challenging to fully understand everyone. Slack profiles serve as a new tool to make these &quot;hidden talents&quot; visible.</p>
</li>
<li><p><strong>Accelerating project support</strong><br>We hope that being able to quickly identify people with the right skills will dramatically improve the speed of project launch and problem solving Until now, finding someone with a specific skill often relied on word-of-mouth searches within the company. However, establishing a network that enables direct communication without relying on hub organizations like us is essential for the organization&#39;s future growth.</p>
</li>
<li><p><strong>Promoting cross-departmental collaboration</strong><br>Until now, the Technical PR Group has organized various initiatives, such as in-house study sessions, exchange events, and study sessions inviting external lecturers. As a result, natural communication has emerged within the company, creating a chain reaction where initial connections lead to an expanding network, new collaborations are established every day. This Slack initiative will surely spur this trend.</p>
</li>
</ol>
<h3>Benefits for All Employees</h3>
<ul>
<li><p><strong>Expanding opportunities for career growth</strong><br>Clearly expressing your skills and interests opens up new possibilities that you may not have noticed before. By connecting with keywords that you may not have thought of yourself, various opportunities emerge at a grassroots level. This goes beyond just work, creating opportunities for individuals with shared challenges to learn from each other and grow together.</p>
</li>
<li><p><strong>Quick access to skilled colleagues</strong><br>Specifically, when a new employee is looking for a frontend engineer with expertise in Next.js, they can quickly and easily search in Slack, which makes learning and problem-solving much more efficient. An internal talent database will be built and made searchable as an extension of searching messages on Slack.</p>
</li>
<li><p><strong>Revitalizing natural internal interactions</strong><br>There are also grassroots activities for various hobbies within the company. Whether or not they can be called as hobbies, there are interest-based channels ranging from back health to sharing easy-to-make recipes. There are also channels for various sports, games, and, of course, new technologies. As individuals become more easily connected to these channels, non-work-related interactions grow, fostering smoother collaboration in urgent work situations through pre-existing relationships.</p>
</li>
</ul>
<h2>A System to Support Profile Creation</h2>
<p>For those who feel unsure about what to write, the Technical PR Group is actively providing support. This approach is more than just gathering information, it is a thoughtful dialogue process designed to unlock each employee&#39;s potential.</p>
<p>Since the launch of our Tech Blog, we have conducted interviews to highlight employees&#39; talents, provided support in writing articles and preparing presentation materials, and organized events and study sessions. If you are reading this article and have not yet complete your Slack profile, or if you are unsure what to include, please reach out. Let&#39;s work together to discover your strengths and share them within the organization!</p>
<h3>Support includes:</h3>
<ol>
<li><p><strong>Uncovering experience and interest through individual interviews</strong><br>One-on-one conversations help discover potential strengths that even the individual may not be aware of.</p>
</li>
<li><p><strong>Templates for creating profiles</strong><br>Even if you are not good at expressing yourself, you can fill it out with confidence.</p>
</li>
<li><p><strong>Assistance in verbalization for those who find self-expression difficult</strong><br>Specialized staff offer close support to help individuals appropriately express their strengths and interests.</p>
</li>
</ol>
<h3>Template:</h3>
<p><img src="/assets/blog/authors/aoi.nakanishi/2024-12-09-slack-profile/slack-profile.png" alt="Slack Profile"></p>
<h2>Search results</h2>
<p><img src="/assets/blog/authors/aoi.nakanishi/2024-12-09-slack-profile/slack-profile-search.png" alt="Slack search results"></p>
<h2>Future Prospects</h2>
<p>Currently, talent search is conducted manually, but in the future, we aim to develop a skill matching system using AI. By effectively utilizing accumulated data, we envision a more efficient and strategic approach to human resource management. Looking ahead, this will enable a deeper understanding of each employee&#39;s potential and link them with the best opportunities.</p>
<h2>Conclusion</h2>
<p>The Slack profile is more than just a self-introduction. It is a strategic tool that unlocks an organization&#39;s hidden potential by connecting people and is the key to unleashing individual possibilities.</p>
<p>By actively sharing your interests, skills, and potential, the possibilities of the entire organization can be expanded. We believe that this small step will eventually lead to a significant transformation.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2024-12-09-slack-profile/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[QA × AIで切り開く未来 〜アイデア満載のブレインストーミング〜]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-03-10-QA-×-AIで切り開く未来-アイデア満載のブレインストーミング-/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-03-10-QA-×-AIで切り開く未来-アイデア満載のブレインストーミング-/</guid>
            <pubDate>Mon, 10 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>QAグループの中西です。(技術広報もKINTO FACTORY開発も色々兼務でやっています^^)</p>
<p>今年、私たちKINTOテクノロジーズは「AIファースト」「リリースファースト」を掲げ、QAグループとしてもAIを活用してリリース速度を向上させる取り組みを進めています。</p>
<p>今回は、そんな思いを持つQAメンバーが集まり、</p>
<ul>
<li>「こんなことができると良いな」</li>
<li>「こんなことをやってみたい！」</li>
</ul>
<p>という気持ちをワイワイと語り合うブレインストーミングを行いました。</p>
<p>この記事では、そこで出てきた魅力的なアイデアを紹介し、AIとQAがどのような可能性を持っているのかを皆さんと共有したいと思います。</p>
<hr>
<h1>話し合いで見えた課題</h1>
<p>QA業務には日々多くの資料が生成されますが、情報量が多すぎるあまり必要な情報を探すのが大変になっています。また、仕様書もプロジェクトや担当者ごとにフォーマットが異なり、共有が難しくなっています。さらに、インシデントに対する振り返りや予防策の仕組みが整っていないため、問題の再発防止にも苦労しています。レビュー作業についても人的負担が非常に高く、効率化が求められています。</p>
<h1>AIで実現できる未来</h1>
<h2>情報の効率的活用（RAG）</h2>
<p>RAG（Retrieval-Augmented Generation）は、最新の生成AI技術を活用した情報検索・分析の手法です。過去に蓄積された膨大な資料やインシデントのデータから関連性の高い情報を素早く抽出し、適切な情報をユーザーに提示します。例えば、あるインシデントが起きた際、AIが過去の類似事例を瞬時に検索・分析し、即座に役立つ解決策を提示します。まるで、過去の経験を全て記憶している優秀な秘書が、必要なときに瞬時にアドバイスをくれるような活躍を期待できます。すでに金融業界やカスタマーサポート業界などで導入され、顧客対応や問題解決の速度を劇的に向上させています。</p>
<h2>仕様・設計の整理と支援</h2>
<p>仕様書内の矛盾や抜け漏れをAIが見つけ出し、問題点を明確に整理して提示します。さらに、具体的な仕様書を入力するとAIがその内容を解析し、適切なテストシナリオを自動的に生成します。例えば、ECサイトのカート機能の仕様書を入力すると、AIが「商品追加→数量変更→決済→注文確認」といったシナリオを瞬時に作成。さらに、エラー発生時の対応シナリオや境界値テストシナリオなども自動生成可能です。これにより、人手によるシナリオ作成にかかる時間や手間を大幅に削減でき、QA業務の精度と効率を飛躍的に向上させます。</p>
<h2>QAプロセスの自動化と効率化</h2>
<p>ユーザー操作ログをAIが分析し、人が見落としてしまいそうな細かなミスも未然に防ぎます。具体的には、ユーザーが頻繁に発生させるエラーや異常操作をAIが抽出し、「特定の画面遷移で起こるエラー」や「入力フォームで頻繁に誤入力される項目」を特定します。これにより、テストシナリオの改善や、潜在的な問題箇所への事前対応が可能になります。また、コンフルでの煩雑なテストケース連番管理をAIが自動化し、手作業による管理ミスや作業時間の大幅な削減を実現します。</p>
<h2>インシデント分析と予防</h2>
<p>過去に発生したインシデントをAIが分析し、具体的な再発防止策を提示します。例えば、過去にECサイトで起きた「特定ブラウザでの表示崩れ」や「支払い機能の障害」などのインシデントをAIが徹底的に分析。その結果、「特定ブラウザのバージョンごとの挙動確認を定期的に行う」や「決済処理前後のエラーハンドリング強化」といった具体的なアクションを提案します。また、緊急性の高いインシデントが発生した際にはAIが即座にリスクレベルを判定し、迅速な対応が取れるように関係者へ自動通知を行うなど、リアルタイムでの問題解決支援を実現します。</p>
<h2>テストデータ作成の効率化</h2>
<p>テストに必要なデータをAIが瞬時に生成します。具体的には、新車や中古車のデータ、ユーザー情報など、リアルな業務シナリオに必要な多様なデータを迅速かつ大量に作成可能です。また、SeleniumやAppiumといったブラウザ操作ツールとAIを連携させることで、手動で行っていたブラウザ操作を自動化し、簡単な設定だけで大量のテストデータを短時間で作成できます。この連携により、人的ミスを防ぎつつ、テストデータ作成にかかる工数を劇的に短縮できるため、QAプロセス全体の効率化が実現されます。</p>
<h2>ツール連携とプロセス自動化</h2>
<p>JIRAやAsanaなどのツールとSlackを連携し、必要な情報をタイムリーに自動通知。多くのツールからの情報を一元化して効率よく管理します。多彩なツールの橋渡し役をAIが担い、プロセスの流れをスムーズにします。</p>
<h2>QA専用AIモデルの活用</h2>
<p>QA業務専用にチューニングされたChatGPTの活用を検討しています。自社専用のAIモデルを構築することで、AIの応答速度を劇的に向上。さらに、開発者自身がAIを活用して手軽にセルフチェックできる環境づくりを推進します。</p>
<hr>
<h1>今後のアクションプラン</h1>
<ol>
<li>AIを活用した情報の整理・統合を最優先で開始</li>
<li>レビュー作業をAI支援で効率化し、人的負荷を軽減</li>
<li>専用AIモデルの開発に向けて継続的なデータ収集を実施</li>
<li>AIによるインシデント分析をプロセス改善に積極的に活用</li>
<li>各種ツール間の連携を自動化し、さらなる業務効率化を実現</li>
</ol>
<hr>
<p>一人では実現できないことも、みんなで考えることで新しい道が開けます。今回のブレインストーミングで出たアイデアをきっかけに様々なAIを活用したQA活動を行っていきます。AIと共に歩むQAの新たな可能性を一緒に探ったり私たちと一緒に新しい取り組みをしたいQAエンジニアの皆様、カジュアル面談なども受け付けていますのでお気軽にご連絡お待ちしております。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-03-10-AI-QA/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[[iOS] From UIKit + Combine to a Tailor-Made SwiftUI Architecture]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-08-unlimited-architecture-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-08-unlimited-architecture-en/</guid>
            <pubDate>Fri, 07 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[I will guide you through the development journey of the KINTO Unlimited iOS app and its transition to a custom architecture.]]></description>
            <content:encoded><![CDATA[<p>This article is the entry for day 8 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<h1>Introduction</h1>
<p>Hello. My name is Nakamoto, and I work in the Mobile App Development Group. I usually work from Osaka and collaborate with team members in Tokyo on iOS development for the <a href="https://kinto-jp.com/unlimited/app/">KINTO Unlimited app</a> .</p>
<p>This article deep dives into the process of enhancing the architecture of the KINTO Unlimited iOS. The app&#39;s architecture evolved gradually, moving through three different phases, and ultimately transitioning to its current structure. I will adress its design aspect and the challenges encountered at each stage below.</p>
<h2>The 1st Generation Architecture</h2>
<ul>
<li><strong>Adopted the VIPER architechture</strong><ul>
<li>Implemented all screens using UIKit + xib/storyboard</li>
<li>Used Combine to update views</li>
<li>Chose an architecture with a proven track record within the company due to the short timeline for the first release</li>
</ul>
</li>
</ul>
<h3>Design of the 1st Gen</h3>
<pre><code class="language-mermaid">flowchart TD
    id1(ViewController) -- publish --&gt; id2(ViewModel) -- subscribe --&gt; id1
    id2 -- request --&gt; id3(Interactor)
    id1 -- apply view property --&gt; id4(UIView)
    id1 -- transition --&gt; id5(Router)
</code></pre>
<ul>
<li><p><strong>ViewController</strong></p>
<ul>
<li>Notify ViewModel of an event</li>
<li>Subscribe to outputs triggered by events from the ViewModel.</li>
<li>Update the View based on the subscription results and invoke the Router to handle screen transitions.</li>
</ul>
</li>
<li><p><strong>ViewModel</strong></p>
<ul>
<li><h2>Use Combine to update the state reactively.</h2>
Transform an event Publisher to output the View state through a Publisher.</li>
</ul>
</li>
<li><p><strong>Interactor</strong></p>
<ul>
<li>Perform requests to API and internal DB</li>
</ul>
</li>
<li><p><strong>Router</strong></p>
<ul>
<li>Perform transitions to other screens</li>
</ul>
</li>
<li><p><strong>UIView</strong></p>
<ul>
<li>Layout using code/xib/storyboard</li>
</ul>
</li>
</ul>
<h3>Issue with the 1st Gen</h3>
<ul>
<li>Layouts built with UIKit come with high development costs and are challenging to modify, particularly when xib/storyboard is used. Transitioning to SwiftUI would significantly enhance the process!</li>
</ul>
<h2>The 2nd Generation Architecture</h2>
<ul>
<li><strong>Transitioning from UIKit to SwiftUI</strong><ul>
<li>Replaced UIKit layouts with SwiftUI to enhance development efficiency.<ul>
<li>Integrated a SwiftUI View into a ViewController using UIHostingController.</li>
</ul>
</li>
<li>UIPerform screen transitions using UIKit as usual.<ul>
<li>At that time, SwiftUI&#39;s screen transition API was unstable, so we decided to continue using UIKit.</li>
</ul>
</li>
<li><strong>Focus on switching to SwiftUI</strong><ul>
<li>Making too many changes at once could raise concerns about potential degradation of functional specifications.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3>Design of the 2nd Gen</h3>
<pre><code class="language-mermaid">flowchart TD
    id1(ViewController) -- input --&gt; id2(ViewModel) -- output --&gt; id1
    id2 -- request --&gt; id6(Interactor)
    id1 -- mapping --&gt; id3(ScreenModel) -- publish --&gt; id1
    id3 -- publish --&gt; id4(View) -- publish --&gt; id3
    id1 -- transit --&gt; id5(Router)
</code></pre>
<ul>
<li><p><strong>ViewController</strong></p>
<ul>
<li>Implement HostingControllerInjectable protocol and add SwiftUI View.</li>
<li>Subscribe to the ViewModel&#39;s output and update the ScreenModel (ObservableObject) accordingly.</li>
<li>Subscribe to the ViewModel output and the ScreenModel Publisher, then utilize the Router to handle screen transitions.</li>
</ul>
</li>
<li><p><strong>ScreenModel</strong></p>
<ul>
<li>An ObservableObject that manages the state of the View.</li>
</ul>
</li>
<li><p><strong>ViewModel/ Interactor/ Router</strong></p>
<ul>
<li>Same features as the 1st Generation</li>
</ul>
</li>
</ul>
<h3>Issue with the 2nd Gen</h3>
<ul>
<li><p>State management is split between the ViewModel and ScreenModel, leading to fragmented logic and increased development and maintenance costs.</p>
</li>
<li><p><strong>Issues from the 1st generation</strong></p>
<ul>
<li>Using Combine for reactive state changes raises concerns about maintainability and, due to the extensive codebase, can result in reduced readability.</li>
<li>Having a single ViewModel for each screen can result in it becoming excessively large on multi-functional screens.</li>
</ul>
</li>
</ul>
<p>Therefore, transitioning away from Combine and ViewModel would be a highly beneficial improvement!</p>
<h2>The 3rd Generation Architecture</h2>
<ul>
<li><strong>Switched from a Combine-driven ViewModel to a ViewStore-based architecture that centralizes state management</strong><ul>
<li>Implemented a structure that directly updates the ObservableObject with event results, eliminating the need for AnyPublisher.</li>
<li>Utilized async/await to achieve reactive state changes without relying on Combine.</li>
<li>State management logic can be modularized by dividing it into functions.</li>
</ul>
</li>
</ul>
<h3>Design of the 3rd Gen</h3>
<pre><code class="language-mermaid">flowchart TD
    subgraph ViewStore
    id1(ActionHandler) -- update --&gt; id2(State)
    end
    id2 -- bind --&gt; id5(View) -- publish action --&gt; id1
    id1 -- publish routing --&gt; id3(ViewController) -- publish action --&gt; id1
    id3 -- transit --&gt; id4(Router)
    id1 -- request --&gt; id6(Interactor)
</code></pre>
<ul>
<li><p><strong>ViewStore</strong></p>
<ul>
<li><p><strong>State</strong></p>
<ul>
<li>An ObservableObject that manages the state of the View and is used within a SwiftUI View.</li>
</ul>
</li>
<li><p><strong>Action</strong></p>
<ul>
<li>Use an enum to replicate the functionality of the INPUT in the transform method of a traditional ViewModel.</li>
</ul>
</li>
<li><p><strong>ActionHandler</strong></p>
<ul>
<li>A handler that accepts an Action as an argument and updates the State accordingly.</li>
<li>Implement using async/await</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>ViewController</strong></p>
<ul>
<li>Subscribe to routerSubject and utilize the Router to handle screen transitions.</li>
</ul>
</li>
<li><p><strong>Interactor / Router</strong></p>
<ul>
<li>Same as 2nd Generation</li>
</ul>
</li>
</ul>
<h2>Splitting ActionHandler</h2>
<p>On multi-functional screens, separating the ActionHandler and State can significantly improve code readability and maintainability.</p>
<ul>
<li>Binding the actionPublisher of one State to another State allows actions to be propagated from one View to another.</li>
</ul>
<pre><code class="language-mermaid">flowchart TD
    subgraph ViewStore
        id2 -- action --&gt; id1
        id1(ActionHandler1) -- update --&gt; id2(State1) 
        id5 -- action --&gt; id4
        id4(ActionHandler2) -- update --&gt; id5(State2) 
        id8 -- action --&gt; id7
        id7(ActionHandler3) -- update --&gt; id8(State3)
    end
    subgraph Parent View
        id3
        id6
        id9
    end
    id2 -- bind --&gt; id3(View1)
    id5 -- bind --&gt; id6(View2)
    id8 -- bind --&gt; id9(View3)
</code></pre>
<h2>Conclusion</h2>
<p>We have been pursuing this initiative for over a year, alongside ongoing feature development. Now, nearly all of the source code has been transitioned to the 3rd Generation Architecture. As a result, the code has become more readable and maintainable, paving the way for smoother future development. We are excited to continue making improvements!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Reflecting on the First Year of Manabi-no-Michi-no-Eki: The Roadside Station of Learning]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-08-look-back-first-year-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-08-look-back-first-year-en/</guid>
            <pubDate>Thu, 06 Mar 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Reflecting on the First Year of Manabi-no-Michi-no-Eki: The Roadside Station of Learning]]></description>
            <content:encoded><![CDATA[<p>This is article is the entry for day 8 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<hr>
<p>Hello, this is HOKA from the Manabi-no-Michi-no-Eki (Learning Roadside Station) Team.</p>
<p>It has been nearly a year since the team was launched, so I will be writing a blog post to reflect on its first twelve months.</p>
<h2>Our Thoughts and Aspirations One Year Ago</h2>
<p>The team was formed exactly one year ago, at the end of November 2023, when Kin-chan, Nakanishi, and I came together with the goal of making the company’s various study sessions more engaging and dynamic. Shortly after the beginning of 2024, we regrouped and created an inception deck. Here is the result:</p>
<p>As a &quot;roadside station&quot; where study sessions converge, we aim to enhance internal activities focused on learning and knowledge sharing.</p>
<ul>
<li>Internal communications<ul>
<li>Keeping everyone informed about the study sessions being held.</li>
<li>Sharing what study sessions are like.</li>
</ul>
</li>
<li>Supporting study sessions<ul>
<li>Providing guidance to those who have concerns, such as:</li>
<li>Wanting to start study sessions but not knowing where to begin.</li>
<li>Struggling to make their existing study sessions more engaging.</li>
</ul>
</li>
</ul>
<p>There are more details in <a href="https://blog.kinto-technologies.com/posts/2024-04-23_%E5%AD%A6%E3%81%B3%E3%81%AE%E9%81%93%E3%81%AE%E9%A7%85%E3%81%AF%E3%81%98%E3%82%81%E3%81%BE%E3%81%97%E3%81%9F/">the Tech Blog post about how it all got started</a>.</p>
<h2>What We Did in the First Six Months</h2>
<p>We started by sharing updates on the team activities during KTC’s monthly all-hands meetings, commonly known as HQ meetings. I imagine the slide below has become a familiar sight to everyone in the company.</p>
<p><img src="/assets/blog/authors/hoka/michinoeki-3/michinoeki1.jpeg" alt=""></p>
<h3>Activity summary</h3>
<ul>
<li>Providing a space for discussing study sessions as a whole—offering advice on starting them, addressing challenges, and finding solutions.  </li>
<li>Popping Into Next Door Study Sessions – an initiative where the administration team visits various study sessions to engage and support participants. ​</li>
<li>KTC Podcast: interviews with study session organizers! Sharing voices from within the company, capturing both the messages and the atmosphere of the study sessions.</li>
<li>Tech Blog: Articles in the Tech Blog showcasing the various study sessions at KTC and sharing experiences of participating in them.</li>
</ul>
<p><img src="/assets/blog/authors/hoka/michinoeki-3/michinoeki2.png" alt=""></p>
<p><img src="/assets/blog/authors/hoka/michinoeki-3/michinoeki3.png" alt=""></p>
<h3>New activities — Part 1</h3>
<p>After about six months, we started to see people supporting and advocating for our activities. We faced the challenge of making it easier for people to search for available study sessions. Fortunately, an engineer from the Mobile App Development Group developed a Slack-based search system to help solve this issue.</p>
<p>This allowed people to search for study sessions in the Slack channel by interacting with a character named “Manabyi.”</p>
<p>For more details, check out the developer’s blog post.</p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-12-04_manabyi/">https://blog.kinto-technologies.com/posts/2024-12-04_manabyi/</a></p>
<p><img src="/assets/blog/authors/hoka/michinoeki-3/michinoeki4.png" alt=""></p>
<h3>New activities — Part 2</h3>
<p>We also faced the challenge of making study session materials and videos accessible to everyone at any time. 
As we were exploring solutions, an engineer from the Corporate IT Group proactively reached out and developed a SharePoint-based portal site for the study sessions. It is called the “Manabi-no-Michi-no-Eki Portal.”</p>
<p><img src="/assets/blog/authors/hoka/michinoeki-3/michinoeki5.png" alt=""></p>
<p><img src="/assets/blog/authors/hoka/michinoeki-3/michinoeki6.jpeg" alt=""></p>
<p>What was once a set of plain, unorganized folders has now been transformed into a YouTube-like experience. With all the videos and materials in one place, people can:
Watch recordings or review materials from study sessions they missed.
Rewatch a session they attended to reinforce what they learned.</p>
<!-- TODO: 詳しくは開発者のBlogをご覧ください -->

<h3>Other Highlights That Made Us Happy</h3>
<p>We began receiving requests from organizers seeking discussions on improving their study sessions. When we invited people to be featured on the podcast, they all eagerly agreed to participate. The term “Manabi-no-Michi-no-Eki” was featured on a poster for a company-wide event. 
Internal materials from group companies included information about Manabi-no-Michi-no-Eki as part of introducing KTC. Less than a year after starting, we never would have imagined that our activities would be reaching not only our own company but also group companies. Whenever employees spontaneously express their appreciation for our team’s activities, it gives us a tremendous boost of motivation. </p>
<h2>Joining the Developer Relations Group</h2>
<p>In September 2024, Manabi-no-Michi-no-Eki officially became part of the Developer Relations Group.</p>
<h4>What is the Developer Relations Group?</h4>
<p>In 2022, KINTO Technologies launched its Tech Blog and established a platform for engineers to share their knowledge through external events and internal study sessions. Delivering results through work is a form of output in itself, but we believe that study sessions and the Tech Blog also play a crucial role in providing engineers with valuable opportunities to share their knowledge and insights. The Technology Developer Relations Group has created outlets for output that can be described as being “between work duties.” When we launched Manabi-no-Michi-no-Eki at the end of 2023, it created a learning forum that fit into the space &quot;between work duties&quot;—essentially, a space for input. This naturally aligned with the activities of the Technology Public Relations Group.
Notably, part of the group&#39;s founder, Nakanishi’s, vision was to support engineers&#39; growth by guiding them from input to output. So, in a way, these two initiatives may have never truly been separate to begin with.</p>
<h4>What the Manabi-no-Michi-no-Eki Team will do in the future</h4>
<p>Please do check out Nakanishi’s TechBlog posts. </p>
<p><a href="https://blog.kinto-technologies.com/posts/2024-12-03-the-next-goal/">https://blog.kinto-technologies.com/posts/2024-12-03-the-next-goal/</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[「Appium Meetup Tokyo」第1回イベント開催レポート]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-20-Appium-Meetup-Tokyo-開催レポート/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-20-Appium-Meetup-Tokyo-開催レポート/</guid>
            <pubDate>Thu, 06 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>2025年2月20日に初開催された「Appium Meetup Tokyo」が、Autify社東京オフィスにて開催されました。当日は約10名が現地参加し、オンラインでも多くの参加者が集まりました。</p>
<h3>オープニングとアイスブレイク</h3>
<p>イベントはAutify社の<a href="https://x.com/tsueeemura">tsueeemura</a>さんのユーモア溢れる軽快なトークでスタートしました。オンライン接続の確認も兼ねて、「今日の夕飯何にしますか？」という質問があり、「今日はお鍋です」という回答が場の雰囲気を和ませました。</p>
<h1>Autify社「Appiumプラグインの活用事例」</h1>
<p>最初の登壇はAutify社のモバイル製品開発担当<a href="https://x.com/rererorerero">rerorero</a>さん。</p>
<p>Autify社が提供する「Autify NoCode Mobile」は、コードを書かずにモバイルアプリのテスト自動化が簡単に実現できるクラウドサービスです。プログラミングの専門知識がなくても直感的なインターフェースでテストを記録し、自動再実行が可能です。これにより、開発者や非エンジニアでも迅速にテスト環境を整備できる点が最大の利点です。さらに、クラウド上で実機やシミュレーターが利用でき、自社での機材調達が不要となり、設備投資を大幅に削減できることも特徴の一つです。</p>
<p>ただし、大量のUI要素が存在する画面では動作が著しく遅くなる課題があり、通常数秒で完了するはずのタップ操作が最大40秒もかかる問題がありました。</p>
<p>reroreroさんはこの問題を解決するため、Facebookが開発した「<a href="https://github.com/facebook/idb">IDB(iOS Development Bridge)</a>」を導入しました。IDBはCLIベースで高速にiOSのシミュレーターや実機を操作するオープンソースツールで、Core Simulator Serviceに直接イベントを送ることで反応速度を劇的に改善します。Appiumのプラグインとして統合し、サーバー間の複雑なネットワーク設定なしで直接利用可能な仕組みを構築した結果として、40秒の操作が40ミリ秒に短縮され、約1,000倍のパフォーマンス向上を実現したそうです。</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/autify-command.png" alt="俯瞰図"></p>
<p><strong>登壇の詳細ポイント</strong></p>
<ul>
<li>Appiumプラグインの導入方法とJavaScriptを使った実装例</li>
<li>IDBが高速なタップ操作を可能にする技術的仕組み（コアシミュレーターサービスへのイベント送信）</li>
<li>パフォーマンス改善の実際のデモンストレーション</li>
</ul>
<p>プラグインは以下のような書き方で実装するそうです</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/autify-code.png" alt="Appiumプラグイン"></p>
<h1>KINTOテクノロジーズ社「効率的なアプリ自動化のためのガイドラインと実践方法」</h1>
<p>続いて、KINTOテクノロジーズの岡さんとパンヌさんが、自動化テスト環境の構築方法と成果を発表しました。</p>
<p>弊社では、多様な端末やOSの組み合わせによる手動テストの負荷が増大していました。そこで、開発初期段階からQAチームと開発チームが協力し、統一されたテスト専用IDを設定する方法を導入しました。これにより、レイアウト変更に伴うXPATHの修正負荷を軽減し、テストの安定性が大幅に向上しました。</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/ktc-id.png" alt="ID"></p>
<p>また、テスト結果はSlackでリアルタイム通知され、詳細なログや動画をBOXで管理する仕組みを構築しました。これにより、関係者全員が容易にテスト状況を確認できる環境を実現しています。</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/ktc-report.png" alt="ID"></p>
<p><strong>登壇の詳細ポイント</strong></p>
<ul>
<li>開発プロセスにおける自動テスト意識の統合</li>
<li>テスト専用IDの設定前後でのメンテナンス負荷の比較</li>
<li>SlackおよびBOXを活用したテスト結果の効率的管理方法</li>
<li>Github Copilotを活用したコーディングの効率化</li>
</ul>
<h3>登壇資料</h3>
<p><a href="https://speakerdeck.com/kintotechdev/xiao-lu-de-naapurizi-dong-hua-notamenogaidoraintoshi-jian-fang-fa">https://speakerdeck.com/kintotechdev/xiao-lu-de-naapurizi-dong-hua-notamenogaidoraintoshi-jian-fang-fa</a></p>
<h1>📌 参加者アンケートから見るE2Eテストの現状と関心トピック</h1>
<p>今回、Meetupにご参加いただいた皆さんを対象にアンケートを実施しました。その結果から得られた興味深い傾向を紹介します。</p>
<h3>① 参加者の職種割合</h3>
<p>参加者の半数以上（54.1%）はQAエンジニアでしたが、SET/SDET、Webやモバイルアプリケーションエンジニアなど多様な職種の方にもお越しいただきました。</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/appium-role.png" alt="職種割合"></p>
<h3>② Appiumの利用経験</h3>
<p>Appiumに関しては、半数以上（55.7%）が「使ったことがない」と回答しており、新規ユーザーや導入を検討中の方が多いことが分かります。一方で一定の経験（1年以上）を持つ方もおり、利用の成熟度には幅がありました。</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/appium-experience.png" alt="Appiumの利用経験"></p>
<h3>③ E2Eテストの実務経験</h3>
<p>E2Eテスト全般では、「1〜3年（27.9%）」や「5年以上（24.6%）」といった比較的経験豊富な層が半数を超えており、実務での活用が広がっている状況が確認できました。</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/appium-e2e-experience.png" alt="E2Eテストの実務経験"></p>
<h3>④ 最も関心のあるトピック</h3>
<p>参加者が特に興味を持ったトピックとしては以下のようなものが挙げられました。</p>
<ul>
<li>Appiumによるテスト導入の成果や事例</li>
<li>Appiumの活用シナリオや注意点、実際の苦労話</li>
<li>CI/CDへの組み込みや、クロスプラットフォーム（React Native、Flutterなど）への対応状況</li>
</ul>
<p>今回のアンケート結果を踏まえて、今後も皆さまの関心やニーズに沿った情報をお届けしていきたいと思います。</p>
<h1>ネットワーキングと今後の展望</h1>
<p>セッション後のネットワーキングではピザを囲んで積極的な交流が行われ、新しいアイデアや協力関係が生まれました。Appium Meetup Tokyoは今後も定期的に開催予定で、登壇者や運営メンバーを募集しています。ぜひ次回もご参加ください。</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/appium-meetup-onsite.jpg" alt="現地"></p>
<h1>参加を検討している方へ</h1>
<ul>
<li><strong>モバイルアプリの自動テストをこれから本格的に導入したい方</strong>  </li>
<li><strong>Appiumに興味があるが具体的な事例やノウハウが欲しい方</strong>  </li>
<li><strong>CI/CDと組み合わせた運用に関心があるエンジニアやQA担当の方</strong>  </li>
<li><strong>他社事例を参考に自社のテスト文化を改善したい方</strong></li>
</ul>
<p>上記に当てはまる方は、ぜひAppium Meetup Tokyoで最新の知見を共有し合いましょう。今後の告知や詳細情報は<a href="https://x.com/AutifyJapan">@AutifyJapan</a>や<a href="https://x.com/KintoTech_Dev">@KintoTech_Dev</a>でさせて頂きます。ご質問やご要望などがございましたら、お気軽にお寄せください。</p>
<p>次回の「Appium Meetup Tokyo #2」でお会いできることを心より楽しみにしています。</p>
<h1>アーカイブ配信</h1>
<p><a href="https://www.youtube.com/watch?v=zV4WbClGquE">https://www.youtube.com/watch?v=zV4WbClGquE</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/aoi.nakanishi/2025-02-20-appium-meetup-tokyo/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Rapid Verification: Generative AI-Powered Chat API for MVP Development]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-07-mvp-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-07-mvp-en/</guid>
            <pubDate>Wed, 05 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for day 7 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<h1>Introduction</h1>
<p>Hello! I&#39;m Iki, from the Cloud Infrastructure Group at the Osaka Tech Lab. In this article, I’ll share how the Osaka Tech Lab team applied the MVP development method to build a chat API powered by generative AI.</p>
<h2>What is MVP (Minimum Viable Product) Development?</h2>
<p>This method consists of first creating a product that has the bare minimum of features, then verifying and improving it while getting feedback from users, in this case, the Product Manager (PdM) who originally suggested creating it. I think it was an extremely efficient development method for handling the rough requirements we had for this project.</p>
<h2>What Inspired Us to Create a Chat AI</h2>
<p>The idea came about naturally.</p>
<p>KINTO Technologies has offices in Tokyo, Nagoya, and Osaka (Osaka Tech Lab), but nearly 80% to 90% of its employees are based in Tokyo. Since work isn&#39;t assigned by location, everyone, regardless of their office, contributes to Tokyo-led projects.</p>
<p>This setup isn&#39;t an issue at all —if anything, Osaka team members are particularly close-knit compared to those at other locations! (At least, in my opinion.)</p>
<p>Still, at some point, the Osaka members started thinking wouldn’t it be great to work on something uniquely Osaka-based? Around that time, they happened to cross paths with a Product Manager who was exploring the potential of chat systems powered by generative AI.</p>
<h2>Theme</h2>
<p>For this project, we decided to use MVP development to verify whether chat using generative AI was a viable idea.</p>
<p>However, the term “chat” covers a wide range of situations, with customer service and reporting to bosses also being examples. Rather than tense, stiff conversations, we focused on whether it could emulate the kind of natural, relaxed way that people chat with family and friends.</p>
<p><img src="/assets/blog/authors/norio_iki/chmcha_objective.png" alt="Theme"></p>
<h2>What We’ve Managed to Create So Far</h2>
<p>Overall structure <img src="/assets/blog/authors/norio_iki/chmcha_configuration.png" alt="Structure using generative AI"> Note: Deciding that it would be better if there was a robot in the chat, we used BOCCO emo, a commercial robot by Yukai Engineering Inc.</p>
<p>Azure schematic ![Azure schematic (simple version)](/assets/blog/authors/norio_iki/chmcha_azure_architecture.png =700x) </p>
<h2>What We Considered When Creating the Chat AI</h2>
<p><strong>Time</strong></p>
<p>The goal this time was to explore whether conversations powered by generative AI could work. However, even if we succeeded by investing a lot of time and effort, generative AI-powered conversations already exist in the world, so achieving that is a given.</p>
<p>In addition, the theme centers on conversation, a natural and intuitive part of human interaction. However, we soon realized that we had chosen an extremely insightful theme, sparking a wealth of ideas for what we aspire to create. Consequently, there would be no end to what we could do if we had the time.</p>
<p>Thinking that it wasn&#39;t worth spending too much time to create an MVP, we proceeded with the creation with an aim of completing it within the timeframe we had set.</p>
<h3>How much time we spent on MVP creation</h3>
<ul>
<li>Considering the requirements and creating the MVP<ul>
<li>2 days</li>
</ul>
</li>
<li>Verifying and improving it while getting feedback<ul>
<li>15 hours (max)</li>
</ul>
</li>
</ul>
<h2>What Did We Actually Do?</h2>
<h3>Considering the requirements and creating the MVP</h3>
<p>At the beginning, we didn&#39;t selected an environment to develop the MVP, while lacking any knowledge of the way to create a generative AI-powered system. With things as they were, before we got as far as verifying the theme (i.e., whether natural, carefree chat can be achieved), we would have first had to start from verifying how to create a generative AI system. However, for expertise about generative AI systems, we got help from <a href="https://zenarchitects.co.jp/">ZEN ARCHITECTS</a>, the company that provides the Azure Light-up program. This created a situation that would enable us to focus on the theme.</p>
<p>Besides accompanying us along the way as we were building the generative AI system, ZEN ARCHITECTS also gave us ideas based on actual experience (for example, things we should be careful about when using generative AI with a theme as rough as ours), and pulled us along so that we managed to complete the MVP in 2 days.</p>
<h3>Verifying and improving it while getting feedback</h3>
<p>Based on comments from actually trying it out, the development members discussed and decided on what improvements to make. We added a feature to let you chat from your current location, and in order to fix issues with robots that only ever chatted in cafes, we spent a month (15 hours) running a cycle of getting feedback on things people noticed about the prompts and so on.</p>
<p>To verify the feature for chatting from your current location, we did not simply do it all from the office by changing our location data, but actually went to different places physically. Since KINTO is a car subscription provider, we also did fittingly unique real-time updates, such as ones deployed while receiving feedback in a car.
<br>Creating it in-house means we can do things like this! (We repeatedly ran verifications and made improvements with this attitude to guide us!)</p>
<p>![Deployment while driving](/assets/blog/authors/norio_iki/drive_deploy.jpg#right =400x)</p>
<h2>Conclusion</h2>
<p>The chat API using generative AI that we created in this project is currently undergoing in-house verification regarding its future potential. If its value exceeds how much it is expected to cost in the future, we plan to push ahead with developing it further.</p>
<p>That said, continued development might get canceled if it is deemed to be premature. However, even if it does not get continued, the things we confirmed and experience in a new area (Azure development of generative AI systems) we gained will still remain as outcomes of the project.</p>
<p>Those outcomes will fuel new ideas. (For example, they can be fed back into existing systems.) Even if this project does not get continued, I will not think of as a failure. I think we can create an environment that will enable us to constantly move forward and run the innovation cycle, in order to do MVP development that will not fail.</p>
<p>I would like to push forward with tackling the challenges that arise with MVPs as an end unto itself!</p>
<p>Also, if you want to know a little more about the details, ZEN ARCHITECTS have published a case study on the project, so please check that out.</p>
<p><a href="https://zenarchitects.co.jp/posts/kinto-technologies-azure-light-up">Azure Light-up</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Getting Started with Minimal CI/CD: Streamlining EOL and SBOM Management]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-04-startwith-freetool-sbom-eol-pipeline-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-04-startwith-freetool-sbom-eol-pipeline-en/</guid>
            <pubDate>Tue, 04 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>Hello. I&#39;m <a href="https://twitter.com/sokasanan">Shimamura</a> from the Platform Group&#39;s Platform Engineering team. I&#39;m responsible for the development, operation, and deployment of tools (taking on roles similar to a team leader. Recently, I&#39;ve also started working on scratch development as well, which has me struggling a bit with things like Scrum and programming languages) based on platform engineering principles.</p>
<p>This article is the entry for day 4 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<h1>Background</h1>
<p>There is a thing called SBOM (Software Bill of Materials).</p>
<p>In 2024, <a href="https://www.meti.go.jp/press/2024/08/20240829001/20240829001.html">the Ministry of Economy, Trade and Industry emphasized the importance of utilizing this for security measures</a>. Similarly, back in 2021, the United States also discussed making it a standard practice. However, introducing new SaaS, OSS, and other tools for this purpose might seem like a significant hurdle. </p>
<p>KINTO Technologies leverages OSS and incorporates it into the CI/CD pipeline to create and manage a standardized SBOM. In the context of DevSecOps, we also perform</p>
<ul>
<li>vulnerability scanning</li>
<li>EOL inspection</li>
</ul>
<p>in conjunction with SBOM generation. I&#39;ll introduce &quot;Minimal Start&quot; and provide examples of improvements made along the way.</p>
<h1>Pros and Cons</h1>
<p><strong>The most important thing is having visibility into what is being used.</strong></p>
<p>Some time ago, you may remember the vulnerability of log4j. There was an inquiry asking, &quot;We don&#39;t use it, do we?&quot;  At that time, we communicated the issue by checking configuration files or applying common temporary workaround settings, but now you can find out in a single search.</p>
<ul>
<li>Pros<ul>
<li>EOL/SBOM management can be started for <em>free</em>! 👌<ul>
<li>There are paid services such as <a href="https://yamory.io/news/start-eol">Yamory(Cloud Service)</a> and <a href="https://www.blackduck.com/ja-jp.html">BlackDuck</a>.</li>
<li>There is also <a href="https://github.com/microsoft/sbom-tool">SBOM-TOOL</a> provided by Microsoft for SBOM generation.</li>
<li>Paid software or SaaS services come with application processes and other hassles.</li>
</ul>
</li>
<li>The tools we use are available as GitHub Actions, making them easy to use.</li>
<li>It can be run locally, so there are many potential use cases to explore.</li>
</ul>
</li>
<li>Cons<ul>
<li>Many of the tools, like EOL and others, are supported by volunteers.</li>
<li>Tools like xeol and syft are frequently updated, and sometimes file inconsistencies occur due to version changes.</li>
</ul>
</li>
</ul>
<h1>Tool List</h1>
<table>
<thead>
<tr>
<th>Name</th>
<th>Function</th>
<th>Overview</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://github.com/anchore/syft/">Syft</a></td>
<td>SBOM generation</td>
<td>A software provided by <a href="https://anchore.com/">Anchore</a> to generate SBOMs from files and container images. Both CycloneDX and SPDX, the standard SBOM format, are supported, but note that the standard is Syft&#39;s proprietary format. At KINTO Technologies, we use CycloneDX in XML format for output. Because with JSON, if the version changes, the configuration will change considerably and consideration will increase when importing.</td>
</tr>
<tr>
<td><a href="https://github.com/xeol-io/xeol">XEOL</a></td>
<td>EOL Scanner</td>
<td>A scanner to determine whether the EOL provided by <a href="https://www.xeol.io/">XEOL</a> is included. The internal configuration is based on Syft and is determined by matching the information in <a href="https://endoflife.date/">endoffile.date</a>. The major languages and operating systems are supported, and the response to issues is impressively quick. While SaaS is also available, we&#39;ll use OSS for this case.</td>
</tr>
<tr>
<td><a href="https://github.com/aquasecurity/trivy">Trivy</a></td>
<td>Vulnerability Scanner</td>
<td>A vulnerability scanner provided by <a href="https://www.aquasec.com/">aqua</a>. Although Anchore also offers a vulnerability scanner called <a href="https://github.com/anchore/grype/">Grype</a>, which pairs well with Syft, we chose Trivy instead because its behavior on CI/CD pipelines (e.g., display) is more favorable for vulnerability detection. It can scan a wide range of targets, including files, repositories, container images, and SBOMs, making it versatile. While it can also generate SBOMs, since XEOL&#39;s core relies on Syft, we opted to use Trivy exclusively for vulnerability scanning this time, considering compatibility.</td>
</tr>
<tr>
<td>GitHubActions</td>
<td>CICD tool</td>
<td>The CI/CD tool included in GitHub. At KINTO Technologies, we utilize GitHub Actions for tasks such as building and releasing applications SBOM management incorporates SBOM generation into the workflow at the timing of container creation.</td>
</tr>
<tr>
<td>CMDB (in-house production)</td>
<td>CMDB</td>
<td>Configuration Management Database。 A configuration management database. Since rich functions were unnecessary, KINTO Technologies has developed an in-house CMDB. Recent additions include repository information management, EOL information, and SBOM packages to be retrieved and searched.</td>
</tr>
</tbody></table>
<h1>Workflow Diagram</h1>
<p><img src="/assets/blog/authors/JumpeiShimamura/20241204/pipeline.webp" alt="Pipeline Diagram"></p>
<h1>Excerpt Pipeline (GitHub Actions)</h1>
<p>:::message
I excluded Container Build and Push steps, as their timing is left to individual judgment. Since the target of Trivy&#39;s vulnerability scan is images, the build process is assumed to occur before this excerpt.
:::</p>
<p>Basically, This task should be executed during the Deployment Phase rather than during the Application Build Phase.</p>
<p>While version management for SBOM files was an option, we chose to overwrite them, as the latest version is deemed sufficient. Note that incorporating into the Build will result in an SBOM that does not reflect the container actually present in the workload.</p>
<p>The reason the pipeline calls XEOL twice is to display logs to GitHub Action and for file generation. Since the modes appear to be different, a single unified operation wasn&#39;t possible, we decided to separate them.</p>
<pre><code class="language-yaml:pipeline">    ## Vulnerability scanning with Trivy
    - name: Run Trivy vulnerability scanner
      uses: aquasecurity/trivy-action@master
      with:
        image-ref: &#39;${{ ImageName }}:${{ IMAGETAG }}&#39;
        format: &#39;table&#39;
        exit-code: &#39;0&#39;  ## If you want to proceed with ImageBuild/Push despite vulnerabilities, set this to &quot;0&quot;
        ignore-unfixed: false
        vuln-type: &#39;os&#39; ## If you want to include Java, etc., add &quot;library&quot;
        severity: &#39;CRITICAL,HIGH&#39;

    ## SBOM generation with SYFT
    - name: Run Syft make sbom files(format cyclone-dx)
      uses: anchore/sbom-action@v0
      with:
        image: &#39;${{ ImageName }}:${{ IMAGETAG }}&#39;
        format: cyclonedx
        artifact-name: &quot;${{ github.event.repository.name }}-sbom.cyclonedx.xml&quot;
        output-file: &quot;${{ github.event.repository.name }}-sbom.cyclonedx.xml&quot;
        upload-artifact-retention: 5 ## Artifact expiration date

    ## EOL library detection (display in WF) and file creation from SBOM in XEOL
    - name: Run XEOL mw/sw EOL scanner from sbom file
      uses: noqcks/xeol-action@v1.1.1
      with:
        sbom: &quot;${{ github.event.repository.name }}-sbom.cyclonedx.xml&quot;
        output-format: table
        fail-build: false

    - name: Run XEOL mw/sw EOL scanner from sbom file and Output file
      uses: noqcks/xeol-action@v1.1.1
      id: xeol
      with:
        sbom: &quot;${{ github.event.repository.name }}-sbom.cyclonedx.xml&quot;
        output-format: json
        fail-build: false

    ## AWS credential settings (SBOM)
    - name: AWS credentials
      uses: aws-actions/configure-aws-credentials@v4
      with:
        role-to-assume: ${{ S3_ACCOUNT_ROLE }} 
        aws-region: ${{ AWS_REGION }} 

    ## Save SBOM/EOL in the Team&#39;s S3Bucket to be managed
    ## Extracted with Cut, as it is hierarchical in S3 by image name or repository name.
    - name: SBOM and EOL file sync to s3 bucket
      run: |
        ECRREPOS=`echo ${{ ImageName }} | cut -d &quot;/&quot; -f 2,3`
        echo $ECRREPOS
        aws s3 cp ${{ github.event.repository.name }}-sbom.cyclonedx.xml s3://${{ s3-bucket-name }}/${{ github.event.repository.name }}/$ECRREPOS/sbom-cyclonedx.xml
        aws s3 cp ${{ steps.xeol.outputs.report }} s3://${{ s3-bucket-name }}/${{ github.event.repository.name }}/$ECRREPOS/eol-result.json
</code></pre>
<h1>Improvements Made and Future</h1>
<p><strong>The results of checking whether my department is using the AWS-CORE library.</strong> <img src="/assets/blog/authors/JumpeiShimamura/20241204/sbom-result.webp" alt="SBOM Search Results"></p>
<p><strong>The results of checking whether my department has EOL components.</strong> <img src="/assets/blog/authors/JumpeiShimamura/20241204/eol-result.webp" alt="EOL Search Results"></p>
<p>Since KINTO Technologies has integrated SBOM/EOL lists into the CMDB, allowing us to search and confirm the following:</p>
<ul>
<li>What libraries are being used in my product?</li>
<li>Are there any components that have reached EOL?</li>
</ul>
<p> With the in-house CMDB, libraries and packages approaching EOL are also highlighted, making it easy to respond in advance.</p>
<p>The first step of SBOM management is to output SBOM files in JSON, formatting them with jq, and manage them in Excel.</p>
<p>In the future, we would like to start the operation of periodically requesting responses by ticket to products that include EOL. This will involve collaboration with the security team and many others.</p>
<h1>Impressions</h1>
<p>I looked into tools for EOL management, but there aren&#39;t many dedicated ones; most seem to be extensions of SBOM management, software management, or asset management. Given the prevalence of development involving OSS and libraries, regularly monitoring EOL can be highly effective as part of vulnerability countermeasures. Libraries often reach EOL within 1 to 2 years, requiring frequent updates to keep up with version changes.</p>
<p>Being able to see the current state is a strong countermeasure against &quot;not noticing&quot; or &quot;ignoring&quot; issues. Similar to observability (O11y), the first goal should be to &quot;make it visible.&quot;</p>
<p>In fact, it may not be effective unless it is built up to the operational step which includes requests for fixes. However, I wrote this article under the title &quot;Starting with Minimal&quot; to say, &quot;Let&#39;s build it first! Let&#39;s get started!&quot; </p>
<h1>Conclusion</h1>
<p>The Platform Engineering team manages and develops tools used internally across the organization. We leverage tools and solutions created by other teams within the Platform Group. Based on the company&#39;s requirements, we either develop new tools from scratch or migrate existing components as needed. We are automating routine tasks for the MSP team and are also starting to explore CDK, so we started programming in addition to Managed Service. If you’re interested in these activities or would like to learn more, please don’t hesitate to reach out to us.</p>
<p>@<a href="https://hrmos.co/pages/kinto-technologies/jobs/1859151978603163672">card</a></p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/JumpeiShimamura/20241204/1204-title.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[[Manabi-no-Michi-no-Eki (Learning Roadside Station)] Embarking on a New Chapter: The Learning Station Team Joins the DevRel Group!]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-03-the-next-goal-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-03-the-next-goal-en/</guid>
            <pubDate>Mon, 03 Mar 2025 10:00:00 GMT</pubDate>
            <content:encoded><![CDATA[<p>This article is the entry for day 3 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄</p>
<hr>
<p>It&#39;s incredible to think that nearly a year has already gone by since the Learning Station was established! I’m Nakanishi from the DevRel (Developer Relations) Group, and I strongly believe that the Learning Roadside Station team plays a pivotal role in enhancing KINTO Technologies&#39; learning culture. In the DevRel team, we work tirelessly connecting the dots of individual growth —from input to output— to reshape our organizational culture.</p>
<h3>A major shift supporting our engineering culture</h3>
<p>This year brought a significant change to our engineering culture: the establishment of the DevRel Group. Until recently, our activities had only been carried out as a project. However, we officially formed a team this spring, making it a fully-fledged part of the organization. From now, some team members are dedicated full-time, no longer balancing this role with other responsibilities. This reflects an important milestone for the company in its effort to empower employees to share their knowledge and insights.</p>
<p>Moreover, this year, in addition to establishing the team, we gained recognition for the input-focused initiatives we had envisioned from the early days of the Tech Blog’s creation.</p>
<h3>Explaining the Technical PR Group’s activities using a car analogy</h3>
<p>If we were to compare our output-focused efforts to a car, it would be like replacing a muffler or modifying the engine structure to improve exhaust efficiency.</p>
<p>On the other hand, our input-focused initiatives, represented by the Learning Roadside Station, is like switching the fuel from regular gasoline to premium or even nitro. It’s also comparable to transitioning from carburetors to fuel injection, or adding a turbocharger. In other words, this team is responsible for transforming the fuel and figuring out how to inject it into the engine as efficiently as possible.</p>
<p><img src="/assets/blog/authors/aoi.nakanishi/2024-12-03-the-next-goal/input-output.png" alt="A diagram showing the flow from fuel to engine and then to exhaust"></p>
<p>If we think of the engine as representing individuals or teams, the type and amount of input needed will differ for each engine. For example, diesel engines and gasoline engines require different methods of fuel input, and the efficiency also varies depending on the engine’s displacement.</p>
<h3>Centralizing learning within the company</h3>
<p>I may have taken a long introduction, but at the Learning Roadside Station, we are working to optimize learning inputs for each individual and create a system that leverages the strengths shared among employees. Over the past year, we have focused on centralizing learning activities. This included visualizing study sessions, sharing and promoting study session information, supporting their organization, and consolidating scattered study resources across the company into one place. From now on, we will focus on &quot;K to K&quot; to bring people together and strengthen connections across the company.</p>
<h3>What is &quot;K to K&quot;?</h3>
<p>&quot;K to K&quot; stands for &quot;KINTO Technologies to KINTO Technologies&quot;. It’s all about helping employees share their skills and learn from one another.</p>
<p>For example, if some says:</p>
<ul>
<li>&quot;I don&#39;t have enough analytical skills.&quot; -&gt; One could consult with the Analysis Group.</li>
<li>&quot;I want to learn coaching.&quot; -&gt; Ask person A who’s could organize coaching sessions.</li>
<li>&quot;I’m curious about project management.&quot; -&gt; Reach out to PdM (Product Management) team members.</li>
</ul>
<p>By connecting the energy of those who wish to learn with the energy of those who want to share their expertise, such as:</p>
<ul>
<li>&quot;I want to make better use of my skill A.&quot;</li>
<li>or “I know someone who has this skill but is looking for the right opportunity to use it”,</li>
</ul>
<p>we can help bring out the best in our employees and energize the organization.</p>
<p>The next action for the Learning Roadside Station is to focus on what we do best: <em>bringing out the strengths of individuals and teams</em> and <em>connecting the dots across the organization</em>. We look forward to tackling the challenges that lie ahead and are excited to see where our efforts will take us in the coming year.</p>
<h3>Other activities</h3>
<p>As part of our input-focused initiatives, we are also considering an expanded version of the podcast we currently run. At the moment, our main activity is interviewing those who host study sessions. However, similar to “K to K” activities, there are many things we would like to share across the company. By transforming these efforts into podcast content, we hope to create more opportunities for employees to learn and even provide a place for output for sharing knowledge beyond the company.</p>
<p>Additionally, we are implementing Udemy Business as part of our efforts. We aim to explore how to use video content effectively and make the most of this platform.</p>
<h3>Conclusion</h3>
<p>Along with tech blogs, events, and presentations, we aim to expand the scope of the Learning Roadside Station as the next central pillar of our efforts. By continuously growing our input-focused initiatives, we hope to broaden our business horizons and create an environment where every employee can grow, thrive, and fully utilize their unique strengths.</p>
<p>We will continue to actively share our progress and plans next year, so please stay tuned!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Introducing an evaluation system into generative AI application development to improve accuracy: Initiatives to automate database design reviews]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-12-schema-review-by-llm-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-12-schema-review-by-llm-en/</guid>
            <pubDate>Fri, 28 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Here’s a story about how the introduction of an evaluation system in generative AI application development (DB design review automation) improved accuracy.]]></description>
            <content:encoded><![CDATA[<p>Hello. We are <a href="https://x.com/_p2sk_">@p2sk</a> and <a href="https://x.com/RmcHoshi">@hoshino</a> from the DBRE team.</p>
<p>The DBRE (Database Reliability Engineering) team is a cross-functional organization focused on resolving database (DB) issues and developing platforms.</p>
<p>In this article, we introduce an automatic review function for DB table designs built with a serverless architecture using AWS&#39;s generative AI service “<a href="https://aws.amazon.com/jp/bedrock/">Amazon Bedrock</a>.” This function works with GitHub Actions, and when a pull request (PR) is opened, the AI ​​automatically reviews it and proposes corrections as comments. We also explain how we designed and implemented the evaluation of generative AI applications. We explain the evaluation methods adopted for each of the three phases in the LLMOps lifecycle (i.e., model selection, development, and operation phases), and in particular introduce generative AI- based automated evaluation utilizing &quot;LLM-as-a-Judge&quot; in the operation phase.</p>
<h1>Purpose of this article</h1>
<p>This article aims to provide easy-to-understand information on generative AI application evaluation, ranging from abstract concepts to concrete implementation examples. By reading this article, we hope that even engineers who do not have specialized knowledge of machine learning, like our DBRE team, will gain a better understanding of the generative AI development lifecycle. We will also introduce the challenges we faced when using generative AI in our services and how we solved them, with concrete examples. In addition, you can read this article also as one implementation example for the &quot;Considerations and Strategies for Practical Implementation of LLM&quot; introduced in the session &quot;<a href="https://pages.awscloud.com/rs/112-TZM-766/images/aws-ai-day-1031-AI-T2-01.pdf">Best Practices for Implementing Generative AI Functions in Content Review</a>&quot; held at the recent AWS AI Day.</p>
<p>I hope this article will be of some help to you.</p>
<h1>Table of Contents</h1>
<p>This article is structured as follows. This is a long article, so if you’re just interested in how the system works, I recommend you read up to the section on the &quot;Completed System.&quot; If you’re interested in developing generative AI applications, I recommend you continue reading beyond that.</p>
<ul>
<li><a href="#%E8%83%8C%E6%99%AF">Background</a></li>
<li><a href="#%E8%A8%AD%E8%A8%88">Design</a> </li>
<li><a href="#%E5%AE%8C%E6%88%90%E3%81%97%E3%81%9F%E4%BB%95%E7%B5%84%E3%81%BF">Completed System (with demo video)</a></li>
<li><a href="#%E5%AE%9F%E8%A3%85%E6%99%82%E3%81%AE%E5%B7%A5%E5%A4%AB%E7%82%B9">Ideas in Implementation</a></li>
<li><a href="#%E7%94%9F%E6%88%90-ai-%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E8%A9%95%E4%BE%A1">Evaluation of Generative AI Applications</a></li>
<li><a href="#%E5%BE%97%E3%82%89%E3%82%8C%E3%81%9F%E5%AD%A6%E3%81%B3%E3%81%A8%E4%BB%8A%E5%BE%8C%E3%81%AE%E5%B1%95%E6%9C%9B">Lessons Learned and Future Prospects</a></li>
<li><a href="#%E3%81%BE%E3%81%A8%E3%82%81">Conclusion</a></li>
</ul>
<h1>Background</h1>
<h3>Importance of database table design</h3>
<p>Generally, DB tables have the characteristic that once they are created, they are difficult to modify.  As the service grows, the amount of data and frequency of references tend to increase, so we want to avoid as much as possible carrying technical debt that makes us regret later, saying, &quot;I should have done it this way at the design stage...&quot;.  Therefore, it is important to have a system in place that allows tables to be created with &quot;good design&quot; based on unified standards. &quot;<a href="https://pages.awscloud.com/rs/112-TZM-766/images/Top%2010%20Things%20to%20Consider%20to%20run%20databases%20on%20AWS_rev.pdf#page=10">10 Things to Do to Get Started with Databases on AWS</a>&quot; also states that a table design is still a valuable task, even though database management has been automated in the cloud.</p>
<p>In addition, the spread of generative AI is making the data infrastructure even more important. Tables designed with uniform standards are easy to analyze, and easy-to-understand naming and appropriate comments have the advantage of providing good context for generative AI.</p>
<p>Given this background, the quality of DB table design has a greater impact on an organization than ever before. One way to ensure quality is creating in-house guidelines and conducting reviews based on them.</p>
<h3>Our current situation regarding reviews</h3>
<p>At our company, table design reviews are carried out by the person in charge of each product. The DBRE team has provided the &quot;Design Guidelines,&quot; but they are currently non-binding.  We considered having DBRE review the table designs of all products across the board, but since there are dozens of products, we were concerned that if DBRE acted like a gatekeeper, it would become a bottleneck in development, so we gave up on the idea.</p>
<p>Against this background, we, DBRE team, have decided to develop an automatic review system that acts as a guardrail and apply the system to our products.</p>
<h1>Design</h1>
<h3>Abstract architecture diagram and functional requirements</h3>
<p>The following is the abstract architecture diagram for the automatic table design review function. <img src="/assets/blog/authors/m.hirose/2024-11-22-12-41-53.png" alt="Abstract architecture diagram of the automatic review function"></p>
<p>To continuously perform automated reviews, it is important to integrate them into the development workflow. For this reason, we have adopted a system that triggers via a PR the automatic execution of an application on AWS and provides feedback within the PR, including comments on suggested corrections to the table definition (DDL). The requirements for an application are as follows:</p>
<ul>
<li>The ability to set our company&#39;s own review criteria</li>
<li>To complement human reviews, it should be as accurate as possible, even if not 100%.</li>
</ul>
<h3>Policies for implementing review function</h3>
<p>There are two possible policies for automating table design reviews: (1) Review through syntax analysis and (2) Review through generative AI. The characteristics of each policy are summarized as follows: <img src="/assets/blog/authors/m.hirose/2024-11-21-19-38-39.png" alt="Comparison table of review function implementation policies"></p>
<p>Ideally, (1) should be applied to review criteria that can be handled through syntax analysis, and (2) to other review criteria. For example, the verification of the naming rule &quot;Object names should be defined in Lower Snake Case&quot; can be handled by (1). On the other hand, subjective criteria, such as &quot;giving object names that allow inferring the stored data,&quot; are better suited for (2).</p>
<p>Ideally, the two policies should be used separately depending on the review criteria, but this time we have decided to implement only &quot;(2) Review through generative AI&quot; for the following reasons.</p>
<ul>
<li>(1) is feasible, but (2) is something whose feasibility we cannot determine until we try it, so we have decided it is worth attempting first.</li>
<li>By implementing the items that can be handled by (1) also in (2), we aim to gain insights into the accuracy and implementation costs of both policies.</li>
</ul>
<h3>Review target guidelines</h3>
<p>To shorten the time to delivery, we have narrowed the review items down to the following six:</p>
<ul>
<li>An index must comply with the DBRE team&#39;s designated naming rules.</li>
<li>Object names are defined in Lower Snake Case.</li>
<li>Object names must consist of alphanumeric characters and underscores only.</li>
<li>Object names must not use Roman characters.</li>
<li>Object names that allow inferring the stored data must be assigned.</li>
<li>Columns that store boolean values ​​should be named without using &quot;flag&quot;.</li>
</ul>
<p>The top three can be addressed with syntactic analysis, but the bottom three are likely to be better addressed with generative AI, which also provides suggested corrections.</p>
<h3>Why create a dedicated system?</h3>
<p>Although several &quot;systems (mechanisms) for review using generative AI&quot; already exist, we have determined that they do not meet our requirements, so we have decided to create a dedicated system. For example, <a href="https://github.com/Codium-ai/pr-agent">PR-Agent</a> and <a href="https://www.coderabbit.ai/">CodeRabbit</a> are well-known generative AI review services. Our company has also adopted PR-Agent for <a href="https://blog.kinto-technologies.com/posts/2024-06-17-pr-agent/">reviewing codes and tech blogs</a>. In addition, <a href="https://github.blog/changelog/2024-10-29-github-copilot-code-review-in-github-com-public-preview/">GitHub Copilot&#39;s automated review function</a> is currently available as Public Preview and may become generally available in the future. This function also allows you to have your code reviewed in Visual Studio Code before pushing it, and it is expected that the &quot;generative AI review system&quot; will become more seamlessly integrated into development flows in the future. Additionally, you can define your own coding standards in the GitHub management screen and have Copilot review based on them.</p>
<p>The following are some reasons why we want to build our own system:</p>
<ul>
<li>It is difficult to check a large number of guidelines with high accuracy using generative AI, and we have determined that it is currently challenging to handle this with external services.</li>
<li>We want to adjust the feedback method flexibly.<ul>
<li>Example: Columns like &quot;data1&quot; have ambiguous meanings, so it is difficult to suggest corrections, so we want to keep them in comments only.</li>
</ul>
</li>
<li>In the future, we aim to improve accuracy with a hybrid structure combining syntax analysis.</li>
</ul>
<p>Next, we will introduce the completed system.</p>
<h1>Completed system</h1>
<h3>Demo video</h3>
<p>After the PR is created, GitHub Actions is executed, and the generative AI provides feedback on the review results as comments on the PR. The actual processing time is approximately 1 minute and 40 seconds, but the waiting time has been omitted from the video. The cost of the generative AI when using Claude 3.5 Sonnet is estimated to be approximately 0.1 USD per DDL review. <a href="https://www.youtube.com/watch?v=bGcXu9FjmJI">https://www.youtube.com/watch?v=bGcXu9FjmJI</a></p>
<h3>Architecture</h3>
<p>The final architecture is as shown in the diagram below. Note that we have built an evaluation application separately to tune the prompts used, which will be described in detail later. <img src="/assets/blog/authors/m.hirose/2024-11-22-21-07-59.png" alt="Final Architecture Diagram"></p>
<h3>Process Flow</h3>
<p>When a PR is opened, a GitHub Actions workflow is triggered, and an AWS Step Function is started. At this stage, save the PR URL and the GITHUB_TOKEN generated in the workflow to DynamoDB. The reason for not passing DDL directly to Step Functions is to avoid input character limits. Extract the DDL on the Lambda side based on the PR URL. Step Functions uses a Map state to review each DDL in parallel. Only one guideline should be checked per review. To review based on multiple guideline criteria, the &quot;post-correction DDL&quot; obtained from the first prompt is repeatedly passed to the next prompt, generating the final DDL (the reason will be explained later). After completing the review, provide feedback as a comment on the PR. The review results are stored in S3, and the generative AI evaluates them using LLM-as-a-Judge (more details will be provided later).</p>
<p>Examples of the results are shown below. <img src="/assets/blog/authors/m.hirose/2024-12-05-20-49-04.png" alt="Example of review results by generative AI"></p>
<p>As feedback from the generative AI, the &quot;applied guidelines&quot; and &quot;suggested corrections&quot; are provided as comments (on the left side of the image). The details have been collapsed and can be expanded to check the specific corrections and comments made to the DDL (on the right side of the image).</p>
<h3>Steps required for implementation</h3>
<p>The table design review feature can be implemented in just two steps. Since it can be set up in just a few minutes, you can easily introduce it and start the generative AI review immediately.</p>
<ol>
<li>Register the key required to access AWS resources in GitHub Actions Secrets.</li>
<li>Add a GitHub Actions workflow for the review function to the target GitHub repository.<ul>
<li>Simply add the product name to the template file provided by the DBRE team.</li>
</ul>
</li>
</ol>
<p>Next, we will introduce some of the ideas we came up with for our implementation.</p>
<h1>Ideas in Implementation</h1>
<h3>Utilization of container images and Step Functions</h3>
<p>Initially, we planned to implement it using only Lambda, but we encountered the following challenges.</p>
<ol>
<li>The library size is too large and exceeds the 250 MB deployment package size limit for Lambda.</li>
<li>When chaining and evaluating multiple guideline criteria, there is a concern that the maximum execution time of Lambda (15 minutes) may be reached.</li>
<li>When serially processing DDLs, the execution time increases as the number of DDLs increases.</li>
</ol>
<p>To solve issue 1, we adopted container images for Lambda. To solve 2 and 3, we introduced Step Functions and changed the design so that each Lambda execution evaluates one DDL against one guideline criterion. Furthermore, by using Map state to perform parallel processing for each DDL, we ensured that the overall processing time is not affected by the number of DDLs. The diagram below shows the implementation of the Map state, where the prompt chain is realized in the loop section.</p>
<p><img src="/assets/blog/authors/m.hirose/2024-12-05-22-49-13.png" alt="The process executed in the Map state of Step Functions"></p>
<h3>Measures against Bedrock API throttling</h3>
<p>During the review, Bedrock&#39;s InvokeModel requests occurred according to the number of <code>DDLs multiplied by the number of guidelines</code>, and errors sometimes occurred due to quota limits. According to the <a href="https://docs.aws.amazon.com/general/latest/gr/bedrock.html">AWS documentation</a>, this limit cannot be relaxed. For this reason, we introduced a mechanism to distribute requests on a per-DDL basis across multiple regions and, in the event of an error, retry in yet another region. This has led to stable reviews, mostly without reaching the RateLimit.</p>
<p>However, we are currently using <a href="https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html">cross-region inference</a>, which dynamically routes traffic among multiple regions and allows us to delegate throttling countermeasures to AWS, so we plan to transition to this in the future.</p>
<h3>Organizing the way to grant permissions to execute GitHub API from Lambda</h3>
<p>In order to enable Lambda to &quot;obtain the changed files of the target PR&quot; and &quot;post comments on the target PR,&quot; we compared the following three methods of granting permissions.</p>
<table>
<thead>
<tr>
<th>Token type</th>
<th>Expiration date</th>
<th>Advantages</th>
<th>Disadvantages</th>
</tr>
</thead>
<tbody><tr>
<td><a href="https://docs.github.com/ja/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens">Personal Access Token</a></td>
<td>Depending on the setting, it can be unlimited</td>
<td>The scope of the permissions is broad.</td>
<td>Dependency on individuals</td>
</tr>
<tr>
<td><a href="https://docs.github.com/ja/actions/security-for-github-actions/security-guides/automatic-token-authentication#permissions-for-the-github_token">GITHUB_TOKEN</a></td>
<td>Only during workflow execution</td>
<td>Easy to obtain</td>
<td>Concerns about insufficient permissions depending on the processing of the target</td>
</tr>
<tr>
<td><a href="https://docs.github.com/ja/apps/creating-github-apps/authenticating-with-a-github-app/making-authenticated-api-requests-with-a-github-app-in-a-github-actions-workflow#authenticating-with-a-github-app">GitHub App（installation access token）</a></td>
<td>1 hour</td>
<td>Granting permissions not supported by GITHUB_TOKEN is also possible</td>
<td>Increased complexity of the steps involved in introduction to a product</td>
</tr>
</tbody></table>
<p>This time we have adopted GITHUB_TOKEN for the following reasons:</p>
<ul>
<li>Tokens are short-lived (only for the duration of the workflow) and pose a low security risk.</li>
<li>Token issuance and management are automated, reducing the operational burden.</li>
<li>Permissions necessary for this processing can be granted.</li>
</ul>
<p>The tokens are stored in DynamoDB with a time to live (TTL) and are retrieved and used by Lambda when needed. This allows you to use tokens safely without needing to check whether the token-passing process has been logged.</p>
<p>In the following, we present evaluation examples of generative AI applications.</p>
<h1>Evaluation of Generative AI Applications</h1>
<p>For generative AI application evaluation, we referred to the diagram below from <a href="https://learn.microsoft.com/ja-jp/azure/ai-studio/concepts/evaluation-approach-gen-ai">Microsoft&#39;s documentation</a>.</p>
<p><img src="/assets/blog/authors/m.hirose/2024-11-22-12-52-25.png" alt="Overview diagram of generative AI application evaluation flow">
<em>Source: Microsoft - <a href="https://learn.microsoft.com/ja-jp/azure/ai-studio/concepts/evaluation-approach-gen-ai">Evaluation of Generative AI Applications</a></em></p>
<p>According to this diagram, there are three types of evaluations that should be performed during the GenAIOps (LLMOps, because the target in this case is LLM) lifecycle.</p>
<ul>
<li>Model selection phase<ul>
<li>Evaluating the base models and deciding which model to use</li>
</ul>
</li>
<li>Application development phase<ul>
<li>Evaluating the application output (≒ response of the generative AI) from the perspectives of quality, safety, etc., and tuning it</li>
</ul>
</li>
<li>Post-deployment operation phase<ul>
<li>Even after deployment to the production environment, quality, safety, etc., are evaluated continuously.</li>
</ul>
</li>
</ul>
<p>Below, we will introduce some examples of how evaluations were conducted in each phase.</p>
<h2>Evaluation during the model selection phase</h2>
<p>This time, we selected an Amazon Bedrock platform model and evaluated it based on the scores from <a href="https://openlm.ai/chatbot-arena/">Chatbot Arena</a> and the advice of <a href="https://blog.kinto-technologies.com/posts/2024-01-26-GenerativeAIDevelopProject/">our in-house generative AI experts</a> and adopted Claude from Anthropic. We reviewed the DDL using Claude 3.0 Opus, which was the highest-performing model at the time of our launch, and confirmed its accuracy to a certain extent. Each model has different base performance, response speed, and monetary costs, but since reviews in this case are infrequent and there is no requirement for &quot;maximum speed,&quot; we selected the model with the greatest emphasis on performance. Based on Claude&#39;s best practices, we determined that further accuracy could be achieved through prompt tuning and moved on to the next phase.</p>
<p>Meanwhile, the higher-performance and faster Claude 3.5 Sonnet was released, which further improved the inference accuracy.</p>
<h2>Evaluation during the application development phase</h2>
<p>Generative AI evaluation methods are clearly summarized in <a href="https://note.com/ray_30cm_ns/n/nea6470deece3">the article here</a>. As the article states, </p>
<blockquote>
<p>&quot;Various evaluation patterns are possible depending on the presence or absence of a prompt, foundational model, and RAG,&quot;</p>
</blockquote>
<p>the evaluation pattern will vary depending on &quot;what is being evaluated.” This time, we will focus on the evaluation of a &quot;single prompt&quot; and provide a concrete example of the design and implementation for the specific use case, which involves &quot;having a database table design reviewed according to our company&#39;s guidelines.&quot;</p>
<h3>Prompt tuning and evaluation flow</h3>
<p>Prompt tuning and evaluation were carried out according to the diagram below, as described in <a href="https://docs.anthropic.com/en/docs/build-with-claude/define-success">Claude&#39;s documentation</a>. <img src="/assets/blog/authors/m.hirose/2024-11-19-14-16-32.png" alt="Prompt tuning flow">    <em>Source:  Anthropic company - <a href="https://docs.anthropic.com/en/docs/build-with-claude/develop-tests">Create strong empirical evaluations</a></em></p>
<p>The key point is to define an evaluation perspective, such as &quot;how close the prompt execution result is to expectations,&quot; as a &quot;score calculated in some way,&quot; and to adopt the prompt with the best score. Without an evaluation system (mechanism), determining the improvement in accuracy before and after tuning may rely on subjective judgment, which could lead to ambiguity and an increase in work time.</p>
<p>In the following, we will first introduce the generative AI evaluation method, followed by examples of prompt tuning.</p>
<h3>What is &quot;generative AI evaluation&quot;?</h3>
<p>The page for the generative AI evaluation product called &quot;<a href="https://www.deepchecks.com/">deep checks</a>&quot; states the following about evaluation.</p>
<blockquote>
<p>Evaluation = Quality + Compliance</p>
</blockquote>
<p>I felt that this was the most concise way to evaluate generative AI applications. Breaking it down further, <a href="https://www.brainpad.co.jp/doors/contents/apply_generative_ai_to_business_tips/">the article here</a> classifies the criteria for evaluating service providers into four perspectives: &quot;truthfulness, safety, fairness, and robustness.&quot; The evaluation criteria and score calculation method should be selected according to the properties of the application. For example, <a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/model-evaluation-report-programmatic.html">Amazon Bedrock</a> uses different metrics for different tasks, such as &quot;BERT score&quot; for text summarization and &quot;F1 score&quot; for question answering.</p>
<h3>Method for calculating evaluation scores</h3>
<p><a href="https://github.com/anthropics/anthropic-cookbook/blob/main/misc/building%5Fevals.ipynb">anthropic-cookbook</a> classifies the methods for calculating scores into the following three main categories: <img src="/assets/blog/authors/m.hirose/2024-11-20-12-26-21.png" alt="Summary of score calculation methods">
<em>Summary of the score calculation methods described in <a href="https://github.com/anthropics/anthropic-cookbook/blob/main/misc/building%5Fevals.ipynb">anthropic-cookbook</a></em></p>
<p>You can choose to use cloud services, OSS, or create your own score calculation logic. In any case, you need to set your own evaluation criteria. For example, if the LLM&#39;s output is in JSON format, &quot;matching each element&quot; may be more appropriate than &quot;matching the entire string.&quot;</p>
<p>Regarding model-based grading, the code provided in <a href="https://github.com/anthropics/anthropic-cookbook/blob/main/misc/building%5Fevals.ipynb">anthropic-cookbook</a> can be expressed more concisely as follows:</p>
<pre><code class="language-python">def model_based_grading(answer_by_llm, rubric):
    Prompt = f&quot;&quot;&quot; Evaluating the answer within the &lt;answer&gt; tag based on the perspective of the &lt;rubric&gt; tag. Answering &quot;correct&quot; or &quot;incorrect.&quot;
      &lt;answer&gt;{answer_by_llm}&lt;/answer&gt;
      &lt;rubric&gt;{rubric}&lt;/rubric&gt;
    &quot;&quot;&quot;
    return llm_invoke(prompt) # Pass the created prompt to LLM for inference

rubric = &quot;Correct answers must include at least two different training plans.&quot;

answer_by_llm_1 = &quot;The recommended exercises are push-ups, squats, and sit-ups.&quot; # Actually, the output of LLM
grade = model_based_grading(answer_by_llm_1, rubric)
&quot;print(grade) # It should be output as &quot;correct&quot;

answer_by_llm_2 = “The recommended training is push-ups.” # Actually, the output of LLM
grade = model_based_grading(answer_by_llm_2, rubric)
print(grade) # It should be output as &quot;incorrect.&quot;
</code></pre>
<h3>Summary of evaluation</h3>
<p>To summarize the content covered so far, the evaluation method is illustrated as the diagram below. <img src="/assets/blog/authors/m.hirose/2024-11-22-19-24-43.png" alt="Summary of evaluation method"></p>
<p>Abstractly, the evaluation of generative AI breaks down into Quality and Compliance. These are further broken down, and specific evaluation criteria are set for each use case. Each criterion needs to be quantified, and this can be achieved based on “Code,” “Human,” or “Model.”</p>
<p>In the following, we will explain the specific evaluation method from the perspective of &quot;database table design review.&quot;</p>
<h3>Evaluation design in DB table design review</h3>
<p>We chose a code-based approach to quality evaluation for the following reasons:</p>
<ul>
<li>The cycle of evaluation and tuning by humans increases man-hours and is not worth the resulting benefits.</li>
<li>We also considered a model-based approach, but since we wanted to assign the best score for a perfect match with the correct DDL, we concluded that a code-based approach was more appropriate.</li>
</ul>
<p>Since it is difficult to newly implement &quot;similarity in DDL&quot; with the correct data, we adopted the Levenshtein Distance, a method for measuring the distance between texts, as the score calculation method. With this method, a perfect match has a distance of 0, and the higher the value, the lower the similarity. However, since this is not an indicator that completely represents &quot;similarity in DDL,&quot; we basically aimed for a score of 0 for all datasets and performed prompt tuning on datasets with non-0 scores. The algorithm is also provided by <a href="https://python.langchain.com/v0.1/docs/guides/productionization/evaluation/string/string_distance/">LangChain&#39;s String Evaluators (String Distance)</a>, which is what we use.</p>
<p>On the other hand, from a compliance perspective, we decided that it was unnecessary this time because it is an in-house application and the implementation limits user input embedded in the prompt to DDL.</p>
<h3>Implementation of evaluation</h3>
<p>The flow of the implemented evaluation is as follows. <img src="/assets/blog/authors/m.hirose/2024-11-20-15-02-22.png" alt="Flow of evaluation and prompt tuning"></p>
<p>For each review perspective, we created 10 patterns of datasets combining input DDL and correct answer DDL.  To efficiently repeat prompt tuning and evaluation, we developed a dedicated application using Python and<a href="https://streamlit.io/">Streamlit</a>. The dataset is saved in jsonl format, and when you specify the file, the evaluation is automatically performed and the results are displayed. Each json contains the &quot;evaluation target guideline&quot;, &quot;parameters for invoking LLM&quot;, &quot;input DDL&quot;, and &quot;correct DDL&quot; as shown below.</p>
<pre><code class="language-json">{
    &quot;guidline_ids&quot;: [1,2],
    &quot;top_p&quot;: 0,
    &quot;temperature&quot;: 0,
    &quot;max_tokens&quot;: 10000,
    &quot;input_ddl&quot;: &quot;CREATE TABLE sample_table (...);&quot;,
    &quot;ground_truth&quot;: &quot;CREATE TABLE sample_table (...);&quot;
}
</code></pre>
<p>When displaying individual results, the diff between the output DDL and the correct DDL is displayed, making it possible to visualize the differences (= tuning points). <img src="/assets/blog/authors/m.hirose/2024-11-20-17-29-34.png" alt="diff in the individual result display screen"></p>
<p>Once the evaluation is complete, you can also check the aggregated score results. <img src="/assets/blog/authors/m.hirose/2024-11-22-18-55-27.png" alt="Example of score aggregation for evaluation"></p>
<h3>Prompt tuning</h3>
<p>Based on <a href="https://docs.anthropic.com/en/docs/build-with-claude/prompt-engineering/overview">Claude&#39;s documentation</a>, we created and tuned the prompts, keeping the following points in mind, and ultimately achieved the best results (0 score) for almost all 60 datasets.</p>
<ul>
<li>Setting roles</li>
<li>Utilizing XML tags</li>
<li>Letting Claude think (instructing Claude to show its thinking process to make debugging easier when the answer is disappointing)</li>
<li>Few-Shot Prompting (providing example output)</li>
<li>Putting reference data at the beginning and instructions at the end</li>
<li>Giving clear and specific instructions</li>
<li>Chaining prompts</li>
</ul>
<p>There are many well-known techniques, so we won&#39;t go into detail here, but let us provide some additional information on the last two points.</p>
<h4>&quot;Giving clear and specific instructions&quot;</h4>
<p>Initially, we embedded the text of our in-house table design guidelines directly into the prompt. However, the guidelines only described &quot;how things should be&quot; and did not include &quot;specific steps to correct the errors.&quot;  Therefore, we rewrote them into &quot;specific correction instructions&quot; in a step-by-step format.  For example, we revised the guideline &quot;Do not use xxx_flag for column names that store boolean values&quot; as follows:</p>
<pre><code>Follow the steps below to extract the column name storing the Boolean value and change it to an appropriate column name if necessary.
1. Extract the name of the column that stores Boolean values. The criterion is whether the column uses the Boolean type or contains the word &quot;flag.&quot;
2. Check the names of the Boolean columns one by one and understand the meaning of the columns first.
3. Check the names of the Boolean columns one by one, and if you determine there is a more appropriate name, modify the column name.
4. Regarding the appropriate column name conditions, refer to the &lt;appropriate_column_name&gt;&lt;/appropriate_column_name&gt; tag.

&lt;appropriate_column_name&gt;
    Without using the word &quot;flag&quot;...
    ...
&lt;/appropriate_column_name&gt;
</code></pre>
<h4>&quot;Chaining prompts&quot;</h4>
<p>The more guidelines there are to check, the more complex the prompts will become if you try to check them all in one go, raising concerns that checks will be missed or accuracy will decrease. For this reason, we limited the items that the AI ​​checks in each prompt execution to one. We also reflected this in the architecture by passing the &quot;corrected DDL&quot; obtained in the first prompt as input for the next prompt (Chain), and repeating the process to obtain the final DDL. Chaining prompts also offers the following advantages:</p>
<ul>
<li>Since prompts are short and tasks are limited to one, accuracy improves.</li>
<li>When adding guidelines, you only need to create a new prompt, so there is no impact on the accuracy of existing prompts.</li>
</ul>
<p>On the other hand, the time and financial costs will increase as the number of LLM Invokes increases.</p>
<h2>Evaluation during the post-deployment operational phase</h2>
<p>In the prompt creation stage, we evaluated the quality using manually created correct answer data.  However, in a production environment, no correct answer data exists, so a different approach for evaluation is required. So, we adopted LLM-as-a-Judge, an approach where an LLM evaluates its own responses. According to the <a href="https://www.confident-ai.com/blog/why-llm-as-a-judge-is-the-best-llm-evaluation-method">Confident AI documentation</a>, there are three methods to this approach.</p>
<ul>
<li>Single Output Scoring (no correct answer data)<ul>
<li>Giving the &quot;LLM output&quot; and &quot;evaluation criteria&quot; and having the LLM provide a score based on the criteria.</li>
</ul>
</li>
<li>Single Output Scoring (with correct answer data)<ul>
<li>In addition to the above, &quot;correct answer data&quot; is also provided. A more accurate evaluation can be expected.</li>
</ul>
</li>
<li>Pairwise Comparison<ul>
<li>Comparing two outputs and determining which is better. You define the criteria for &quot;better&quot; yourself.</li>
</ul>
</li>
</ul>
<p>This time, we used Single Output Scoring (with no correct answer data). This approach is also <a href="https://python.langchain.com/v0.1/docs/guides/productionization/evaluation/string/scoring_eval_chain/">supported by LangChain</a>, and we used the provided function. Currently, implementation by <a href="https://docs.smith.langchain.com/">LangSmith</a> is recommended.</p>
<p>The following two criteria are defined, and each is scored on a 10-point scale.</p>
<ul>
<li>Appropriateness<ul>
<li>Has the LLM output been appropriately corrected in accordance with the guidelines?</li>
</ul>
</li>
<li>Formatting Consistency<ul>
<li>Are there no unnecessary line breaks or spaces, and is the format consistent?</li>
</ul>
</li>
</ul>
<p>The code and prompt images are below:</p>
<pre><code class="language-python">    input_prompt =&quot;&quot;&quot;
    &lt;input_sql_ddl&gt;CREATE TABLE ...&lt;/input_sql_ddl&gt;
    &lt;table_check_rule&gt;Ambiguous object names are...&lt;/table_check_rule&gt;

    Instructions: Based on table_check_rule, correct input_sql_ddl to the appropriate DDL.
    &quot;&quot;&quot;

    output_ddl = &quot;CREATE TABLE ...&quot; # Actually, DDL generated by LLM is set

    appropriateness_criteria = {
        &quot;appropriateness&quot;: &quot;&quot;&quot;
            Score 1: ...
            ...
            Score 7: Responses generally following the input instructions have been generated with no more than two inappropriate corrections.
            Score 10: Responses completely following the input instructions have been generated.
        &quot;&quot;&quot;
    }

    evaluator = langchain.evaluation.load_evaluator(
        &quot;score_string&quot;, llm=model, criteria=appropriateness_criteria
    )

    result = evaluator.evaluate_strings(
        prediction=output_ddl, input=input_prompt
    )

    print(result)
</code></pre>
<p>This implementation produces the following outputs: (Some parts omitted)</p>
<pre><code>This answer completely follows the given instructions. The following are the reasons for the evaluation.

1. Extraction and evaluation of column names:
   The answerer has extracted all column names and appropriately judged whether each column name could infer the contents of the data.
2. Identification of ambiguous column names:
   All column names in the provided DDL clearly indicate their purposes and the type of stored data. For example, ...
...

This answer fully understands and properly executes the given instructions.

Rating: [[10]]
</code></pre>
<p>This mechanism corresponds to the red boxes in the architecture diagram below. <img src="/assets/blog/authors/m.hirose/2024-11-22-21-12-14.png" alt="Parts related to LLM-as-a-Judge in the architecture diagram"></p>
<p>Once the LLM review results are stored in S3, the Lambda for LLM-as-a-Judge is launched asynchronously via SQS. This Lambda performs the evaluation, stores the results as logs in S3, and sends the score as a custom metric in CloudWatch. Moreover, CloudWatch Alarm will notify Slack if the threshold is not satisfied.</p>
<p>This score is not 100% reliable, but since it is intended for an in-house system, it is an environment that makes it easier to get feedback from users. So, we have established a system to continuously monitor the performance using quantitative scores and collect user feedback on a regular basis.</p>
<h1>Lessons Learned and Future Prospects</h1>
<p>Finally, we will summarize what we learned from our attempt at developing generative AI applications and our future direction.</p>
<h3>Evaluation is very important but difficult</h3>
<p>By evaluating the prompt results from the same perspective, we were able to quickly repeat tuning and evaluation while eliminating subjectivity. This experience strongly highlighted the importance of evaluation design. However, the three evaluations in GenAIOps (during model selection, development, and operation) need to be judged for each use case, and we felt that &quot;judging the validity of our evaluation design&quot; was difficult. Furthermore, a lack of evaluation perspectives also poses the risk of delivering applications with compliance issues. We believe that, in the future, the provision of more systematic and managed evaluation methods and mechanisms will make it easier to realize GenAIOps.</p>
<h3>Our scope of imagination for generative AI use cases has broadened.</h3>
<p>By conducting research and implementing generative AI applications ourselves, we were able to gain a clearer understanding and broaden the range of use cases we can imagine. For example, we can now envision a system that combines <a href="https://aws.amazon.com/jp/bedrock/agents/">agents</a> with mechanisms for <a href="https://blog.kinto-technologies.com/posts/2024-03-05-aurora-mysql-stats-collector-for-blocking/">collecting information on lock contention</a>, enabling more managed and faster incident investigations.</p>
<h3>Utilizing generative AI as a replacement for programmable tasks</h3>
<p>There are the following two main ways to use generative AI in application development.</p>
<ul>
<li>Having generative AI perform the task itself.</li>
<li>Improving the productivity of program development with generative AI</li>
</ul>
<p>This time, we used generative AI to implement not only tasks that should be inferred by generative AI, but also tasks that would normally be processed by a program. Initially, we had hoped that by devising innovative prompts, we might be able to obtain high-precision results quickly. But we soon became acutely aware that prompt tuning actually requires a lot of time. On the other hand, by running multiple Claude models on the same task, we found that the more accurate the model, the clearer the improvement in results. Furthermore, we found that more accurate models reduce unpredictable behaviors and require less time for prompt tuning.</p>
<p>Based on these experiences, if model accuracy continues to improve in the future, depending on the requirements, there may be an increasing number of cases where the approach of having a generative AI perform a task itself, instead of having it write a program to do so, will be chosen.</p>
<h3>Future prospects</h3>
<p>In the future, we plan to focus on the following:</p>
<ul>
<li>Expansion of response guidelines</li>
<li>Expansion of introduced products</li>
<li>Creating a hybrid configuration with programmatic syntax analysis</li>
<li>Improved clarity when providing feedback on review results to users</li>
<li>Expanding the current simplified LLMOps to not only include monitoring but also enable prompt and model improvements using logs<ul>
<li>Reference: <a href="https://twitter.com/hiro_gamo/status/1862408699253596284">Post</a> by <a href="https://twitter.com/hiro_gamo">@Hiro_gamo</a></li>
</ul>
</li>
</ul>
<h1>Conclusion</h1>
<p>In this article, we introduced an automated review function for database table design, implemented using Amazon Bedrock within a Serverless architecture. We also explained how to evaluate generative AI applications. At the recent AWS AI Day session titled &quot;<a href="https://pages.awscloud.com/rs/112-TZM-766/images/aws-ai-day-1031-AI-T2-01.pdf">Best Practices for Implementing Generative AI Functions in Content Review</a>,&quot; <a href="https://pages.awscloud.com/rs/112-TZM-766/images/aws-ai-day-1031-AI-T2-01.pdf#page=7">key considerations and strategies for the practical introduction of LLMs (large language models)</a> were presented. Below, we outline how our efforts align with the items covered in the session.</p>
<table>
<thead>
<tr>
<th>Item</th>
<th>Content</th>
</tr>
</thead>
<tbody><tr>
<td><strong>Separation from other methods</strong></td>
<td>●The DBRE team is taking on the challenge of automation using LLM.<br>● In the future, we aim to combine LLM with a rule-based approach.</td>
</tr>
<tr>
<td><strong>Accuracy</strong></td>
<td>● Designing evaluation criteria based on the use case &quot;table design review&quot;<br>● Developing a dedicated application to rapidly repeat prompt tuning and evaluation <br>● Executing prompt tuning according to the best practices for the selected model (Claude).<br>● Limiting the number of items the AI checks per prompt execution to one, combined with using prompt chains, to improve accuracy</td>
</tr>
<tr>
<td><strong>Cost</strong></td>
<td>● Approximately $0.1 per DDL, depending on the number of characters in the DDL<br>● Model selection focusing on accuracy over cost (Claude 3.5 Sonnet) because of the low frequency of reviews<br>● We decided that using a prompt chain would similarly increase costs but provide the benefit of improved accuracy.</td>
</tr>
<tr>
<td><strong>Availability/Throughput</strong></td>
<td>● Implementing request distribution and retry processing between regions with awareness of quotas <br> ● We plan to transition to a more managed <a href="https://docs.aws.amazon.com/bedrock/latest/userguide/cross-region-inference.html">cross-region inference</a>.</td>
</tr>
<tr>
<td><strong>Response speed</strong></td>
<td>● Since there is no requirement to be &quot;as fast as possible,&quot; the model selection prioritized accuracy over speed.<br>● Reviewing each DDL in parallel improved the speed.<br>● Responses were returned within 2-5 minutes for dozens of DDLs.</td>
</tr>
<tr>
<td><strong>LLMOps</strong></td>
<td>● Continuous accuracy monitoring was performed using LLM-as-a-Judge.</td>
</tr>
<tr>
<td><strong>Security</strong></td>
<td>● For integration with GitHub, a GITHUB_TOKEN valid only during the execution of the GHA workflow was adopted.<br>● Since in-house applications and inputs are limited to DDL, the evaluation of compliance with LLM responses has not been conducted yet.</td>
</tr>
</tbody></table>
<p>This product is currently being introduced into multiple products, and improvements will continue to be made based on user feedback. Generative AI applications, including development services such as <a href="https://docs.aws.amazon.com/ja_jp/bedrock/latest/userguide/flows.html">Amazon Bedrock Prompt Flow</a>, continue to evolve, and we believe they will become even more convenient in the future. We will continue to actively venture into the field of generative AI.</p>
<p>The KINTO Technologies DBRE team is actively looking for new members to join us! Casual interviews are also welcome, so if you&#39;re even slightly interested, feel free to contact us via <a href="https://x.com/_p2sk_">DM on X </a>(formerly Twitter). If you&#39;d like, feel free to follow <a href="https://x.com/KintoTech_HR">our company’s recruitment X account</a> as well!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/m.hirose/cover_image_hirose.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[TypeScript "infer" Tips]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-02-typescript-infer-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-02-typescript-infer-en/</guid>
            <pubDate>Thu, 27 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[This article introduces some usage patterns of "infer" in TypeScript.]]></description>
            <content:encoded><![CDATA[<p>This article is part of day 2 of <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a>.</p>
<h2>Introduction</h2>
<p>Hello!<br>This is high-g (<a href="https://x.com/high_g_engineer">@high_g_engineer</a>) from the New Car Subscription Development Group at the Osaka Tech Lab.<br>Recently, solving TypeScript type puzzles, known as type-challenges, has become part of my daily morning routine before work.<br>In this article, I will introduce some <code>infer</code> tips in the TypeScript type system that are a little quirky.<br>First, before explaining infer, I will explain <code>Conditional Types</code>, which are essential for using infer.</p>
<h2>What are Conditional Types?</h2>
<p>Conditional Types, also called code branching, are features of the type system that allow conditional branching at the type level.<br>I&#39;d like to demonstrate an example of Conditional Types, as shown below:</p>
<pre><code class="language-ts">type ConditionalTest&lt;A&gt; = A extends &#39;a&#39; ? true : false
</code></pre>
<p>The right-hand side above has the following meaning:</p>
<ul>
<li>If A type can be assigned to a literal type &#39;a&#39;, it is a true type</li>
<li>If A type is other than the above, it is a false type</li>
</ul>
<p>It behaves like the conditional operator in general programming languages.<br>By the way, the <code>extends</code> keyword here has a different meaning than inheritance in general object-oriented programming.<br>In this case, extends becomes the keyword to check the type&#39;s assignability.</p>
<h2>What is infer?</h2>
<p>infer is only available in Conditional Types, introduced in TypeScript 2.8.<br>I&#39;d like to demonstrate an example of Conditional Types, as shown below:</p>
<pre><code class="language-ts">type InferTest&lt;T&gt; = T extends (infer U)[] ? U : never
</code></pre>
<p>On the right-hand side, we utilize Conditional Types, and to the right of the extends keyword, we specify the type we aim to obtain with <code>infer</code>.<br>In the case of the above type, if the T type is &quot;an array of any element type,&quot; it means that the type of the element is returned.<br>(Note: Here &quot;never&quot; is the type returned if the conditions are not met.) <code>(infer U)[]</code> represents an array of any element type, so any array types such as string[], number[], boolean[] are applicable.<br>So if the T type was number[], the type resolution would be as follows.</p>
<pre><code class="language-ts">type Result = InferTest&lt;number[]&gt;  // number
</code></pre>
<p>This is just an example, but there are many other ways to use infer.</p>
<h2>Functional manipulation using infer</h2>
<h3>To get the return type</h3>
<pre><code class="language-ts">const foo = (): string =&gt; &#39;Hello, TS!!&#39;

type MyReturnType&lt;T&gt; = T extends (...args: any[]) =&gt; infer R ? R : never
type FunctionReturn = MyReturnType&lt;typeof foo&gt;  // string
</code></pre>
<p>Write the Conditional Types as before, and specify the desired type to the right of the extends keyword  </p>
<p>This time, I want to extract the return type, so I place the function type to the right of extends and infer the return value.<br>This is the same behavior as TypeScript&#39;s built-in utility type <code>ReturnType&lt;T&gt;</code>.</p>
<h3>When retrieving the type of the argument</h3>
<pre><code class="language-ts">const foo = (arg1: string, arg2: number): void =&gt; {}

type MyParameters&lt;T&gt; = T extends (...args: infer Arg) =&gt; any ? Arg : never
type FunctionParamsType = MyParameters&lt;typeof foo&gt;  // [arg1: string, arg2: number]
</code></pre>
<p>Since the arguments are tuple types, the rest parameter (spread syntax) can be used to handle any number of arguments.<br>You can extract types by describing Conditional Types + the type you want to get + infer.<br>This is the same behavior as TypeScript&#39;s built-in utility type <code>Parameters &lt;T&gt;</code>.</p>
<h2>Manipulation of array types (tuple types) using infer</h2>
<h3>If you want to get the last element</h3>
<p>When retrieving the type of the first element of a tuple type, the type definition is as follows:</p>
<pre><code class="language-ts">type Tuple = [number, &#39;1&#39;, 100]
type GetType = Tuple[0]  // number
</code></pre>
<p>However, if you want to get the type of the last element of a tuple type, you cannot write <code>Tuple[length-1]</code> in TypeScript.<br>The best solution is infer. The type definition is as follows:</p>
<pre><code class="language-ts">type ArrayLast&lt;T&gt; = T extends [...infer _, infer Last] ? Last : never
</code></pre>
<p><code>[...infer _, infer Last]</code> extracts the last element type as Last if the T type is an array or tuple type.</p>
<pre><code class="language-ts">type Test1 = ArrayLast&lt;[1, 2, 3]&gt;  // 3
type Test2 = ArrayLast&lt;[string, number, boolean]&gt;  // boolean
type Test3 = ArrayLast&lt;[]&gt;  // never
</code></pre>
<h2>Manipulating literal types using infer</h2>
<h3>To get the type of the first character of a literal type</h3>
<pre><code class="language-ts">type LiteralFirst&lt;T extends string&gt; = T extends `${infer First}${string}` ? First : never
</code></pre>
<p><code>${infer First}${string}</code> extracts the beginning character of the string as First and treats the rest as string.</p>
<h3>To obtain a literal type with the first letter capitalized</h3>
<pre><code class="language-ts">type FirstToUpper&lt;T extends string&gt; = T extends `${infer First}${infer Rest}` ? `${Uppercase&lt;First&gt;}${Rest}` : never
</code></pre>
<p>As before, the string is processed separately, and <code>Uppercase&lt;First&gt;</code> uses a utility type to convert the first letter to uppercase and combine it with the rest of the string. Returns the never type if the string is empty.</p>
<h3>To trim spaces, newline characters, and similar elements from the beginning and end of a literal type.</h3>
<pre><code class="language-ts">type Space = &#39; &#39; | &#39;\n&#39; | &#39;\t&#39;
type Trim&lt;S extends string&gt; = S extends `${Space}${infer T}` | `${infer T}${Space}` ? Trim&lt;T&gt; : S;
</code></pre>
<p>If you define a type called Space that includes a space character and a newline character, you can use Conditional Types to recursively apply the Trim type, effectively removing spaces from the beginning and end of the string.</p>
<h2>Conclusion</h2>
<p>As shown in these examples, using infer enables you to extract specific parts from a given type, significantly enhancing flexibility in defining and working with types.<br>It&#39;s a bit unconventional, so it may take some time to adapt, but it&#39;s an incredibly useful feature.</p>
<p>Using types enables robust development; however, incorrectly representing types can lead to the following risks:</p>
<ul>
<li>Poor code readability due to unnecessary type definitions</li>
<li>Increased maintenance costs due to unnecessarily complex type definitions</li>
<li>Reduced type safety</li>
</ul>
<p>By effectively leveraging TypeScript type systems, such as infer, you can achieve concise and clear type expressions while consistently focusing on enhancing development productivity and quality.</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[JSConf JP 2024 Participation Report]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-06-JSConfJP参加レポート-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-06-JSConfJP参加レポート-en/</guid>
            <pubDate>Wed, 26 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[JSConf JP 2024 Participation Report]]></description>
            <content:encoded><![CDATA[<h1>Introduction</h1>
<p>Hello! <a href="https://jsconf.jp/2024/">JSConf JP 2024</a> recently took place, and we were proud to support it as a premium sponsor! In this post, we’d like to share a session report from our team members who attended the event firsthand!</p>
<h1><a href="https://blog.kinto-technologies.com/authors/ITOYU/">ITOYU</a></h1>
<h2>You Don’t Know Figma Yet - Hacking Figma with JS</h2>
<p><a href="https://jsconf.jp/2024/talk/hiroki-tani-corey-lee/">https://jsconf.jp/2024/talk/hiroki-tani-corey-lee/</a></p>
<p>Since Figma runs in a browser, I learned that you can use JavaScript in DevTools to manipulate it. You can use Figma’s official API via the <code>figma</code> global object to retrieve CSS information from elements or create new layers. Leveraging Figma’s browser-based nature unlocks its full potential, and I was truly amazed by the limitless possibilities it offers. Please refer to the official Figma documentation below for information on what can be done through global objects.</p>
<p><a href="https://www.figma.com/plugin-docs/api/global-objects/">https://www.figma.com/plugin-docs/api/global-objects/</a></p>
<h2>All happy projects are alike; each unhappy project is unhappy in its own way</h2>
<p><a href="https://jsconf.jp/2024/talk/mizchi/">https://jsconf.jp/2024/talk/mizchi/</a></p>
<p>Mizchi-san shared insights on common issue patterns based on their experience in performance tuning consulting His remark that adopting an easy anti-pattern can lead to an explosion later, reminded me of my own past experiences. Even though it&#39;s marked as deprecated in the documentation, using a hack from StackOverflow to fix an immediate issue <strong>might lead to even bigger problems later.</strong> This session reinforced the importance of seeking fundamental solutions instead of relying on makeshift fixes.
This session reinforced the importance of seeking fundamental solutions instead of relying on makeshift fixes. <strong>might lead to even bigger problems later.</strong></p>
<h1><a href="https://blog.kinto-technologies.com/authors/nam/">nam</a></h1>
<h2>Solving a Coding Test with Generative AI (by HireRoo)</h2>
<p><a href="https://jsconf.jp/2024/talk/hireroo/">https://jsconf.jp/2024/talk/hireroo/</a></p>
<p>Hosted by HireRoo, a provider of coding test services, this workshop was a true coding exam. It was a workshop where we tried to solve this real coding test with generative AI, which is something we don&#39;t usually have the opportunity to try.
(I thought it was a session, but when I went to watch, it seems that a PC was required... I&#39;m sorry, but I solved it on HireRoo&#39;s PC. Sorry...) I tried solving it using ChatGPT, but simply pasting the problem text as-is didn’t produce the expected code. It turns out some ingenuity is required after all. Things don’t always go as smoothly as you’d hope. By the way, it seems that using generative AI during actual coding tests can provide quite a bit of insight.</p>
<h2>LT (Lightning Talk): The Ecosystem behind JavaScript (Comedy)</h2>
<p><a href="https://jsconf.jp/2024/talk/ecma-boys/">https://jsconf.jp/2024/talk/ecma-boys/</a></p>
<p>In this session, the speaker shared an amusing story where their mother had forgotten the names of certain package managers and bundlers. Based on the characteristics she described, they speculated on what she might have been referring to. The talk featured several hilarious “power words,” such as &quot;the package manager my Okan (mom) is curious about,” which made it incredibly engaging. Personally, one part that stuck with me was when they declared, &quot;If the configuration file is easy to understand, it’s definitely not webpack,&quot; during the discussion about bundlers.</p>
<h1><a href="https://blog.kinto-technologies.com/authors/%E3%81%8D%E3%83%BC%E3%82%86%E3%81%AE/">Kiyuno</a></h1>
<h2>LT (Lightning Talk): JavaScript Module Resolution Interoperability</h2>
<p><a href="https://jsconf.jp/2024/talk/berlysia/">https://jsconf.jp/2024/talk/berlysia/</a></p>
<p>In this session, the topic was about resolving JavaScript modules. Recently, I encountered CJS/ESM issues while implementing UT for components that included Swiper, and it made me realize my lack of knowledge in module resolution. It gave me a sense of urgency to improve in this area. Additionally, the migration from Jest to Vitest had also been a minor topic within the team, so I felt it was necessary to fully understand this subject. By the way, the topic was very challenging for me, and I could barely keep up. I hope to become someone who can nod along and fully grasp these discussions someday soon. 🥲</p>
<h2>LT (Lightning Talk): The Experience of In-House Production of a Car Subscription Service with Next.js and One Year Later</h2>
<p><a href="https://jsconf.jp/2024/talk/kinto-technologies/">https://jsconf.jp/2024/talk/kinto-technologies/</a></p>
<p>This was our company’s session. Since the in-house production project was completed long before I joined the company, I attended the session to learn about its history. The technology stack introduced during the session was almost the same as the one I am currently working on, so I felt the challenges and future prospects were highly relevant to me.</p>
<h1><a href="https://blog.kinto-technologies.com/authors/Ren.M/">Ren.M</a></h1>
<h2>LT (Lightning Talk): romajip: A Story about Creating an English Address Conversion Library Using Japanese Address CSV Data</h2>
<p><a href="https://jsconf.jp/2024/talk/kang-sangun/">https://jsconf.jp/2024/talk/kang-sangun/</a></p>
<p>In this session, the speaker talked about creating a library called romajip. Personally, I was surprised to learn that not only the post office but also the Digital Agency provides a Japanese address master. Additionally, I felt that handling issues like place names with the same name (e.g., Nihonbashi in Tokyo and Osaka) seemed quite challenging. If I ever have the opportunity to create a library myself, I’d like to document the challenges I encounter and the areas I pay particular attention to!</p>
<h2>Introduction of Prerender Technology at LINE Yahoo Japan and Its Effects</h2>
<p><a href="https://jsconf.jp/2024/talk/masanari-hamada-tomoki-kiraku/">https://jsconf.jp/2024/talk/masanari-hamada-tomoki-kiraku/</a></p>
<p>In this session, the speaker discussed the verification process for introducing Prerender technology at LINE Yahoo Japan. Prerender is a technology that pre-loads the destination page when you hover over a link! The result is significantly faster page loading times and a greatly improved user experience. After conducting various tests, LINE Yahoo Japan ultimately decided not to adopt the technology due to issues like link congestion. However, depending on how it’s used, Prerender seems to have the potential to greatly enhance loading speeds. I’m interested in studying it further myself!</p>
<h2>Novelty</h2>
<p>We visited the booths of the sponsor companies and received various novelties! Personally, I was happy with Mercari&#39;s &quot;SOLD OUT” keychain! <img src="/assets/blog/authors/Ren.M/JSConf/Novelty1.webp" alt="Official T-shirts and Tote bags " title="Official T-shirts and Tote bags"> <em>Official T-shirts and Tote bags</em> <img src="/assets/blog/authors/Ren.M/JSConf/Novelty2.webp" alt="Sponsor novelties " title="Sponsor novelties"> <em>Sponsor novelties</em></p>
<h1>We Are Hiring!</h1>
<p>KINTO Technologies is looking for new teammates to join us! We’re happy to start with a casual chat, so feel free to reach out. If you’re even a little curious, please apply using the link below! </p>
<p><a href="https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303141">https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303141</a></p>
<h1>Conclusion</h1>
<p>How was it? I hope we’ll be able to participate in next year&#39;s JSConf JP in person! Thank you for reading all the way through!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/Ren.M/JSConf/JSConfJP.webp" length="0" type="image/webp"/>
        </item>
        <item>
            <title><![CDATA[10月・11月入社メンバー紹介]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-26-newcomers-introduction-oct-nov/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-26-newcomers-introduction-oct-nov/</guid>
            <pubDate>Wed, 26 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[KINTOテクノロジーズに2024年10、11月に入社されたみなさまを紹介します。]]></description>
            <content:encoded><![CDATA[<h1>こんにちは</h1>
<p>こんにちは、2024年10月入社のたなちゅーです！
本記事では、2024年10,11月入社のみなさまに、入社直後の感想をお伺いし、まとめてみました。
KINTOテクノロジーズ（以下、KTC）に興味のある方、そして、今回参加下さったメンバーへの振り返りとして有益なコンテンツになればいいなと思います！</p>
<h1>H.I</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-H_I.png" alt="H.I"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>はじめまして。このたびモバイルアプリ開発グループに配属されましたH.Iです。</li>
<li>これまでAndroidの開発に携わってきましたが、新しい環境で皆さんとともに成長し、貢献できることを楽しみにしています。</li>
<li>特に開発だけではなくデーター分析やサービスGrownに関心があり、チームの一員として力を発揮していきたいと考えています。まだまだ学ぶことも多いですが、精一杯頑張りますので、どうぞよろしくお願いいたします！</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>私の所属するチームでは、スクラムを採用し、アジャイルな開発プロセスを通じて業務を進めています。スプリントを軸に計画・開発・振り返りを行いながら、短いサイクルで継続的に改善を図っています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>大企業のグループ会社だと思い、少しは硬い感じの雰囲気を考えましたが、入社してみたら、驚くほど、柔軟な雰囲気でした。勉強会もたくさんありますし、これからが楽しみです。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>メンバーはみんな高い技術力を持ち、向上心も高いと感じました。気軽く質問したり、提案したりできる雰囲気だと思います。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>できるだけ書きたいと思います。自分が覚えたことや経験を配信して行きたいと思います。</li>
</ul>
</li>
<li><strong>ほりちゃん → H.Iさんへの質問</strong><ul>
<li><strong>社内で好きなイベント(公式/非公式問わず)はありますか？</strong><ul>
<li>開発の勉強会や社外イベント参加がたくさんあります。自分が知らないことや興味があったけどできなかったことを共有してもらうのでこれから自分の成長の種になると思います。</li>
</ul>
</li>
<li><strong>地元自慢をどうぞ！</strong><ul>
<li>韓国ソウルの近くの島です。自然が豊富でウナギや高麗人参生産地で有名です。お寺など歴史ある場所もたくさんあるので観光客も結構来てます。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>たなちゅー</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-tanachu.png" alt="tanachu"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>10月入社のたなちゅーです。セキュリティ・プライバシーGのサイバーセキュリティディフェンスチームに所属しています。</li>
<li>前職では、セキュリティベンダーでサイバーセキュリティに関するインシデントレスポンスやログ解析などを行っていました。</li>
<li>現在のチームでは、主にSIEMを用いたセキュリティログ監視や監視体制構築などに従事しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>グループ全体で9名体制です。日本を含め4つの国出身のメンバーで構成されています。</li>
<li>グループは4つのチームで構成されており、セキュリティやプライバシーに関するインシデントレスポンスやセキュリティガイドラインのアセスメント、社内サイバーセキュリティ体制の構築、脆弱性診断、SOCなどの業務を行っています。</li>
<li>所属チームは4名体制です。主に、脆弱性診断メンバーとSOCメンバーで構成されています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>入社前から色々な国出身のメンバーが在籍していると聞いていましたが、所属チーム内に日本人が自分しかいなかったことには少し驚きました。</li>
<li>会社として想像以上に生成AIの活用を強く推進しており、手軽に生成AIを業務で活用できる環境であることはポジティブなギャップでした。生成AIとのチャット以外に、自身の業務で活用できないか日々模索しています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>週に1回、参加可能なメンバーでランチ会を開催しており、メンバー間のコミュニケーションを大切にしている印象です。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>入社前に見ていたブログだったので、自分のメッセージが掲載されることに少し不思議な感覚です。</li>
</ul>
</li>
<li><strong>H.Iさん → たなちゅーへの質問</strong><ul>
<li><strong>自分だけのリラックスする方法があれば教えてください！</strong><ul>
<li>一日の終わりにテレビ東京のニュース番組「ワールドビジネスサテライト」を観ることです！</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>lksx</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-lksx.png" alt="lksx"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>11月入社のlksxです。DX開発Gでバックエンドエンジニアを担当しています。前職でも同じくバックエンドエンジニアをしていました。緊張もありましたが、学びが多く、とても充実した日々を過ごしています。どうぞ、よろしくお願いいたします。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>私が所属している開発チームは現在8人で構成されています。そのうち7人は室町オフィス勤務で、1人は大阪オフィスからリモートで参加しています。多拠点にメンバーがいるので、オンラインでのやり取りも多く、効率的な連携が求められるチームです。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>KTCはスタートアップという印象が強く、複数のプロダクトが同時並行で進行しているのがとても新鮮でした。また、POC段階のプロダクトもいくつかあるのが印象的で、「新しいことに挑戦していく会社なんだな」と感じました。今のところ、特に大きなギャップは感じていませんが、スピード感のある環境なので、自分もそのペースに慣れていきたいと思っています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>現場はみんな話しやすく、和やかな雰囲気です。分からないことや困ったことがあったときにも気軽に相談できる環境なので、入社したばかりの私でも安心して仕事に取り組めています。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>あんまりブログは書いたことがなく、今回始めてなので、ワクワクしてます。</li>
</ul>
</li>
<li><strong>たなちゅー → lksxさんへの質問</strong><ul>
<li><strong>平均的な一日の仕事の流れ（業務開始直後は〇〇をする。午後は〇〇の業務をすることが多いなど）はどのような感じですか？</strong><ul>
<li>まだ入社したばかりで、覚えることや勉強しないといけないことがたくさんあります。そのため、朝9:30から約30分ほど業務スキルの勉強をしています。午前中は頭を使う業務に集中し、午後は体力を使う業務をメインに進めることが多いです。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>かーびー</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-kirby.png" alt="kirby"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>2024年10月入社のかーびーです。</li>
<li>東京のIT事業会社で6年間ディレクター・PdMとしての経験を積み、地元大阪で自分のスキルを活かしながら生活ができたらと考え帰阪。リアルイベントの企画・運営業務を経て、KTCに入社しました。</li>
<li>現在は新サービス開発GのKINTO FACTORY開発チームに所属し、Osaka Tech Lab.で勤務しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>KINTO FACTORY開発チームは、KTCではマネージャー1名、PdM2名、フロントエンドエンジニア4名、バックエンドエンジニア5名、QA1名で構成され、車のオーナーに向けた愛車のカスタム・機能向上サービスのサイト開発を行っています。Osaka Tech Lab.では、バックエンドエンジニアと私を含めた2名で開発を進めています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>東京・名古屋・大阪と拠点が離れているため、コミュニケーション面が少し心配でした。しかし、KINTO・KTCにもTOYOTAグループの「直接会って話すこと」を大切にする文化（面着）が根付いており、オフラインでも頻繁に顔を合わせてコミュニケーションをとる機会があります。そのため、良い意味でギャップがありました。（このブログも今名古屋で書いています。）</li>
<li>Osaka Tech Labのメンバーはプロジェクトを超えたコミュニケーションも活発で、横のつながりが強く、技術的な挑戦をしたいエンジニアの方も多く触発されています。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>プロダクトやサービス開発に携わりたい、より良いサービスにしたいという想いを持ったメンバーと一緒に働くことができて嬉しいです。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>入社前にがっつり見ていたテックブログに参加できて嬉しいです！</li>
</ul>
</li>
<li><strong>lksxさん → かーびーさんへの質問</strong><ul>
<li><strong>KTCで「これをやってみたい！」と思うことは？</strong><ul>
<li>KINTO FACTORYのサービスグロース貢献はもちろん、Osaka Tech Lab.だけで1つのプロジェクトを手掛けられるような挑戦もしていきたいです！</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>Jun</h1>
<!-- 画像無し -->

<ul>
<li><strong>自己紹介</strong><ul>
<li>2024年11月に入社しましたJunです。</li>
<li>事業会社で7年近くWebエンジニアとして経験を積んだ後、よりチャレンジングな職場でスキルを磨きたいと考えKTCに転職を決意しました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>私が所属しているのは新車サブスク開発グループのバックエンドチームで、チームには13名所属しています。チーム内では常に複数のプロジェクトが並行で進行しており、プロジェクト発足時にメンバーが選出されて2~4人程で一つのプロジェクトを回しています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>学習意欲に溢れるエンジニアが多い印象でした。チーム内での勉強会はもちろんですが、会社横断での勉強会も頻繁に開かれており、多くのエンジニアが意欲的に参加されていました。エンジニアリングの学習は一人でコツコツ行うものと思っていたのでいい意味で裏切られました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>皆さん中途入社で幅広い知見をお持ちで、こうしなければいけないというのが無いので技術選定や運用方法のディスカッションは非常に盛り上がっています。毎日いい刺激を受けながら仕事ができています。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>テックブログは以前も書いていたのですが長い間更新が止まっていたので、また再開したくなりました。</li>
</ul>
</li>
<li><strong>かーびーさん → Junさんへの質問</strong><ul>
<li><strong>気分を切り替えるおすすめルーティーンがあれば教えてください！</strong><ul>
<li>コーヒーが好きなので、ちょっと考えが煮詰まってしまった時などはコーヒを淹れてリフレッシュしてます。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>H.I.</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-H_I_.png" alt="H.I."></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>11月入社のH.I.です。</li>
<li>これまで数社のITベンチャーでマーケと開発、PdMを経験してきました。現在はKINTO UnlimitedのPdMとして東京の室町オフィスで勤務しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>私が担当しているKINTO Unlimitedでは、PdMが私含めて3人、エンジニアが20名ほどいます。</li>
<li>3人いるPdMの役割分担としては私がデータ分析とマーケ系、一人が開発系、もう一人がUI/UXの分野を担当されています。PdMとしての役割をうまいこと分担できていて、それぞれの強みを発揮できるかなりいいバランスが取れていると思っています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>これまで経験してきた会社に比べ、年齢層が少し高めだったので少し気を張っていましたが、みなさんいい人ばかりで安心して仕事ができています（ほんとに）。</li>
<li>一方で入社前に思ってたよりもずっと大きな組織だったので、ステークホルダーの多さや、誰が何をやっているかのわからなさは、ベンチャー出身の私としては適応しなければいけないギャップでした。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>おそらくプロダクトを持ってる一般的な会社に比べ潤沢なリソースがあるので、アイデアさえあれば様々な企画ができます。</li>
<li>別チームの方とも仲良くさせていただいていて、最近私の席の近くではクリスマスにみんなでケーキを食べたり、豆やミルを持ち寄って小さなコーヒー屋さんができたりしています笑</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>いつか自己紹介ではないことも書いてみたいです！</li>
</ul>
</li>
<li><strong>Junさん → H.I.さんへの質問</strong><ul>
<li><strong>前職で培ってきた技術やスキルセットで特にKTCで活かせている・役にたっていると思うスキルセットはなんですか？</strong><ul>
<li>趣味で勉強したpythonや統計の知識がかなり役立っています。</li>
<li>それと、マーケと開発どちらも経験していたので、PdMの立場としては守備範囲を広げられていたと感じました。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>福原（Fúyuán）</h1>
<!-- 画像無し -->

<ul>
<li><strong>自己紹介</strong><ul>
<li>2024年11月に入社した開発支援部企画管理グループのFúyuánです。Osaka Tech Labに勤務しています。</li>
<li>受託開発業務にかかわる予算編成や請求支払などKTCの会計関連のバックオフィス業務を担当しています。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>企画管理グループは5名体制で、KTCの受託開発業務にかかわる予算編成・契約・請求支払・知財管理等の各業務を担当分けして業務を行っています。</li>
<li>また、グループメンバーの勤務地は東京・名古屋・大阪とバラバラで物理的な距離は遠いですが、slackで常につながっているので感覚的には隣で仕事しているくらいの近さでコミュニケーションをとっています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>「THE JTC」な会社から転職したので、 決めて行動するまでのレスポンスが早く、社内の人間関係がフラットでコミュニケーションがとりやすいことに驚きを感じました。</li>
<li>入社面接でも面接官の方に同様の印象を得てそれを魅力に感じて入社を決めたのでギャップは無かったです。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>メリハリがとても効いていて、静かにもくもくと作業をしている時間とワイワイ雑談に花を咲かせているときのギャップがすごいです。皆さん国籍・年齢・社歴等バックグラウンドが様々なので日々良い刺激を受けてます。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>普段ROM専でプライベートでもSNSやブログ等をやっていないので、びびっております…</li>
</ul>
</li>
<li><strong>H.I.さん → 福原（Fúyuán）さんへの質問</strong><ul>
<li><strong>在宅勤務の日のモーニングルーティンを教えてください！</strong><ul>
<li>体を動かしてから仕事を始めると頭がさえるのでラジオ体操をすることを心掛けています</li>
<li>天気が良い日にベランダで日光を浴びながら体操するのが一番好きです（早く花粉シーズンよ終われ…！）</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>R.O</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-R_O.png" alt="R.O"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>新サービス開発部 プロジェクト推進グループに所属してます。プロジェクトマネージャーとしてKINTO ONEの開発プロジェクトに携わっています。</li>
<li>前職ではオフショア開発会社に勤めておりプロジェクトマネージャーとしてベトナムメンバーと協力しながら建設・エンタメ・金融系など様々な開発プロジェクトに従事してました。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>パートナーさんも含めた10名ほどのチームになります。</li>
<li>全員がプロジェクトマネージャーでそれぞれがKINTO ONEに関する別々の開発プロジェクトに参画しています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>前職より年齢層が少し上がることと中途採用の方のみという情報を聞いていて社内の雰囲気や社員の方との距離感を掴むのに勝手に緊張していましたが、スマートな方が多く相談や質問など連携がしやすい環境だと思いました。</li>
<li>社内勉強会の開催と社外勉強会への参加が想像より活発でした。</li>
<li>他拠点メンバーとオフラインで連携を取るために必要に応じて出張しているメンバーが結構いること。オンラインでのやり取りがメインだと想像してましたが、必要に応じて遠方にいるプロジェクト関係者と直接会う機会を得やすいのは良いなと思いました。</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>自分が所属しているプロジェクト推進グループは賑やかというよりは落ち着いた雰囲気で仕事の相談や雑談が穏やかに行われてます。</li>
<li>普段はSlack等のコミュニケーションツールでメッセージベースのやり取りが多いのですが、開発メンバーがいる席に出向いたりして直接仕様の確認などをする機会も結構あるので部署を跨いだ会話や相談がしやすい雰囲気だと感じました。</li>
<li>エンジニアさんが多いチームだと賑やかに仕様や実装に関して会話しながら仕事を進めているチームもあるのでチームによって雰囲気の色は違うなーとも感じてます。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>自分の感じたことや思考を文字に起こすのは好きなので楽しみ</li>
<li>入社前の情報収集に活用していたブログに自分も寄稿する機会がもらえて嬉しい</li>
</ul>
</li>
<li><strong>Fúyuánさん → R.Oさんへの質問</strong><ul>
<li><strong>世の中の新しい技術やプロダクト、サービスのキャッチアップはどの様にされてますか？</strong><ul>
<li>X(旧Twitter)でそういった情報を発信しているアカウントのみをフォローしたアカウントを作成して、欲しい情報のみが流れてくるTLから拾う</li>
<li>他業界の友人・知人と連携（飲み会）して情報収集</li>
<li>サービスや新技術周辺の熱量まで含めて感じたい時はカンファレンスなどのオフラインイベントへの参加</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>ほりちゃん</h1>
<p><img src="/assets/blog/authors/tanachu/newcomers/icon-horichan.png" alt="horichan"></p>
<ul>
<li><strong>自己紹介</strong><ul>
<li>2024年10月入社のほりちゃんです！広島出身のカープファンで、芋焼酎が好きです。</li>
<li>元々はAndroidエンジニアで、2年前に前職でPdMに転向しました。現在はトヨタ販売店様の課題を解決するためのシステム開発にディレクターとして携わっており、日々奮闘中です。</li>
</ul>
</li>
<li><strong>所属チームの体制は？</strong><ul>
<li>モビリティプロダクト開発部 DX Solution Gは7人で、プロデューサー1人、ディレクター3人、デザイナー3人がそれぞれ担当のプロジェクトを進めています。</li>
</ul>
</li>
<li><strong>KTCへ入社したときの第一印象？ギャップはあった？</strong><ul>
<li>第一印象は「大手とベンチャーのいいとこ取りな会社」です！家賃補助や有給日数の多さなど環境は大手っぽいけど、社員主体で勉強会(やBeerBash)が頻繁に行われていたり超本部会のエネルギーがすごいところはベンチャーっぽくて好きです。</li>
<li>ギャップは強いて言うならツールですかね・・前職では社内向けシステムを担当していたので資料系は全部 FigJam か Notion で作成していたのですが、今は PowerPoint がメインツールです。でもけっこう慣れてきました！この調子でパワポマスター目指します💪</li>
</ul>
</li>
<li><strong>現場の雰囲気はどんな感じ？</strong><ul>
<li>グループのみなさんは優しくてのびのびと勉強させていただいてます！ランチも楽しいです🫶</li>
<li>プロジェクトチームではそれぞれの職種のプロフェッショナルたちがみんなで販売店様の課題解決を目指すワンチーム感があります。</li>
</ul>
</li>
<li><strong>ブログを書くことになってどう思った？</strong><ul>
<li>前職でも何本か書いたことがあるので気になってました！参加できてうれしいです☺️</li>
</ul>
</li>
<li><strong>R.Oさん → ほりちゃんへの質問</strong><ul>
<li><strong>KTCで面白いなーと感じた会社の文化や出来事はありますか？</strong><ul>
<li>経営層のお話を聞く機会がたくさんあるところです！入社時研修で社長の小寺さんと副社長の景山さんとの懇談会がそれぞれあるのが既に驚きでしたが、その後も月一の全体会議や超本部会、1on1(希望者)やU35イベント(希望者)など盛りだくさんです😳</li>
<li>入社時研修の時に小寺さんが飲みに誘ってくれても良いよとおっしゃっていたので実際に何人か誘って一緒に飲みに行ったんですが、色々なお話ができて楽しかったです🍶(R.Oさんとたなちゅーさんも一緒に行きましたね！🙌)</li>
</ul>
</li>
</ul>
</li>
</ul>
<h1>さいごに</h1>
<p>みなさま、入社後の感想を教えてくださり、ありがとうございました！</p>
<p>KINTO テクノロジーズでは日々、新たなメンバーが増えています！
今後もいろんな部署のいろんな方々の入社エントリが増えていきますので、楽しみにしていただけましたら幸いです。</p>
<p>そして、KINTO テクノロジーズでは、まだまださまざまな部署・職種で一緒に働ける仲間を募集しています！
詳しくは<a href="https://www.kinto-technologies.com/recruit/">こちら</a>からご確認ください！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[From Hackathon to Business Asset: Toyota Community by KINTO]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-03-toyota-community/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-03-toyota-community/</guid>
            <pubDate>Tue, 25 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Explore how Toyota Community by KINTO platform evolved from a hackathon concept into a business asset, navigating challenges to balance user value and business objectives.]]></description>
            <content:encoded><![CDATA[<h1>👋  Introduction</h1>
<p>Hi there! I’m Moji, the product designer behind <a href="https://toyota-community.kinto-technologies.com/">Toyota Community by KINTO</a> platform.</p>
<p>Today, I’m excited to share the story behind its creation; a journey of collaboration, innovation, and designing with purpose.</p>
<p>For me, design goes far beyond aesthetics; it’s about solving problems and balancing user needs with business goals. That’s why the Toyota Community by KINTO has been such an inspiring project for me. </p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/overview.png" alt="Toyota Community by KINTO">
<em>Toyota Community by KINTO logo and mobile interface mockups showing platform features</em></p>
<h1>🚀 Background</h1>
<p>What started as <a href="https://blog.kinto-technologies.com/posts/Innovation-Days-03-ja/">a simple idea during a hackathon</a> has evolved into a product (responsive web application) that we are now preparing to pitch as a scalable solution for better customer engagement, aimed at connecting Toyota customers and enthusiasts under one global platform. </p>
<p>Over the past 2 years, my journey with this project, hasn’t just been about <strong>design and development</strong>, it’s been an ongoing process of <strong>navigating corporate feedback</strong>, understanding the <strong>nuances of regional markets</strong>, and consistently refining the product to align with both <strong>business objectives and user’s needs</strong>.</p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/innovation-days-2022.png" alt="Highlights from KINTO Global Innovation Days 2022">
<em>Photos from KINTO Global Innovation Days 2022, showcasing the winning hackathon team, team collaboration, and the Innovator of the Year awards</em></p>
<h1>💡 The Challenge</h1>
<h3>Decentralized Structure and Value Chain Gaps</h3>
<p>In the automotive industry, some brands operate with a decentralized structure. While this approach allows for flexibility and customization to meet local market needs, it can sometimes create challenges in <strong>maintaining global brand consistency and engaging customers effectively after purchase.</strong> </p>
<p>This highlights an exciting opportunity for a centralized hub where brands can gain valuable customer insights, and strengthen their image in a positive and impactful way.</p>
<h2>Competitive Analysis</h2>
<p>To better understand the online community landscape, I conducted a <strong>comprehensive analysis</strong> of existing online car communities and competitive platforms. </p>
<h3>Key Insights</h3>
<ul>
<li><p>🚗 Toyota enthusiasts, especially those with older or restored models, are heading to external platforms like <strong>Toyota Nation</strong> (owned by VerticalScope) or <strong>Reddit</strong> to interact, share feedback, and discuss their vehicles. </p>
</li>
<li><p>🌐 Most platforms already offer community spaces (forums) where users can connect, exchange ideas, and learn from one another.</p>
</li>
</ul>
<p>However, one significant gap stood out! </p>
<ul>
<li><p>Car lovers are deeply passionate about showcasing their vehicles.</p>
<ul>
<li><p>There wasn’t a specific section that allowed car owners to create a dedicated virtual garage where they could:</p>
<ul>
<li>🛠️ Showcase modifications and customizations.</li>
<li>📸 Share photos of their cars.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2>Identifying the Opportunity</h2>
<p>The following three strategic integrations ensured that our platform stood out from competitors and wasn’t just <strong>a siloed social space</strong> but became a gateway where users could consume and discuss authentic content as well.</p>
<h3>1. Connecting with Mr. Land Cruiser</h3>
<ul>
<li><p>As part of the broader Toyota network, we had the privilege of connecting with ex-Chief Engineer of Toyota Land Cruiser, <a href="https://toyotatimes.jp/en/spotlights/landcruiser_70th/040.html">Mr. Sadayoshi Koyari</a>, also known as <strong>Mr. Land Cruiser</strong>.</p>
<ul>
<li>This connection allowed us to feature <strong>exclusive videos</strong> and a dedicated <strong>Ask Me Anything (AMA)</strong> channel, where users could directly interact with him, ask questions, and gain valuable insights.</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/mr-landcruiser.png" alt="Collaboration with Sadayoshi Koyari, Also Known as Mr. Land Cruiser">
<em>Collaboration with Sadayoshi Koyari, famously known as Mr. Land Cruiser, featuring team discussions and filming sessions.</em></p>
<h3>2. Sharing Reputable Automotive News from Toyota Times</h3>
<ul>
<li><p>Additionally, because a reputable automotive news is also valuable, we negotiated with <a href="https://toyotatimes.jp/en/">Toyota Times</a> a well-known source, and they approved sharing their official articles on our web application. </p>
<ul>
<li>This gave users access to both <strong>reliable brand content</strong> and a space to share personal stories and engage in meaningful discussions.</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/toyota-times-articles.png" alt="Toyota Times Articles on the Toyota Community by KINTO Platform">
<em>A screenshot of the Toyota Community by KINTO platform showing an article from Toyota Times about the Land Cruiser</em></p>
<h3>3. Introducing the &quot;Garage&quot; Feature</h3>
<ul>
<li><p>Also, for car lovers, we know how much joy comes from showing off their ride, sharing the customizations and modifications they’ve worked hard on, and connecting with others over that passion.</p>
<ul>
<li><p>So, we introduced the &quot;Garage&quot; feature in a fun and personalized way for car owners to:</p>
<ul>
<li>Showcase photos of their cars.</li>
<li>Document modifications, customizations, and significant milestones.</li>
<li>Share their vehicle&#39;s unique story, all within a visually appealing interface.</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/garage-example.png" alt="Example of Virtual Garage Feature">
<em>A screenshot of the Toyota Community by KINTO platform showcasing a user&#39;s 1981 Land Cruiser FJ40 Soft Top in the virtual garage, with details on modifications and personal history.</em></p>
<h1>🎨 Designing the Solution</h1>
<h3>Distributor Feedback</h3>
<p>Before starting the development of the MVP (Minimum Viable Product), I designed mockups and prototypes to gather early feedback from key stakeholders. After multiple iterations based on their input, we presented the finalized prototype to a few regional distributors across different markets to solicit feedback. </p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/early-mockups.png" alt="Early Mockups for Feedback on Toyota Community Proposal">
<em>Early mockups of the Toyota Community, showcasing proposed features to solicit feedback on the design proposal.</em></p>
<p>Given the feedback received, we adjusted the platform&#39;s design to align with our new business goals and highlight our key differentiators. </p>
<p>This allowed us to begin the actual development of the MVP, with the goal of securing a proof of concept (PoC) before investing more time and resources.</p>
<h1>🛠️  Phase 1 - Development</h1>
<h3>Crafting the MVP (Minimum Viable Product)</h3>
<p>With limited resources, we designed and developed an MVP (Minimum Viable Product) that addressed the gaps and differentiated itself from existing platforms. The product came to life in July 2024 around two core features:</p>
<h4>💬 Community Section</h4>
<p>A space for discussions around Toyota culture by creating threads and adding reactions by using emojis.</p>
<ul>
<li><p>Channels Dedicated to Mr. Land Cruiser:</p>
<ul>
<li>Exclusive videos featuring his garage, experiences, and interviews.</li>
<li>A dedicated channel to ask him anything, allowing enthusiasts to connect directly with him.</li>
</ul>
</li>
<li><p>Toyota News Channel:</p>
<ul>
<li>A dedicated channel sharing reliable automotive news about Toyota.</li>
</ul>
</li>
</ul>
<h4>🚘 Virtual Garage</h4>
<p>A dedicated space where users could showcase their cars with images and personal stories, and share them with others.</p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/design-process.png" alt="Design Process Overview">
<em>Figma design screens and a timeline highlighting design stages and milestones</em></p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/mobile-view-screens.png" alt="Mobile View Screens">
<em>Three mobile screens showing Garage, Home, and Community sections of the Toyota Community by KINTO platform</em></p>
<h2>Gathering User Feedback</h2>
<p>A critical moment for the product came after our product owner attended a <a href="https://buschtaxi.org/buschtaxi-treffen/">Land Cruiser event in Germany</a>. </p>
<p>This direct interaction with Toyota enthusiasts provided invaluable insights. Attendees loved the idea of having a space to showcase their vehicles while connecting with Toyota icons like Mr. Koyari.</p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/event-photos.png" alt="Buschtaxi Event Highlights 2024">
<em>Photos from the Land Cruiser event in Germany, featuring modified vehicles and a group photo of Land Cruiser owners and enthusiasts.</em></p>
<p>Recognizing the significance of this event, we decided to create a strong presence that users could remember. </p>
<p>I took the lead in designing a logo and marketing materials, working closely with stakeholders to create one that aligned seamlessly with both KINTO and Toyota&#39;s brand identities. </p>
<p><img src="/assets/blog/authors/moji/2024-12-toyota-community/TCbK_logo.png" alt="Toyota Community Marketing Materials">
<em>A collection of promotional materials for the Toyota Community by KINTO, featuring flyers promoting the virtual garage and community benefits.</em></p>
<p>This combination of user feedback and last-minute branding helped refine both our <strong>product’s direction</strong> and its <strong>visual identity</strong>, giving us a clearer path forward.</p>
<h1>🔄 Phase 2 - Refinement</h1>
<h3>Adoption and Scaling</h3>
<p>While we haven’t yet had the resources to test the MVP with a larger audience, <strong>insights gained from distributor feedback and the Land Cruiser event have guided ongoing improvements to the platform</strong>. These refinements are paving the way for the next stages of development.</p>
<p>The Toyota Community by KINTO platform is being positioned as a scalable solution, showcasing its potential to address challenges tied to a fragmented value chain while offering the flexibility to adapt to diverse regional markets.</p>
<h3>What is Next</h3>
<p>We remain focused on fine-tuning the platform for further review and exploring opportunities to highlight its value.</p>
<h1>✨ Conclusion</h1>
<h3>A Scalable Future For Toyota&#39;s Engagement</h3>
<p>Toyota Community by KINTO offers an opportunity to reconnect and unify global engagement within a centralized hub.</p>
<p>With additional resources, this platform has the potential to become a valuable asset, enhancing not only customer engagement but also fostering stronger brand loyalty opportunities.</p>
<p>This project wasn’t just about <strong>UX design</strong> or <strong>feature-building</strong>. It has shown me that user-centered design’s true power lies in its ability to <strong>adapt</strong>, to both business goals and cultural needs. </p>
<hr>
<p>If you enjoyed this, feel free to explore my past articles, where I dive into topics like <a href="https://blog.kinto-technologies.com/posts/2023-12-21_Diversity-and-Inclusivity-in-UIUX-Design/">importance of diversity and inclusivity</a> as well as <a href="https://blog.kinto-technologies.com/posts/2023-08-15-Redesigning_TechBlog/">the process of redesigning the TechBlog</a> itself. </p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/moji/2024-12-toyota-community/cover.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[GitHub Copilot と Cursor エディタ、両方課金してメリット・デメリットを比較してみた]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-25-cursor-vs-copilot/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-25-cursor-vs-copilot/</guid>
            <pubDate>Tue, 25 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Copilot と Cursor エディタ、両方に課金して使い倒して比較した結果を紹介します。]]></description>
            <content:encoded><![CDATA[<p>KINTO テクノロジーズの酒巻裕也です。<br>普段はデータの分析から施策提案、機械学習を用いた機能の開発に従事しています。
以前は <a href="https://ppap.kinto-jp.com/">Prism Japan</a> のAI機能開発を担当していました。</p>
<p>社内で Cursor と GitHub Copilot の比較検証を行ったので、その結果をご紹介します。</p>
<hr>
<h2>前提</h2>
<ul>
<li>KINTOテクノロジーズ社内では GitHub Copilot が標準備品として使えるルールがある。</li>
<li>Cursor エディタを利用することで、さらに生産性の向上が見込めないか、検証する。</li>
</ul>
<h3>Cursor</h3>
<p>AIを搭載したコードエディタであり、Visual Studio Codeをフォークして作成されている。</p>
<h3>Github Copilot</h3>
<p>GithubがOpenAIと共同開発したツールであり、様々なIDE(Visual Studio Code, JetBrains, Eclipseなど)のプラグインとして利用が可能。</p>
<h2>免責事項</h2>
<ul>
<li>本記事には私の主観が多分に含まれます。Cursor の有用性を否定するものではありません。</li>
</ul>
<h2>検証したプラン</h2>
<ul>
<li><strong>Copilot Enterprise</strong> ... $39 / month</li>
<li><strong>Cursor Business</strong> ... $40 / month</li>
</ul>
<h2>結論</h2>
<ul>
<li><strong>2025年2月時点</strong>で、GitHub Copilot と Cursor の機能差はほぼ感じない。</li>
<li><strong>2024年12月時点</strong>では Cursor を推したかったが、Copilot の驚くべき進化速度によって、Cursor エディタを新たに選択する必要性を感じなくなった。</li>
</ul>
<h2>評価期間</h2>
<ul>
<li><strong>2024年12月 〜 2025年2月</strong></li>
</ul>
<hr>
<h2>比較表（Copilot vs. Cursor）</h2>
<p>Copilot と Cursor を併用してみて、よく議論に上がったポイントを表にまとめました。
それぞれの項目について、詳細は後述します。</p>
<table>
<thead>
<tr>
<th>項目</th>
<th>GitHub Copilot</th>
<th>Cursor</th>
</tr>
</thead>
<tbody><tr>
<td><strong>UI/操作性</strong></td>
<td>- VSCode上でインラインの提案やチャット可能<br>- 右クリックでの操作が多い</td>
<td>- AI利用ショートカットがすぐに表示される<br>- インラインメニューがわかりやすい</td>
</tr>
<tr>
<td><strong>QuickFix（import などの補完）</strong></td>
<td>- VSCode標準のクイックフィックスが強力</td>
<td>- 不安定で出ないことがある<br>- <code>Fix in Composer</code> など少し手間</td>
</tr>
<tr>
<td><strong>類似修正の自動反映</strong></td>
<td>- 何かを書き始める必要がある</td>
<td>- タブで次修正箇所を予測・提案してくれる (複数箇所の修正に便利)</td>
</tr>
<tr>
<td><strong>モデル選択</strong></td>
<td>- 変更可能だが種類は少なめ</td>
<td>- 追加・選択が豊富 (AIモデルを複数使い分けできる)</td>
</tr>
<tr>
<td><strong>ルールファイル</strong></td>
<td>- <code>.github/copilot-instructions.md</code>の設定で対応可能</td>
<td>- <code>.cursorrules</code>ファイルでプロジェクト毎のルールを適用可能</td>
</tr>
<tr>
<td><strong>ドキュメント読み込み (@Docs など)</strong></td>
<td>- プラグイン等で対応可</td>
<td>- @Docs 機能でフレームワークやライブラリの文書を読み込める</td>
</tr>
<tr>
<td><strong>エージェント機能</strong></td>
<td>- Copilot Chat / Copilot CLI で類似機能あり</td>
<td>- エージェントモードで自動実行・try &amp; error</td>
</tr>
<tr>
<td><strong>テストコード自動生成</strong></td>
<td>- 生成自体は可能 (ただし精度はまちまち)</td>
<td>- 同様に生成可能だが精度は微妙。要修正が必須。</td>
</tr>
<tr>
<td><strong>価格 (Business/Enterprise)</strong></td>
<td>$39</td>
<td>$40</td>
</tr>
</tbody></table>
<blockquote>
<p>※評価内容は 2025年2月時点の主観的なものです。</p>
</blockquote>
<hr>
<h2>Cursor のメリット（GitHub Copilot に比べて）</h2>
<ol>
<li><p><strong>AI利用ショートカットがすぐに表示される</strong><br>コードを選択した際に AI 利用のショートカットが自動で表示されるため、特別な操作を覚える手間が省ける。
<img src="/assets/blog/authors/yuya_sakamaki/cursor/shortcut.png" alt="alt text"></p>
<ul>
<li><em>Copilot で同様のことは？</em><ul>
<li>インラインチャット自体は可能。ただし他のファイルを参照する COMPOSE モードはなく、右クリックメニューから呼び出す必要があるなど、ひと手間かかる。</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>類似修正をまとめて提案してくれる</strong><br>似たような修正を複数箇所に当てはめたいとき、Cursor だと「次にどこをどう直すか」をタブで順番に予測してくれる。
<img src="/assets/blog/authors/yuya_sakamaki/cursor/similar_fix.png" alt="similar"></p>
<ul>
<li><em>Copilot で同様のことは？</em><ul>
<li><del>何かを書き始めないと予測をしてくれないため、Cursorほどスムーズな一括提案は難しい。</del></li>
<li><a href="https://code.visualstudio.com/updates/v1_97#_copilot-next-edit-suggestions-preview">Copilotでも同様の機能が可能に</a>※執筆中にも状況が変わってきました</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>モデル選択・追加が柔軟</strong><br>Cursor は自分で任意のモデルを追加・選択しやすい構造になっている。</p>
<ul>
<li><em>Copilot で同様のことは？</em><ul>
<li>Copilot Chat もモデル切り替えが可能だが、現状できる範囲は限定的。</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>@Docs 機能でフレームワーク／ライブラリのドキュメントを読み込める</strong><br>ドキュメントを事前に読ませておくことで回答精度を上げられる。</p>
<ul>
<li><em>Copilot で同様のことは？</em><ul>
<li>プラグインなどで近いことは可能だが、標準機能としてはそこまで充実していない。</li>
</ul>
</li>
</ul>
</li>
<li><p><strong>エージェントモードでの try &amp; error 自動実行</strong><br>Cursor 内のエージェントモードでは、AI が一定の権限のもとコマンドを自動実行して試行錯誤してくれる。</p>
<ul>
<li><em>Copilot で同様のことは？</em><ul>
<li><del>Copilot CLI や Copilot Chat にも類似機能が出始めているが、まだ細かい部分で操作が必要。</del></li>
<li>まだプレビュー版ではあるが<a href="https://github.blog/news-insights/product-news/github-copilot-the-agent-awakens/">Copilotでもエージェントモードが利用可能に</a>なった。</li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
<h2>Cursor のデメリット（GitHub Copilot に比べて）</h2>
<ol>
<li><p><strong>import補完が少し手間</strong><br>VSCode + Copilot では自動で import を補完してくれるが、Cursor ではマウスカーソルを合わせクイックフィックスを選択しないとimportしてくれない。
<img src="/assets/blog/authors/yuya_sakamaki/cursor/quick_fix_cursor.png" alt="similar"></p>
<ul>
<li><em>Copilot のほうが安定して優秀</em></li>
</ul>
</li>
<li><p><strong>テストコードの自動生成精度がイマイチ</strong><br>自動生成をするとストレートにはテストが通らず、結局修正が必要なケースも多い。</p>
<ul>
<li>Copilot も同様ではあるが、Cursor だからといって大きく向上している印象はなかった。</li>
</ul>
</li>
<li><p><strong>運用に合わない部分もまだ多い</strong><br>開発中のアプリによっては、Cursor に含まれる AI 機能が必要以上に多く、かえって混乱するメンバーもいた。</p>
</li>
</ol>
<hr>
<h2>Cursor の「Composer」と「Chat」の違い</h2>
<p>Cursor には大きく分けて <strong>Composer</strong> と <strong>Chat</strong> の2つのモードが存在します。使い方やコンテキストの扱いに違いがあるため、社内で比較した結果を簡単にまとめました。</p>
<table>
<thead>
<tr>
<th>機能項目</th>
<th>Composer</th>
<th>Chat</th>
</tr>
</thead>
<tbody><tr>
<td><strong>コンテキスト理解</strong></td>
<td>自動的に現在のファイルを紐づける。<br>関連ファイルを自動提案する。</td>
<td>初期状態ではコンテキストが空。<br>必要に応じて手動で共有する必要あり</td>
</tr>
<tr>
<td><strong>主な用途</strong></td>
<td>コード生成・編集がメイン。<br>提案内容をインラインで即時修正しやすい</td>
<td>一般的な質問や説明を受ける用途が中心。<br>長めのやりとりに向いている</td>
</tr>
<tr>
<td><strong>履歴管理</strong></td>
<td>コントロールパネルに自動保存</td>
<td>チャット履歴を選択可能</td>
</tr>
<tr>
<td><strong>UI</strong></td>
<td>インラインメニューでも利用可能。<br>サイドメニューからも操作できる</td>
<td>サイドメニューのみ</td>
</tr>
<tr>
<td><strong>コードブロック実行</strong></td>
<td>できない</td>
<td>可能（サイドメニュー内のChatでコマンドを実行）</td>
</tr>
</tbody></table>
<p><strong>Composerを使うべき場面</strong></p>
<ul>
<li>コンテキストがシンプルなコード修正</li>
<li>コード生成・編集をすぐに行いたいとき</li>
</ul>
<p><strong>Chatを使うべき場面</strong></p>
<ul>
<li>コンテキストが大きめのコード修正</li>
<li>エラーメッセージの解析、一般的なプログラミング質問</li>
<li>長期間コンテキストを維持しながら作業したいとき</li>
</ul>
<hr>
<h2>再度結論</h2>
<ul>
<li><p><strong>2025年2月時点では、Copilot と Cursor の機能差はそこまで大きくない</strong><br>むしろ Copilot の進化が想像以上に早く、2024年12月当時に「Cursor の勝ち」と思っていた部分を、数ヶ月で Copilot が埋めてしまった印象があります。</p>
</li>
<li><p><strong>Cursor を使うメリットが少ないわけではない</strong><br>AI搭載のIDEとして専用のショートカットやエージェントモードなど、Cursor ならではの強みも依然としてあります。ただし、Copilot も対応を進めてきているため、あえて新規導入するメリットはやや薄れてきたかもしれません。</p>
</li>
<li><p><strong>結局どちらを使うか？</strong></p>
<ul>
<li>もし社内で GitHub Copilot が既に標準備品として利用可能なら、当面は Copilot だけで事足りる場面が多いでしょう。</li>
<li>とはいえ、大規模なコード修正やチャット駆動型の開発を積極的に取り入れたい場合、現状Github CopilotでのAIエージェントはVSCode Insidersで利用可能となっていますが、Cursor の Composer / Chat 機能を試してみる価値はあります。</li>
<li>価格面でもほぼ同等なので、UI・操作感で選んでしまうのも一手です。</li>
</ul>
</li>
</ul>
<hr>
<h2>開発アプリ</h2>
<ul>
<li><a href="https://apps.apple.com/jp/app/prism-japan-%E8%A6%B3%E5%85%89-%E3%81%8A%E5%87%BA%E3%81%8B%E3%81%91%E5%85%88%E6%A4%9C%E7%B4%A2%E3%82%A2%E3%83%97%E3%83%AA/id1604282082">Prism Japan - iOS</a></li>
<li><a href="https://play.google.com/store/apps/details?id=com.kinto.ppap&hl=ja">Prism Japan - Android</a></li>
</ul>
<hr>
<h2>参考</h2>
<ul>
<li><a href="https://www.cursor.com/ja">Cursor - The AI Code Editor</a></li>
<li><a href="https://docs.github.com/ja/copilot/using-github-copilot/getting-code-suggestions-in-your-ide-with-github-copilot">GitHub Copilot を使用して IDE でコードの提案を取得する</a></li>
</ul>
<p>以上、Cursor と GitHub Copilot を使い倒してみた結果でした。<br>製品のアップデート速度はますます加速しているので、今後もぜひ定期的に検証していきたいと思います。</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[langgraph-supervisorでマルチエージェントシステムを構築した]]></title>
            <link>https://blog.kinto-technologies.com/posts/2025-02-28-building-Multi-Agent-system-by-using-langgraph-supervisor/</link>
            <guid>https://blog.kinto-technologies.com/posts/2025-02-28-building-Multi-Agent-system-by-using-langgraph-supervisor/</guid>
            <pubDate>Tue, 25 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[LangGraphの最新アップデートの構築事例の紹介]]></description>
            <content:encoded><![CDATA[<h2>はじめに</h2>
<p>こんにちは！<br>KINTOテクノロジーズの生成AI活用PJTで生成AIエンジニアを担当しているAlexです。</p>
<p>最近、LLM（大規模言語モデル）を利用したマルチエージェントシステムの需要が急速に高まっています。今回、LangGraphの最新アップデート「langgraph_supervisor」を活用し、わずか30分以内でSupervisor型マルチエージェントシステムを構築した実例をご紹介します。このシステムは、複数の専門エージェントを中央のスーパーバイザーが効率的に調整・連携させる仕組みとなっており、業務の自動化やカスタマイズ可能な提案シナリオの生成など、さまざまなユースケースに応用可能です。</p>
<h2>LangGraphとは</h2>
<p><strong>LangGraph</strong>は、AIエージェントやRAG（Retrieval-Augmented Generation）システムの構築を容易にするPythonライブラリです。LangChainと組み合わせることで、複雑なワークフローやタスクを効率的に設計・実装できます。状態（State）の永続化、ツールの呼び出し、そして「人間の介在（human-in-the-loop）」や「後からの検証」が容易な中央パーシステンス層を提供しており、今回のSupervisor型システムの基盤となりました。</p>
<h2>langgraph-supervisorとは</h2>
<p><strong>langgraph-supervisor</strong>は、LangChainに最近発表されたLangGraphを活用して階層型Multi Agentシステムを構築するためのPythonライブラリです。中央のスーパーバイザーエージェントが各専門エージェントを統括し、タスクの割り当てや通信を管理します。これにより、複雑なタスクを効率的に処理できる柔軟なシステムの構築が可能です。</p>
<h3>主な機能</h3>
<ol>
<li><p><strong>Supervisor Agentの作成：</strong>
  複数の専門エージェントを統括し、全体のオーケストレーションを行います。</p>
</li>
<li><p><strong>ツールベースのAgent間Handoff機構：</strong>
  エージェント間の円滑な通信を実現するためのメカニズムを提供します。</p>
</li>
<li><p><strong>柔軟なメッセージ履歴管理：</strong>
  会話の制御を容易にするため、メッセージ履歴を柔軟に管理できます。</p>
</li>
</ol>
<p>@<a href="https://github.com/langchain-ai/langgraph-supervisor/">card</a></p>
<h2>Supervisor型Multi Agentシステムとは</h2>
<p><img src="/assets/blog/authors/alex.q/supervisor.png" alt="Supervisor型Multi Agentシステムの図解">
<em>出典：<a href="https://langchain-ai.github.io/langgraph/tutorials/multi_agent/agent_supervisor/">https://langchain-ai.github.io/langgraph/tutorials/multi_agent/agent_supervisor/</a></em></p>
<p>Supervisor型Multi Agentシステムとは、Supervisorと呼ばれる全体を統制するAgentがツールコール対応の各LLM Agentと連携して、どのAgentをいつ呼び出すか、またそれらのAgentに渡す引数を決定するMulti Agent構造です。</p>
<h2>langgraph-supervisorでMulti Agentシステムを構築</h2>
<p>以下は、実際にlanggraph-supervisorでMulti Agentシステムを構築する手順を紹介します。
今回は、匿名化した顧客の基本情報を入れるだけで、その顧客にお勧めする車と、お勧めのエンジンタイプも出力するシステムを構築します。
また、車の情報とエンジン情報に関しては、ローカルで格納している「車両情報.csv」からAgentが取得していきます。</p>
<h3>環境準備</h3>
<p><strong>Azure OpenAIのAPIキーの用意</strong></p>
<p>今回は、Azure OpenAI経由でGPT-4oを使用します。ご自身の状況によって、OpenAI APIやAnthropic APIなどを使用することも可能です。</p>
<p>また、Azure OpenAIのAPIキーやエンドポイントを環境変数として設定します。</p>
<pre><code class="language-python">os.environ[&quot;AZURE_OPENAI_API_KEY&quot;] = &quot;YOUR API KEY&quot;
os.environ[&quot;AZURE_OPENAI_ENDPOINT&quot;] = &quot;YOUR ENDPOINT&quot;
os.environ[&quot;AZURE_OPENAI_API_VERSION&quot;] = &quot;YOUR API VERSION&quot;
os.environ[&quot;AZURE_OPENAI_DEPLOYMENT&quot;] = &quot;YOUR DEPLOYMENT NAME&quot;
</code></pre>
<p><strong>LangGraph、LangChainのインストール</strong></p>
<pre><code class="language-terminal">pip install langgraph
pip install langchain
pip install langchain_openai
</code></pre>
<p><strong>langgraph-supervisorのインストール</strong></p>
<pre><code class="language-terminal">pip install langgraph-supervisor
</code></pre>
<h3>LLMおよび各Agentで使うツール関数のセットアップ</h3>
<p>Azure OpenAI GPT-4oでモデルを定義します。
また、エージェント用のツール関数も定義します。</p>
<pre><code class="language-python">from langchain_openai import AzureChatOpenAI
import pandas as pd

# LLMの初期化
llm = AzureChatOpenAI(
    azure_deployment=os.getenv(&quot;AZURE_OPENAI_DEPLOYMENT&quot;),
    api_version=os.getenv(&quot;AZURE_OPENAI_API_VERSION&quot;),
)

car_information_path = &quot;/xxx/xxx/車両情報.csv&quot;

# ツール関数の定義例

# CSVファイルから車両情報を読み込み、候補を生成する例
def get_car_features():
    &quot;&quot;&quot;The python code to get car information.&quot;&quot;&quot;
    path = car_information_path  # CSVファイルのパスを指定
    df = pd.read_csv(car_information_path)
    car_features = df[[&quot;車種名&quot;, &quot;ボディタイプ&quot;, &quot;説明&quot;]].drop_duplicates()
    return car_features.to_dict(orient=&quot;records&quot;)

# 選択された車種に対してエンジンタイプを抽出する例
def get_engine_type(selected_car):
    &quot;&quot;&quot;The python code to get engine type and engine information.&quot;&quot;&quot;
    path = car_information_path
    df = pd.read_csv(car_information_path)
    engine_types = list(df[df[&quot;車種名&quot;] == selected_car][&quot;エンジンタイプ&quot;].unique())
    return engine_types, &quot;エンジンに関する補足情報&quot;
</code></pre>
<h3>各Agentの定義</h3>
<p>LangGraphの「create_react_agent」を利用して、各専門エージェント（車種推薦、エンジンタイプ選択）を定義します。</p>
<pre><code class="language-python">from langgraph.prebuilt import create_react_agent

# 車種推薦エージェント
car_agent = create_react_agent(
    model=llm,
    tools=[get_car_features],
    name=&quot;car_agent&quot;,
    prompt=&quot;&quot;&quot;
    # 指示書
    提案パターンに基づいて、推薦する車種を選び、200文字程度の根拠を説明してください。
    &quot;&quot;&quot;
)

# エンジンタイプ選択エージェント
engine_agent = create_react_agent(
    model=llm,
    tools=[get_engine_type],
    name=&quot;engine_agent&quot;,
    prompt=&quot;&quot;&quot;
    # 指示書
    推薦車種に最適なエンジンタイプを選び、200文字程度の根拠を説明してください。
    &quot;&quot;&quot;
)
</code></pre>
<h3>Supervisorの定義</h3>
<p>各エージェントを統括するSupervisorを作成し、顧客情報に基づいた最終提案を生成します。
ここで工夫した点としては、Supervisorに入力するプロンプトをモジュール化して、より高い汎用性を実現しました。roleやtask変数の中身と、紐づくAgentを変更すれば、他のタスクにも流用可能です。</p>
<pre><code class="language-python">from langgraph_supervisor import create_supervisor

# プロンプトの各モジュールの内容を作成
role = &quot;車のセールスマン&quot;
task = &quot;顧客情報をもとに、最適な車とエンジンタイプを提案してください。&quot;
guideline = &quot;&quot;&quot;
    - 出力は必ず上記JSON形式で行ってください。
    - 各根拠は200文字程度の豊かな文章で記述してください。&quot;
    &quot;&quot;&quot;
output_format = &quot;&quot;&quot;
    {
        &quot;提案内容&quot;: {
            &quot;提案する車種&quot;,
            &quot;車種の根拠&quot;,
            &quot;選択したエンジンタイプ&quot;,
            &quot;エンジン選定理由&quot;
        }
    }
    &quot;&quot;&quot;

# プロンプトを作成
system_prompt = f&quot;&quot;&quot;
    ## ロール
    あなたは優れた{role}です。

    ## タスク 
    {task}

    ## ガイドライン
    {guideline}
    
    ## 最終生成物のフォーマット
    {output_format}
    &quot;&quot;&quot;

# Supervisorの作成
workflow = create_supervisor(
    # 先ほど作成したエージェントと紐づく
    [car_agent, engine_agent],
    model=llm,
    prompt=system_prompt
)

# グラフのコンパイル
app = workflow.compile()
</code></pre>
<h3>実行例</h3>
<pre><code class="language-python">from langchain.schema import HumanMessage
import time

# 顧客情報の例（必要に応じて詳細情報を追加）
customer_info = &quot;顧客情報: 年齢35歳、燃費重視、現在乗車中の車はコンパクトカー&quot;

# 実行例
start_time = time.time()
result = app.invoke({
    &quot;messages&quot;: [
        {&quot;role&quot;: &quot;user&quot;, &quot;content&quot;: customer_info}
    ]
})
end_time = time.time()

# 最終出力の表示
print(&quot;最終提案:&quot;)
print(result[&quot;messages&quot;][-1].content)
print(&quot;実行時間: {:.3f}秒&quot;.format(end_time - start_time))
</code></pre>
<h3>Supervisorからの返答</h3>
<pre><code class="language-markdown">最終提案:
{
    &quot;提案内容&quot;: {
        &quot;提案する車種&quot;: &quot;ハイブリッド技術を採用した最新コンパクトカー&quot;,
        &quot;車種の根拠&quot;: &quot;顧客は現在コンパクトカーに乗車中であり、燃費性能の良さを重視しています。ハイブリッド技術を採用した車は、燃料使用の効率が高く、経済的負担や環境への配慮が優れています。また、ハイブリッドシステムは短距離や都市部での走行にも最適です。そのため、最新のハイブリッドコンパクトカーが最適な選択肢となります。&quot;,
        &quot;選択したエンジンタイプ&quot;: &quot;ハイブリッドエンジン&quot;,
        &quot;エンジン選定理由&quot;: &quot;ハイブリッドエンジンは燃費性能が非常に優れており、顧客のニーズである燃料効率を最優先に考慮しています。さらに、コンパクトカーとの相性も良く、都市部での利用や日常の移動において高いパフォーマンスを発揮します。このエンジンの選択は、快適性、経済性、環境性能を全て満たします。&quot;
    }
}
実行時間: 21.938秒
</code></pre>
<h2>実際に構築してみた感想</h2>
<p><strong>迅速なプロトタイピング</strong>
langgraph_supervisorを利用することで、従来のLangGraphに必要だった複雑なAgent間の連携や状態管理がシンプルなコードで実装でき、30分以内にプロトタイプを構築できた点は非常に印象的でした。</p>
<p><strong>柔軟性と拡張性</strong>
各Agentは独自のpromptやツール関数で実装できるため、ビジネスニーズに合わせたカスタマイズが容易です。また、状態管理が中央で行われるため、将来的な改善や新しい機能の追加もスムーズに行えそうです。</p>
<hr>
<h2>We Are Hiring！</h2>
<p>KINTOテクノロジーズでは、事業における生成AIの活用を推進する仲間を探しています。まずは気軽にカジュアル面談からの対応も可能です。少しでも興味のある方は以下のリンク、または<a href="https://twitter.com/cognac_n">XのDM</a>などからご連絡ください。お待ちしております！！
@<a href="https://hrmos.co/pages/kinto-technologies/jobs/1955878275904303115">card</a></p>
<p>ここまでお読みいただき、ありがとうございました！</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/common/thumbnail_default_×2.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Troubleshooting Slack CLI: Resolving block_id Conflicts When Sending Messages]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-04_slackcli_blockid-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-04_slackcli_blockid-en/</guid>
            <pubDate>Fri, 21 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Troubleshooting Slack CLI: Resolving block_id Conflicts When Sending Messages]]></description>
            <content:encoded><![CDATA[<p>This article is the entry for Day 5 of the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a></p>
<hr>
<p>Hi, I&#39;m Ryomm, also known as &quot;The Phantom Bot Craftsman&quot;, and I develop the My Route iOS app at KINTO Technologies. Here’s a quick tip from <a href="https://blog.kinto-technologies.com/posts/2024-12-04_manabyi/">Manabyi</a>.</p>
<p>Although this article focuses on Slack CLI, it’s important to note that it operates using the Slack API internally. Consequently, the same issue can arise when interacting directly with the Slack API.</p>
<h1>Background</h1>
<p>While using Slack CLI, I encountered an issue where Block Kit messages failed to send under the following conditions:</p>
<ol>
<li>A form input contains a value in rich_text format.</li>
<li>The value is stored in the DataStore as rich_text.</li>
<li>Multiple rich_text entries are retrieved from the DataStore, concatenated, and formatted.</li>
<li>A block is created and sent using <a href="https://api.slack.com/methods/chat.postMessage"><code>postMessage</code></a></li>
</ol>
<p><img src="/assets/blog/authors/ryomm/2024-12-04-2/01.png" alt="Process Flow"></p>
<p>However, the process failed with the error: <code>parameter_validation_failed</code>.</p>
<p>![Error](/assets/blog/authors/ryomm/2024-12-04-2/02.png =600x)</p>
<h1>Cause</h1>
<p>The error was triggered due to an invalid parameter during message transmission. Upon investigation, I found that the <code>block_id</code> values were duplicated in the block I was trying to send, as shown below.</p>
<pre><code class="language-tsx">[
  {
    &quot;type&quot;: &quot;rich_text&quot;,
    &quot;block_id&quot;: &quot;xdrwH&quot;, // &lt;- this
    &quot;elements&quot;: [
      /* ... */
    ]
  }, {
    &quot;type&quot;: &quot;rich_text&quot;,
    &quot;block_id&quot;: &quot;xdrwH&quot;, // &lt;- this
    &quot;elements&quot;: [
      /* ... */
    ]
  }
]
</code></pre>
<p>It appears that the message can&#39;t be sent to Slack because the <code>block_id</code> in the message you&#39;re trying to send conflicts with an existing one.</p>
<h2>block_id</h2>
<p><code>block_id</code> is a unique identifier for a block. The official documentation explains it as follows:</p>
<blockquote>
<p>A unique identifier for a block. If not specified, a block_id will be generated. You can use this block_id when you receive an interaction payload to identify the source of the action. Maximum length for this field is 255 characters. block_id should be unique for each message and each iteration of a message. If a message is updated, use a new block_id.</p>
<p><a href="https://api.slack.com/reference/block-kit/blocks">https://api.slack.com/reference/block-kit/blocks</a></p>
</blockquote>
<p>If you create a block without specifying a <code>block_id</code>, a <code>block_id</code> will be automatically generated. This is mainly used for interactive elements, such as identifying which block a button was pressed on during user interactions. </p>
<p>A block_id must be unique within a single message or across a sequence of repeated messages (i.e., a series of bidirectional interactions). Additionally, when a message is updated, a new <code>block_id</code> must be used.</p>
<p>In this case, the <code>block_id</code> contained in the received rich_text was automatically generated.</p>
<p>Additionally, since the rich_text input was received as separate messages during the initial process, <code>block_id</code> duplication was likely to occur. As a result, this issue occurred due to a conflict with the auto-generated<code>block_id</code> </p>
<p>Here is a Haiku for this occasion.</p>
<p><em>Thought it was safe, Auto-generated failed me, Block_id conflict</em></p>
<p>This haiku reflects my shock and disbelief—I had believed that an auto-generated block_id, like a UUID, wouldn’t easily collide, yet it did.</p>
<p>This is just my speculation, but it seems that Slack generates <code>block_id</code> based on the block’s content. To test this, I inputted the exact same text multiple times, and each time, the same <code>block_id</code> was generated.</p>
<p>![Error](/assets/blog/authors/ryomm/2024-12-04-2/03.png =600x) <em>Type hoge in rich_text</em></p>
<p>Each time, the generated block_id was <code>RlmLN</code>.</p>
<pre><code class="language-tsx">{
  &quot;type&quot;: &quot;rich_text&quot;,
  &quot;block_id&quot;: &quot;RlmLN&quot;,
  &quot;elements&quot;: [
    {
      &quot;type&quot;: &quot;rich_text_section&quot;,
      &quot;elements&quot;: [
        {
          &quot;text&quot;: &quot;hoge&quot;,
          &quot;type&quot;: &quot;text&quot;
        }
      ]
    }
  ]
}
</code></pre>
<p>Therefore, if there’s a possibility of identical inputs, it’s safe to assume that <code>block_id</code> collisions are just as likely.</p>
<h1>Solution</h1>
<p>When merging multiple blocks into a single message for Slack, each block_id within the message must be unique.</p>
<p>The simplest solution is to delete the <code>block_id</code>, especially if interactivity isn’t needed. Since Slack will automatically generate a new <code>block_id</code> if one isn’t specified, you can remove it and proceed with sending the message.</p>
<p>Here’s part of the formatting method: The delete operator is used to remove the block_id property from the object.</p>
<pre><code class="language-tsx">// The items obtained from client.apps.datastore.query are included in the event argument
// Reference: https://api.slack.com/methods/apps.datastore.query#examples
function eventMessage(event) {
  // ...
  
  event.description.forEach((description) =&gt; {
    if (description.block_id) {
      delete description.block_id // 🐈❗️
    }
    message.push(description)
  })
  
  // ...
}
</code></pre>
<p>Now, you can send messages without worrying about <code>block_id</code> conflicts!</p>
<p>However, removing <code>block_id</code> from the object is not the best approach if you need interactive functionality. In that case, it’s best to generate and assign a <code>block_id</code> on the application side before sending the message.</p>
<h1>Conclusion</h1>
<p>This was a story about <code>block_id</code> conflicts preventing messages from being sent!</p>
]]></content:encoded>
            <enclosure url="https://blog.kinto-technologies.com/assets/blog/authors/ryomm/2024-12-04-2/thumbnail.png" length="0" type="image/png"/>
        </item>
        <item>
            <title><![CDATA[Migrating to MapKit: A New Chapter for Unlimited App]]></title>
            <link>https://blog.kinto-technologies.com/posts/2024-12-07-migration_to_mapkit-en/</link>
            <guid>https://blog.kinto-technologies.com/posts/2024-12-07-migration_to_mapkit-en/</guid>
            <pubDate>Thu, 20 Feb 2025 10:00:00 GMT</pubDate>
            <description><![CDATA[Welcome to the journey of MapKit. Discover the process of transitioning to Apple’s powerful mapping framework.]]></description>
            <content:encoded><![CDATA[<p>This aricle is the entry for day 19 in the <a href="https://qiita.com/advent-calendar/2024/kinto-technologies">KINTO Technologies Advent Calendar 2024</a> 🎅🎄 Hello, I am GOSEO, an iOS engineer in the Mobile App Development Group at KINTO Technologies. Currently, I’m working on the app Unlimited. Interestingly, 66% of the iOS developers working on Unlimited are from overseas, and I enjoy chatting with them in English every day. I am also a fan of Paradox games and am currently hooked on Crusader Kings Ⅲ.
This article is the entry for day 19 in the [KINTO Technologies Advent Calendar 2024]</p>
<h2>Evaluating the migration from Google Maps to MapKit in the Unlimited App</h2>
<p>In recent years, improvements in Apple Map’s performance have sparked interest in transitioning from Google Maps to MapKit. This shift is expected to reduce usage fees and enhance app performance and user experience. In this article, I will walk you through the implementation process, the challenges we encountered, and the outcomes of migrating from Google Maps to MapKit in the Unlimited app.</p>
<h1>Evaluating the migration from Google Maps to MapKit and its Process</h1>
<h3>1. <strong>Rendering maps and creating gradient lines</strong></h3>
<p>In the Unlimited app, gradient lines are rendered on Google Maps. To replicate this functionality in MapKit, we tested the use of <code>MKGradientPolylineRenderer</code>. By setting colors and specifying the start and end points using <code>locations</code>, we examined whether this implementation would work effectively. Additionally, I considered that this feature could be used in the future to dynamically change the line&#39;s color based on the user&#39;s speed exceeding the limit.</p>
<pre><code class="language-swift">func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -&gt; MKOverlayRenderer {
    if let polyline = overlay as? MKPolyline {
        let gradientRenderer = MKGradientPolylineRenderer(polyline: polyline)

        gradientRenderer.setColors(
            [Asset.Colors.primary.color, Asset.Colors.cdtRouteGradientLight.color],
            locations: [0.0, 1.0]
        )
        gradientRenderer.lineWidth = 2.0

        return gradientRenderer
    }
    return MKOverlayRenderer(overlay: overlay)
}
</code></pre>
<hr>
<h3>2. <strong>Differences in tap detection</strong></h3>
<p>In Google Maps, d