DEV Community

Cover image for The Salesforce Data Model Mistakes That Come Back to Haunt You
Olivia Parker
Olivia Parker

Posted on

The Salesforce Data Model Mistakes That Come Back to Haunt You

Data model decisions in Salesforce have a specific quality that makes them different from most other technical decisions.

They're easy to make. They're hard to unmake. And the consequences of making them wrong don't show up immediately - they show up six months later, or eighteen months later, when the business needs to do something the data model wasn't designed to accommodate and the cost of changing it is suddenly very visible.

I've seen this play out enough times to recognize the pattern. An implementation moves fast, the data model gets built to fit current requirements, everyone's happy at go-live. Then the business evolves - which businesses do, always - and the data model that made perfect sense at launch becomes the thing standing between the business and what it needs to do next. Not broken, exactly. Just expensive to change.

The mistakes that create this situation are usually not dramatic. They're small decisions that seemed reasonable in context and accumulated into constraints. Here's where they consistently show up.

Using Accounts for Everything

Salesforce's Account object is powerful and flexible. It's also the most abused object in the platform.

The temptation is understandable. Accounts are there, they have relationships to everything else, and when you need to track a new category of organization - a partner, a vendor, a subsidiary, a prospect that's not quite a lead - putting it on the Account object with a record type to differentiate it feels clean. Quick to implement. Familiar to users. Done.

The problem arrives when the business needs to treat those categories differently in ways that the record type differentiation can't accommodate. Different page layouts handle different field visibility - fine. Different automation logic based on the Account type - gets complicated. Different sharing rules, different territory assignments, different rollup calculations - each one manageable in isolation, collectively creating an Account object with so many conditional branches in its automation that nobody fully understands what fires when.

The deeper problem is relationship logic. Contacts relate to Accounts. Opportunities relate to Accounts. Activities, cases, contracts - all related to Accounts. When Accounts are carrying multiple conceptually different entities, the relationships between records become ambiguous. Is this Contact a contact for the customer Account or the vendor Account? When those are the same object with different record types, the relationship model gets muddled in ways that affect reporting, automation, and user experience simultaneously.

The decision worth making explicitly early: what is an Account in this org, specifically. Not "organizations we interact with" - that's too broad. The more precisely the Account object is defined, the more consistently it behaves and the less the data model fights you when requirements evolve.

Picklist Values That Encode Process Instead of State

Picklist fields are everywhere in Salesforce. Opportunity Stage. Lead Status. Case Status. Custom picklists on every custom object. They're easy to create and immediately useful.

The mistake is using picklist values to encode process steps rather than genuine state.

There's a difference between a picklist value that describes what something is - "Closed Won", "Active Customer", "Qualified" - and a picklist value that describes what someone did - "Sent Proposal Email", "Left Voicemail", "Scheduled Demo Call". The first type of value tells you the state of a record. The second type tells you what activity happened, which is what Activity records are for.

When picklist values encode process steps, several things go wrong over time. The picklist grows as the process evolves, because every new step in the sales or service process gets a new value. Reporting becomes complicated because the values represent actions rather than states, making it hard to answer simple questions like "how many opportunities are currently at the proposal stage" when "proposal stage" is mixed in with a dozen action-based values. Automation that fires based on picklist values becomes brittle because the values have implicit ordering assumptions baked in.

The fix requires distinguishing clearly between state - what a record is - and activity - what happened to it. State goes in picklists. Activity goes in activity records, tasks, or separate tracking fields. This distinction, made clearly at data model design time, prevents the picklist bloat that makes Salesforce orgs increasingly hard to report on over time.

Ignoring the Implications of Many-to-Many Relationships

Salesforce's standard relationship types are lookup and master-detail. One-to-many. Clean, simple, and insufficient for a significant category of real business requirements.

Many-to-many relationships - a Contact who belongs to multiple Accounts, an Asset linked to multiple Cases, a Product associated with multiple Campaigns - require junction objects. Salesforce supports this through custom junction objects, but implementing them well requires thinking through implications that aren't immediately obvious.

Which object is the master in each master-detail relationship on the junction object matters for ownership, sharing, and cascade delete behavior. Getting this wrong doesn't break anything visibly at implementation time. It creates data integrity problems and sharing behavior surprises later when real data accumulates and real users interact with the junction records in ways the implementation didn't anticipate.

The teams that get this right spend time during data model design explicitly working through the cascade delete scenarios. If the junction object has a master-detail to Object A, deleting a record in Object A cascades to delete all junction records - and through them, potentially affects relationships to Object B in ways that need to be understood before they happen to real data.

This work is not exciting. It's the kind of thing that gets skipped when implementation timelines are tight and everything seems to be working correctly in a sandbox with test data. It's the kind of thing that produces data integrity incidents in production with real data volumes.

Custom Object Proliferation

At some point in almost every Salesforce implementation, someone decides they need a custom object to track something specific. Then another one. Then another one. Each decision is locally reasonable. The cumulative result is an org with thirty custom objects, unclear relationships between them, overlapping data, and no coherent data architecture.

Custom object proliferation happens because the immediate cost of adding a custom object is low and the long-term cost is deferred. Creating a new object is fast. The cost shows up when you need to understand how all the objects relate to each other, when you're trying to build a report that spans multiple objects, when you're onboarding a new admin who needs to understand the data model, or when you're trying to automate a process that touches records across multiple objects.

The discipline that prevents this is asking, before creating a new custom object, whether the data really needs its own object or whether it belongs on an existing object with additional fields or a child relationship. Custom objects make sense when the data has its own lifecycle, its own relationships, its own reporting requirements. They don't make sense as a way to add a few fields to an existing object without "cluttering" it.

The clutter of extra fields on an existing object is much cheaper than the architectural clutter of unnecessary custom objects. Page layouts and field-level security handle field visibility. A proliferating object model doesn't have an equivalent cleanup mechanism.

Field Naming That Made Sense to One Person Once

This one is less architectural and more operational but it comes back to bite teams consistently.

Salesforce fields have API names that are set at creation and cannot be changed. The label - what users see - can be changed. The API name - what automation, code, and integrations reference - cannot. A field created as "Initial_Contact_Date_c" when the team was thinking about it one way will still be "Initial_Contact_Date_c" in every Flow, every Apex class, every API integration, and every data export five years later when the concept has evolved into something different.

Teams that don't think carefully about API names at field creation time accumulate fields whose API names no longer reflect what the field actually stores - because the field's purpose evolved but the API name couldn't. This creates confusion for every developer and admin who works in the org after the original implementation team. It makes code harder to read. It makes data exports harder to interpret.

The convention worth establishing before field creation begins: API names should be generic enough to remain accurate as the field's use evolves, specific enough to be meaningful without the label, and consistent with whatever naming convention the team has agreed on. Establishing this convention after a hundred fields have been created is retrofitting work that rarely fully happens.

Not Planning for Reporting at Data Model Time

Reports are the mechanism through which the business gets value out of the data in Salesforce. If the data model doesn't support the reports the business needs, the data model hasn't done its job.

This sounds obvious. It's consistently underweighted during data model design, which tends to focus on data entry and automation rather than data retrieval. Relationships that make data entry clean don't always make reporting clean. Data that's normalized for storage efficiency sometimes needs to be denormalized for reporting. Fields that capture process detail at a granular level may not aggregate in the ways that business dashboards require.

The discipline that prevents this is reviewing reporting requirements alongside data model design - not after the model is built. What are the ten most important questions this org needs to be able to answer? Can the proposed data model support those reports? If not, what needs to change?

This conversation is harder to have during implementation because business stakeholders often can't articulate reporting requirements until they see what's possible. The implementation that handles this well creates a feedback loop - preliminary reports built on the proposed model, reviewed with stakeholders, used to identify gaps in the data model before it's finalized rather than after it's in production.

Conclusion

Salesforce data model mistakes are expensive not because they break things but because they constrain things. The org still works. It just doesn't work the way the business needs it to work as requirements evolve, and changing a data model that has real data in it and real processes depending on it is genuinely difficult work.

The decisions that prevent these problems are made during implementation, often in conversations that feel theoretical at the time. What exactly is an Account? What state does this picklist actually represent? What reports does the business need to run? These questions feel less urgent than the visible progress of building screens and automation. They're more important.

Partnering with a Salesforce development company like Hyperlink InfoSystem that treats data model design as a first-class deliverable - not a precursor to the real work but the foundation that determines whether everything built on top of it holds up - is what changes the outcome eighteen months after go-live.

The data model mistakes that haunt you are the ones that felt like small decisions at the time. They almost always are.

Top comments (0)