# Apex file conventions

## Change log header

Every Apex file opens with a change log. Append a line per change; do not rewrite history.

```apex
/**
 * ProblemTriggerHandler
 *
 * Change log
 * ----------
 * 2026-09-22  DS   Initial version — routes Problem__c trigger events.      US-12345
 * 2026-09-30  LK   Skip closed problems in the after-update path.           BUG-2291
 */
public with sharing class ProblemTriggerHandler {
```

Date, name or initials, description of the change, and the user story or bug number. The ticket reference is the part people actually need six months later.

## Trigger skeleton

One trigger per object, no logic, **all 7 events declared** — Trigger Hook depends on the full set being present even where a handler ignores some.

```apex
trigger ProblemTrigger on Problem__c (
    before insert, before update, before delete,
    after insert, after update, after delete, after undelete
) {
    new TriggerTemplateV2().run(new ProblemTriggerHandler());
}
```

## Bypass, per handler

Bypass logic sits in the trigger and is keyed per handler class, so one piece of automation can be switched off without disabling the object.

```apex
trigger ProblemTrigger on Problem__c (
    before insert, before update, before delete,
    after insert, after update, after delete, after undelete
) {
    TriggerTemplateV2 template = new TriggerTemplateV2();
    template.run(new ProblemTriggerHandler());
    template.run(new ProblemNotificationHandler());   // disable this one alone when needed
}
```

## Filtered scope, never raw `Trigger.new`

Do not hand `Trigger.new` to a handler method. Filter to the records that piece of logic actually operates on and pass those — the handler then states its own scope, and bulk behaviour stays predictable.

```apex
public void afterUpdate(List<Problem__c> records, Map<Id, Problem__c> oldMap) {
    List<Problem__c> newlyClosed = new List<Problem__c>();
    for (Problem__c record : records) {
        if (record.Status__c == 'Closed' && oldMap.get(record.Id).Status__c != 'Closed') {
            newlyClosed.add(record);
        }
    }
    if (!newlyClosed.isEmpty()) {
        ProblemClosureService.handleClosure(newlyClosed);
    }
}
```

## Record type Ids

Through `Utils`, held in a `static final` so the describe happens once per transaction.

```apex
private static final Id DIGITAL_SUPPORT_RT = Utils.getRecordTypeId('Case:Digital_Support');
```

Never a hard-coded Id — they differ per org — and never an inline describe call in a loop.

## Async work

Use the org's `Async` class so execution context is handled for you rather than reasoned about at each call site.

```apex
Async.run(new ProblemSyncJob(problemIds));
```

Never call an async method from inside a loop: each iteration consumes one of the transaction's async invocations.

## Credentials

Callout credentials live in `Credential__c` or a Named Credential — never in code, a static resource or a hard-coded string.

Prefer a **Named Credential** whenever the callout can use one; the platform then holds the secret and it never reaches Apex. Where `Credential__c` is used, remember it is ordinary data:

- store the secret in an **encrypted field**;
- restrict **field-level security** to the integration user that needs it;
- keep the records **out of change sets and packages** — they are environment data, seeded per org.

## DML

Use `Database` methods so partial success is a deliberate choice, and inspect what comes back.

```apex
List<Database.SaveResult> results = Database.update(records, false);
for (Database.SaveResult result : results) {
    if (!result.isSuccess()) {
        Logger.error('Problem update failed', result.getErrors());
    }
}
```

## Sharing

Every class states its sharing model explicitly — `with sharing` by default, `without sharing` only with a comment explaining why. A class with no declaration inherits unpredictably depending on its caller.
