GTM Systems
We Built the Same Field Twice Because Nobody Drew the Object Model First
A rep found three "Contract End Date" fields on the Account and asked which one was real. None of them were. Here is why you architect the object model before you create a single custom field, and what skipping that step actually costs.
· 8 min read
A rep pinged me on a Tuesday with a screenshot. Three fields on the Account, all named some version of “Contract End Date,” and she wanted to know which one to trust for a renewal. The honest answer was none of them. One was populated by an integration that had been switched off eighteen months earlier. One was a formula pointing at a lookup that half the accounts did not have. One was free text that a 2022 data load had filled with strings like “EOY” and “ask legal.” Three fields, one question, zero answers. Nobody had ever drawn the object model, so every team that needed a contract date had created their own, and the org quietly accreted the same concept three times over.
That is the failure I want to talk you out of. The instinct when a stakeholder asks for a field is to go make the field. It takes ninety seconds in Setup and it makes the requester happy. The problem is that a custom field is not a note; it is a column in a schema that will outlive the person who requested it, feed reports, drive automation, and get copied into three integrations. You are making a data-modeling decision, and if you make it one field at a time with no map, you get the three-contract-date org. Architect the model first, on paper, before you touch Setup.
A field is a schema decision wearing a costume
The reason field sprawl feels harmless is that each field, on its own, is harmless. The cost is not in any single field; it is in the interactions between hundreds of them that no one designed. Every custom field you add shows up in page layouts, report type field lists, SOQL you write later, and the field-level security matrix on every profile and permission set. A hundred fields is a hundred FLS rows per profile. When a rep says “I cannot find the field I need,” the cause is usually that they are hunting through the sixty near-duplicates nobody culled.
And fields are close to permanent. Creating one takes ninety seconds. Deleting one safely takes weeks, because you first have to prove nothing references it: no report, no list view filter, no formula, no flow, no Apex, no integration mapping. The asymmetry is the whole problem. Cheap to add, expensive to remove, so they only ever accumulate. Watch how the two paths diverge as the org grows.
View as table
| Point | Value |
|---|---|
| Q1 | 120 |
| Q2 | 190 |
| Q3 | 260 |
| Q4 | 340 |
| Y2 | 480 |
| Y3 | 640 |
Draw the entities and their relationships first
Before any field, answer the structural questions on paper. What are the real entities in your revenue motion, and how do they relate? In a standard B2B Salesforce org the spine is Account, Contact, Opportunity, and the line-item and product objects, plus whatever custom objects your motion actually needs (a Contract__c, an Asset, a usage record). The mistake I see most is stuffing a concept onto the wrong object because it was convenient. Contract end date does not belong on the Account; an account can have many contracts over time, so it belongs on a Contract record with a lookup to the Account. Put it on the Account and you have guaranteed the three-field mess the moment a second contract exists.
The relationship type is the decision that is genuinely hard to reverse. Master-detail versus lookup determines ownership, sharing, cascade delete, and roll-up summaries, and you cannot convert master-detail to lookup once the child has data without unwinding it. Decide it deliberately.
Naming and ownership are part of the model
Once the entities and relationships are set, the field-level rules keep the model legible. Name every field so its API name tells you what it is and where it came from. Give each field one owner: the system or team responsible for keeping it correct. The three contract dates existed partly because nobody could say who owned “the” date, so three teams each made one they controlled. A field with no named owner is a field that will be duplicated the first time someone new needs the concept and cannot tell whether the existing one is maintained.
The worked example: one afternoon of paper saved a quarter of cleanup
Model first versus field first
| Field-first (build on request) | Model-first (architect, then build) | |
|---|---|---|
| When a field is requested | Create it in Setup, move on | Check the model: does this concept already exist? |
| Where a concept lives | Wherever was convenient that day | On the entity that owns it, once |
| Duplicates | Three "contract date" fields | One field, one owner |
| Relationships | Discovered later, hard to change | Decided up front, master-detail vs lookup |
| Reporting | Which field is real? | The model tells you |
| Retiring a field | Nobody dares, so it stays | Ownership makes deprecation possible |
Here is how I architect the model before a single field
- 1
List the real entities in the motion
Account, Contact, Opportunity, line items, and the custom objects your motion actually needs. Write them as nouns. If you cannot name the entity, you are not ready to name the field.
- 2
Draw the relationships and pick the type
One-to-many, many-to-many (junction object), lookup vs master-detail. This is the decision that is hard to reverse once data exists, so make it on paper first.
- 3
Place each concept on the entity that owns it
Contract end date belongs on Contract, not Account. Ask "can a record have more than one of these?" If yes, it is a child record, not a field.
- 4
Name and assign an owner to every field
The API name should say what it is. One team or system owns each field and keeps it correct. No owner means a guaranteed future duplicate.
- 5
Only now, create the fields
Build against the map. Every field traces to an entity, a relationship, and an owner. The request that does not fit the model is the one worth a second conversation.
- 6
Re-check the model when a request does not fit
A request that has no home is a signal the model is missing an entity, not a signal to bolt on another orphan field. Extend the model deliberately.
The three contract dates were not a data-quality problem; they were a design problem that showed up as a data-quality problem two years later. A field is the smallest, cheapest, most permanent thing you build in a CRM, and building one without a model is how you end up maintaining the same concept three times and trusting it zero times. Draw the entities, decide the relationships, place each concept once, and give it an owner. Then, and only then, open Setup. The afternoon you spend on the map is the cheapest quarter of cleanup you will ever avoid. Once the model holds, the next fight is the automation that fires on top of it, which is where forty flows fight on save.
Keep reading
One email. Every week.
One email a week: a systems problem I architected or untangled, with the schema, the config, and what I would change. No roundups, no theory, unsubscribe whenever it stops being useful.
The newsletter opens soon.
Connect a provider in src/config.ts