命名
※参考: Choose Naming Conventions
Learning Objectives | TRAILHEAD
- クラス名・インターフェース名・列挙名は大文字で始める(インナークラスも含む)。
- 列挙子は大文字で宣言する。
- 定数(
static
かつfinal
で宣言されたフィールド)は大文字で宣言する。 - 変数名、メソッド名、プロパティ名はローワーキャメルケースにする。
スタイル
- クラス内で、フィールド定義はメソッド定義より先に記述する。
- for文、if文、else文、while文、do-while文の中括弧は省略しない。
- 一行で複数の変数を宣言しない。
- インデントのスタイルは統一する。タブとスペースを混ぜない。
パフォーマンス
繰り返し処理
- ループ内でDML操作、クエリを行わない。
- データをリストにまとめて、ループ外で操作を行う。
- ただし、sObject リスト形式のSOQL For ループでは使用可能(後述)。
- 内部でDML操作、クエリを行うロジックの例
- Messaging.renderEmailTemplate
- Messaging.renderStoredEmailTemplate
- SObject.recalculateFormulas;
- Formula.recalculateFormulas;
- ループ内で非同期処理をコールしない。
sObject リスト形式は for ループの を 200 件の sObject のリストごとに 1 回実行します。そのため、多少理解しにくく、使用が難しくなりますが、for ループの本文内で DML ステートメントを使用する必要がある場合に最適です。DML ステートメントは、sObject のリストを一括処理します。(参考: SOQL For ループ | Apex開発者ガイド)
クエリ
- クエリは極力セレクティブにする。
- 特にトリガから呼ばれる可能性がある場合、非セレクティブなクエリでクエリ対象が100万件件超える場合にはエラーが発生する。(セレクティブなクエリについてはこちらに解説記事を作成しました→SOQLの高速化 | Qiita)
- クエリで取得したレコードを繰り返し処理を行うときはSOQL For ループを使用する。リストに詰めるよりヒープサイズを削減することができる。(参考: SOQL For ループ | Apex開発者ガイド)
- 大量の子レコード(200件以上)を取得する可能性がある場合は、直接アクセスせずにforループで子レコードを反復処理する。(参考: SOQL For ループ | Apex開発者ガイド)
子レコードで反復処理を行う
for (Account acc : [SELECT (SELECT Id FROM Contacts) FROM Account WHERE Id IN :accIds]) { // Integer count = acc.Contacts.size(); ← 子レコードが200件以上ある場合は例外が発生 // System.QueryException: Aggregate query has too many rows for direct assignment, use FOR loop External entry point Integer count = 0; for (Contact c : acct.Contacts) { count++; } }
-
COUNT()
およびCOUNT(fieldname)
以外集計関数を使用するときはガバナ制限(Too many query rows)に注意。集計に含まれるレコード数もカウントされる。-
COUNT()
とCOUNT(fieldname)
はSummer'18で1行としてカウントされるようになった(リリースノート)。なお、COUNT_DISTINCT(fieldname)
は他の集計関数と同様、対象件数すべてがカウントされる。
-
VFコントローラ
- 状態の維持に不可欠ではなく、ページの更新時にも不要な変数は
transient
キーワードを使用する。 - 使用しない項目はクエリしない。
Lightningコンポーネントコントローラ
- JSONを返さない。オブジェクトを直接返す。JSでJSON.parseを使用しなくても自動でオブジェクトに変換される。
- キャッシュが使用できる場面では
@AuraEnabled(cacheable=true)
を使用する。
(補足)Apexのパフォーマンスの検証方法
Apexのパフォーマンスに問題がある場合、Apex Log Analyzerを使用して問題箇所を特定することが可能です。
セキュリティ
ハードコーディング
- 秘密鍵、初期化ベクトルをハードコードしない。
- 資格情報をハードコードしない。指定ログイン情報を使用する。
- RESTコールアウトでhttpを使用しない。httpsを使用する(または指定ログイン情報を使用する)。
CRUD権限
Apexではシステムモードで動作するため、現在のユーザのオブジェクトレベル権限と項目レベルセキュリティは考慮されない。
- SOQL/SOSL/DML操作の前にアクセス許可をチェックを行う(参考: オブジェクト権限と項目権限の適用 | Apex開発者ガイド)。
攻撃対策
- リクエストパラメータなど外部から取得したURLにリダイレクトさせない。
- 動的SOQLの使用は極力避ける。
- 動的SOQLの使用が避けられない場合は、
escapeSingleQuotes
メソッドを使用してSOQLインジェクションを防ぐ(参考: SOQL インジェクション | Apex開発者ガイド)。
- 動的SOQLの使用が避けられない場合は、
- エスケープされない
addError
メソッド(第2引数がfalse)を使用しない。
Error Prone コード
不完全であったり、ランタイムエラーとなりやすい構造を避ける。
- いかなるSFIDもハードコーディングしない。
- 空のcatch文を使用しない。
- 空のtry文、finally文、if文、else文等使用しない。
- 処理を行わないメソッドを使用しない。
- コンストラクタでないメソッドで、クラス名と同じ名前を使用しない。
- Trigger.new、Trigger.oldの配列に直接アクセスしない。繰り返し処理でアクセスする。
-
=+
を記述しない(たいてい代入演算子+=
の誤り)。 - SOQLで複数選択リストをフィルタするとき、
IN
を使用しない(たいていINCLUDES
の誤り)(参考)。 - SObjectのSetを使用しない。Mapを使用する(参考)。
- updateしていないレコードに対してrecalculateFormulasメソッドを使用しない(クロスオブジェクト数式項目を除く)。
- クエリする時、SalesforceIDでソートしない。SalesforceIDは昇順で採番されるという担保がない。
複雑さ
※参考: Apex Rules | PMD Source Code Analyzer Project
- if文の深いネスト(3つ以上)は使用しない。
- メソッドの認知的複雑度(Cognitive Complexity)が15を下回るようにする。
- クラスの認知的複雑度(Cognitive Complexity)が50を下回るようにする。
- 認知的複雑度とはコードの理解しにくさを定量的に表したもの。こちらの記事が詳しいです→Cognitive Complexity、コードの読みやすさを定量的に計測しよう | Qiita
- Salesforce CLI Scannerで検出できる。
- メソッドの循環的複雑度(Cyclomatic Complexity)が10を下回るようにする。
- クラスの循環的複雑度()が40を下回るようにする。
- 循環的複雑度もコードの理解しにくさを定量的に表したもの。こちらの記事が詳しいです→循環的複雑度について | Qiita
- Salesforce CLI Scannerで検出できる。
- クラスの行数は1000行未満にする。
- メソッドの仮引数の個数は3つまでにする。
- publicのメンバは20個を下回るようにする。
- フィールドは15個以内にする。
- コンストラクタは20ステップを下回るようにする。
- メソッドはで40ステップを下回るようにする。
- クラスはで500ステップを下回るようにする
ベストプラクティス
一般
-
global
は極力避ける。管理パッケージではシグニチャを変更することができなくなるため。Schedulableなどもpublicで宣言できる。-
webservice
キーワードが定義されているメソッドを含むクラスはglobal
にする必要がある。 -
@RestResource
アノテーションのついたクラス、Apex RESTアノテーション(@HttpGet
など)のついたメソッドはglobal
にする必要がある。
-
- 他のクラスから参照することを想定していないメンバは
private
にする。 -
debug
メソッドにはログレベルを設定する。 - 使用しない変数を宣言・代入しない。
- コレクションを返却するメソッドでnullを返却しない。空のコレクションを返すようにする。
トリガ
- オブジェクトごとにトリガは1つまで。
- ファイル名はオブジェクト名を含む一貫した命名規則を使用する(例: AccountTrigger)。
- トリガに直接ロジックを書かない。トリガハンドラを使用する(sfdc-trigger-framework
)。 - SOQLはセレクティブにする。Apexトリガでクエリ対象が200,000件を超えるオブジェクトに対し、セレクティブではないクエリを発行するとエラーになる。
- バルク処理を前提とする。一度に複数レコードが適切に処理されるようにする。
- 非同期処理でDML処理が実行されるオブジェクトのトリガに
@Future
メソッドは使用しない。- トリガで同期処理でコールアウトはできないことに注意(ヘルプ記事)。
- 主従関係のカスケード削除では削除トリガは実行されない。
VFコントローラクラス
- サイトで使用しているVisualforceのアクションメソッドは必ず例外をキャッチして処理を行う。
- サイトのVisualforceで例外が発生すると認証エラーページに遷移してしまう。
- アクションメソッド内でDML操作している場合はロールバックを行う
バッチクラス
-
Database.Stateful
は処理上必要なときのみ使用する。-
Database.stateful
を使用すると、バッチのトランザクション間で更新したインスタンスメンバ変数の値が引き継がれるが、各プロセスが直列で実施されるため、パフォーマンスが低下する。
-
- startメソッドでgetQueryLocatorを使用する場合は、サブクエリを使用しない。
- サブクエリを使用しているとgetQueryLocatorの処理の負荷が高くなり、
Apex CPU time limit exceed
のガバナ制限に抵触しやすくなる。 - サブクエリを使用していると
INVALID_QUERY_LOCATOR
が発生ことがある(参考)。 - 代替として、executeメソッド内で子レコードを取得する。
- サブクエリを使用しているとgetQueryLocatorの処理の負荷が高くなり、
- バッチのエラー・例外はプラットフォームイベントで処理する。
- try-catchできないガバナ制限エラーも処理することができる。
- バッチApexに
Database.RaisesPlatformEvents
インターフェースを実装し、BatchApexErrorEventトリガを作成する。 - Apex 一括処理からのプラットフォームイベントの起動 | Apex 開発者ガイド
テストクラス
- assertionメソッドにはメッセージを含める。
- テストメソッドには
assertion
メソッドを含める。 - テストメソッドには
@IsTest
アノテーションを付ける(testMethod
キーワードは非推奨となっている)。 -
seeAllData=true
を使用しない。 - テストデータ作成にはテストデータファクトリを使用する(参考)。
ドキュメンテーションコメント(ApexDoc)
※参考: ApexDoc | PMD Source Code Analyzer Project
- ApexDocコメントを、オーバーライドとテストクラスを除く、public、globalのクラス、メソッド、プロパティに記述する。
- ApexDocコメントは
@description
を含める。 - 戻り値を返すメソッドのApexDocコメントは
@return
を記述する。 - 戻り値を返さないメソッド、コンストラクタに
@return
を含めない。 - 引数をもつメソッドのApexDocには、シグニチャと同じ順で各パラメータの
@param
を記述する。
例
/**
* @description Hello World
*/
public class HelloWorld {
/**
* @description Bar
* @return Bar
*/
public Object bar() { return null; }
}
その他留意事項
- 非同期処理のコンテキストで
@Future
メソッドを呼び出すことはできない。バッチでDML操作を行うとき、トリガで@Future
メソッドが呼ばれないようにする。 -
@AuraEnabled
アノテーションを使用し、Auraコンポーネント、LWCからコールする場合、Apexクラスの参照権限が必要。 - トリガが実行されない処理がある。カスケード削除でレコードが削除される時、削除の起因となったレコード以外では削除トリガが実行されない。Apexトリガだけでなく、トリガフローも同様。
参考
命名
Salesforce CLI Scanner (静的解析ツール)
- Salesforce CLI Scanner Plug-In
- Improve Code Quality with Salesforce CLI | YouTube
- Improve Your Code Quality with the Salesforce CLI Scanner | Salesforce Developers Blog
パフォーマンス
セキュリティ
- ApexおよびVisualforce開発のセキュリティガイドライン | SALESFORCEヘルプ
- コールアウトエンドポイントとしての指定ログイン情報 | Apex開発者ガイド
- オブジェクト権限と項目権限の適用 | Apex開発者ガイド
- SOQL インジェクション | Apex開発者ガイド
- セキュアな Web アプリケーションの開発 | TRAILHEAD
テストクラス
- Apexテストクラス チートシート - Qiita
- ベストプラクティスのテスト | Apex開発者ガイド
- Apex Test Class Best Practices | ApexHours
- Apex Best Practices: The 15 Apex Commandments | Salesforce Developers Blog
ベストプラクティス
- Apex Design Best Practices | salesforce
- SOQL For ループ | Apex開発者ガイド
- Apex Rules | PMD Source Code Analyzer Project
- Database.Statefulインタフェースとは | エスパーラボ
- トリガハンドラフレームワーク kevinohara80/sfdc-trigger-framework
- 20 Tips for Salesforce Developers | ApexHours
- Apex Code Best Practices | ApexHours
- 12 Salesforce Apex Best Practices | SalesforceBen
参考になるソースコード
- Salesforce公式のサンプルアプリケーション:SAMPLE GALLERY | salesforce
- 特にApex Recipesは参考になる。
- Salesforce LabsのAppExchangeのソース:SFDC Assets | GitHub
- AppExchangeはこちら。