The Salesforce platform offers an astonishing range of tools and technologies for creating modern cloud-based applications. With countless APIs, Apex for custom business logic, and a sophisticated cloud database, both developers and admins can build and customize solutions in powerful ways. Yet beneath these modern conveniences, the platform also carries several historical design decisions—haunted relics from the days when Salesforce first began. These quirks persist even as we enjoy the world’s leading CRM, causing seasoned developers to sometimes whisper about the “dark secrets” lurking under the hood. I call these secrets the Curse of the Ancient Objects.
Our journey starts at the turn of the century, when Salesforce was just a small startup perfecting its CRM. Back then, the first Salesforce API emerged via XML-RPC, followed quickly by a SOAP-based API. At this early stage, there was no robust platform, marketplace, or even modern Packaging features. During this foundational era, core Standard Objects were born: Account, Attachment, User Role, Lead, Group, Pricebook, Document, and many others. Over the years, new Custom Objects followed stricter and more predictable design principles, but these early Standard Objects still carry unique behaviors that can puzzle developers. And thus, the Curse of the Ancient Objects continues to test our wits.
Users and Groups and Roles
Among the most perplexing architectural decisions in early Salesforce history is the way Users, Groups, and Roles interrelate. Originally, User records could be placed in Group records, and Group records could even be placed inside other Group records. A special junction object, GroupMember, was introduced to link any User or Group to another Group. Meanwhile, each User also belongs to a Role, forming an Org chart from the CEO downward.
Early designers also wanted each Role, along with its subordinate Roles, to be recognized as part of a Group. But the GroupMember object only directly linked User or Group records to another Group. To solve this, Salesforce created a type of special, unnamed Group that is really just an indirect representation of a Role. For each Role, two such invisible Groups exist: one for the Role itself and another for “Role and all subordinates.” When you want a Role to belong to a Group, you must use its associated special Group record.
This arrangement causes headaches for administrators and developers alike. It can be surprisingly difficult to figure out all the Groups to which a particular User belongs. Similarly, given one Group, it is not always straightforward to list every member because that Group could include Roles, sub-Roles, or nested Groups of Groups. This is why you might someday find yourself on the receiving end of a random Group email in Salesforce: you unknowingly ended up in a labyrinth of invisible Groups. Your admin might be able to untangle the web—or maybe they can’t.
The Account Hierarchy
Moving from Users to Accounts, we face another ancient quirk in Salesforce: the infamous Account Hierarchy. While the Role hierarchy can be easily enumerated for the Org chart, the Account hierarchy demands more craft. Each Account can potentially have a parent, making it part of a tree structure. However, there is no single field that directly points to an immediate parent and to the entire branch all at once. Instead, we rely on a ParentId field that links to the parent object until you reach the top-level Account.
To illustrate, you might write a SOQL query that attempts to climb the ParentId chain up to 6 levels in one shot. If you still haven’t reached the apex (where ParentId = NULL), you must recurse and query again. Once you finally determine the uppermost Account for that tree, you can then query downward through child Accounts. Because each child can itself have more children, that also requires a separate set of recursive calls if you exceed the 6-level limitation—hence the sigh of exasperation from longtime developers who have tackled complex Account trees.
Opportunity Pricebooks
Salesforce’s Opportunity object and the concept of Pricebooks add more twists. Every Opportunity has Opportunity Line Items, and each of these line items references a PricebookEntry. Conveniently, each PricebookEntry automatically maps a Product to a Pricebook. However, every Opportunity also holds a direct reference to a Pricebook field, which must match the Pricebook used by any associated line items. This integrity requirement is enforced on the server side, so if you change the Opportunity’s Pricebook reference, the system automatically deletes all Opportunity Line Items that belong to the old Pricebook.
From a data integration perspective, this constraint can be surprisingly tricky. When you insert or update Opportunity Line Items via the Data API, you must be certain to pick the correct PricebookEntry that belongs to the same Pricebook referenced by the Opportunity. Many new developers discover the server-side “Pricebook mismatch” error the hard way. And if you ever change the Opportunity’s Pricebook, be prepared for all line items to vanish in a puff of server-enforced logic. This is yet another instance of how the original architecture can still catch you off guard.
Polymorphic Confusion
Standard Objects in Salesforce frequently enjoy superpowers beyond those of Custom Objects—one such superpower being polymorphic foreign keys. For example, the ParentId field on an Attachment can reference nearly any object, including Account, Contact, Case, or even EmailMessage. In the Describe API, such fields are labeled as “Polymorphic,” meaning their type is not fixed to a single object.
Another layer of complexity is the “Name Pointing” designation. If ParentId is Name Pointing, you can run a SOQL query that references the name field of the related record. But if some of the possible parent types (like EmailMessage) don’t have a standard name field, these queries break. So, fields can be polymorphic, name pointing, or both—or neither. Often, developers end up checking the first three characters of an ID (the “key prefix”) to guess which object type it belongs to.
If you look at a typical Developer Org, excluding certain utility objects, you’ll see around 500 Standard Objects and over 10,000 fields. Roughly half of these objects have a Name field. You might discover about 250 Polymorphic fields overall and 150 that are Name Pointing. Suffice to say, you are dealing with a sprawling ecosystem that formed incrementally across numerous platform releases, each partial enhancement layered on top of what came before.
Frankenstein’s Id
Object IDs are perhaps the oldest living artifacts on the platform, tracing back to Salesforce’s earliest days. Each record has a Base-62 ID consisting of letters and numbers. Initially, these IDs were 15 characters: 3 for the “key prefix” (which identifies the object type) and 12 for the unique record identifier. For example, key prefix 001 indicates an Account, 003 indicates a Contact, and so forth. Custom Objects get a distinct prefix for each Org.
When the Salesforce API was introduced, the company realized that some programming languages are not case sensitive. This led to a 3-character suffix to preserve case sensitivity, producing 18-character IDs. As Salesforce expanded to more servers (known as Pods), three characters after the key prefix were reserved to store the Pod number. The rest of the ID ensures a massive range of possible records.
This ID structure can yield challenges for data migrations. For instance, consider splitting Account exports by ID so that each batch stays within a manageable size. You might start by leveraging just the 9-character portion (the “Actual Object Id” segment). But because Orgs can have records born on different Pods—and might move from one server to another—you’ll have to consider multiple possible Pod prefixes or ranges of IDs. It can feel like you’re chasing a patchwork of Sub-IDs across the continuum of time and server migrations.
The Tail Wagging the Dog?
Despite all these peculiarities, the Salesforce platform keeps evolving. Today, everyone is talking about Salesforce DX, DevOps Center, and Visual Studio Code integrations. Yet, if you explore the Unpackaged Metadata in any Org, you will unearth a vast constellation of Standard Objects, fields, and Metadata Types dedicated to configuring products and features across the Salesforce ecosystem. These fundamental assets are updated each release, layering further complexity on top of the older architecture.
But fear not: the Standard Objects, with their eccentricities and specialized use cases, are here to stay. They trace the platform’s evolution from a fledgling CRM to a robust, customizable engine that can power global enterprises. Hopefully, this closer look at some of the oddities lurking in Users, Groups, Roles, Accounts, Opportunities, and beyond will help you navigate the labyrinth.
If you find yourself overwhelmed by the complexities of these ancient objects—or simply need advanced Org management—please take a look at our Snapshot product on the AppExchange. We would be happy to see how we can help tame the Salesforce beast and lift the Curse of the Ancient Objects from your Org.
References & Helpful Notes:
- First three characters of an ID (prefix) can indicate object type.
- “Polymorphic” fields can reference multiple objects from the same field.
- Account hierarchies require creative SOQL and data modeling to traverse more than 6 levels.
- Pricebook constraints can delete entire sets of Opportunity Line Items if mismatched.
- Groups, Roles, and subordinates can create a web of indirect Special Groups.
- Always confirm the Pricebook references when working with Opportunity Line Items.
- Audit or investigate Group membership to avoid unintentional email blasts and record access.
- Remember the 18-character ID expansion ensures case sensitivity for APIs.
- Watch out for server Pod migrations when referencing ID ranges across environments.
- Consult platform documentation for the latest details on polymorphic, name-pointing fields.