はじめに
こんにちは、ユーザベース Sales System Engineering Teamの竹本(あだ名:たけたけ)です!
前回の記事 では、僕たちユーザベースのSalesforce構成をご紹介しました。
その中で、スクラッチOrgを用いたソース駆動開発へ転換を目指す…と締めくくりましたが、ようやく形になってきたので今回はそのストーリーをお伝えできればと考えています。
このブログを通して、よりよいSalesforce 開発体験を作る議論のきっかけになると嬉しい限りです。
なぜ組織駆動開発 > ソース駆動開発に移行したかったのか
① 変更セットでのデリバリー運用の限界を迎えたため
② DevEx(開発者体験)を推進するため
の大きく2つの理由があります!
① について
もともと僕たちは開発者全員が1つのSandboxで開発を行っていました。
開発チームの人数も増えてきて、
同じ資材を触っていることに気づかず仕掛中の内容を本番にデリバリーしてしまったり
デグレが発生してしまったり
デリバリーに伴うミスが多発するようになりました。
また、それを防ぐためにコミュニケーションが肥大化し、開発者が本来のアプリケーションの開発に集中できる時間が少なくなっていました。
② について
Dev Sandbox > UAT Sandbox > Production という3つのOrgを開発環境としています。
Dev Sandboxで開発し、UAT Sandboxでユーザー確認を行い、その後Productionにデリバリーする流れです。
1つの開発プロセスにおいて、2回変更セットを用いた手動リリースが必要になるプロセスになっています。
そのため、リリース作業やリリース後確認作業に時間が取られ、開発時間が減るのもそうですが、開発者体験がそもそも良くないなぁと感じていました。
加えて、アジャイル開発で小さくデリバリーを行う&振り返りを行う開発の進め方と噛み合わなくなってきたのも大きかったです。
(リリース作業が面倒だからなるべくまとめてリリースしたい思考になり、なんちゃってアジャイル化になりつつありました…笑)
開発チームの状態を計測&評価できるようにしたかったのですが、報告のための作業が増えるのは嫌で手が出せずにいました。
ソース駆動でGitHubをベースにした開発プロセスに転換できると、GitHubから様々なデータを取れるようになり計測ができるようになる点も大きなきっかけでした。
また、GitHub CopilotやClaude CodeなどAIを開発プロセスに組み込む土台を作れず、昨今のAI開発の流れに乗れないままの現状を打破したい思いもありました。
業務系SaaSの中でも、特にSalesforceは、設定情報を含むメタデータをリトリーブできる土台があるので、AI時代の開発に乗せやすいなと感じています。
自動レビューや変更影響の推定、標準化みたいなところまで効いてくる。
だからこそ、組織駆動からソース駆動へ寄せたいと思いました。
フェーズ1.「よし、やるか!」環境構築は順調だった
事前準備や環境構築は順調でした!ざっくりやったことは下記のとおりです。
① スクラッチOrgを作成できるようにDevHubを有効化する
Salesforce画面の設定でポチポチするだけで完了!不可逆的な設定なので念のため注意が必要です。
② スクラッチOrgで有効化する機能を決める(config/project-scratch-def.jsonを作る)
設定の簡略化のため僕たちは組織シェイプ機能を利用しました。
features やsettingsを1つ1つ定義しなくて良いので楽だなと感じました!
ただし、組織シェイプを利用しても一部機能は明記する必要があります。
③ マニフェスト戦略を作る(manifest/package.xmlを作る)
管理するメタデータの範囲を決める感じですね!
僕たちは自分たちでカスタマイズしているところは丸っと管理する方針としました。
念のため、メタデータを棚卸しして「これは要る」「これは要らない」の仕分けを行いました。
その後はマニフェストファイルを作っていくのですが、メタデータの記載の仕方が分からず公式リファレンスとめちゃくちゃ格闘しましたね!
④ ブランチ戦略を作る
トランクベース、Git Flow、GitHub Flowなどいくつかの開発フローを比較検討し、結果僕たちはGitHub Flowを採用しています。
①〜④のあと、実際にsfコマンドでスクラッチOrgを作成すると無事に作成され、本番と同等の機能がアクティベートされた状態になりました。
フェーズ2.「あれ…?」デプロイしたら大量エラー
環境構築が整い、いよいよProductionからretrieveしたメタデータをスクラッチOrgにデプロイするフェーズです。
「本番からそのまま取ってきたんだから、そのままデプロイできるでしょ!」
…そんな甘い考えは、すぐに打ち砕かれました(笑)
「retrieveできる ≠ デプロイできる」という現実。。。
今思えば当たり前なのですが、Productionには、様々な追加機能・管理パッケージ・ライセンスが有効になっています。
retrieveは、その現状をそのままファイルに落とすだけなので、機能が有効な前提で書かれたメタデータが大量に含まれる状態です。
一方、スクラッチOrgは素の状態から始まるため、本番固有の機能・パッケージが入っていません。
そこにそのままデプロイしようとすると…エラーの嵐です(笑)
(実際に出たエラー画面はこんな感じで、、196ものエラーが返ってきました)

ただ、数は多かったのですが、エラーを読み解いていくと対処方法は意外とシンプルで、以下の2パターンに集約されました!
① 管理パッケージを先にインストールする
DocuSignやMarketo連携などAppExchangeパッケージのコンポーネントは、パッケージ自体がインストールされていないとデプロイできないため、先にパッケージをインストールすることで解消できます。
② メタデータから該当ブロックを削除する
プロファイルや権限セットには、スクラッチOrgで使えない権限やオブジェクトへの参照が含まれていることがありました。
僕たちの場合は、ActiveScratchOrg やSalesforceInvoiceなどです。
「開発する上でこの権限は本当に要るの?」など検討し、ほとんどが不要だったので、該当の <userPermissions> や <objectPermissions> ブロックをファイルから丸ごと削除しました。
実際は、これらをひとつひとつ確認しながら削除していくのは、正直かなり地道な作業でした…(笑)
フェーズ3.「これは沼だ」終わらない壁たち
上で紹介した対処パターンで大半のエラーは解消できたのですが、まだまだ沼は続きます、、、。
その中から比較的イメージがしやすい2つをご紹介します。
① 標準選択リスト値のAPI名問題
StandardValueSet をデプロイしようとしたところ、こんなエラーが出ました。
Duplicate label: 完了 (CampaignStatus)
原因は、ProductionとスクラッチOrgのAPI名が異なるため同名ラベルの値が2つ存在する状態になり、ラベル重複エラーとなっていました。
僕たちのProductionでは、選択リスト値がラベル: 完了 対応中 、API名:完了 対応中のようにどちらも日本語になっています。
一方、スクラッチOrgでは、ラベル:完了 対応中 、API名:Completed In Progress となっています。
※スクラッチOrgの言語設定を日本語に明示的に指定してもAPI名は英語のまま変わらずでした。
僕たちは解決策として、ラベル:Completed In Progress 、API名:Completed In Progressのメタデータをデプロイし、その後に自分たちのメタデータをデプロイするという2段階の手順で回避しました。
② 機能の「有効化順序」問題
Opportunity レイアウトをデプロイしたところ、次のエラーが表示されました。
Invalid field:CAMPAIGN.NAME in related list:RelatedCampaignInfluence2List
「本番からretrieveしたメタデータなのになぜ?」と調べると、原因は、スクラッチOrgのユーザーにキャンペーンインフルエンス機能に対しての権限が付いていなかったためでした。
解決策はシンプルで、デプロイ前にスクラッチOrgのユーザーに必要な権限を付与することで解決です!
今回の例だと、権限セットライセンスSales User・権限セット Sales User を付与することでキャンペーンインフルエンス機能を使えるようになり、デプロイも通るようになりました。
当たり前ですが、スクラッチOrgは毎回新しいOrgを立ち上げることになるので「Org作成 → 権限付与 → デプロイ」の順で対応することが必要です。
他にもざっくり下記のような問題があり、それぞれどう対応するかトライ&エラーを繰り返し解決していきました。
Tableauコンポーネントのプロパティが知らないうちに廃止されていた
Field Serviceが無効でWorkOrderのレイアウトが丸ごとデプロイ不可に
プロファイルのIP制限設定が多すぎて上限を突破していた
Pardotが追加する標準ボタンが「そんなボタン知りません」と怒られる
Data Cloud関連はそもそもスクラッチOrgで事前にData Cloudをアクティベートしていないとダメ
移行してみての感想
ここまで紹介してきたように、組織駆動からソース駆動への移行は決して簡単な道のりではありませんでした。
正直なところ「これ、本当に終わるのか…?」と思った瞬間もありました(笑)
ただ、やり遂げた今振り返ると、この移行を通じて得たものは本当に大きかったと感じています。
① 変更セット運用の限界 → PRレビューで品質が仕組みで守られるように
変更が全てPull Requestとして可視化されるようになり、チーム内でレビューが健全にまわりはじめました。
▼PRレビューの様子① オープンな場でのコードレビュー

▼PRレビューの様子② GitHub Copilotによる自動レビュー

「この変更、なぜ必要なんだっけ?」「ここ影響範囲大丈夫?」といった会話が自然に生まれるようになったのはもちろん、PRで1人以上の承認を得ないとmainブランチにマージできない仕組みにしたことで、デグレや意図しない変更の混入を人の注意力ではなく仕組みで防止できるようになりました。
以前は「気をつけよう」「声をかけよう」で防いでいたものが、プロセスとして組み込まれた安心感は大きいです。
② DevExの推進 → リリース作業の手間削減とデータに基づく改善が可能に
リリース作業がコマンドベースになり、手動で変更セットを組み立てる手間が大幅に削減されました。
「リリースが面倒だからまとめてやりたい」というなんちゃってアジャイル化も解消され、小さく・頻繁にデリバリーするサイクルが自然と回るようになりました。
もうひとつの変化は、開発の状況がデータとして見えるようになったことです。ソース駆動にしたことで特別な作業をしなくても様々なデータが蓄積されていきます。
日々の振り返りで「なんとなく忙しい」「次は頑張る」ではなく、データに基づいてチームの状態を把握&評価できるようになったのは、チームにとって大きな一歩だと感じています。
▼開発チーム健康診断ダッシュボード(DORAレポート FourKeysメトリクスを使ってパフォーマンスの見える化をはじめてみました)

おわりに
いかがでしたでしょうか?
今回は、Salesforceの組織駆動開発からソース駆動開発への移行について、実体験をもとにご紹介しました。
今回のソース駆動への移行はゴールではなくスタートラインだと思っています。
引き続き、CI/CDパイプラインの構築やAIを活用した開発プロセスの改善にどんどんチャレンジしていきます!
同じようにSalesforce開発をソース駆動化されようとしている方々の参考に少しでもなれば嬉しいです。