Salesforceの開発加速とSandbox運用の話


こんにちは。LIFULLの@ohbashです。
この記事はLIFULL Advent Calender2017 その2の19日目の記事になります。

LIFULL内ではまだ少数なSalesforceエンジニアです。
今回は、そんなSalesforceのプロジェクトについて書きます。

概要

あまり大勢での開発が得意でないSalesforceですが、今回、開発者7人で5ヵ月ほどの中規模開発を経験したので、そこで試した開発フローについてまとめます。
プロジェクト概要は、組織統合といって、今まで別々のインスタンスで動いていたSalesforceの組織を一つにマージしようというもの。

今回試したことは、開発者は自分のSandboxを持ち(通称:ひとり1サンド)、開発はそれぞれの環境で行い、Gitでリソースのマージ、Antでテスト用のSandbox(統合環境)と同期をとるというフローです。
要は、ユニット環境とテスト環境を用意するという、Web開発では一般的な状態をSalesforceでも実践したというお話です。

※今回の記事には一部、プロジェクトでは未検証のもの(プロジェクトを経て、次はこうしようと思ったもの)を含みます。

今までの開発方法

今までは、開発チームで一つのSandboxを使っていました。
ファイルの更新は基本的に上書き方式ですので、修正ファイルが被りデグレを起こさないように、チャットで修正対象を共有していました。(声掛け大事!)

変更履歴を残す目的で、Gitでリソース管理をしていました。
SandboxからGitリポジトリへのリソースの取り込みはデプロイツール(Ant)を使用しています。
デプロイツールは開発したApexクラスから、プロファイルやロールといったメタデータまでをSandboxから取得できるツールです。
Gitでのリソース管理対象は所謂開発リソースのApex、Visualforce、Trigger、Componentです。
package.xml(Antでのデプロイ対象を管理するファイル)はざっとこんな感じ。

package.xml
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>*</members>
        <name>ApexClass</name>
    </types>
    <types>
        <members>*</members>
        <name>ApexTrigger</name>
    </types>
    <types>
        <members>*</members>
        <name>ApexPage</name>
    </types>
    <types>
        <members>*</members>
        <name>ApexComponent</name>
    </types>
    <version>41.0</version>
</Package>

今までの開発では、修正範囲が局所的なことが多かったため運用できていましたが、今回のプロジェクトでは修正ファイルの量がとにかく多かったこと、汎用クラスのバグで他の開発が停滞することを防ぐために、開発者毎にSandboxを用意することを選択しました。

大きな問題としては、Sandbox間の同期についてです。
Salesforceでは殆どの処理にProfile毎の権限が絡んでおり、権限周りのメタデータをSandbox間で管理するのは大変です。
その辺をAntの活用と運用フローでなんとかしました。

開発方法

さっそく開発方法ですが、GitやAntを使い慣れていない開発メンバーや、コマンドがややこしかったり(変更すれば良かったのかもしれません)したので、いくつかのルールだけ徹底するようにしました。

開発ルール

  • 開発は個人のSandboxでのみおこなう
  • リソースのマージはGit操作上でのみおこなう
  • Antのアクセスは自分のSandboxとBranch間でのみ
  • Antでpullとpushの順番を間違えると開発した内容がきれいに消え去るので十分に注意すること

開発フロー

  1. 開発者は個人のSandboxで開発と動作確認を実施
  2. 動作確認完了後、個人のGitブランチにant pullでリソースを取得
  3. 差分が結構でるので、自分の修正ファイルのみ選定してgit commit
  4. 統合環境に対応するGitブランチとマージし他の開発者の修正分を取り込み
  5. 自分のsandboxに向けてant pushしリソースの同期・動作確認
  6. developブランチにPullRequestを出して、自分の修正分の反映依頼

管理対象リソース

package.xml
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>*</members>
        <name>ApexClass</name>
    </types>
    <types>
        <members>*</members>
        <name>ApexTrigger</name>
    </types>
    <types>
        <members>*</members>
        <name>ApexPage</name>
    </types>
    <types>
        <members>*</members>
        <name>ApexComponent</name>
    </types>
        <members>*</members>
        <name>AuraDefinitionBundle</name>
    </types>
    <types>
        <members>*</members>
        <name>FlexiPage</name>
    </types>
    <types>
        <members>*</members>
        <name>CustomObject</name>
    </types>
    <types>
        <members>*</members>
        <name>Layout</name>
    </types>
    <types>
        <members>*</members>
        <name>Profile</name>
    </types>
    <version>41.0</version>
</Package>

開発リソースであるApexやVF、Lightningコンポーネントに加え、更新頻度の高いオブジェクト、レイアウト、プロファイルを管理対象にしています。
プロジェクトの時は、権限セットやロール、承認プロセスなどその他多数のメタデータも管理対象としていましたが、通常の開発では更新頻度の高いものだけに絞ったほうが良いと思っています。
また、membersに関しても、上のpackage.xmlでは「*」としていますが、実際は必要なものだけに絞っています。

membersについては、レポート・ダッシュボードのようなフォルダ名を明記しないと取得してくれないリソースもあり、注意が必要です。
オブジェクトの取得では、<members>*</members>でカスタムオブジェクトは全て取得してくれますが、Account等の標準オブジェクトに関しては明記(<members>Account</members>)しないと取得してくれません。

新規リソースの追加

新規ページや新規クラス、オブジェクトの追加についてです。
新しくリソースを作成する場合、同時にプロファイルのアクセス権限等を設定する必要があります。
プロジェクトでは、新規追加系の対応と権限設定は管理者がまとめて統合環境で実施しましたが、管理者の負荷が高かったりとあまりイケてないので、開発者の管轄内で新規追加ができ、他Sandboxに同期できるフローを考えました。
※運用がちゃんと回るかは未検証です。

プロファイルやオブジェクトのメタデータは開発リソースとは違い、差分更新でデプロイされるため、開発者は新規作成したリソースの権限のみを記述してcommitするようにします。

事前準備

  • gitの管理リソースにprofile_deployフォルダを作成
  • 中身が空のprofileファイルを作成しておく(コメントで記述テンプレート)

※Antでのデプロイでは特定の名前のディレクトリ以外は無視されるので、リソース管理用に勝手にディレクトリを増やすことができます。

例)profile_deploy/Admin.profile
<?xml version="1.0" encoding="UTF-8"?>
<Profile xmlns="http://soap.sforce.com/2006/04/metadata">
    <!-- オブジェクトの新規追加
    <objectPermissions>
        <allowCreate>true</allowCreate>
        <allowDelete>true</allowDelete>
        <allowEdit>true</allowEdit>
        <allowRead>true</allowRead>
        <modifyAllRecords>true</modifyAllRecords>
        <object>Account</object>
        <viewAllRecords>true</viewAllRecords>
    </objectPermissions>
  -->
    <!-- 項目の新規追加
    <fieldPermissions>
        <editable>true</editable>
        <field>Account.field1__c</field>
        <readable>true</readable>
    </fieldPermissions>
  -->
    <!-- レコードタイプの新規追加
    <recordTypeVisibilities>
        <default>false</default>
        <recordType>Account.RecordType1</recordType>
        <visible>true</visible>
    </recordTypeVisibilities>
    -->
    <!-- Apexの新規追加
    <classAccesses>
        <apexClass>ApexClassName</apexClass>
        <enabled>true</enabled>
    </classAccesses>
    -->
    <!-- VFの新規追加
    <pageAccesses>
        <apexPage>VisualForceName</apexPage>
        <enabled>true</enabled>
    </pageAccesses>
    -->
</Profile>

開発者

  • 新規追加した場合、profile_deployディレクトリ内のprofileデータに必要な分だけアクセス権を記述する
  • 他のリソース(ObjectやApex)と共にPullRequestでマージ依頼

ちょっと手間ですが、開発者の裁量で誰がどこを操作できるかを明確にすることと、同時に権限レビューも実施するといいと思います。

管理者

  • PullRequestのマージ後、profileディレクトリをリネームする
    profile → profile_bak、profile_deploy → profile
  • ant pushする
  • ant pushが成功したらant pullする(先程のdeployでprofile_deployの内容がant pullされたprofileに含まれていることを確認する)
  • profile_deployの内容を空にしてcommitする

Antで取得するリソースの種類を増やすとデプロイエラーのリスクが増えるため、あまり更新しないリソースについては、Gitでの管理対象に含めず、変更セットでの同期を考えています。
弊社でいうと、コミュニティサイトのメタデータやワークフローについては修正機会が多くないので変更セットで各環境に同期します。
更新頻度が多くなってきたら、Gitの管理対象に含めることを検討します。

これで一通りのフローになります。
開発を進めていく上でイレギュラーも起きますので、統合環境を修正し、各Sandboxは帳尻を合わせるような形で進めていきました。

つらみ

運用を開始してみたところ、とにかくAntでのデプロイエラーが多かったです。
管理リソースを選定していったことで、ある程度落ち着いて運用ができる状態までなったと思います。
下記3点には、特に苦戦しました。

  • 標準オブジェクトと標準プロファイル
    それぞれのSandboxでデフォルトで作られる標準系のリソースは整合性が取れずデプロイエラーの原因になることが多いです。
  • アップデート
    バージョン切り替え期間は、Sandboxのリフレッシュタイミングやインスタンスの違いによってバージョンずれが発生し、APIバージョンの違いによりデプロイエラーになることがあります。
  • 新機能 上のアップデートとも関連しますが、新機能はまだメタデータAPIに対応しておらず取得できなかったりが発生します。各Sandboxで手動で設定する等で対応しました。

結論

Salesforceの開発において、ひとり1サンドを実施することで開発者が独立して動きやすくなり、無駄なコミュニケーションコストを支払わずに済むようになりました。
副次的に自分のSandboxで「ちょっとお試し開発してみる」みたいなことも容易になったと思います。

今回は触れませんでしたが、レビュー時の動作環境として、使い捨てできる実行環境があると便利だと感じました。
Salesforce DX(Developer Experience)でGitとの親和性が上がったり、Scratch Orgとして、まさに使い捨ての環境ができるそうなので、今後その辺も取り入れていきたいと思います。

また、いまのところマージ処理において、まだまだ開発者・管理者の手間が多いので、人の判断が必要なところと、そうでないところを切り分けて、自動化を進めていくと幸せになりそうです。