コネヒト開発者ブログ

コネヒト開発者ブログ

Difyを使った問い合わせ回答 AI チャットBOT作成で見えた課題(ラスボスはデータ準備)

こんにちは、コネヒトのさとやんです。 私は社内でRun with Techという社内のAI導入やDX推進を行う活動を行っていました。 今年、その一環でAIを活用したチャットBOTをDifyで作成したので、その時の話をブログとして書きたいと思います。

また、こちらはコネヒトのアドベントカレンダーの23日目の投稿でもありますので アドベントカレンダーの他の記事も良ければご覧ください

Difyってなに?

Dify自体をご存知ない方もいらっしゃると思うので、簡単にDifyについて説明します。

Dify(ディファイ)とは、プログラミング知識がなくても、直感的な操作(ノーコード/ローコード)で生成AIアプリケーションを開発・運用できるオープンソースのプラットフォームです。

画像も貼っているので、そちらを見てもらえると分かりますが、Webの画面上で様々なパーツを組み合わせてAIを活用したアプリケーションを作ることができます。 それでは、ここから制作過程とその中で発生した課題について書いていきます。

プランA:最初に考えたAIチャットBOTの構成

この構成を考えた理由
  • エージェントブロックの活用: エージェントブロックにツールを設定し、MCPクライアントとMCPサーバーのような動作を実現できないかと考えました。
  • 1ブロックでの完結: 問い合わせたユーザーが具体的に何を知りたいのか、どんなキーワードで検索すればよいかをAIに自律的に考えさせることで、ひとつのブロックで処理が完結できると想定しました。
  • 検索先の振り分け: 社内の規定・マニュアルを検索すべきか、Web検索で一般的な情報を調べるべきかを、MCPクライアントがサーバーを選ぶようにAIに判断させられるか試したい意図がありました。
  • Notionを直接参照させればデータの最新化に追従できるのでAIが参照するデータ更新の必要性がなくなる
結果

下記の通り上手くいかなかったため、断念しました。

  • エージェントブロックの精度不足

    • 最初から用意されているエージェントブロックやプラグインの「エージェンシー戦略」では、期待していた賢さには程遠い結果となりました。
    • エージェントブロックを使用するには「エージェンシー戦略」の設定が必須なためプラグインで追加しましたが、プロンプトの指示を実行しなかったり、精度が低かったりしました。
    • 具体例:
      • 質問から検索用キーワードを抽出させようとしたが、質問内容と異なるキーワードを作成してしまう。
      • 内容に応じて「Notion検索」か「Web検索」かを判断するようプロンプトを記述したが、検索自体を行わずに回答してしまう。
  • 評判と代替案の失敗

    • 調査したところ、デフォルトのエージェントブロックは評判があまり良くなく、私と同様に精度の低さを指摘している人もいました。
    • 代替案として紹介されていた「エージェントブロック自体を自作し、そのAPIをHTTPリクエストブロックで叩く」という方法も試しましたが、こちらも思考やアクションの精度が安定しなかったため、一旦不採用としました。
  • Notion連携の課題

    • 参照させるNotion DB内のページ構造がバラバラだったため、うまく検索ができませんでした。その結果、問い合わせ文からのキーワード抽出や作成が安定せず、取得してほしいページが検索結果に出てこない事象が発生しました。
    • DBプロパティの問題:
      • キーワードに合致する情報が含まれているはずのNotion DBページが、なぜか検索できないケースがありました。
      • 調査の結果、NotionツールのDB検索機能では「ページ本文」の内容は検索できるものの、「プロパティに登録されている情報」は検索対象外であることが発覚しました。(カスタマイズ要素であるプロパティの有無に合わせて実装されていない点は納得できますが、検索できないのは痛手でした)

プランB(採用した方法):Difyのナレッジを使う方式

これを作っていた当初では最も確実な方法を採用しました。Notion上にある規約やマニュアルをDifyの「ナレッジ」に取り込み、知識検索ブロックで「ハイブリッド検索(全文検索+ベクトル検索)」を行う設定です。 この検索結果を、AIエージェントを設定しているLLMブロックに渡して回答させる方式が、最も精度の高い回答を得られました。

作成過程

1. Difyナレッジの作成

全工程において最も大変だった作業です(正直、もうやりたくありません)。 前述の通り、ページ構成のばらつきや、DBプロパティ上の情報はナレッジに取り込めないという課題がありました。これを解消するために、以下の作業が必要となりました。

  1. Notion DB内ページのプロパティに書かれている情報を、ページ本文に転載する。
  2. ナレッジに取り込みやすく、AIが理解しやすい構造に整形する。

1に関しては、もはや手作業でやるしかありませんでした。対象のNotion DBの中から該当ページを見つけ、ひとつずつ修正していきました。

2に関して、すべて手作業で行うのは気が遠くなる作業でしたが、幸いナレッジに取り込みたいページがすべてNotion DBページだったため、Zapierを使って以下のように半自動化しました。

これで「ナレッジ取り込み専用のNotion DB」を作成し、これをDifyへ連携させました。あとはDify上でナレッジを作成する際にこのDBを指定し、チャンクを設定して取り込みを実施しました。

  • 親子分割モード(親): 設定を「全文」にしています。規約やマニュアルはページ全体でひとつの意味を成すデータであるため、後述する検索処理において文章全体を見て検索を行えるようにするためです。
  • 子チャンクの設定: 改行2つで段落の切れ目としてある程度うまく分割できたこと、また「512 char」であれば規約やマニュアルのひとつの段落が収まる範囲だったため、この設定にしました。

2. 検索設定(ハイブリッド検索)

検索設定には、全文検索とベクトル検索を両方使う「ハイブリッド検索」を採用しています。 質問文に含まれるキーワードを「全文検索」で拾いつつ、質問内容の意味合いを「ベクトル検索」で補完する組み合わせが、最も精度良く検索できました。

Rerankモデルは以下の動作を行い、ハイブリッド検索の長所を活かしてくれるため採用しています。

  1. キーワード検索で候補を探す。
  2. ベクトル検索で候補を探す。
  3. 「ユーザーの質問に最も適切なのはどれか?」を改めて採点し直し、並べ替える。

これにより、「キーワードは合っているが内容は無関係」といったノイズを除去し、本当に役立つ情報だけを回答に使うことができます。

  • トップK: 「検索結果の上位何件をAIに読ませるか」の設定です。数値が多すぎるとノイズとなるデータが増えてしまうため、実際に動かして検索ヒット数を見ながら調整しました。
  • スコア閾値: ヒットしたデータの「関連度がどれくらい高ければ参照させるか」の設定です。当初は設定していましたが、適切なデータであってもスコアが低く判定されることがあったため、途中で設定を外しました。
3. LLMブロック

こちらは特別な設定はしておらず、プロンプトもシンプルな内容です。 ごく稀に英語で回答することがあったため、日本語での回答を強制するとともに、ハルシネーション(嘘の回答)を防ぐため、「回答に使えるデータが見つからない場合は回答しない」という指示をプロンプトに含めています。

あなたは、社内規定に関する質問に答える優秀なAIアシスタントです。 提供されたコンテキスト(ナレッジ)に書かれている情報のみを使い、ユーザーの質問に日本語で 回答してください。 もしコンテキストに答えが書かれていない場合は、無理に答えを作らず 「その情報は見つかりませんでした。」と回答してください。

以上の手順と構成で、Difyを使ったAIチャットボットが作成できました。 実際の回答画面には社内規定などの情報が含まれるため画像は載せられませんが、試験版としては問題ない精度で回答してくれています。最後に、実際に作ってみて感じたことや課題をまとめておきます。

感じたことや課題

  • AIが参照するデータの原本が直接利用できない場合や、AIが理解しづらい構造の場合は、どうしても「参照用データ」を別途作成する必要があります。その場合、そのデータを最新化するための仕組み作りも必要になります(今回は試験的なものだったので作っていません)。
  • AIツールの作成において、参照データの準備(整形など)に手作業が発生する場合もあり、この「データ準備」が最も大変な作業であり壁となります。
  • どういったデータをどんなセグメント(塊)で参照させたいかによってチャンク設定が変わるため、作りたいものや期待する結果に合わせて適切なチャンク設計をすることが重要です。
  • ナレッジに追加データを投入した際、新規作成時のチャンク設定を引き継がずに取り込んでしまう仕様があるため、意図せず不適切なチャンク構造のデータが混入することがあります。
  • 現状のエージェントブロックは、Dify上でゼロから自作してもまだ安定性に欠ける印象です。LLMブロックで事足りる要件であれば、LLMブロックを使うのが無難だと感じました。

以上がDifyを活用したAIチャットBOT作成過程と課題でした。 データ準備が大変ですが、技術的には難しいものではありません。 今後は継続的な運用を見据えて、AIが参照するデータの最新化の仕組みを整えていきたいと思います。 ここまで読んでいただき、ありがとうございました。

【iOS】URLSessionWebSocketTaskを用いたリアルタイムチャット機能の実装パターン

本記事はコネヒト Advent Calendar 2025の17日目のエントリーになります。

adventar.org

こんにちは、iOSエンジニアのyoshitakaです!

この記事ではAdvent Calendar 2025の13日目のエントリー「API Gateway WebSocket API と Lambda で作る、ママリのリアルタイムチャット機能(サークル機能)を支えるサーバーレスなインフラ設計」で紹介された、ママリのリアルタイムチャット機能について、iOSアプリ開発の視点で知見を共有したいと思います。

tech.connehito.com

はじめに

ママリの「サークル機能」とは、出産育児に関するさまざまなテーマのサークルに参加し、ユーザー同士がチャット形式でコミュニケーションを取れるというものです。

ママリではQ&A形式でユーザー同士がやり取りできる機能を提供していますが、このサークル機能ではよりリアルタイム性を重視しました。

「API Gateway WebSocket API と Lambda で作る、ママリのリアルタイムチャット機能(サークル機能)を支えるサーバーレスなインフラ設計」で詳しく紹介されていますが、リアルタイムチャット機能にはWebSocketを使っています。

WebSocketを使ったリアルタイム通信をSwiftでどのように実装しているのか、具体的なコードとともに紹介します。

WebSocketをiOSアプリで使うには

iOS13以降ではURLSessionWebSocketTaskを実装することで実現できます。

developer.apple.com

WebSocketの接続と送受信周りの処理を紹介します。 なお、今回のサークル機能では常に1つのコネクションのみを使用する仕様のため、紹介するコードも複数接続は考慮していません。

ハンドシェイク(接続)部分

URLSessionwebSocketTaskを生成しresumeすることで接続が開始できます。

webSocketTask(with:) | Apple Developer Documentation

func webSocketTask(with request: URLRequest) -> URLSessionWebSocketTask

今回の設計では接続はサークルごとに確立しています。サークルIDをリクエストに含めてwebSocketTaskを作成します。

func setup() {
        let request = WebSocketRequestProvider.createRequest(circleId: circleId)
        let session = URLSession(
            configuration: .default,
            delegate: self,
            delegateQueue: nil
        )
        socket = session.webSocketTask(with: request)
        socket?.resume()
    }

接続の結果はURLSessionWebSocketDelegateで受け取ります。

URLSessionWebSocketDelegate | Apple Developer Documentation

ハンドシェイクが成功した場合urlSession(_:webSocketTask:didOpenWithProtocol:)が呼ばれるので、接続状態のステートを更新します。

※ 異常系は接続管理部分で書きました

extension CircleChatWebSocketService: URLSessionWebSocketDelegate {
    nonisolated public func urlSession(
        _ session: URLSession,
        webSocketTask: URLSessionWebSocketTask,
        didOpenWithProtocol protocol: String?
    ) {

        // 接続成功
        Task { @MainActor in
            connectionState = .connected
            resetReconnectAttempts()
        }
    }

受信部分

無事接続が確立できると、次はWebSocketから値が流れてくるようになります。

値の受け取りはwebSocketTaskreceive()を使用します。

注意点として、このメソッドは再帰的に呼ぶ必要があります。

func receive() async throws -> URLSessionWebSocketTask.Message

https:// https://developer.apple.com/documentation/foundation/urlsessionwebsockettask/receive()

completionHandler版の方には解説があります。

receive(completionHandler:) | Apple Developer Documentation

WebSocketから受け取る値は、String or Dataの形になります。

startReceivingは初回接続時以外にも接続が切れた際の再接続時には常に呼び出す形になります。

    private func startReceiving() {
        stopReceiving()

        receiveTask = Task { [weak self] in
            while !Task.isCancelled {
                do {
                    guard let webSocket = self?.socket else { break }

                    let message = try await webSocket.receive()

                    switch message {
                    case .string(let string):
                        self?.handleTextMessage(string)

                    case .data(let data):
                        self?.handleBinaryMessage(data)

                    @unknown default:
                        break
                    }
                } catch {

                    // エラーハンドリング
                    if !Task.isCancelled, self?.connectionState == .connected {
                        try? await Task.sleep(for: .seconds(1))
                        continue
                    }
                    break
                }
            }
        }
    }

    private func stopReceiving() {
        // 受信待機停止
        receiveTask?.cancel()
        receiveTask = nil
    }

送信部分

値の送信はwebSocketTasksend(_:)を使用します。

func send(_ message: URLSessionWebSocketTask.Message) async throws

send(_:) | Apple Developer Documentation

completionHandler版の方には解説があります

send(_:completionHandler:) | Apple Developer Documentation

送信前には接続を確認しておき、接続が切れている場合は再接続をするように処理を入れました。

    public func send(request: CircleWebSocketRequest) async throws {
        guard connectionState == .connected else {
            switch connectionState {
            case .disconnected:
                // リクエスト送信前に接続を確認、切断されている場合は再接続トライ
                connect()
                throw WebSocketError.notConnected
            case .connecting:
                throw WebSocketError.connecting
            default: return
            }
        }

        let dataString = try request.createDataString()

        try await socket?.send(.string(dataString))
    }

この送信処理では、REST APIとは異なりレスポンスを受け取りません。 つまり、送信したデータをサーバー側が受信したかどうかは別途確認応答を送ってもらう必要があります。

サークル機能ではテキストや写真を投稿できるため、投稿の成功・失敗をユーザーにフィードバックする必要があります。

そこで、すべての送信データにクライアント側でリクエストIDを発行し、そのIDを確認応答で受け取ることで成功・失敗を判定するようにしました。

接続管理

接続が確立してから一定期間送受信がないと接続がタイムアウトしてしまいます。 タイムアウトの時間はサーバー側の設定次第ですが、意図しないタイムアウトを避けるため、pingを一定間隔で送るようにします。

sendPing(pongReceiveHandler:) | Apple Developer Documentation

    func setupPingTimer() {
        pingTimerCancellables.removeAll()

        Timer.publish(every: pingInterval, on: .main, in: .common)
            .autoconnect()
            .sink { [weak self] _ in
                self?.ping()
            }
            .store(in: &pingTimerCancellables)
    }

    func ping() {
        // ping送信前に接続状況を確認、切断されている場合は再接続をトライ
        if connectionState == .disconnected {
            connect()
        }

        socket?
            .sendPing(pongReceiveHandler: { error in
                // 必要に応じてエラーハンドリング
            })
    }

また、意図しない切断が発生した場合は、再接続を行います。

接続が切断される場合は、接続成功時と同じくURLSessionWebSocketDelegateにて切断理由とともに値を受け取ることができます。

urlSession(_:webSocketTask:didCloseWith:reason:) | Apple Developer Documentation

また、電波遮断等が原因の切断を検知できるようにURLSessionTaskDelegatedidCompleteWithErrorの実装も追加しておくと安心です。

urlSession(_:task:didCompleteWithError:) | Apple Developer Documentation

意図した切断以外は再接続をトライ(遅延を入れて数回行う)するようにしました。

    // サーバーからの切断
    nonisolated public func urlSession(
        _ session: URLSession,
        webSocketTask: URLSessionWebSocketTask,
        didCloseWith closeCode: URLSessionWebSocketTask.CloseCode,
        reason: Data?
    ) {
        let reasonString = reason.flatMap { String(data: $0, encoding: .utf8) } ?? "No reason"

        Task { @MainActor in
            let wasConnected = connectionState == .connected
            connectionState = .disconnected
            socket = nil

            if wasConnected && closeCode != .normalClosure {
                // 接続状態からの異常切断、再接続を試みる
                attemptReconnect()
            }
        }
    }

    // 電波断絶やタイムアウトなど
    nonisolated public func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didCompleteWithError error: Error?
    ) {
        guard let error else { return }

        // 接続失敗時の再接続
        Task { @MainActor in
            let wasConnecting = connectionState == .connecting
            connectionState = .disconnected
            socket = nil

            if wasConnecting {
                attemptReconnect()
            }
        }
    }

おわりに

URLSessionWebSocketTaskを使った具体的なリアルタイムチャット機能の実装方法について紹介しました。

WebSocketを使ったデータのやり取りにおけるリクエスト成功/失敗の管理は想定よりもクライアント側でやることが多く大変でした。

リアルタイムチャット機能の開発ではUI側の実装で苦労した部分も多かったので、別の記事で紹介できればと思います!

API Gateway WebSocket API と Lambda で作る、ママリのリアルタイムチャット機能(サークル機能)を支えるサーバーレスなインフラ設計

本記事はコネヒト Advent Calendar 2025の13日目のエントリーになります。

adventar.org

こんにちは、コネヒトでインフラエンジニアをしております @sasashuuu です!

私ごとではありますが、一年前くらいに小田原へ引っ越しまして、自然に囲まれたのどかな生活を送っております。

地方の良さをしみじみと感じている今日この頃です!

はじめに

さて、本日は2025年にリリースされたママリのリアルタイムチャット、その名も「サークル機能」に関するインフラ設計に関する記事をお届けします。

※以下は最近の開発環境のキャプチャです。本機能のイメージを掴んでいただければと思います。

開発環境の UI

サークル機能の施策背景や詳細なコンセプト、アプリケーションの実装側に関する内容については、本記事では割愛させていただきますのでご了承ください。

また、メインで利用しているクラウドプラットフォームが AWS となりますので、AWS を用いた内容が中心となります。

これから AWS を用いたリアルタイム性の高いアプリケーションを作る方のご参考となれば幸いです。

アプリケーション開発の要件・想定するワークロード

複数人が参加する空間でのリアルタイムチャットを既存のアプリケーション実装していくというのがざっくりとした要件となります。悩めるママさん同士のつながりや、継続的な交流をより強く意識していくことがテーマとなっていたので、従来の掲示板形式のコミュニケーションからアップデートしていく必要がありました。

ワークロードに関しては、結論として正確に見積もるのが難しいという状況でした。そのため、PdM 主導のもと、一定の KPI(メッセージ投稿数、UU 投稿数等)を定める形とし、その指標を前提として耐えうる設計を進めていく形となりました。また、考慮すべき変数などはいくらかありつつも、開発時点のママリ登録者数約400万を最大値とし、既存のアクティブユーザー数に耐えうる基盤も視野に入れる必要がありました。

アーキテクチャ

はじめに全体のアーキテクチャをお見せします(※既存のママリのインフラに関しては一部を除き基本的に割愛しています)。

全体のインフラアーキテクチャ

今回、サークル機能のために採用した主要なマネージドサービスは以下となります。

  • API Gateway WebSocket API
  • Lambda
  • RDS Proxy
  • SNS(Simple Notification Service)

終端に API Gateway の WebSocket API(※以下、WebSocket API と称します)を置いて WebSocket 通信を行い、チャット機能そのものに関する API サーバーやデータストアには Lambda と DynamoDB を利用しています。データストアに一部ママリの既存データ(ユーザーの属性データ等)を保持している Aurora MySQL や Elasticache Redis を利用する必要があり、Aurora では特に負荷に対する懸念の対策を打ちたかったため、コネクションプールとして RDS Proxy を置いてデータベースへのコネクション数による負荷を軽減するという構成になっています。また、Push 通知には SNS を利用しています。

実際の処理を抜粋して解説すると以下のようなフローです(クライアントA、B がメッセージを送受信する例)。

接続時

  1. クライアント A、B から WebSocket API へ接続リクエスト
  2. Lambda Authorizer による認可処理(ステートフルのため認可処理はこのタイミングのみ)
  3. WebSocket API によるコネクションの確立
  4. WebSocket API への接続リクエスト成功をトリガーに Lambda が起動、コネクション確立時の処理の実行
    • DynamoDB へコネクション ID の保存等

メッセージ送受信時

  1. クライアント A から WebSocket API へメッセージ送信リクエスト(API サーバー向け)
  2. WebSocket API から Lambda へのルーティング、メッセージの処理
    • DynamoDB へメッセージの保存
    • DynamoDB からコネクション ID の取得
    • 対象のコネクション ID をもとに WebSocket API へメッセージ送信リクエスト(クライアント向け)
  3. クライアント B は WebSocket API を通じてメッセージを受信

切断時

  1. クライアント A、B によるコネクションの切断
  2. コネクション切断をトリガーに Lambda が起動、コネクション切断時の処理の実行
    • DynamoDB からコネクション ID の削除

※他にもユーザー同士のブロックに関する除外処理があるなど、もう少し細かな実装になっていますがここでは割愛しています。

少し補足をしておくと、WebSocket の接続そのものは WebSocket API を通じて、クライアントと WebSocket API 間で完結しており、具体のビジネスロジック(メッセージ送信等)は Lambda が担っているというイメージです。Lambda が接続中のクライアントに向けて REST API を WebSocket API へリクエストし、WebSocket API を経由してメッセージがクライアントに届くという仕組みになっています(ただし、接続に関するコネクション ID の管理という意味ではある種 Lambda も WebSocket の接続に関わってはいるかなと思います)。図解すると以下のようなイメージです。

WebSocket を用いた処理の補足

技術選定の背景

ここからは技術選定について解説をしていきます。データストア、WebSocket サーバー、API サーバーなどの観点から分けて解説します。

データストア

DynamoDB を採用しました。ママリで利用していた既存の Aurora MySQL に相乗りする形の案もあがっていました。社内で馴染みのある RDB を用いた方が開発速度が出せること、コンピューティングリソースの使用状況とリリースの初期フェーズで想定するワークロードを加味すると既存の RDB でも耐えられないことはないだろうという見解となっていました。しかし、書き込みに対する水平スケーリングへの課題感、他のサブシステムやプロダクトからも利用される共用の RDB であるという影響範囲の広さ、中長期で見た将来的な可用性やスケーリング、移行コストを考えるとこの時点で NoSQL をベースに作り込んでおいた方が良いだろうという結論となり、Aurora MySQL の採用は見送る結果となりました。

WebSocket サーバー

API Gateway の WebSocket API を採用しました。こちらは観点を分けて説明します。

そもそもの通信方法に関する観点

ポーリング形式で HTTP 通信を採用する案もあがっておりました(既存のママリの API である CakePHP に相乗りする形で実装するという方針)。しかし、ユーザー体験(メッセージ送受信の遅延)や将来を見据えた機能の拡張性(サーバー負荷等)を踏まえ、あらたに WebSocket 通信を採用し、最適な実行環境を選定していくという方針となりました。

どのように WebSocket 通信を実現するかの観点

社内に WebSocket を用いたプロダクト開発の知見が豊富ではなかったため、なるべく WebSocket に関する関心ごとの負担を減らし、プロダクト開発へよりフォーカスできるようにすることなども加味し、WebSocket API を採用しました(もちろん WebSocket に対する技術的好奇心はメンバー間で話題にあがっており、WebSocket そのものにフォーカスした技術検証なども楽しんで取り組んでいました)。

今回のケースで WebSocket API を利用することのメリットはざっくりと以下の点です。

  • WebSocket サーバーそのものの管理が不要
    • コンピューティングリソースの消費やスケーリング、可用性観点での懸念がない(※クォータの制限等は除く)
  • WebSocket の接続管理が不要
    • 既に用意されているルート(\$connect、\$disconnect)を元に簡単に WebSocket の接続・切断のハンドリングが可能
    • ルーティング先の Lambda のビジネスロジックの実装に集中できる

余談として他にも WebSocket 接続を抽象化してくれるマネージドサービスとして、AppSync や Cloud Firestore(データストアも含めた採用案) などの案もあがっていましたが、社内での採用実績がなかった GraphQL(※AppSync は GraphQL がベースとなる)のキャッチアップコストや、Firestore のコスト面の懸念の理由により、どちらも採用には至りませんでした。

API サーバー

Lambda を採用しました。既存のママリの API サーバー(CakePHP と Fargate)を使う案もありましたが、以下の点で見送り、Lambda を採用する方針となりました。

  • 常駐型のアプリケーションよりイベント駆動のようなアーキテクチャが適している
  • コスト面
  • WebSocket API に併せたサーバーレスな構成
  • 可用性のためのスケーリング速度

ちなみにランタイムは Go が採用されました。Lambda では Go のマネージドランタイムが非推奨となっていますが、OS 専用ランタイムを用いてバイナリの実行環境を用意すれば問題なく稼働することができるため、問題なく採用することができました。

その他のサブシステム

Push 通知には SNS を利用しています。SNS を用いた Push 通知自体は既にママリでの利用実績があるため、あえて他の選択肢を取るという判断にはなりませんでした。既に運用中である現行のPush 通知基盤に相乗りする形となりました。

コスト面について

本記事では詳しく解説しておりませんが、今回採用したマネージドサービスは、今回のようなケースではコストパフォーマンスに優れていると感じます(少なくともリリース初期〜安定するまでなど)。特に WebSocket 接続の部分に関しては、利用者数がはっきりと見込めず、ワークロードが安定しないようなシチュエーションであれば WebSocket API を軸に置いたアーキテクチャから始めるという選択肢は有力かと思います。もちろんトレードオフではあると思うので、初めから多額のコストがかかることが分かっている、開発工数や運用にそれなりのコストをかけられるという場合は WebSocket に特化したマネージドサービス以外の選択肢もあるかなと思います。また、データストアで採用した DynamoDB に関しては、オンデマンドとプロビジョニングといった料金体系の使い分けもありますので、まずはオンデマンドから始め、傾向が見えてきたらプロビジョニングへ切り替えるといった形で段階を踏むことでコスト対策を打っていけるかと思います。

おまけ

Distributed Load Testing を用いた負荷試験も行いました。本記事では詳細や使い方などは割愛させていただきますが、過去に弊社のテックブログでも取り上げられておりますので、詳細や使い方等気になる方はそちらもご覧ください。

tech.connehito.com

どの程度耐えうるのか限界性能試験を行いたかったのですが、API Gateway のクォータ制限に引っかかってしまい、ベストエフォートでの実施となりました。可能な範囲で行えた試験(クォータ制限の影響が出る前のテスト)を一部ご紹介します。

ざっくりとしたテスト内容

  • Distributed Load Testing 側の各種設定
    • TASK COUNT(負荷試験リクエスト元の Fargate タスク数):5
    • CONCURRENCY(タスクごとの同時接続数):100
    • Ramp Up(CONCURRENCY に達するまでの時間):5m
    • Hold for(CONCURRENCY の持続時間):1m
  • jmx ファイルのシナリオ
    1. コネクション接続
    2. 単調なメッセージ送信リクエスト(例:「これは負荷試験のテストです」等)
    3. Ping/Pong
    4. 1000ms 秒待機
    5. メッセージ削除リクエスト
    6. コネクション切断

結果

テスト項目 結果
リクエストの総数 434,648
成功したリクエストの総数 434,635
エラーの総数 13
秒間平均リクエスト 1,210

上記の試験では、99.99%以上の成功率となり、安定した稼働となっていました。

おわりに

WebSocket API と Lambda のクォータ(同時実行数等)や、Lambda に関するコールドスタートのパフォーマンス等については状況により気にする必要がありそうですが、基本的にはサーバーレス構成の恩恵を受けているので、コンピューティングリソースや可用性、スケーリング等に関する懸念が少なく済んでいます。また、リアルタイムチャットなどの開発における実績や知見が社内になかったことも相まって、皆で協力して意見を出し合い調査を行ったり、AWS ソリューションアーキテクトの方への技術相談を行ったりと開発のプロセス自体も価値のある取り込みだったと思います!以降のアドベントカレンダーではアプリケーション側の実装に関する記事も公開予定ですのでお楽しみに!

コネヒトのウェブアクセシビリティに関する取り組み

この記事は コネヒトAdvent Calendar 2025、08日目の記事です。

コネヒトで働く人たちがお届けするアドベントカレンダーです。

コネヒトは「あなたの家族像が実現し続けられる社会へ」をビジョンに掲げ、「ママリ」などの家族向けプロダクトや事業を展開しています。

過去2年のアドベントカレンダー

adventar.org


こんにちは、コネヒトAndroidエンジニアの中島(id:nacatl)です。 今までAndroidに関する記事ばかり書いていた自分ですが、今回はちょっと違う分野としてアクセシビリティをテーマにした取り組みを紹介します。

背景

コネヒトはKDDIグループウェブアクセシビリティ方針に基づき、アクセシビリティの確保に取り組んでまいります。

connehito.com

www.kddi.com

というわけで、コネヒトでは会社としてアクセシビリティの確保に向けて活動を開始しています。 推進委員会的なメンバーが3名おり、その末席に自分がいるという形です。

しかし、一口にアクセシビリティと言ってもさまざまな項目があります。 今まで普段の作業の中で気にしていたメンバーも少なく、推進と言ってもどこから手をつければいいか五里霧中でした。

この記事では、どうやってメンバーにアクセシビリティを推進しているか、実際に行なった取り組みの一つである社内ワークショップの内容を紹介します。

基礎知識としてのアクセシビリティ

はじめに少し、予備知識としてアクセシビリティとは何かについて、自己解釈も含めて軽く触れておきます。 有識者の方は、この節は読み飛ばしていただいて構いません。

アクセシビリティとは

アクセシビリティはよくユーザビリティと比較されます。 どちらもユーザーフレンドリーにするという意味では同じですが、アプローチは真逆です。 その違いについて、筆者は下記の資料の8pにある表現が最もしっくり来ているので、ここで引用させていただきます。

ユーザビリティは山の頂点を高める活動

特定のユーザー(状況)に響く

アクセシビリティは山の裾野を広げる活動

幅広い層の人々にとって使える

speakerdeck.com

アクセシブルなコンテンツ

言葉遊びのようですが、「アクセシビリティを確保する」とは、「コンテンツをアクセシブルにする」ことです。 ここでは「アクセシブルなコンテンツ」とは何かを軽く解説します。

ユーザーがコンテンツを利用する際には、時と場合によりさまざまな障壁が立ちはだかります。 ここでいう障壁とは、いわゆる障がい者の方々についてのものだけではありません。 高齢者や他言語利用者、怪我をしていたり手が塞がっていたりするシーン等の、一時的なものも含まれます。

  • 身体的なもの
    • 目が見えない
    • 耳が聞こえない
    • 老眼 / 遠視 / 色弱など
    • 手指が使えない(怪我なども含む)
  • 状況的なもの
    • 乗り物が揺れて小さい文字が読めない
    • 旅行先などで言葉が通じない / 文字が読めない
  • デバイス的なもの
    • タッチパネルじゃない(スマホアプリだと忘れがち)
  • etc......

こういった状況下にあるすべての人に対して場所やシーンによらず、どのような状況でもサービスの価値を提供できる状態が、完璧な「アクセシブルなコンテンツ」と言えるでしょう。 (もちろん、完璧に対応するには相応の努力が必要になりますし、現実的にはかなり難しいと思います)

「アクセシビリティの確保を推進する」とは、「アクセシブルなコンテンツ」としての完成度を上げていく活動と言えます。

カーブカット効果

「アクセシブルなコンテンツ」になることで恩恵を受けるのは、障壁があるユーザーだけに限りません。

車椅子の移動のために歩道の縁石に簡素なスロープを作ったことにより、車椅子利用者だけでなくベビーカーや台車、スーツケースなどの利用にも恩恵がもたらされた現象があります。

このことから、裾野を広げることで一般的にも恩恵がある、という概念はカーブカット効果(Curb Cut Effect)と呼ばれています。

ssir-j.org

readspeaker.jp

WCAG

Web Content Accessibility Guidelines の略で、 World Wide Web Consortium(W3C) の下部組織が打ち出している、Webコンテンツ用のアクセシビリティガイドラインです。

waic.jp

www.w3.org

WCAGでは以下の4大原則が掲げられています。 ここでは紹介に留めますがより詳しいことが知りたければ、検索すればもっと詳しい記事が見つかると思いますので、そちらを参照していただければと思います。

  1. 知覚可能
    • 情報及びユーザインタフェース コンポーネントは、利用者が知覚できる方法で利用者に提示可能でなければならない。

    • 視覚 / 聴覚 / 色覚 など、それぞれ一つだけに依存した提示でないことが求められます。
  2. 操作可能
    • ユーザインタフェース コンポーネント及びナビゲーションは操作可能でなければならない。

    • タッチパネルやマウスに依存せず、キーボードだけなどの入力デバイスでも操作ができることが求められます。
  3. 理解可能
    • 情報及びユーザインタフェースの操作は理解可能でなければならない。

    • 知覚したものに対して、例えば「それが何をするボタンであるのか」などを理解できる必要があります。
  4. 堅牢
    • コンテンツは、支援技術を含む様々なユーザエージェントが確実に解釈できるように十分に堅牢 (robust) でなければならない。

    • 表示するブラウザや端末に左右されないことが求められます。

また、WCAGでは項目ごとに対応レベルが設定されており、以下のような定義となっています。

  • A(最低レベル)
  • AA
  • AAA(最高レベル)

コネヒトでは執筆時の最新である WCAG 2.2 に基づき、AAレベル の項目までの対応を目標としています。

コネヒトでの取り組み -社内ワークショップ-

先にも述べたように、アクセシビリティ確保と言ってもまず何をすればいいのか、という段階から始まるのがほとんどかと思います。 正直な話、人間、ただ「やれ」と言われても普通は身が入りません。自分もそうです。 なぜ何をやるのか、やって何が良くなるのかを知り、納得を得ることが、当人のパフォーマンスを十全に発揮する近道だと自分は考えています。

ですので、まずは「なぜ必要なのか」「何をやるのか」を知ること、情報と知識の社内浸透が必要かと考えました。

コネヒトでは普段の仕事から少し離れた視点で考える取り組みとして、マンスリーでワークショップを開いています。

connehito.com

その場を借りてアクセシビリティに関するワークを全社員メンバーに行なってもらい、まずは触れてもらおう、知ってもらおうと考えました。

第1回「スクリーンリーダー体験会」

2025年02月、第1回を開催しました。

目的

目的は「今まで知らなかった世界のママリを知ってもらう」としました。 まず具体的な内容より前に、普段と違うものがあることを知ってもらう狙いです。

内容

内容は「スクリーンリーダーを用いてママリアプリを触ってみる」です。

Androidユーザーには TalkBack、iOSユーザーには VoiceOverをそれぞれオンにして、その状態でママリアプリを触ってもらいました。 自由にアプリを触ってもらい、使おうと思ってできなかった機能や、こうだったらわかりやすい / やりやすいと思ったことを書き出すまでをワークとしました。

工夫点

スクリーンリーダーはスマホの操作方法が完全に変わるため、動かし方がわからないなどのトラブルが多く起こることを想定しました。 そのため、対応策を事前に考え以下のような対策を取りました。

  • 先に少数メンバーを集めて要望を聞く
  • オフラインで開催し、推進委員会メンバーがトラブル対応のために待機する
  • スクリーンリーダーの基本動作を先に説明、マニュアルも紙で作成して配る

結果 / 改善点

事前準備を厚めにしたことで、基本的には大きなトラブルはなく終えられました。 ほとんどのメンバーが初めて触れたこともあり、知らない世界に触れてもらうという意味では成功したと言えるでしょう。

ただやはりスクリーンリーダーは難しかったとの意見は多く、ワーク内容の操作指示などをより細かく指定した方が良かったかもしれません。

第2回「アクセシビリティを満たさないウェブサイトを触る会」

2025年11月、第2回を開催しました。

目的

今度の目的は「具体的な対応項目の例を知ってもらう」としました。

コネヒトではアクセシビリティの対応をする際、「サービスとしてまず優先して対応する予定の項目」を10項目ほど定めています。 それらを具体的に体験することで対応内容のイメージを掴んでもらうことが狙いです。

以下、優先対応予定とした項目の一例を抜粋します。

- 自動で動くものは全て一時停止を可能にする
- キーボードトラップ(キーボード操作で抜け出せなくなるUI)を作らない
- 画像の代替テキストを適切に追加する
- 色だけで情報を伝えない
- コントラスト比を十分に確保する
- etc......

内容

内容は「アクセシビリティの項目をわざと満たさずに作成された悪い例のWebサイトを触ってみる」としました。

今回はママリではなく、 駒瑠市〜アクセシビリティ上の問題の体験サイト〜 というWebサイトを用いて、ある項目が満たされなかった場合に何が問題なのかを考えてもらいました。 前述の対応すべき項目を先に提示し、各項目について満たされないことで何が問題なのかを書き出してもらうワークとしました。

a11yc.com

工夫点

今回はオフライン / オンライン混合で開催されることが先に決まっており、スクリーンリーダーの時のような対面でのサポートは全員にできないことが想定されました。 また、対応すべき項目の中にキーボードトラップ排除などがあることもあり、キーボードが使える環境でのワークを考える必要がありました。外部キーボードでスマホアプリを触ってもらうといった形では、Bluetooth接続機器などの所持を全員に求めることになってしまいます。 そのため、全員が使える機器でできることを念頭に置き、普段から業務で利用しているPCで触れるWebサイトでワークすることを決めました。

結果 / 改善点

ワーク終了後、参加者にアンケートで「アクセシビリティのために、サービスにどのようなことが必要かイメージすることはできましたか?」について答えてもらいました。回答は、「1(全くイメージできなかった)」から「5(イメージできた!)」までの5段階としました。

質問「アクセシビリティのために、サービスにどのようなことが必要かイメージすることはできましたか?」の結果の棒グラフ。内訳は続く表の通り
アンケート結果

回答 回答者数 %
5 (イメージできた!) 11人 40.7%
4 13人 48.1%
3 2人 7.4%
2 1人 3.7%
1 (全くイメージできなかった) 0人 0%

アンケート結果では対応内容がイメージできたという返答が多く、目的は達成できたかなと思います。

改善点としては、前回に続き「難しい」「スパルタ」といった感想をいただいてしまったことです。 これに関しては明らかに失念していたことがあって、駒琉市には「全て対応した困らない市」のリンクもあることを伝え忘れていました……

最初から「対応済みがあるからこれと比較もしてみるとよさそう」と伝えていれば、もう少し難易度が下がっていたかもしれません。

おわりに

今回の記事では、コネヒトにおけるアクセシビリティの確保推進について、社内での取り組みを紹介しました。 筆者もこの分野では知見が浅く、計画もまだまだ途上の段階なので、何が正解なのか今も手探りな状態です。

この記事が、同じようにアクセシビリティの確保や社内への浸透を考えている方々の一助となれば幸いです。

「現状起点の最適化」に夢中になっていた私

こんにちは!エンジニアのaboyです。これは自戒であり私の学びです。

コネヒト Advent Calendarの他の記事もお楽しみください。

私が携わったとある施策がありました。その施策はできるだけすぐに実現したかったので、当時思いつく仕組みで作りました。また、その後も不定期的に発生することが分かっていたので運用フローを定めました。

AIエージェントやツールによる段階的な自動化

その運用フローがこちらです。

  1. 👤 営業)Google Sheetsに指定項目を記入して、開発チームに連絡する
  2. 👤 開発)情報集約用のGitHub Issueを作成し依頼内容をまとめる
  3. 👤 開発)Issueの内容をもとに2つのリポジトリで実装する
  4. 👤 開発)Pull Requestをレビュー → マージ → デプロイする
  5. 👤 開発)担当者に完了連絡をする

最初に大枠の仕組みを作っておいたので、手順3で必要な実装というのは、2つのGitHubリポジトリ内に保持している定数を変更・追記するだけでOK。実装内容は数行程度なのでラクチンです。

数回繰り返すうちに、よくやるし対応方法も決まっているし簡単なので、手順3をラクにするためにAIエージェントに実装をさせるようにしました。その運用フローがこちらです。

  1. 👤 営業)Google Sheetsに指定項目を記入する
  2. 🤖 Zapier)1日1回、対応すべきものを検知してSlackに通知する
  3. 👤 開発)情報集約用のGitHub Issueと実装用のsub-issueを作成する
  4. 🤖 GHA※)sub-issueの対応をDevinに指示する(※GitHub Actions)
  5. 🤖 Devin)sub-issueの分だけDevinが起動し実装する
  6. 👤 開発)Pull Requestをレビュー → マージ → デプロイする
  7. 👤 開発)担当者に完了連絡をする

コネヒトのママリ事業部では、要求管理用のGitHubリポジトリにIssueを立てて、細かいタスクはシステム毎のリポジトリにIssueを立てるという方法がよく取られます。GitHubにはsub-issueという仕組みがあるので、Issueに紐づいているsub-issueの対応を一括でDevinに依頼できるワークフローを作りました。Devinのような自律型AIエージェントは、複数のリポジトリでの作業が必要なIssueに対して、それぞれのリポジトリごとに独立したセッションで仕事を進めてくれるのでとても便利です。

実装内容がシンプルということもあり、Devinは毎回完璧に実装してくれました。これで、営業メンバーが開発メンバーに連絡する手間と、開発メンバーが複数リポジトリで実装してPull Requestを出す手間が削減できました。

この運用で数回繰り返すうちに、今度GitHub Issueを人間が作成する手順3が無駄に思えてきます。どんなIssueを作るかというパターンは決まっています。この手順をGoogle Apps Scriptを用いて自動化した運用フローがこちらです。

  1. 👤 営業)Google Sheetsに指定項目を記入する
  2. 🤖 GAS)1日1回、対応すべきものを検知してGitHub Issueとsub-issueを作成する
  3. 🤖 GHA)sub-issueの対応をDevinに指示する
  4. 🤖 Devin)sub-issuesの分だけDevinが起動し、Pull Requestを開発チームに出す
  5. 👤 開発)Pull Requestをレビュー → マージ → デプロイする
  6. 👤 開発)担当者に完了連絡をする

以前の手順2,3をGASに置き換えました。Zapierだとsub-issueを作成する標準アクションが無かったため、どうせAPIリクエストを組み立てないといけないのであればこのタイミングでコネヒトで全員が使えるGeminiから直接コード生成ができるGASに変えました。地味にアクセストークンの管理などで考えなきゃいけないことがあったため、この過程でGASから自社のGitHubを操作するための簡単な仕組みも用意して、社内に展開しました。

さて、ここまででできた運用フローと、一番最初の運用フローを比べてみると、人間が行う作業は確実に減りました。

  1. 👤 営業)Google Sheetsに指定項目を記入して、開発チームに連絡する
  2. 👤 開発)情報集約用のGitHub Issueを作成し依頼内容をまとめる
  3. 👤 開発)Issueの内容をもとに2つのリポジトリで実装する
  4. 👤 開発)Pull Requestをレビュー → マージ → デプロイする
  5. 👤 開発)担当者に完了連絡をする

  1. 👤 営業)Google Sheetsに指定項目を記入する
  2. 🤖 GAS)1日1回、対応すべきものを検知してGitHub Issueとsub-issueを作成する
  3. 🤖 GHA)sub-issueの対応をDevinに指示する
  4. 🤖 Devin)sub-issueの分だけDevinが起動し、Pull Requestを開発チームに出す
  5. 👤 開発)Pull Requestをレビュー → マージ → デプロイする
  6. 👤 開発)担当者に完了連絡をする

営業メンバーが入稿し、機械が実装し、開発メンバーがデリバリーするという運用フローになりました。やったね。

ある日、チームメンバーから「これって管理画面でできたほうがいいんですかね」という意見を貰いました。

現状の最適化か、あるべき姿の実現か

「た、たしかに〜」と思い、”管理画面から設定できる”をゴールに設定し、そのゴールをどうすれば実現できるかという観点で色々と見直してみました。

見直した結果、管理画面から設定できるようにするには、データストアも含めた5つのシステムに関連する改修が必要だろうということが分かりました。

やることは多いけど「できる」ということが分かりました。

もちろん、できるはできるが、果たして今やるべきことなのかどうかは判断しないといけません。ただこうして一歩引いて考えられたことは良いことでした。

管理画面からできるようにすることが本当にやるべきことなのか、というのも考えないといけません。管理画面からできるようにした後の運用フローというのは、この施策が世の中に価値を提供するまでの流れの中の開発メンバーの介入にしか影響しておらず、まだ営業メンバーの介入が残っています。

  1. 👤 営業)管理画面から入稿する

そもそも、この施策は誰にどんな価値を提供したかったのか。当時はイージーな方法で実現しましたが、改めてこの問いについて考えてみると、全然違う言葉のほうがこの施策を適切に表しているんじゃないかと思ったりしてきます。何十回と行われ、今後も行われるため、持続可能性の向上も考慮する必要があります。

未来を見据えて適切に課題を設定し、それに伴う変化をリードすることこそが私がすることだったのに、当時の意思決定から地続きの現状起点で考えていて、いつしか近視眼的になっていました。

ゴールを決めるとそのゴールに向けた方法がたくさん思い浮かんできます。「XXという作業を自動化する」というゴールを決めた場合、生成AIの普及も手伝って自動化する方法は本当にたくさんあります。今ある業務を効率化するという観点では便利なツールがたくさん存在しますが、仕事における「誰にどんな価値を提供するのか」「なぜこれを課題に設定したのか」そういったコンテキストを操作する思考を忘れたくないな〜という自戒でした。

コネヒトの開発組織にみられる「+αの改善を肯定しフォローする」文化

こんにちは!エンジニアのaboyですԅ( ˘ω˘ԅ)

今回はコネヒトの開発組織の文化の一つ、「+αの改善を肯定しフォローする」ことについてサクっと紹介します。組織の文化というのは組織にいる人間が当たり前のように持っている価値観だったり前提のようなものです。

形式的に説明すると、誰かの主体的に加えられた付加価値に対して、他の人がまずその意図と行動を肯定し、その後の目的達成を支援することを意味します。

+αの改善というものについては、以前ボーイスカウト・ルールについて記事にしました。この記事でも「コネヒトにはボーイスカウト・ルールを当たり前のように実践できる人がたくさんいる」と書きましたが、ボーイスカウト・ルールに限らずこの文化は適用されます。 sizu.me

いくつかの事例を紹介すると、例えば、多くのリポジトリのデフォルトブランチがmasterになっているのを全てmainにしようと呼びかると、チームを超えてたくさんの協力者が集まり、あっという間にmainブランチへのリネームをやりきりました。

デフォルトブランチのリネームをシュッとやりきった

他には、必要であればチーム外の人間が課題設定をしてパフォーマンス改善策を実行したりしても受け入れられます。このあと、さらなるパフォーマンス改善がチーム内で実行されました。

チーム関係なく課題解決

あとは、生成AIについてのもくもく会や勉強会を始めると、みんなでワイワイできたり、フィードバックが集まったり。

生成AIもくもく会

こういった活動というのは、一人目のあとに続くフォロワーの存在も重要です。フォロワーが出てくるからこそ言い出しっぺが独りにならないし、物事が進んでいきます。失敗を必要以上に恐れずに主体的に考えた行動をしやすくなり、リーダーシップの発揮がしやすい風土になります。

とはいえ、このような組織文化を再現性をもって維持するには仕組みと仕掛けが重要だと思っていますが、その点でコネヒトには課題もあります。Mission/Vision達成のために必要な組織文化を属人化させず、意図的に作れるよう、開発組織全体で取り組んでいきます。

この記事で、開発組織の雰囲気を知っていただけたなら幸いです!

DroidKaigi 2025に参加しました!

こんにちは、コネヒトAndroidエンジニアの中島(id:nacatl)です。 コネヒトは、さる2025/09/10~2025/09/12の3日間に開催されましたDroidKaigi2025にサポータースポンサーとして協賛しておりました。

2025.droidkaigi.jp

このブログを読まれている方の中にも、現地参加された方がいらっしゃるかと思います。 中島もセッションの2日間に現地に赴き、色々学びを得られました。

今回は、自分が聴講した中でも特に印象深かったセッションを3つほど、感想を交えながらご紹介したいと思います。 今年はセッション動画も上がるのが早く、すでにほぼ全てYoutubeにて視聴できるようです。運営の方々の努力には頭が上がりません。

基礎から学ぶ大画面対応 〜「Large screen differentiated」認定アプリの開発知見〜

2025.droidkaigi.jp

tomoya0x00さんによる、アプリの大画面対応についてのセッションです。 ママリでは、実はつい最近ようやくEdge-to-edgeの対応をしました。 しかし時の流れは早く、世の中ではすでにSDK37の対応についての話がなされています。 技術的な注意点だけでなく、実装する画面の優先付などについても、実体験からくる説明で納得感がありました。

Deep dive into Kotlin Flow

2025.droidkaigi.jp

Jumpei Matsudaさんによる、Kotlin Flowの実装を深掘りするセッションです。 StateFlowの扱いに関してはLiveDataからの移行で実装レベルでは把握しているのですが、ChannelFlowなど普段実装で使えておらず、そういった知見が欲しくて聴講しました。 様々なFlowについて、普段の実装だけでは気にしない、気付けない点など改めて知ることのできたセッションでした。

スマホ新法って何?12月施行?アプリビジネスに影響あるの?

2025.droidkaigi.jp

公正取引委員会の鈴木健太さんによる、今年12月18日に施行される「スマートフォンにおいて利用される特定ソフトウェアに係る競争の促進に関わる法律」、スマホ新法に関する招待公演です。 正直なところ、情けなくもスマホ新法と言われても内容まで詳しく知らなかった身としては、詳しく知る場としてとてもありがたかったです。 現実的な問題としてこれが施行されたからと言って急に世界が変わるとまでは言えないでしょうが、「安全弁を整備する」という面で言えば重要なことなのかなといった認識を持ちました。 ただそれはそれとして、こういう機会は国民側への展開としてめちゃくちゃ重要なのではと思いました。

まとめ

運営の方々、またスピーカーの方々、素晴らしいカンファレンスをありがとうございました。 紹介しきれなかったセッションも素晴らしいものばかりで、これからも開発でしたいことがどんどん増えていくのを実感しています。 セッション外でも、すっかりお馴染みになったバリスタコーヒーコーナーが、混雑回避のための整理券制になっていたりと進化を感じます。

自分もコネヒトも進歩の足を止めず、負けじと日々成長していきたいと思いました。