技術改善の第一歩、カンリー店舗集客リニューアルの4ヶ月
株式会社カンリーのCTO室でエンジニアをしている波多野(@hatamasa1988)です。
最近は同時にカンリー店舗集客のテックリードとしてプロダクトチームにもコミットしています。
私は2025年1月に入社して、早いもので4ヶ月が経過しました。
入社エントリーも書いていますのでよければご覧ください。
私は入社後すぐにカンリー店舗集客のリニューアルチームに入り、今年の2月に無事リリースされました。
引き続きテックリードとしてチームに入り、技術課題や開発フローの改善をメインにさまざまな技術課題の解決に動いていて、少しづつ改善が進んできたので、今までの振り返りとこれからの取り組みと展望について一部を紹介できればと思います。
組織的な改善については、同じくカンリー店舗集客のEM須藤(@canly_em_aki)がブログを書いてくれているのでこちらの記事もぜひ読んでください。
カンリー店舗集客について
はじめに、プロダクトや開発体制について簡単に紹介させてください。
プロダクト
「カンリ―店舗集客は」、弊社の主幹プロダクトでもあり、店舗のGoogleマップ、HP、Facebook・InstagramといったSNSの管理を集約し効率化する店舗情報一括管理サービスです。
例えば、店舗情報の更新から、SNSでのコメントへの返信などをアカウントを横断して行うことができる機能を要しています。数百店舗を抱える企業様では、この更新や返信といった作業だけで膨大な時間がかかってしまいます。店舗数が多いチェーン店ほど「カンリー店舗集客」による業務効率化の効果は大きく、平均して約86%管理運用コストを削減できています。
このように店舗で運用している方々の業務を効率化することで、店舗の人々にはもっとクリエイティブな仕事をしてもらえるようにしていき、そこで生まれた価値が世の中を循環することで、すべての人の豊かな暮らしが社会にあふれていく未来を作っていきたいと考えています。
開発体制
現在の開発チームは、下記の体制としています。
エンジニアは各自技術スタックの強みを持ってますが、職能別で分けることはせず、エンジニアとしてプロダクト機能の開発をミッションとして担ってもらっています。
- EM:1名
- TL(テックリード):1名
- EL(エンジニアリーダー):1名
- エンジニア:10名
- PdM:3名
- デザイナー:1名
技術スタック
技術スタックは下記としています。
バックエンドがPHPとGoで分かれているのが特徴かと思いますが、元々既存の「カンリー店舗集客」がPHPで作られていたところに、機能の特性に合わせてGoへのリプレイスを徐々に進めているため、現在は複数の技術スタックでの構成になっているという経緯になります。
- バックエンド : PHP/Laravel, Go/Echo
- フロントエンド : TypeScript/Next.js(React)
- インフラストラクチャ : AWS
- データベース : Aurora MySQL
- CI/CD : Github Actions
- IaC : Terraform
取り組みの紹介
OpenAPI generatorでのスキーマドリブンな開発
以前はOpenAPIの開発フローではそれぞれのリポジトリごとにopenapi.yamlからOpenAPI Generatorを利用してコード生成をしていました。
各リポジトリでのopenapi.yamlの重複管理と作業が開発者の負担を大きくしていて、具体的には下記の課題がありました。
- openapi.yamlの重複管理による信頼性低下: リポジトリごとに分散管理していることで最新の反映漏れやそれによる修正の不一致などが発生しやすい状況になっていました。openapi.yamlをSSOTで管理し、信頼性向上をする必要がありました。
- openapi.yaml変更時の開発ボトルネック: フロントエンドのリポジトリを主としていたため、フロントエンドリポジトリで自動生成ファイルをマージしないとバックエンドを開発できないフローになっていました。ボトルネックをなくし並行で開発できるように、適用タイミングを柔軟にコントロールできるようにする必要がありました。
- 手動の生成プロセスの作業者負荷: 手動でOpenAPI Generatorを実行することで作業者の負荷になっていました。CIパイプラインにOpenAPI Generatorの実行を組み込むことで、手動でのgenerateコマンド実行とコミットを不要にし、開発者の負担を軽減する必要がありました。
これらの課題を解決するために、openapi.yamlを中央管理するリポジトリを分離させ下記手順での開発フローを実現しました。
1. 仕様の更新: OpenAPI管理リポジトリでopenapi.yamlを更新
2. 自動配布: 更新後、GitHub Actionsによって各リポジトリのworkflowをトリガー
3. コード生成: 各リポジトリでOpenAPI仕様からコードを自動生成
4. PRの作成: 更新されたファイルとコード生成物を含むPRが自動作成される
それぞれ、
フロントエンドではorvalを使用してAPIClient、SchemaTypes、ZodSchemasなどを自動生成しています。
Laravelバックエンドではopenapi generaterを使用してGeneration Gapパターンに従って生成されたクラスを抽象クラスとして具象クラスで継承した上で使用しています。
Goバックエンドではoapi-codegenを使用してGoコードを生成しています。
これにより、SSOTが実現され、さらに開発フローの自動化により作業者負荷と作業ミスもなくすことができました。
migration改善
カンリー店舗集客のバックエンドでは、これまでLaravelリポジトリに統合されたArtisanコマンドを用いてDBスキーマのmigrationを実行していました。しかし、この方式は以下の課題を抱えていました。
- デプロイの密結合: LaravelリポジトリのデプロイとDBスキーマの変更が強く結びついており、デプロイ頻度やタイミングの制約となっていました。
- 他チームへの影響: 共通で参照されるDBスキーマであるにも関わらず、migrationの実行がLaravelリポジトリに依存しているため、他チームの開発フローにも影響を与える可能性がありました。
カンリー店舗集客はプロダクト基盤を支える主幹プロダクトになっているため今後はチーム横断的に改修が入る可能性もありました。
ゆくゆくはDB基盤としてマイクロサービスとなる可能性もありますが、まずは近々にあるこれらの課題を解決し、より柔軟で独立性の高い開発体制を構築する必要がありました。
そのため、DBスキーマのmigrationをLaravelリポジトリから独立したリポジトリに分離し、各チームはDBスキーマの変更をより自律的に管理できる構成としました。
これにより、デプロイのボトルネックが解消されることが期待され、より柔軟で独立性の高いアーキテクチャが構築できました。
また、今回の変更からスキーマ駆動開発を導入することでスキーマ管理の効率性を上げる取り組みも考えました。スキーマ駆動開発を行う点で前述のOpenAPI generatorの仕組みと似たようなフローで実現ができています。
以前は、手書きでERDを作成し、レビューを経てからDDLを記述するという開発フローを行っており、ERDと実際のスキーマでの二重管理になっていて、変更コストやSSOTの観点から信頼性にかける状態になっていました。
最初に考えたのは、リニューアルプロジェクトが佳境な中でラーニングコスト、スイッチングコストを最小限に抑える必要があったため、これまでのDDLを直接記述する開発フローを維持することをまず考えました。結果的にはGooseを採用することに決定しました。
Gooseを採用することで下記のメリットがありました。
- チームメンバーがDDL記述に慣れていること、諸々のコストを最小限に抑えられる
- チーム全員がGoを書けるわけではなが、SQLでDDLが記述できる
追加で、CIの中で下記を行うように変更しました。
- dbdocsでmigration後のDBからdbmlを自動生成することで重複管理を回避
- migrationのUp/Down両方のテストを回し、rollbackに備えた安全性向上
- スキーマ変更をトリガーにアプリケーションのリポジトリでモデルクラスを自動生成
今回の修正で近々の課題は解決できたのですが、より効率的なスキーマ管理であれば、Prismaのようにデータモデルを記述・変更して、migrationを生成するというアプローチもあるかと思いますが、それは次への課題として残しております。
余談となりますが、GoのORMが持っているgeneratorがユースケースに合わない部分があり、MySQL MCPの活用も検討しながらAIでの自動化の検討を進めています。
一覧画面パフォーマンス改善
リニューアルリリース直前にカンリー店舗集客では深刻なパフォーマンスの課題に直面していました。
移行のテスト段階でもあるにかかわらず投稿・クチコミ一覧がすでにタイムアウトして開かない状態になっており、今後さらにデータ量の急増が予想される状況でした。
また今後の連携媒体の追加によってさらなるパフォーマンスの低下が懸念されていました。
チューニングにおいては段階的なアプローチで対応を進めていき、リリース前の短期的な対応として、インデックスの追加やSQLのチューニング、一覧表示の期間指定へのデフォルト値適用(取得期間を短くする)などの施策を実施しました。(リリースまで2週間ほどに迫られた中の対応であったため暫定対応を余儀なくされたという背景もあります・・・)
さらに、長期的な解決策として2段階の計画を立てていて、第1段階として非正規化や検索用テーブルでの対応を行い、第2段階としてElasticSearchへの移行を予定しています。この方針は、将来的なデータ量の増加やキーワード検索への柔軟な対応を見据えたものとしてADRも記載しエンジニアとプロダクトチーム全体で共通認識を持ち対応しました。
リニューアルリリース後の第1段階の非正規化の対応が終わりパフォーマンスが大幅に改善させることができ、現段階ではElasticSearchへの移行は先送りすることができています。
ADR(Architecture Decision Record)導入
以前のカンリー店舗集客チームでは、議事録による記録は行われているものの、議事録のみでは意思決定の経緯を追跡するのに時間がかかるという課題がありました。
具体的には、どの意思決定がどの会議で行われているのか、また発言の記録からはその背景が分かりづらく、後から参照する際に手間がかかるという問題が存在していました。
そのため、意思決定と背景の透明性の向上を期待してADRを開発フローに正式に導入しました。
ADR自体の説明はここでは割愛しますが、ADR導入の副次的な効果としては、エンジニアの技術力向上にも効果があると考えています。ADR作成の過程で技術選定の理由を言語化する力が養われ、調査・比較の習慣が身につくことが期待されます。
さらに、チーム全体での知見共有が進み、設計意図への理解が深まることで、コードの読みやすさや保守性の向上にも今後期待です。
カンリーでは他チームでも導入の話が進んでいるチームがあるため、今後社内横断的にADR標準を構築することも視野に入れています。
また、過去のADRについては導入済みのNotionAIのチャットで過去の議事録から生成して配置できそうなので検証して取りんでいる途中であります。
今動いている取り組み
アプリケーションログ方針
リニューアルリリース後、バックエンドのログ出力が課題にあり、バグ調査などの保守運用作業で十分情報が見つからないこともありました。
具体的には下記の課題です。
- Laravel, GoのAPIやbatchやworkerで共通の取り決めがなく、開発者の裁量に委ねられている
- LaravelとGoでログ構造が異なり、ログレベルの定義も統一されていない
- ログが複数の場所(CloudWatch、Datadog)に分散している
そのため下記を目的に改善を進めています。
- 外部に独立したロガーを共通で使用する方式を採用
- 共通のログレベルを定義し、方針に従ってログを出力
- ログレベルに応じたエスカレーションフローを確立
方向性としては設計は最低限のルールのみとし、運用しながら段階的にブラッシュアップしていく方針です。また、すでに導入されているPR-Agentを活用してログレベルの方針に基づいたレビューの自動化も進める予定です。
アプリケーションアーキテクチャの整理
Laravelではクリーンアーキテクチャの要素を入れたレイヤードアーキテクチャを採用していますが、リニューアルの開発段階でアーキテクチャが少しずつ変わっており、新旧のアーキテクチャが入り混ざった状態になっています。
また、Sevice層が適切な責務分解ができておらず肥大化していたり、Repositoryでは集約を管理する責務を超えてビジネスロジックが実装されていました。
今回のリファクタの流れとしては、まず基本形を実装する機能を決定し、テストを実装し、ドメインモデルから実装を進めています。
まずリファレンスとなる基本形を作り、Devin, Cursor, PR-Agent等でのVibeCodingで一気にリファクタを完成させてしまうことを計画しています。
Feature Toggle導入によるデプロイフローの改善
カンリー店舗集客のリニューアルプロジェクトでは、開発効率とリリース品質の向上を目指し、Feature Toggle(機能フラグ)の導入を決定しました。
ブランチ戦略ではGitLabフローを一部変更したフローを採用しており、mainブランチへのマージが即座に本番環境へのデプロイとリリースにつながる仕組みになっています。
機能ごとにfeatureブランチを作成する現状のブランチ戦略では下記の問題があります。
- 機能がマージされるまでのリードタイムが長くかかる
- ビッグバンリリースになりやすい
- 機能開発中の変更管理やステージング環境でのQA待ちが発生する
- 問題発生時の切り戻しコストが高い
これらの課題に対応し、生産性を向上するため、以下の方針でFeature Toggleを導入を進めています。
- データベースによる機能のON/OFF管理と、キャッシュ保持による効率的なフラグアクセス
- バックエンドでのON/OFF両状態のテストケース実装と、QAプロセスでの検証
この取り組みにより、デプロイとリリースを分離し、より安全で効率的な開発フローの実現を目指します。また、障害発生時のリスクを低減し、より柔軟な機能提供が可能となることが期待されます。
最終的にはトランクベース開発を取り入れ高速なデリバリーが実現できることを目標にしています。
Devin全社導入に向けた検証PJ
カンリーでは、開発生産性の向上を目指してAIツールの活用を進めています。
エンジニア組織ではすでにCopilot, Cursorの活用は進めており、そこに続いてDevinの導入検証を進めています。
Copilot, Cursorが自身のコーディングパワーのスケールアップだとしたら、自立型のAIエージェントDevinはスケールアウトと考え、それぞれを最適なシーンで利用することで開発生産性の向上が見込めると考えています。
検証は2段階に分けていて、すでにphase2のギルド検証に取り掛かっています。
(チームを横断したメンバー複数人のプロジェクトをカンリーではギルドと呼称しています。)
phase1の検証については下記参考。
phase2のギルド検証では、各チームから選出されたメンバーによるエンジニアチームを跨いだギルドで実施しており、Devinを一人のジュニアエンジニアとして扱いながら、隔週での定期的なギルドMTGを通じて知見を共有していきます。
この取り組みを通じて、効果的なDevinの活用パターンの確立や、チーム全体での知見共有の仕組み作り、将来的な全社展開に向けた基盤づくりを実現し、AIツールを活用した開発生産性向上を実証していきます。
この取り組みについてAWS Loftで行われたCoding Agent at Loft #2 〜 AIコーディング活用事例Night - 効果的な組織導入と実践〜にて登壇して発表しているのでスライドを載せておきます。
現状はエンジニア組織全体でAIコーディングをガチるためにAIナレッジを整える過程にあります。
Devin, Cursorをはじめ、いつどのAIツールに変わっても汎用的に使えるナレッジの構築を目標に進めています。
これについては別途ブログで紹介できればと思います。
さいごに
これまでの取り組みを通じて、カンリー店舗集客の開発組織とプロダクト基盤は、より強固で柔軟なものへと進化を遂げ始めました。
技術から品質を高めていく道のりは中長期的な道のりになりますが、必ず必要なものであると考えています。
また、進化するAIツールの恩恵を最大限受けれるように我々のエンジニアリングもアップデートして、より変化への適応力を高めながら、開発生産性を向上させなければなりません。
さいごになりますが、今後の展望を記事とともに紹介できればと思います。
カンリー店舗集客はリニューアルを経て「より多くの価値を、より早くユーザーに届けるために—開発スピードアップに向けた取り組みを加速させる」必要があります。
下記の記事で紹介されているように、生産性を向上させていくために、組織としてより「当たり前」にAIを活用することで生産性を高めていくことや、アーキテクチャとチームの形を合わせていくということをやる必要があります。
今回カンリー店舗集客のリニューアルを機に新たにAIエージェント構想などのようにプロダクトを横断したデータを活用した取り組みも視野に入れた拡張性の担保やアーキテクチャーの改善なども入ってくるでしょう。
カンリー店舗集客では、技術の進化を常に視野に入れ、新しい手法やツールを積極的に取り入れながら、プロダクトと組織を成長させていきます。
変化を恐れず、常に挑戦し続けることで、店舗経営を支える世界的なインフラを目指していきます!
この道のりに興味ある人、一緒にやっていきたい人を求めています。是非お話ししましょう!

株式会社カンリーは「店舗経営を支える世界的なインフラを創る」をミッションに、店舗アカウントの一括管理・分析SaaS「カンリー店舗集客」の開発・提供、他複数のサービスを提供しております。 技術系以外のnoteはこちらから note.com/canly
Discussion