top of page
  • Writer's pictureMichael Kolodner

Extension Objects: There’s Nothing to Like

Freebie riding a motorcycle with a sidecar attached. He's scowling at the beat up extension that has a flat tire.

Continuing our exploration of the new Nonprofit Cloud and the new Education Cloud, let's look at the Achilles heel of Industries solutions: the extension object. I noted in The Emperor’s New Clouds that extension objects are a workaround for the fact that the new clouds are built "on core." More specifically, extension objects are how building "on core" deals with adding fields that really ought to be added to standard (or "hero") objects like Account and Contact without having a managed package.

I don't understand why is so dead set against having a managed package for the new clouds. As we're going to see, not having fields where they belong is a critical flaw that makes the new clouds difficult (I would say nearly impossible) to use and increases the costs of implementation and upkeep exponentially. Even Financial Services Cloud has a managed package, so it's not like building on "the Industries model" requires that everything you put out be "on core."

Recap on Extension Objects, or "Sidecar Objects"

A quick reminder. has chosen to build the new clouds "on core" and not have a managed package. That means that functionality relates to objects you can only see with special licensing that opens up access to Industries’ “standard” objects, some of which are new for the nonprofit clouds and others come from prior clouds like Financial Services, Health Cloud, and Public Sector. Any new fields referenced in new cloud functionality are on Industries objects because fields can’t be added to the “hero” objects that have been around forever. New fields can’t be placed on hero objects unless you publish a managed package, nor can picklist values be added to existing picklists. In those places where the engineers would have added a field on a hero object in the past in a managed package (say, in NPSP), they instead create an extension object.

In my prior post, I used the analogy of an apartment and parking garage to explain these additional objects. I've also heard them called "sidecar objects," an analogy to adding additional passenger seating to a motorcycle or bike.

A vintage bicycle with a child in an attached sidecar.

I like that analogy because it makes it easy to start to point out the absurdity of extension objects. A sidecar is a nice addition to a motorcycle, it allows the passenger better visibility and allows the driver to see them during the ride, unlike riding behind the driver. In a pinch, I suppose, a sidecar would allow three passengers on a motorcycle. Plus they're useful for those that want to bring a dog along. (I don’t think dogs would be that good at riding pillion, which is one inoffensive term for riding tandem. I probably just taught you a new word. You’re welcome!) 

Freebie looking at a fuel gauge, with a gas can behind him.

Of course, sidecars often take flak for being uncool. And they're murder on fuel efficiency. (This analogy is getting more and more apt!) Plus you very rarely see motorcycles with more than one sidecar. If you need multiple passengers that's what cars are for.


The other thing about extension objects is that they add massive bloat to the entity relationship diagram (ERD), the visual depiction of the tables in your database and their relationships to each other. That's no small problem! I don't particularly care that the diagram is a mess—though it is. But a data model that complicated is makes it harder to understand and internalize, harder to support, and nearly impossible to analyze using Salesforce's reports and dashboards.

I headed this section "bloatware" because, indeed, there is object bloat going on within the new clouds. Now, I'm not opposed to complexity where needed. If you want to enable new features, you're going to have some complexity under the hood. But we want to do everything possible to hide the complexity for users. That makes it easier to adopt and stick with using Salesforce. So it’s a balance between complexity and functionality. I think it's clear that the fundraising model of the new clouds has failed in this respect. And it's happening in other areas as well, with the main culprit being the extension objects. Earlier, I wrote about Donor Gift Summary and Party Relationship Group. Now let me tell you about one more: Contact Profile.

This one's so bad that I keep blocking it from my conscious mind. 

The Dumping Ground

You know how sometimes people have more than one email address? Like a work email and a personal email. You’ve encountered this, I would imagine. Since Salesforce was originally a sales tool, there's only ever been a single standard Email field, which presumably was the work email of people at the accounts you were selling to. NPSP and EDA both added personal, work, and alternate email addresses, plus a Preferred Email field, and automation to move the preferred email address into the standard Email field. Sometimes even three isn't sufficient, so people make custom email fields to add to the mix. But building "on core" means Salesforce can't put extra fields on Contact (or on Person Account), so they can't have parity with NPSP in that sense.

At the same time, sometimes it's important for organizations to know demographic information about their constituents. Beyond Gender, which is a standard field on Contact, sometimes they need to capture race/ethnicity, language spoken, veteran status, and more. These are fields that describe the person, so according to normal database design, they would belong on the Contact object. But no.

The new clouds introduce Contact Profile, the sidecar object for storing demographic fields that clearly belong on the contact. Solves the problem, right?

Wrong. Say a new client has just walked in the door. You're doing an intake with that person and you want to enter her name, birthdate, contact information, and demographics. Thanks to extension objects, within the standard Salesforce user interface, you’ll have to do this in two steps. Name, gender, address, and birthdate can all go on the Person Account. What to do with email addresses is ambiguous because there is one email field, but she just gave you three valid email options. Assuming multiple phone numbers (home, work, mobile, alternate) and multiple email addresses, those have to go on Contact Profile. Plus all the demographics have to go there as well. So you would have to create and save the Person Account, then click New in the Contact Profile related list, then enter the other pieces of data. How are we going to make that understandable for a new user? And even if they understand what data goes where, they're going to rightly hate all the extra clicks.

Someone [that shall remain nameless] at Salesforce made the argument to me that it won't be common to want to create a contact and at the same time fill in their demographic details, so this complication doesn’t matter. I respectfully and very strongly disagree.

Sure, in theory we could replace the new person account screen with a screen flow. Good luck ensuring users never find a "new contact" link in some obscure part of the platform user experience. And Yes, webforms like FormAssembly can make sure the data gets to the different objects for applications and the like. But the connector mapping is not so simple anymore, particularly because contact profiles are 1:1 with a contact. And these fixes make everything massively more complicated.

“No filters for you!”

But even if we get past the point of entering the new people into the system, we're not going to be able to do the things we need to do after that point.

First of all, we can't use List Views as we would like. I'm a huge fan of list views. They're an easy way to give users some bulk update access without introducing an additional tool or having to build complicated automation. List views' power comes from filtering to find the records you need and then working with them in bulk. But you can't filter based on fields that aren't on the object whose tab you’re on!

Say you wanted to quickly filter a list view for all your constituents that are veterans over 65 years old. Perhaps you're going to use the handy Add to Campaign button to put them in a campaign and then send them a list email. Or you just did a survey and then added a field for which branch of the military they served in and you want a user to use the list view for convenient bulk data entry. In the new cloud data model you can't do either of these things. On the Contacts tab you won’t be able to filter for veterans because that's data on contact profile. Nor could you add their military branch if you'd added that field on Contact Profile alongside the other demographic fields. But on the Contact Profile tab you don’t have the Add to Campaign button. You also don’t have access to the Birthdate field (it’s on Contact), so you couldn't filter for the over 65s.

This particular very simple example you could theoretically solve on a report. First you’d have to build a custom report type that included Contact and Donor Profile, since the new clouds don’t come with any report types preinstalled for the new objects. But inline editing on reports is much less convenient than on list views, so updating the branches of service would be misery. 

Plus don't even get me started on the fact that these are contact profiles and would have to be a report type of Contacts with Contact Profiles. In the new clouds we’re using Person Accounts. Users should basically never even hear the word "contact" and should never interact directly with the contact object. So we're gonna confuse them with stuff like this.

But next we're going to find that reporting is actually a huge problem.

Reports Are Impossible

Here is a very simple example based on a real client request:

"I would like to make a report of all donations that came in last week from donors that have given more than $1,000 in their lifetime so that the executive director can send a personal thank you for the gift. I need columns for donor name, salutation, address, etc, as well as their total lifetime giving, gifts this year, gifts last year, and information about the gift that came last week."

An overgrown garden path with chain across and a sign, "Path Closed."

In Salesforce language, in the data model of the new clouds, the request is:

Report Type: a custom report type (along the lines of Accounts with Gift Transactions)

Filter: Transaction Completion Date = LAST WEEK

Filter: Total Gifts Amount >= $1,000

Uh oh! Total Gifts Amount (the rollup that represents total lifetime giving for a donor) is neither on Account nor on Gift Transaction. It's on Donor Gift Summary, an extension object. Not a field available in this report type! And columns for Gifts This Year, and Gifts Last Year also aren't available either.

Try as you might, there is no way to modify the custom report type Accounts with Gift Transactions to show fields on Donor Gift Summary. First of all, you can't add Donor Gift Summary as one of the objects in that report type. It's simply not choosable. And if you make a report type of Accounts with Donor Gift Summaries, you can't add Gift Transactions.

If you're familiar with building custom report types, you might think about adding fields by lookup.


Donor Gift Summary is child to Account—how would Salesforce know which of the child DGS records to pull from? There is no way to patiently explain to the custom report type builder, "there will only ever be a single DGS for any account, so use that one." For the same reason, you can't add formula fields on Account to pull this information up either. (Theoretically you could create rollup summary fields, but you’d run out of those pretty quickly.)

We simply can't make this report. ⛔️

Now let's consider whether this is an unreasonable or unforeseeable reporting need? 🤔 No. In fact, I think something like it is very common, if not ubiquitous, in fundraising organizations.

Putting fields on extension objects instead of where they really belong has just broken the most basic Salesforce analytics. 🤦🏻‍♂️ This gifts and rollups problem is just one example. Impossibilities in reporting turn up in multiple places in NPC/EdCloud. It's a critical issue.

[I actually have come up with a method to be able to solve reporting like this. But it’s not a trivial fix on many levels. I’ll write about how to do this in my next post. But the idea that every fundraising org would have to build something like what I' going to write about is more than a little ridiculous. Just because I figured out how to solve it, doesn’t mean the problem isn't very very bad.]

Late Edition: Merging Broken Too

Freebie the Newspaper Boy (again)

Do you sometimes find duplicate contacts in your org? (Or, for the new clouds, duplicate person accounts?) I bet you do.

It's already enough of a pain to find them, merge them, and then prevent new ones from appearing.

What if I told you that you won't be able to merge duplicates? It turns out that if there are 1:1 extension objects on both of the records you're trying to merge, the merge will fail!

Imagine duplicate donors, probably caused when someone donated with their personal email the first time and their work a few months later. Both will have donor gift summaries (DGSs) storing the rollups about their donations. When you try to merge, the system is going to reparent both DGS records to the remaining parent account. But the DGS field that looks up to the account is DonorId and it's set to be unique. In the reparenting process they are both getting the same DonorId, which isn't allowed. So the merge fails.

This failure will occur for any 1:1 extension objects, as far as I can tell. But let's assume the only thing both duplicates have are donor gift summaries. In that case you'll get an ugly error:

A red error pop-up that says: We couldn't merge these accounts. duplicate value found: DonorId duplicates values on record with id 6SXH0000000PD270AG

As an experienced admin with access to the back end and a good deal of hard-earned obscure knowledge, I can figure out that a record Id starting with "6SX" is a Donor Gift Summary. Then I can delete one of the records and let the merge go through. Since rollups are generally recalculated over night, the remaining DGS will be inaccurate for up to 24 hours, but at least it will fix itself.

Of course regular users aren't going to know what to do in this situation. And that was a relatively easy fix, because DGSs get recalculated each night. There are other extension objects that don't get calculated, let alone recalculated. (Such as Contact Profile.) For those, someone is going to have to manually compare the values to determine which to keep, transfer them to the record that will remain, delete the records that are no longer needed, and then do the merge.

A Solution Only an Engineer Could Love

Extension objects instead of putting fields on the object they describe just don't make sense. Nobody would ever build this way when customizing Salesforce. In fact, I think my example of the reporting problems makes it pretty clear that this would, in fact, be a wrong architecture choice. But with the decision already made that there will be no managed package, I trust that the software engineers at Salesforce have done the best they could. It just doesn’t actually fix anything.

A T Shirt Design: Engineering: Solving problems you didn't know you had in ways you can't understand.


Recent Posts

See All


bottom of page