<span id="hs_cos_wrapper_name" class="hs_cos_wrapper hs_cos_wrapper_meta_field hs_cos_wrapper_type_text" style="" data-hs-cos-general-type="meta_field" data-hs-cos-type="text" >The complete guide to Salesforce field history tracking</span>
04/28/2021

The complete guide to Salesforce field history tracking

"Hey, this doesn't look right. When did that get changed?"

If you're a Salesforce Admin you've had someone ask you this about a field. And, let's face it, you've probably muttered it to yourself as well. Luckily Salesforce offers field history tracking that lets you monitor specific fields and record their changes so you can go back and see what happened. In this guide, we'll walk you through everything you need to know to use Salesforce field history tracking.

Here's the agenda:

  • Enabling field history tracking for standard and custom objects
  • Anatomy of the history object (aka how Salesforce stores field history)
  • Field history tracking limitations
  • Querying field history with SOQL
  • Viewing field history on record detail pages
  • Building field history reports
  • What fields should have history tracking?

Enabling field history tracking

For standard objects (e.g. Lead, Account, Contact, etc), click "Set History Tracking" when viewing Fields & Relationships for a given object in Object Manager. On the next screen select "Enable {ObjectName} History" and then select the field you want to track.

Salesforce field history tracking for a standard object

For custom objects, you'll first need to make sure that "Track Field History" is selected in the custom object definition by clicking "Edit" on the Details page for the object.

Enable "Track Field History" for a Salesforce custom object

Once that's done, go to the Fields & Relationships for the custom object, click "Set History Tracking" and select the fields you'd like to track.

Edit Salesforce custom object field history tracking

Anatomy of the history object

Salesforce isn't entirely consistent in how it keeps track of history for each kind of object. The vast majority of standard Salesforce objects follow the same model, but there are a few exceptions (particularly with Opportunity; see below). Custom objects also support history tracking but have a few minor differences in their history objects.

Most of the standard Salesforce objects are paired with history objects designed to support field history tracking. These standard history objects are all named {ObjectName}History (e.g. LeadHistory, AccountHistory) and have the same standard structure:

Field Type Description
{ObjectName}Id Lookup The Id of the record that changed. Replace {ObjectName} with the name of the standard object being tracked (e.g. LeadId, AccountId, etc)
DataType Picklist The data type of the field that was changed
Field Picklist The name of the field that changed
NewValue Anything The value the field was changed to
OldValue Anything The value the field previously had
CreatedBy User Lookup The user that made the change
CreatedDate Datetime The time the change was made

You can see the full list of History objects in the Salesforce docs.

There are a few exceptions to the standard history object described above. One of the most important is OpportunityHistory which is designed to keep track of key lifecycle changes an Opportunity might go through, not changes to individual fields. If you track individual field history on an Opportunity, that data will be stored in OpportunityFieldHistory which follows the standard object model described above. Is that confusing? Yes, yes it is. is it what we've got to live with? Also, yes.

Finally, we come to custom objects. Those objects follow the same model as the standard history objects with two differences:

  • Object name - They're named {CustomObjectName}__History instead of {ObjectName}History (note that if you have a namespace, the name will be {Namespace}__{CustomObjectName}__History). If your custom object is named ProductUsage__c, the History object will be named ProductUsage__History.
  • Lookup field - The field that references the custom object being changed is called ParentId, not {ObjectName}Id. This field name is used regardless of the name of your custom object.

The above exceptions and caveats sound complex, but the good news is that you generally won't have to think too hard about them. Most of the time you'll be dealing with a standard data model for field history that works transparently with related lists and reports (see below).

Field history tracking limitations

As you might imagine, keeping track of all these changes can create a large amount of data in your Salesforce org. Luckily, field history tracking doesn't count against your org's data limits. However, Salesforce imposes several limits to ensure that your data doesn't get out of control:

  • Max of 20 fields per object - This might sound like a lot, but most Salesforce orgs I've seen have this maxed out for at least some of their objects.
  • No formula or roll-up summary fields - While it would be nice to be able to snapshot these fields, it makes sense that these can't be included in history because they're frequently recomputed based on changes to another data source.
  • No values for text fields over 255 characters - For large text fields, Salesforce will record that there was a change but not what actually changed. While it makes some sense not to snapshot thousands of characters on each edit for a big text field it can still be a little annoying. If you need to snapshot this, you'll have to manage it yourself with some custom automation.
  • No values for multi-select picklists - Just like large text fields, multi-select picklists won't have their values recorded but Salesforce will track that they changed.
  • Second precision can lead to out-of-order history - Like all Salesforce objects, the CreatedDate field only stores a DateTime that's recorded down to the second. Since the CreatedDate represents the time the change was made, changes made within the same second may appear out of order. This isn't usually an issue but can be annoying, especially if you have automation that runs immediately on create or update which modifies a tracked field. In that case, it may be impossible to tell the order in which certain changes happened.
  • You can't keep it forever - Salesforce only keeps so much history around. Here's how that works:
    • Last 18 months - You can access the last 18 months directly in your org via related lists, reports and SOQL queries.
    • From 18-24 months - Salesforce retains this data but you have to use Data Loader or the API to access it.
    • Beyond 24 months - It's gone. If you need to retain data longer than two years, take a look at Field Audit Trail which is designed for long-term storage for compliance purposes. (Be prepared to pay up.)

For some more information about the above limits, take a look at Salesforce's field history tracking docs.

alternative to date loader and spreadsheets

Querying field history

If you're not inclined to directly query data in Salesforce using SOQL, feel free to skip this section. If you enjoy SOQL, read on.

Let's take a look at how you might query AccountHistory:

SELECT AccountId, DataType, Field, NewValue, OldValue, CreatedById, CreatedDate
FROM AccountHistory
ORDER BY CreatedDate ASC
LIMIT 10

If you've enabled field history tracking for Account and you run the above query in Developer Console, you'll get back a list of results:

Using SOQL to query a Salesforce field history objectDepending on what changes have been made in your org recently, you might notice a couple of odd things:

  • A field named "created" - When a new record is created, Salesforce adds a record into the history object with a Field of "created" and an empty OldValue and NewValue.
  • Two rows for every lookup field - Whenever a lookup field (like Owner) changes, Salesforce creates two History records. One with a DataType of EntityId and one with a DataType of Text. The EntityId record stores the old and new Ids for the lookup record while the Text record stores the names of those related records.

Be aware of these wrinkles when doing queries on history objects as you might see more changes than you expect without the appropriate criteria.

Viewing field history

Field history acts just like any related list. You can easily add it to page layouts for standard and custom objects. Just beware that you'll only be able to see the most recent 18 months of field history. So, if you're looking at a record that was last modified more than 18 months ago, expect list related list to be blank.

To add a history related list, just drag and drop it into the page layout using the standard editor:

Add a field history related list to a Salesforce object page layoutOnce you've saved the page layout, you'll see the history related list show up on the record detail page:

Salesforce record detail page showing field history related listThat's all there is to it! Add related history lists to any object where you're tracking history. Just remember that large related lists can slow down your record pages.

Building field history reports

Salesforce offers built-in reports for many of the standard object history types. If you've enabled reporting on your custom objects, their history reports will show up as well.

Salesforce field history reports

If you're familiar with Salesforce reporting generally, it's not too hard to build a report that gives you a log of changes for a given record. The following report is just a basic grouping on Account order by the Edit Date (aka CreatedDate on the History object).

Edit Salesforce account field history report

There are a few places where dealing with history reports can get frustrating: lookup fields and cross-object history.

If you're dealing with changes to lookup fields, you likely want to include some additional data about the old record and the new record referenced by the lookup field. Unfortunately, you can't do that because the old value and new value themselves aren't lookups, they're just text with a name for the referenced record.

One other area where reporting falls short is cross-object history. Let's say your Sales reps might own accounts, contacts and leads at any given point in time. There is, unfortunately, no way to build a report that might show you the assignment history across all those objects for a given user.

What fields should have history tracking?

First, you should ensure you're keeping track of ownership changes. We recommend that you enable field history tracking for the Owner field on standard objects like Lead, Account, Contact, Opportunity and even Task. You should also enable history tracking on other custom fields you have that represent some form of ownership.

These ownership fields are critical to managing sales rep assignments throughout the customer lifecycle, especially if you use round robin assignment and the assignee isn't determined by territory rules. When a sales rep (or manager) wants to know how something got moved out of their name, it's very important that you can answer that question because this can impact everything from close rates to rep compensation.

Another key area to track is "stages" or progression through a lifecycle. If you have fields that represent this (e.g. a Status picklist), you should enable field history tracking. That way you can always reconstruct the stages that a given record went through. Opportunities are obviously the primary example of this kind of lifecycle but remember that there is a special OpportunityHistory object designed just for them.

Finally, track field history for any field with significant business impact. Perhaps you have an account score that changes periodically. It's important to be able to reconstruct what the score was at various points in the past as that might have had an impact on how a rep worked that account or how it was marketed to.

Remember that you can only track 20 fields per object, so choose wisely. It's not uncommon to have to triage what gets tracked and what doesn't. Generally speaking, that will need to be a business decision for your org so make sure to involve stakeholders when making those kinds of calls. You might also consider using a product like Gradient Works that keeps a separate comprehensive audit trail for ownership changes. This can free up precious field history space.

Happy field history tracking!

If you found this valuable, take a look at our other posts for Salesforce admins covering similar topics.

Want free Salesforce Flow help? Click to learn more!

Related Posts