---
title: "Big objects, and what you give up"
url: "https://trailassembly.com/salesforce-big-objects/"
topic: "Architecture"
published: "2026-09-18T14:10:45+00:00"
updated: "2026-10-06T13:58:19+00:00"
---

# Big objects, and what you give up

**Straight answer:** a [big object](https://trailassembly.com/salesforce-archiving-strategy/) stores billions of rows cheaply and gives up almost everything else. No triggers, no validation rules, no [sharing rules](https://trailassembly.com/sharing-rules/), no ordinary page layouts, and queries only along an index you define once and can never change. Used for the job it was built for, which is archive and event history, it works well. Used as a general-purpose custom object with more room, it will make you unhappy.

## What you get

Two kinds exist. Standard big objects such as FieldHistoryArchive come with the platform. Custom ones carry a `__b` suffix and are defined through the [Metadata API](https://trailassembly.com/apex-metadata-api-from-apex/), with Setup showing them under Big Objects.

The definition is fields plus an index. The index is the important part:

```apex
<indexes>
 <fullName>InteractionIndex</fullName>
 <label>Interaction Index</label>
 <fields>
 <name>Account__c</name>
 <sortDirection>ASC</sortDirection>
 </fields>
 <fields>
 <name>Interaction_Date__c</name>
 <sortDirection>DESC</sortDirection>
 </fields>
</indexes>
```

Up to five fields, all required, in a fixed order. That composite key is the record’s identity and the only path to the data. Query it left to right: filtering on the first field works, filtering on the first two works, filtering on the second alone does not.

Field types are limited to text, text area, number, date, date/time, email, phone, URL and lookup. No formulas, no roll-up summaries, no picklists.

## Designing the index

Spend your time here, because the index cannot be altered after the object is created. Changing it means deleting the object and starting again, which means the data goes too.

Work backwards from the questions. If the only question is “show me everything that happened to this account, newest first”, then account and date, in that order, is right. If someone also asks “show me everything a given user did”, that is a different index and probably a second object holding the same rows. Denormalising is normal here. It is the price of the query model.

The index is also the uniqueness constraint. Writing a record whose index values match an existing one replaces it silently. Two interactions on the same account at the same timestamp collapse into one, so include something distinguishing in the key when collisions are possible.

## Writing records

Ordinary Apex DML does not apply. You use immediate operations:

```apex
List<Customer_Interaction__b> rows = new List<Customer_Interaction__b>();
// populate rows
Database.SaveResult[] results = Database.insertImmediate(rows);
```

These commit outside the normal transaction, so they do not roll back when the rest of it fails. Handle the save results yourself. Bulk API and Data Loader also load big objects, the usual route for an archive migration.

Deletion is awkward by design. `Database.deleteImmediate` handles rows you can query, and there is no convenient truncate. Plan the retention rule before the first load, not after a billion rows are in.

## Async SOQL

Standard SOQL works when the filter matches the index prefix and you accept a limited result set. Anything else needs Async SOQL, which is a background query submitted to a REST endpoint rather than run in the query editor.

The key detail: Async SOQL does not return rows to you. It writes results into a target object, which can be a custom object or another big object, and you read that. A monthly aggregation writing counts into a small reporting object is the standard shape, and it solves reporting too, since a big object is not something you point a report builder at.

## What you give up

- **Automation.** No triggers, no flows, no validation rules, no workflow. Data quality is entirely the responsibility of whatever writes the rows.

- **Sharing.** There is no record-level security. A user who can read the object can read every row, so anything with per-user confidentiality does not belong here.

- **Interface.** No standard tab, [list view](https://trailassembly.com/salesforce-list-views-and-filters/) or record page. Displaying the data means a Lightning component you build, usually calling Apex that queries by index.

- **Editing.** There is no update. Rewriting the same key replaces the row wholesale.

What you gain is scale at a storage cost far below ordinary object storage, and a modest allotment of rows included before you buy more.

## Frequently asked

**Can I add a field later?** You can add non-index fields. You cannot add a field to the index, reorder it, or change its fields, and that restriction is the one that decides whether the design was right.

**Do big object records count against my data storage?** No, they are allocated separately from standard data storage, which is the main reason to use one.

**Can a flow read a big object?** A [Get Records](https://trailassembly.com/flow-get-records-behaviour/) element can query one if the filter matches the index prefix. For anything more involved, call Apex from the flow.

**Is a big object the right place for archived cases?** Often yes, provided nobody needs to edit them, run reports over them, or apply sharing to them. If any of those three come back later, you will be rebuilding.

---

Source: https://trailassembly.com/salesforce-big-objects/
Trail Assembly — independent Salesforce write-ups.
