apex-schema-describe

Apex Schema describe API patterns — `Schema.getGlobalDescribe()`, `SObjectType.getDescribe()`, `DescribeFieldResult`, `getPicklistValues()`, and the per-namespace describe cost. Covers the lazy-vs-eager describe pattern (cache the SObjectType reference, not the full describe), the `SObjectField.getDescribe()` overhead at scale, and the FLS / record-type metadata access patterns. NOT for SOQL injection prevention via Schema (use apex/dynamic-soql), NOT for the Tooling API metadata layer (use apex/tooling-api).

Best use case

apex-schema-describe is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Apex Schema describe API patterns — `Schema.getGlobalDescribe()`, `SObjectType.getDescribe()`, `DescribeFieldResult`, `getPicklistValues()`, and the per-namespace describe cost. Covers the lazy-vs-eager describe pattern (cache the SObjectType reference, not the full describe), the `SObjectField.getDescribe()` overhead at scale, and the FLS / record-type metadata access patterns. NOT for SOQL injection prevention via Schema (use apex/dynamic-soql), NOT for the Tooling API metadata layer (use apex/tooling-api).

Teams using apex-schema-describe should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/apex-schema-describe/SKILL.md --create-dirs "https://raw.githubusercontent.com/PranavNagrecha/AwesomeSalesforceSkills/main/skills/apex/apex-schema-describe/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/apex-schema-describe/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How apex-schema-describe Compares

Feature / Agentapex-schema-describeStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Apex Schema describe API patterns — `Schema.getGlobalDescribe()`, `SObjectType.getDescribe()`, `DescribeFieldResult`, `getPicklistValues()`, and the per-namespace describe cost. Covers the lazy-vs-eager describe pattern (cache the SObjectType reference, not the full describe), the `SObjectField.getDescribe()` overhead at scale, and the FLS / record-type metadata access patterns. NOT for SOQL injection prevention via Schema (use apex/dynamic-soql), NOT for the Tooling API metadata layer (use apex/tooling-api).

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# Apex Schema Describe

Schema describe is one of the most-used and most-misused APIs in
Apex. `Schema.getGlobalDescribe()` looks free — it isn't. Calling
`SObjectField.getDescribe()` per record in a 200-record trigger
multiplies a small cost into a CPU-budget problem. FLS checks
against `DescribeFieldResult.isAccessible()` look correct — but
the API surface has subtle differences between
`Schema.SObjectType.newSObject()` describe and
`Schema.DescribeFieldResult` describe.

This skill covers the patterns that make Schema describe fast
and correct: caching, FLS checks, picklist enumeration, and the
loop-cost trap.

NOT for SOQL string-binding (`apex/dynamic-soql`); NOT for
Tooling API metadata work (`apex/tooling-api`).

---

## Before Starting

- **Identify the call site.** One-shot at request start (cheap,
  any pattern works) vs in a loop (must cache).
- **Identify what you need.** SObjectType reference,
  DescribeSObjectResult, DescribeFieldResult, picklist values —
  each has a different API call.
- **Identify whether the input is static or dynamic.** Compile-time
  known type → use `MyObj__c.SObjectType` directly (no describe
  call). Runtime string → `Schema.getGlobalDescribe().get(name)`.

---

## Core Concepts

### Static-vs-runtime SObjectType lookup

```apex
// Compile-time: zero describe cost
Schema.SObjectType t = Account.SObjectType;

// Runtime (string): one global-describe lookup
Schema.SObjectType t = Schema.getGlobalDescribe().get('Account');
```

`Schema.getGlobalDescribe()` is the expensive call — it materializes
the entire org's SObject inventory. Cache the result if you call
it more than once per transaction.

### Caching describe results

```apex
public class SchemaCache {
    private static final Map<String, Schema.SObjectType> TYPES =
        Schema.getGlobalDescribe();
    private static final Map<String, Map<String, Schema.SObjectField>> FIELDS_BY_TYPE =
        new Map<String, Map<String, Schema.SObjectField>>();

    public static Schema.SObjectField field(String objName, String fieldName) {
        Map<String, Schema.SObjectField> fields = FIELDS_BY_TYPE.get(objName);
        if (fields == null) {
            Schema.SObjectType t = TYPES.get(objName);
            if (t == null) return null;
            fields = t.getDescribe().fields.getMap();
            FIELDS_BY_TYPE.put(objName, fields);
        }
        return fields.get(fieldName);
    }
}
```

`static final` ensures the global describe runs once per class
load. Per-type field maps are lazy.

### FLS check via DescribeFieldResult

```apex
Schema.DescribeFieldResult dfr = Account.SSN__c.getDescribe();
if (!dfr.isAccessible()) {
    throw new SecurityException('No FLS read');
}
if (!dfr.isUpdateable()) {
    throw new SecurityException('No FLS update');
}
```

Modern alternative — `Security.stripInaccessible` (preferred for
DML) handles bulk records:

```apex
SObjectAccessDecision dec = Security.stripInaccessible(
    AccessType.UPDATABLE, records
);
update dec.getRecords();
```

### Picklist value enumeration

```apex
List<Schema.PicklistEntry> entries =
    Account.Industry.getDescribe().getPicklistValues();

List<String> activeValues = new List<String>();
for (Schema.PicklistEntry pe : entries) {
    if (pe.isActive()) {
        activeValues.add(pe.getValue());  // API name; use getLabel() for display
    }
}
```

Inactive entries are still in the list — filter explicitly.

---

## Common Patterns

### Pattern A — Hot-path describe with class-level cache

```apex
public class FieldValidator {
    private static final Map<String, Schema.SObjectField> FIELDS =
        Account.SObjectType.getDescribe().fields.getMap();

    public static Boolean isAccessible(String fieldName) {
        Schema.SObjectField f = FIELDS.get(fieldName);
        return f != null && f.getDescribe().isAccessible();
    }
}
```

`static final` + per-type field map = describe runs once per
class load.

### Pattern B — Bulk FLS via stripInaccessible

```apex
public static List<Account> readAccessible(List<Account> records) {
    SObjectAccessDecision dec = Security.stripInaccessible(
        AccessType.READABLE, records
    );
    return (List<Account>) dec.getRecords();
}
```

Removes inaccessible field values from the records — bulk-safe,
governor-cheap.

### Pattern C — Dynamic SObject creation by name

```apex
public static SObject create(String objName) {
    Schema.SObjectType t = Schema.getGlobalDescribe().get(objName);
    if (t == null) throw new IllegalArgumentException(objName);
    return t.newSObject();
}
```

Use `Schema.getGlobalDescribe().get(name)` — NOT `Type.forName(name)`.
Type.forName returns `System.Type`, not an SObject prototype.

---

## Decision Guidance

| Situation | Approach | Reason |
|---|---|---|
| Compile-time-known SObject type | `Account.SObjectType` directly | Zero describe cost |
| Runtime SObject name as string | `Schema.getGlobalDescribe().get(name)` | The only path for string-driven |
| Repeated describe in a loop | Cache as `static final` | Avoid per-iteration describe overhead |
| FLS check before bulk DML | `Security.stripInaccessible` | Bulk-safe; cleaner than per-field |
| FLS check on a single field | `getDescribe().isAccessible()` | Single-field is fine direct |
| List of active picklist values | `getPicklistValues()` + `isActive()` filter | Inactive entries still in the list |
| RecordType lookup by name | `getRecordTypeInfosByDeveloperName().get(name)` | Stable identifier across orgs |
| Schema query on managed-package object | Include namespace prefix | Required for cross-package access |

---

## Recommended Workflow

1. **Identify call frequency.** Loop body = cache. One-shot = anywhere.
2. **Use compile-time references when possible.** Skip describe entirely.
3. **For dynamic strings, cache `Schema.getGlobalDescribe()` once.**
4. **For FLS on bulk DML, use `Security.stripInaccessible`.**
5. **For picklist enumeration, filter `isActive()` explicitly.**
6. **Test the namespaced case** if managed packages are in scope.

---

## Review Checklist

- [ ] No `Schema.getGlobalDescribe()` inside loops.
- [ ] Per-type field map cached as `static final` when used in hot paths.
- [ ] FLS checks use `Security.stripInaccessible` for bulk DML.
- [ ] Picklist enumeration filters by `isActive()`.
- [ ] Dynamic SObject creation uses `Schema.getGlobalDescribe()` not `Type.forName`.
- [ ] Managed-package SObjects include namespace prefix.

---

## Salesforce-Specific Gotchas

1. **`Schema.getGlobalDescribe()` is expensive.** Cache. (See `references/gotchas.md` § 1.)
2. **`getDescribe()` on a field in a loop multiplies cost.** Cache the field map at class scope. (See `references/gotchas.md` § 2.)
3. **Inactive picklist entries appear in `getPicklistValues()`.** Filter. (See `references/gotchas.md` § 3.)
4. **`Type.forName` returns `System.Type`, not SObjectType.** Use `Schema.getGlobalDescribe().get()`. (See `references/gotchas.md` § 4.)
5. **Managed-package fields require the namespace prefix.** (See `references/gotchas.md` § 5.)

---

## Output Artifacts

| Artifact | Description |
|---|---|
| Cached schema utility class | `static final` field maps; one place to evolve |
| FLS-aware DML helpers | `stripInaccessible`-based wrappers for bulk DML |
| Picklist enumeration helpers | Active-only value lists with label-vs-API distinction |

---

## Related Skills

- `apex/dynamic-apex` — broader dynamic-Apex patterns; this skill is the schema-describe slice.
- `apex/dynamic-soql` — SOQL string binding (different concern: SOQL injection prevention).
- `apex/apex-switch-on-sobject` — when describe-driven dispatch fits a switch-on-SObject pattern.
- `security/field-level-security-design` — broader FLS architecture; this skill is the Apex-side enforcement.

Related Skills

apex-managed-sharing-patterns

8
from PranavNagrecha/AwesomeSalesforceSkills

Grant row-level access programmatically via __Share records when declarative sharing rules cannot express the policy. NOT for OWD, role hierarchy, or criteria-based sharing rule design.

lwc-imperative-apex

8
from PranavNagrecha/AwesomeSalesforceSkills

Call Apex methods imperatively from LWC — on button click, lifecycle hooks, or conditional logic. Covers import syntax, cacheable vs non-cacheable, async/await patterns, error handling, loading states, and Promise.all. NOT for wire service (use wire-service-patterns) and NOT for testing Apex mocks (use lwc-testing).

platform-event-schema-evolution

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when modifying the schema of a Platform Event that already has live publishers and subscribers — adding fields, deprecating fields, or splitting events. Triggers: 'add field to platform event without breaking subscribers', 'platform event versioning', 'evolve event schema safely', 'rename a field on a published event'. NOT for initial event design (use integration/platform-events-integration) or for Change Data Capture event schemas.

dataweave-for-apex

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when transforming structured data inside Apex — CSV → JSON, XML → SObject list, JSON → flattened CSV, or schema-mapping a third-party payload to a Salesforce model — and the existing options (`JSON.deserialize`, `Dom.Document`, hand-written loops) are getting unwieldy. Triggers: 'apex transform csv json xml without external library', 'system.dataweave script', 'salesforce native dataweave apex execute', 'transform xml to sobject apex no mulesoft', 'json reshape salesforce apex script'. NOT for MuleSoft Anypoint DataWeave running off-platform (use mulesoft-anypoint-architecture), NOT for Apex JSON serialization basics (use apex-json-serialization), NOT for Bulk API CSV ingest (use bulk-api-2-patterns).

flow-invocable-from-apex

8
from PranavNagrecha/AwesomeSalesforceSkills

Author @InvocableMethod Apex classes that Flow can call as Actions. Design the input / output variable contract, bulk semantics (one list in, one list out), null handling, and error surfacing. Also covers the inverse direction: calling a flow from Apex via Flow.Interview. NOT for general Apex authoring (use apex-service-selector-domain). NOT for REST-exposed Apex (use apex-rest-resource-patterns).

flow-apex-defined-types

8
from PranavNagrecha/AwesomeSalesforceSkills

Design and use Apex-Defined Types as Flow variables for structured non-sObject data (HTTP callout payloads, External Service responses, complex configuration). Trigger keywords: apex-defined type, flow variable, @AuraEnabled class, flow http callout response. Does NOT cover building HTTP Callout Actions themselves, External Services schema, or raw Apex invocable methods.

scheduled-apex-failure-detection-and-monitoring

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when nightly batch / scheduled Apex jobs are failing without anyone noticing — covers why uncaught exceptions in `execute()` go to the debug log instead of email, how to query `AsyncApexJob` for `Status`, `NumberOfErrors`, and `ExtendedStatus`, when to implement `Database.RaisesPlatformEvents` so the platform publishes `BatchApexErrorEvent` on uncaught failures, how to subscribe to that event with an Apex trigger and notify operators, and how to layer a custom watcher schedule on top so silent-failure modes (job that never started, scheduled class deleted, queue stuck on `Queued`) still surface. Triggers: 'nightly batch failed at 2am with no notification', 'how do we know if a scheduled apex job is failing', 'BatchApexErrorEvent vs custom retry logic', 'Setup Apex Jobs only shows last 7 days, where else can I look', 'job is stuck in queued status nobody noticed for a week'. NOT for general Apex exception handling patterns (use apex/apex-exception-handling-and-logging), NOT for Batch Apex authoring or chunking strategy (use apex/batch-apex-design), NOT for Setup → Apex Jobs UI walkthrough as an admin task (use admin/batch-job-scheduling-and-monitoring), NOT for retry logic itself (use apex/scheduled-apex-retry-patterns once authored).

platform-events-apex

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when publishing or subscribing to Salesforce Platform Events from Apex, comparing Platform Events with Change Data Capture, or designing event-triggered error handling and monitoring. Triggers: 'EventBus.publish', 'platform event trigger', 'CDC vs Platform Events', 'replay ID', 'high-volume event'. NOT for Flow-only publish/subscribe automation.

health-cloud-apex-extensions

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when extending Health Cloud via Apex: implementing HealthCloudGA managed-package interfaces, automating care plan lifecycle hooks, processing referrals using Industries Common Components invocable actions, or enforcing HIPAA-compliant logging governance for clinical Apex code. Trigger keywords: CarePlanProcessorCallback, HealthCloudGA namespace, ReferralRequest, ReferralResponse, care plan invocable actions, clinical Apex extension, Health Cloud Apex API. NOT for standard Apex triggers or generic Apex development unrelated to Health Cloud managed-package extension points.

fsl-apex-extensions

8
from PranavNagrecha/AwesomeSalesforceSkills

Use when writing Apex that calls Field Service Lightning scheduling APIs — AppointmentBookingService, ScheduleService, GradeSlotsService, or OAAS — to book, schedule, grade, or optimize service appointments programmatically. Trigger keywords: FSL Apex namespace, GetSlots, schedule service appointment via code, appointment booking API, FSL optimization API. NOT for standard Apex patterns unrelated to FSL, admin-level scheduling policy configuration, or declarative FSL scheduling.

fsc-apex-extensions

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when extending Financial Services Cloud (FSC) behavior through Apex: customizing financial rollup recalculation, disabling built-in FSC triggers to write custom trigger logic, implementing Compliant Data Sharing (CDS) participant/role integrations, or building custom FSC action handlers. NOT for standard Apex unrelated to the FSC managed package, standard Salesforce sharing rules, or configuring FSC rollups through the Admin UI — use the admin/financial-account-setup skill for declarative rollup setup.

entitlement-apex-hooks

8
from PranavNagrecha/AwesomeSalesforceSkills

Use this skill when writing Apex triggers or classes that interact with CaseMilestone records — completing milestones, detecting violations, or reacting to SLA state changes. Trigger keywords: CaseMilestone trigger, auto-complete milestone Apex, milestone violation polling, CompletionDate write pattern. NOT for entitlement process admin setup, milestone configuration in Setup UI, or Flow-based milestone actions.