---
title: "Describe calls, and getting a record type Id safely"
url: "https://trailassembly.com/apex-sobject-describe/"
topic: "Apex and Development"
published: "2026-09-18T06:28:52+00:00"
updated: "2026-10-06T13:57:50+00:00"
---

# Describe calls, and getting a record type Id safely

**The short version:** stop calling `Schema.getGlobalDescribe()`. It builds a map of every object in the org, which on a mature org with managed packages is thousands of entries, and it costs real CPU and heap every time you touch it. Use a token you already have, `Type.forName` when the name is dynamic, and cache anything you look up more than once. Record type ids should never be hardcoded, and getting them by developer name is a one liner.

## Why getGlobalDescribe is the wrong default

The method returns `Map<String, Schema.SObjectType>` covering every standard, custom and packaged object. You almost always want one entry from it. On an org with a few managed packages installed, the map runs to several thousand keys, and building it consumes a meaningful slice of your 10,000 ms synchronous CPU budget along with a chunk of the 6 MB heap.

Calling it inside a loop over trigger records is one of the classic slow trigger causes, and it does not throw a limit exception. It just makes everything take longer until CPU time runs out somewhere unrelated.

If you need an `SObjectType` from a string name, this is the cheap way:

```apex
Schema.SObjectType t = ((SObject) Type.forName('Account').newInstance())
    .getSObjectType();
```

Or, when the name may carry a namespace and you want the describe directly:

```apex
List<Schema.DescribeSObjectResult> results =
    Schema.describeSObjects(new List<String>{ 'Account', 'Contact' });
```

`describeSObjects` takes a list, so one call covers every object you care about. That is the pattern to reach for when the object names come from custom metadata or configuration.

## Tokens, and why they are cheap

An `SObjectType` is a lightweight token, not a describe result. `Account.SObjectType` and `Account.Industry` compile to tokens directly, so there is no runtime lookup at all. Tokens are also the compiler’s way of telling you a field was renamed, which a string never will.

```apex
Schema.SObjectType acc = Account.SObjectType;
Schema.SObjectField ind = Account.Industry;

Boolean canEdit = ind.getDescribe().isUpdateable();
String label     = acc.getDescribe().getLabel();
```

The cost arrives at `getDescribe()`, which loads labels, field maps and relationship information. Pull it once into a local variable rather than chaining it repeatedly in a loop.

Where you only need a shallow answer, ask for it lazily:

```apex
Schema.DescribeSObjectResult d =
    Account.SObjectType.getDescribe(SObjectDescribeOptions.DEFERRED);
```

`DEFERRED` skips loading the field map and child relationships until something actually asks for them. If all you want is `isAccessible()` or the label, it is markedly cheaper than the default.

## Record type ids without hardcoding

A record type id is different in every org, so a hardcoded one works in the sandbox it was written in and nowhere else. It is also 18 characters of nothing, which makes the code unreadable.

```apex
Id businessRt = Schema.SObjectType.Account
    .getRecordTypeInfosByDeveloperName()
    .get('Business_Account')
    .getRecordTypeId();
```

Use `getRecordTypeInfosByDeveloperName()`, not `getRecordTypeInfosByName()`. The label is translatable and an admin can change it without warning. The developer name is stable and is what the metadata actually keys on. This mistake produces a null pointer exception the first time someone enables a second language.

Going the other way, from a record on hand:

```apex
Schema.RecordTypeInfo info = Schema.SObjectType.Account
    .getRecordTypeInfosById().get(acc.RecordTypeId);
String devName = info.getDeveloperName();
```

Cache the map in a static. Record type configuration cannot change mid transaction, so there is no reason to look it up twice.

## The caching pattern

A small static holder removes almost all of the cost and gives you one place to look.

```apex
public class DescribeCache {
    private static Map<String, Schema.SObjectField> accFields;

    public static Schema.SObjectField accountField(String name) {
        if (accFields == null) {
            accFields = Account.SObjectType.getDescribe().fields.getMap();
        }
        return accFields.get(name.toLowerCase());
    }
}
```

Note the `toLowerCase()`. The field map is keyed on the lowercase API name, and a lookup with the cased version returns null. That detail costs people an afternoon roughly once per career.

Statics live for the transaction, not beyond it, so this saves you within one execution. For genuinely cross request caching, put the result in the platform cache.

Measured in a Summer ’26 scratch org on 14 September 2026: `Schema.getGlobalDescribe()` returned **1,390 object types** in **108 ms**. Calling it once per record in a loop is how a transaction reaches the CPU limit without doing any work.

## Frequently asked

**Is there a governor limit on describe calls?** Not a documented count limit any more. The constraint is CPU and heap, which is worse in a way, because you get slow code instead of a clear failure.

**How do I check field level security from a describe?** `isAccessible()`, `isCreateable()` and `isUpdateable()` on the field describe. For queries and DML, prefer `WITH USER_MODE` and the `AccessLevel` overloads, which do the whole check for you. `WITH SECURITY_ENFORCED` is no longer supported.

**Can I detect whether an object exists before referencing it?** Yes. `Type.forName('Namespace__Object__c')` returns null when nothing matches, which is the standard way for a package to handle an optional dependency without failing to compile.

**Does the field map include fields the user cannot see?** Yes. Describe returns the metadata regardless of permissions, and the [accessibility](https://trailassembly.com/lwc-accessibility/) flags are how you find out what the [running user](https://trailassembly.com/dashboard-running-user/) may do with it.

---

Source: https://trailassembly.com/apex-sobject-describe/
Trail Assembly — independent Salesforce write-ups.
