Category: Salesforce

  • Identity resolution in Data Cloud: the unglamorous work every agent depends on

    Nobody gets excited about identity resolution. It doesn’t demo well. There’s no slide where you show a reconciliation rule and the room leans in. But every time I’ve seen an Agentforce rollout wobble after go-live, the root cause traces back to the same place: the agent didn’t actually know who it was talking to.

    Data Cloud’s whole pitch is a unified view of the customer. Identity resolution is the machinery that builds that view. If it’s loose, every downstream capability, segmentation, personalisation, and now agent reasoning, inherits the mess.

    What identity resolution is actually doing

    Strip away the marketing language and it’s three steps. First, match rules decide which records across your Data Model Objects probably belong to the same person, using identifiers like email, phone, or a loyalty ID. Second, reconciliation rules decide which source wins when two matched records disagree on a field value, most recent, most trusted source, or a custom rule you define. Third, the result gets written to a unified individual profile that every other Data Cloud capability reads from.

    That sounds clean in a diagram. In a real org it’s messier, because the inputs are messier. A contact-centre case system might use a work email. A marketing platform might have a personal one, captured years ago through a campaign form. A loyalty system might key on phone number and nothing else. Exact match on email alone will quietly fail to link all three, and you’ll end up with three “unified” profiles for one actual customer.

    A scenario I keep seeing

    Picture a contact-centre team that’s just turned on an Agentforce service agent. A customer calls in, the agent looks up the profile, and it finds one of the three fragments, the one tied to the phone number, not the one with the full purchase history sitting under the old email. The agent answers politely and wrongly, because it was never shown the full picture. Nobody wrote a bug. The match rules just weren’t tuned for how this business actually collects data.

    This is the part people miss: identity resolution failures don’t throw errors. They just produce a confidently wrong answer, which is a worse failure mode than a system that visibly breaks.

    Why it matters more once an agent is involved

    A human service rep who gets handed a thin profile usually notices something’s off and asks a clarifying question. An agent, especially one running a scripted topic with a defined action, often doesn’t. It takes the profile it’s given as ground truth and reasons forward from there. Feed it half a customer and it will give you a confident, half-right decision.

    And the stakes go up with the kind of actions an agent takes. A recommendation engine with a fragmented profile gives you a slightly worse suggestion. An agent with write access that updates a case, applies a discount, or triggers a refund based on a fragmented profile can take an action against the wrong history entirely. Identity resolution isn’t just a data quality nicety anymore. It’s the input validation layer for everything built on top of it, and I’d put it ahead of almost anything else on the checklist before an agent gets real write access.

    Where this quietly breaks in practice

    A few patterns show up again and again.

    • Match rules left on the out-of-box defaults, never tuned against a sample of real source data before go-live.
    • Exact-match-only logic on fields that are inherently inconsistent, like name or address, where fuzzy matching or standardisation would catch far more true matches.
    • No ongoing monitoring of the unification rate, so a source system change six months later (a new form field, a renamed source object) silently degrades matching and nobody notices until someone complains.
    • Reconciliation rules that default to “most recent wins” everywhere, even for fields where a specific system should always be the trusted source, like a billing address that should come from the ERP, not whichever marketing tool touched the record last.
    • Merge and unmerge handled as an afterthought, so when identity resolution gets it wrong, there’s no clean process to split a profile back apart without losing history.

    What actually helps

    Start with a sample of real production-like data before you finalise match rules, not synthetic test records that are too clean to reveal anything. Pull a few hundred genuine customer records with the kind of inconsistency your business actually produces, and run your match rules against them by hand. You’ll find the gaps fast.

    Be deliberate about which identifiers you trust for matching and in what combination. Email alone is rarely enough once a business has more than one channel of contact. Layer in phone, and where you have it, a persistent identifier like a loyalty number or account ID that doesn’t change when someone switches jobs or email providers.

    Set reconciliation rules field by field, not as one blanket policy. Billing-sensitive fields deserve a named trusted source. Preference fields are usually fine on recency.

    And treat the unification rate as a metric worth watching on an ongoing basis, the same way you’d watch API error rates or job failures. A sudden drop after a source system change is a signal, not noise.

    Before you let an agent act on any of this

    If you’re building toward agent write access, identity resolution deserves a review of its own before that conversation even starts. Ask what happens when the agent is handed a fragmented profile: does the topic design account for incomplete data, or does it assume a fully unified customer every time? If the answer is the latter, that’s a gap worth closing before the agent goes live, not after the first wrong decision gets escalated.

    The unglamorous work pays for itself quietly. Nobody notices good identity resolution. Everyone notices the agent that got it wrong.

  • Koa and Salesforce’s own reasoning model: what it means for agent cost and latency

    Every Agentforce build eventually hits the same question: which model should actually answer this topic? Salesforce now gives you a real choice instead of a default. Point the agent at its own reasoning-tuned model, Koa, running inside the platform, or bring your own through the BYOLLM framework and call out to Claude, GPT, or whatever your org has already standardised on. That choice isn’t cosmetic. It changes your latency budget, your token bill, and honestly, how much you trust the agent’s output when a case gets weird.

    What Koa is actually for

    Koa sits inside the Agentforce stack as the model Salesforce built and tuned specifically for the reasoning steps an agent runs through: picking a topic, choosing an action, deciding whether it has enough grounding to answer or needs to hand off. It isn’t trying to be a general-purpose chat model. It’s trying to be fast and cheap at the narrow job of routing and tool selection, the parts of an agent’s turn that happen dozens of times before the user ever sees a sentence of output.

    That’s a different design goal to a frontier model like Claude, which you’d reach for when the actual content of the response needs real reasoning depth: summarising a messy case history, drafting a response that needs genuine judgment, working through a multi-step policy question. Mixing the two, a fast native model for orchestration and a stronger external model for the parts that need it, is where most of the interesting architecture decisions live right now.

    Why this isn’t just a pricing table exercise

    I’ve seen teams try to settle this by comparing per-token rates and calling it done. That misses most of the real cost. An Agentforce turn isn’t one model call. It’s a chain: intent detection, topic selection, grounding retrieval, action input validation, and only then a response. If each of those hops goes out to an external model over the network, you’re paying for round trips you didn’t budget for, and your users are feeling every one of them as delay. Koa running natively inside the platform skips a chunk of that network cost because it’s sitting next to the data it needs.

    Where latency actually bites

    In a contact-centre deployment, the latency that matters isn’t the model’s raw inference time. It’s everything wrapped around it: a grounding query against Data Cloud, a permission check, an Apex action with its own callout, then back to the model to decide what to do with the result. Add an external LLM into that loop and you’ve added another network hop, another cold-start risk, another vendor’s rate limit sitting between your agent and the answer. For a customer sitting on hold, a few hundred extra milliseconds per reasoning step adds up fast across a multi-turn conversation.

    This is the practical argument for using Koa, or any native reasoning model, for the orchestration layer: topic and action selection, slot filling, deciding when to escalate to a human. Reserve the external model call for the one or two points in the flow where you genuinely need deeper reasoning or a model your org has already validated for a specific kind of output.

    When I’d still reach for BYOLLM

    Native isn’t always the right call. A few situations where I’d bring in an external model anyway:

    • Your governance team has already done the work to validate a specific model for a regulated use case, and redoing that validation for a new model isn’t worth the saved latency.
    • The task needs stronger reasoning than routing requires: drafting a response that has to read as careful and specific rather than templated, or working through an ambiguous multi-step request.
    • You’ve got existing prompt engineering, evaluation sets, or fine-tuning invested in a particular model, and swapping it out would mean redoing that work for a marginal latency gain.
    • Data residency or contractual terms point you at a specific vendor regardless of what’s fastest.

    None of those are reasons to route every single turn through an external model by default. They’re reasons to be deliberate about which specific actions get the expensive call and which ones don’t.

    Designing actions so the model choice doesn’t matter later

    The mistake I’d actively guard against is baking a specific model’s quirks into how you write topics and actions. If your instructions are tuned around how one model happens to interpret ambiguous phrasing, you’ve built something fragile. Keep the action’s input schema strict, keep the instructions plain and unambiguous, and let the model, whichever one you’ve pointed at it, do the narrower job of filling in well-defined slots rather than guessing at intent from loose prose.

    That discipline pays off twice. It makes the agent easier to test, because you can swap Koa for an external model in a sandbox and check whether behaviour holds. And it means that when Salesforce next ships a meaningful update to its reasoning model, or your org decides to standardise on a different external one, you’re changing a model selector, not rewriting every topic in the agent.

    The actual decision framework

    Boiled down, here’s how I’d set the default for a new Agentforce build. Route orchestration and routine action selection to the native reasoning model first. Reserve external model calls for the specific actions where output quality depends on deeper reasoning, not just correct routing, and name those actions explicitly rather than leaving the choice implicit. Measure latency per turn, not per model call, because that’s what your users actually experience. And revisit the split every time Salesforce updates Koa or your org re-evaluates its external model contracts. This isn’t a decision you make once and forget.

    Cost and latency used to be an infrastructure conversation that happened after the agent was built. With model selection now a first-class design decision in Agentforce, it’s part of the build itself. Worth getting the split right before go-live, rather than discovering the bill, or the hold time, three months in.

  • What I check in the first 30 minutes on a new Salesforce org

    You get dropped into a new org with no handover doc, maybe a Slack message that says “here’s access, good luck.” This happens constantly in consulting and platform support work: a client project starts, the previous partner is gone, and you’re the one who has to figure out what you’re actually dealing with before you write a single line of code. I’ve done this often enough now, across Service Cloud, Marketing Cloud and straight enterprise Apex orgs, that I’ve got a fixed order I run through. Not because the order is sacred, but because skipping steps is how you end up three weeks in, discovering the sharing model is held together with a single “grant access using hierarchies” checkbox and a prayer.

    Start with who can see what, not what the org does

    The instinct is to go look at the automation first, because that’s where the interesting bugs usually are. Resist it for ten minutes. Security model first, every time.

    I check org-wide defaults before anything else. Private, Public Read Only, Public Read/Write on the big objects (Account, Contact, Case, whatever custom objects carry the real business process). That one setting tells you more about how the org actually works than a week of reading Apex. An org with everything set to Public Read/Write usually means sharing rules were never designed properly, just avoided. An org with everything Private and forty sharing rules layered on top means someone tried to do it right and the business kept adding exceptions.

    Then profiles and permission sets

    Count them. A client org running a few hundred users with sixty profiles is telling you something: profile sprawl almost always means permissions got bolted onto profiles instead of layered through permission sets, which means every new requirement turns into a new profile clone instead of a clean add. I pull the profile-to-user-count breakdown early. If half the profiles have one or two users assigned, that’s legacy cruft nobody cleaned up.

    Automation habits tell you about the team, not just the org

    Once I’ve got the security shape, I move to automation. Not to read every flow line by line, that comes later. I’m counting and categorising.

    • How many triggers exist per object, and is there one trigger per object or five fighting each other?
    • Are flows and Apex triggers doing the same job on the same object, silently racing?
    • Is there a trigger framework, or did everyone just add an “if” block to the last trigger that existed?
    • Field update automation: are there process builders still live alongside newer flows, both firing?

    This is where you find the actual risk. I’ve walked into orgs where a single Account update triggered four separate automations that each queried the same related list, none of them aware the others existed. Nobody intended that. It accreted, release by release, because nobody was checking what already ran before adding something new. Thirty minutes won’t tell you every interaction, but it tells you whether bulk safety was ever a design principle or an afterthought.

    A quick governor-limit sanity check

    I’ll run a rough check on the heaviest objects: largest data volume, most automation attached, most integration traffic. If those objects also have SOQL inside loops or non-bulkified DML, that’s not a future problem, that’s a live one waiting for the next data load or integration batch to hit a limit. Worth flagging immediately, before you’re blamed for a failure that predates you.

    What’s actually bolted onto the data model

    Next, the object model itself. Standard objects customised past recognition are common and not inherently bad, but I look for the tells of a model that grew without anyone stepping back: custom fields with names like Temp_Field_2__c still in active use, lookup relationships where a master-detail should exist (or the reverse), and record types that have multiplied because someone needed “basically the same thing but slightly different” and nobody wanted to refactor.

    I also check whether Marketing Cloud and Service Cloud (when both exist) actually agree on where contact data lives, or whether there are two versions of the truth syncing in one direction only. That mismatch is one of the most common sources of “why does this contact look different depending on which system you’re in” tickets, and it’s rarely obvious from the data model diagram alone, if one even exists.

    Integration points: where the org actually talks to the outside world

    Every org has integration points, and most of the ones that cause outages weren’t designed, they accumulated. I look for Named Credentials and External Credentials first, because that tells you what’s connected and how recently someone bothered to use the newer, more secure pattern versus a legacy hardcoded endpoint sitting in a custom setting from 2019. Then API usage: is the org anywhere near its daily limit, and if so, is that from one chatty integration or genuinely high real-time volume? Those two situations need completely different fixes.

    Release process, because it tells you what breaks next

    Last thing in the thirty minutes, and arguably the most predictive: how does code actually get to production here? Is there a CI pipeline with real test gates, or is someone doing manual change sets at 6pm on a Thursday? I’m not judging the team for whatever I find, every org has a history and most of these patterns exist for a reason that made sense at the time. But the release process tells you how fragile the next six months will be, and whether the technical debt you’re seeing in the data model and automation is actively getting worse or is at least being managed.

    Why this order, specifically

    Security first because it’s the hardest thing to retrofit safely once business processes depend on the current (possibly wrong) access model. Automation second because that’s where live risk sits. Data model third because it’s slower-moving and more forgiving of a short delay in understanding it properly. Integrations and release process last in this pass, not because they matter less, but because by that point you’ve got enough context to actually interpret what you’re looking at instead of reading it cold.

    None of this replaces a proper discovery phase. Thirty minutes won’t surface everything, and it shouldn’t try to. What it gives you is an informed starting point: the three or four things worth asking the client or the previous team about before you commit to an approach, and the confidence that whatever you build next won’t quietly collide with something that was already there.

  • Admin vs developer vs architect: where the real leverage sits on a Salesforce team

    Walk into most Salesforce teams and the org chart tells you almost nothing about where the impact is actually coming from. You’ll see an admin, two or three developers, and an architect wheeled in for the bigger projects. Six months later, look at what actually moved the needle: fewer cases reopened, a release that didn’t need a weekend rollback, an integration that kept running when volume tripled at month end. Trace any of those back and the person responsible rarely matches the title on their badge.

    I’ve worked across all three roles over the years, often in the same week on the same project. The lines between them get drawn on org charts for hiring and pay bands, not for where the actual work happens. Worth being honest about that, especially if you’re trying to build a team or figure out where you fit in one.

    What each title is actually sold as

    The admin owns configuration: page layouts, flows, validation rules, reports, user setup. The developer owns code: Apex, Lightning Web Components, integrations, anything that needs a compiler. The architect owns the big picture: data model, sharing design, how the clouds talk to each other, the decisions that are expensive to reverse once twenty people are building on top of them.

    That’s the textbook version. It’s clean, it’s teachable, and it’s wrong often enough that I wouldn’t hire against it.

    Where the real work actually sits

    Admins who quietly run architecture

    I’ve seen admins who’ve been inside one org for six or seven years make better data model calls than architects parachuted in for a quarter. They know why that one field got added in 2019, which three automations depend on it, and which “simple” change will break a contact-centre workflow nobody’s touched the Visualforce page for since 2017. That’s architectural knowledge. It just doesn’t come with the title or the pay.

    Developers who never touch strategy

    Plenty of developers write clean Apex, pass code review, ship on time, and never once ask whether the trigger they’re adding should exist at all. Good hands, no say in the decisions that determine whether those hands are building the right thing. Useful, but not where the leverage sits.

    Architects who’ve never run a sandbox refresh

    And I’ve met architects who can draw a beautiful diagram of an event-driven integration pattern and have never sat through a deployment that failed at 11pm because a validation rule referenced a field that didn’t exist in the target org yet. Their diagrams are correct. Their diagrams also don’t survive contact with a live org that real people use every day.

    Where the leverage actually sits

    Not in the title. In the combination of three things, regardless of who holds them: enough technical depth to know what’s actually possible, enough org-specific history to know what’s actually safe, and enough authority (formal or earned) to say no to a bad idea before it ships.

    On a team I supported earlier in my career, the person who moved a customer-acquisition journey from 96 clicks down to 17 wasn’t the most senior architect in the room. It was someone who understood the Apex underneath the flow, had sat with the agents using it every day, and had enough credibility to push back on a product owner who wanted to add three more fields “just in case.” Title didn’t matter. Combination did.

    That’s the pattern I’d bet on every time: depth plus context plus the standing to act on both. Any one alone gets you competent work. All three together is what actually changes outcomes.

    The mistake most teams make when staffing

    Teams hire for the title and then wonder why nothing improves. They bring in a senior architect for six months, get a diagram and a data model, and watch it sit unimplemented because nobody closer to the ground had the context to execute it against the mess of automations already in production. Or they load up on developers to “get through the backlog” and end up with a pile of well-tested, individually correct components that collectively make the org harder to reason about than before anyone touched it.

    The fix isn’t more senior titles. It’s making sure at least one person on the team has all three things at once, and giving that person room to actually use the authority, not just the knowledge.

    What I look for instead of the title

    When I’m sizing up a Salesforce team, I ignore the title split for a minute and ask three questions instead. Who in this room actually understands why the data model looks the way it does, not just what it looks like? Who gets pulled into a room when something’s about to go wrong in production, regardless of their pay grade? And who’s allowed to say “we’re not building that” and have it stick?

    Sometimes that’s one person. Sometimes it’s split across two or three. Either way, that’s your real leverage point. Everything else on the team is support for that centre of gravity, useful and necessary, but not where the outcome gets decided.

    For anyone trying to grow into this

    If you’re an admin wondering whether you need a developer title to matter more, or a developer wondering whether you need “architect” on a business card before anyone listens to you, stop waiting for the title. Go get the second and third piece instead. Learn enough of the layer above and below your current work that you can hold a real conversation about trade-offs, not just execute a ticket. Sit in on the production incidents even when nobody asked you to. Earn the standing to say no once in a while, and use it sparingly enough that it means something when you do.

    The title catches up eventually. It’s rarely the thing that got the work done in the meantime.

  • Field-level security audits: what most orgs get wrong

    Ask most Salesforce admins when they last audited field-level security and you’ll get a pause, then something like “we set that up at go-live.” That’s usually true. It’s also the problem. Field-level security isn’t a one-time configuration task. It’s a control that decays every time someone adds a permission set, clones a profile, or wires up a new integration user. I’ve walked into more than one org where the field security model hadn’t been touched in years, while the data model underneath it had doubled.

    The gap between “configured once” and “actually enforced” is where most of the real exposure sits. Here’s what I check for, and what I tell clients to check for themselves before their next release.

    Why these audits get skipped

    Nobody schedules a field-level security review because nothing visibly breaks when it’s wrong. A missing validation rule throws an error. A broken flow fails loudly, in front of a user, immediately. Over-permissioned access just sits there quietly until a support rep edits a field they shouldn’t, or someone in a mid-tier role sees salary data on a contact record because they got added to a permission set six months ago for an unrelated reason and nobody ever removed it.

    It’s also tedious work in the standard UI. Clicking through Field Accessibility for forty fields across twelve profiles is not where anyone wants to spend a Friday afternoon. So it gets deferred. And deferred work on a shared, constantly-changing org compounds.

    Three mistakes that show up in almost every org

    Treating page layouts as security

    Page layouts control what a user sees on a record page. They don’t control what a user can query, export, or touch through the API, a list view, a report, or a flow. I’ve seen teams remove a field from a layout and call the data protected. It isn’t. Anyone with read access to that object and field through their profile or permission set can pull it through a report export in under a minute, layout or no layout.

    If a field needs to be hidden, hide it with field-level security, not a layout edit. If the data genuinely shouldn’t leave the org at all, you’re looking at encryption, masking, or a separate object, not a UI trick.

    Permission set sprawl nobody reviews

    This is the big one. A project needs a field unlocked for three people. Someone creates a permission set, assigns it, and moves on. Eighteen months later that permission set has forty assignees, half of whom changed roles twice since, and nobody remembers what it was originally for. Multiply that across every enhancement an org has shipped and you end up with a permission model that bears no resemblance to the org chart or the actual job functions using the system.

    The fix isn’t clever tooling, it’s discipline. Name permission sets by purpose, not by project code. Review assignments against current role on a fixed schedule, not whenever someone happens to notice. Retire anything that hasn’t granted meaningful access in a year. Permission set groups help here too, because they let you reason about access in terms of a job function rather than a pile of individually stacked sets.

    Integration users with god-mode access

    Every org I’ve reviewed has at least one integration user with View All and Modify All on half the data model, set up that way years ago because it was faster than scoping the actual fields the integration needed. That user is now a single credential with more reach than most of your human admins, usually with a password that hasn’t rotated and permissions nobody remembers granting. If that credential leaks, through a misconfigured middleware tool or a departed contractor’s saved session, the blast radius is the whole object graph, not just the three fields the integration actually reads.

    Scope integration users to exactly the fields and objects the integration touches. It’s more setup work up front. It’s a lot less work than explaining a breach to the business afterward.

    What an actual audit looks like

    I don’t rely on clicking through Setup for this. A few things I run on every org before a release, or on a quarterly cadence for anything handling sensitive data:

    • Run Security Health Check first. It’s a blunt instrument, but it catches the obvious misses: weak password policies, loose session settings, sharing defaults left wide open where they shouldn’t be.
    • Query FieldPermissions and ObjectPermissions directly through the Tooling API or a short SOQL script rather than clicking through dozens of profile pages one at a time. It takes minutes and gives you a flat list you can actually diff against last quarter’s export.
    • For the fields that genuinely matter (financial data, health information, anything covered by the Privacy Act), build a short list of who should be able to see that data by job function, then compare it against who can actually see it through every profile and permission set combined. The gap between those two lists is your real finding.
    • Check integration users as a separate category from human users. They should always come out as the tightest, never the loosest.

    Building it into the release cycle instead of a one-off

    The teams that keep this clean don’t run a heroic annual audit. They treat field-level security as part of the definition of done for any change that touches a profile, a permission set, or a new integration. One line in the release checklist, does this change grant access to a new field and if so to exactly whom, stops most of the sprawl before it starts. It’s a small cost paid often instead of a large one paid rarely, and rarely is exactly when the regulator or the breach report shows up.

    None of this needs new tooling or a platform migration. It needs someone willing to run the query, read the output, and remove access that stopped making sense a while ago. Most orgs have precisely zero people doing that right now. That’s the actual gap, and it’s a cheap one to close.

  • How to scope a Salesforce enhancement so it doesn’t quietly become three

    A stakeholder asks for one thing: a validation rule so reps can’t close a case without entering a resolution code. Half a day’s work, by the sound of it. Three weeks later you’re deep in a Flow that also updates a custom object, fires an email alert, and feeds a new field into a report someone in finance now quietly depends on. Nobody sat down and decided to build all that. It just accumulated, one reasonable-sounding addition at a time.

    This is the most common way Salesforce enhancement work goes sideways. Not through bad estimates. Through scope that was never actually scoped in the first place.

    Why this happens more on Salesforce than elsewhere

    Declarative tools make it cheap to say yes to one more thing. There’s no build step, no deploy pipeline slowing anyone down mid-conversation. You can add a field, wire up a Flow branch, and show the stakeholder the result in the same meeting. That speed is the whole appeal of the platform, and it’s also exactly why scope drifts. When the cost of “just one more field” looks like zero, nobody treats it as a decision.

    Stakeholders see this too, even if they can’t name it. They watch you build live, so every follow-up thought feels like it belongs in the same ticket. “While you’re in there, can it also notify the queue owner?” “Since we’re touching this object, can we clean up that other field too?” Each ask is small. The sum of them is a different project than the one you quoted.

    Ask what breaks if the answer is no

    Before you open Setup, ask the person requesting the change one question: what happens if we do exactly what you asked, and nothing else? If the honest answer is “that’s fine, it solves the problem,” you have a real scope. If the answer involves three other systems, a different team, or “well, we’d probably also need,” you’ve just found the second and third tickets hiding inside the first.

    Most requesters haven’t separated the must-have from the nice-to-have in their own head yet. That’s not a failure on their part. It’s your job to pull that apart, not theirs to hand it to you pre-sorted. A five-minute conversation here saves a week of rework later.

    Write the scope down before you touch anything

    One sentence, before any configuration work starts: “This enhancement will do X for Y users, and nothing else.” Put it in the ticket. Get the requester to read it back and confirm. It feels bureaucratic for something that might take an afternoon, and that’s exactly why people skip it, and exactly why skipping it is how afternoons turn into weeks.

    This isn’t about process for its own sake. A written scope is the thing you point back to when someone asks, two weeks in, why the notification piece isn’t in yet. Without it, every later addition feels retroactively like it was always part of the plan. With it, you have a clean line: that’s a new request, let’s size it separately.

    Where the quiet third ticket usually hides

    A few patterns show up again and again in Salesforce work specifically:

    • Related automation. You add a field, and someone asks for a Flow to populate it automatically from somewhere else. That’s a second build, with its own edge cases and its own testing.
    • Reporting asks. “Can we track how often this happens?” sounds like adding a report. It often means a new field, a new way of recording history, and a dashboard nobody scoped.
    • Data cleanup. Half the time a new validation rule surfaces how messy the existing data is. Backfilling records so the new rule doesn’t lock users out of thousands of existing cases is its own project, and it’s rarely small.
    • Permission changes. A new field or object usually needs field-level security and permission set updates across every profile that touches it. On an org that’s been customised for years, that step alone can take longer than the change that triggered it.

    None of these are wrong to build. They’re just not the thing that was originally asked for, and they deserve their own estimate, their own priority, and their own sign-off.

    A practical way to hold the line

    When a new idea surfaces mid-build, and it will, don’t shut it down and don’t just absorb it either. Write it down as its own line item, note roughly how big it looks, and ask the requester to rank it against whatever else is in the queue. Most of the time this is enough to settle the person down: they weren’t attached to getting it this week, they just didn’t want the idea lost. Parking it properly protects both the current ticket and the idea itself.

    Keep a running “related but separate” list on every enhancement of any real size. On a recent piece of platform support work, that list is what let a reporting change ship on time while three follow-on requests moved into the next sprint instead of silently inflating the first one. The original ticket closed in the time it was quoted for. The other three got proper attention later, with their own scope and their own testing, instead of being bolted onto something that was never designed to carry them.

    What this actually protects

    Scoping tightly isn’t about saying no to good ideas. It’s about making sure every piece of work gets built with the same care, instead of the first idea getting a proper design conversation and the fourth one getting squeezed in under time pressure with no testing to speak of. A Salesforce org that’s grown this way for a few years is usually easy to spot: automation that does three unrelated things because it was easier to extend the existing Flow than start a new one, fields nobody can explain the purpose of, permission sets that grew sideways instead of on purpose.

    The fix costs almost nothing upfront. One sentence of written scope, one honest conversation about what breaks if you say no, and a parking list for everything else. Cheap insurance against the kind of technical debt that takes months to notice and years to unwind.

  • Retiring old automation: the unglamorous work that pays off

    Every Salesforce org I’ve worked in has automation that nobody can explain. A process builder that fires on a field update from 2019. A validation rule that blocks a scenario nobody’s hit in three years. A trigger with a comment that says “TODO: remove after migration” and a commit date from a sprint that ended a long time ago. Nobody built it to be permanent. It just never got removed.

    Why dead automation never dies

    Admins and developers rotate through orgs faster than the automation does. The person who wrote that process builder left the team eighteen months ago. Whoever inherited the org after them didn’t touch it because it wasn’t broken, and “not broken” gets mistaken for “needed.” Nobody wants to be the one who deletes something and breaks a quarter-end report they didn’t know existed.

    So it stays. And it compounds. Every new flow has to account for what’s already running on that object. Every release review takes longer because nobody’s confident what fires in what order, or in what sequence. Governor limits get tighter not because the business grew, but because three generations of automation are all doing slightly overlapping work on the same records.

    I’ve seen this most clearly in case management. A contact-centre team I worked with had five separate pieces of automation touching the Case object: two process builders, a flow, an Apex trigger, and a workflow rule that predated all of them. Three were still firing. Two hadn’t done anything useful in over a year, but they still ran, still consumed execution time, and still showed up as “something to check” every time someone touched a case field.

    Finding out what’s actually still doing something

    Before you can retire anything, you need evidence, not memory. A few places worth checking:

    • Setup Audit Trail tells you when something was last changed. It won’t tell you whether it still matters, but it’s a starting point for dating the automation.
    • Field history tracking on the fields an automation updates. If a flow is supposed to set a field and that field hasn’t changed in six months, the flow either isn’t firing or isn’t doing what it was built for.
    • Debug logs with flow and process builder entries enabled, run over a representative week. This is the most reliable signal: if something doesn’t show up across a week of normal business activity, it’s either rare or redundant.
    • Flow Trigger Explorer, on orgs that have consolidated onto Flow, shows execution order on an object. It won’t catch Apex triggers or Process Builder, so check those separately.

    None of these on their own gives you certainty. Combine them. An automation that hasn’t changed in two years, doesn’t touch any field that’s changed recently, and doesn’t show up in a week of debug logs is a strong candidate. One signal alone usually isn’t enough to act on.

    Talk to the business before you touch anything technical

    The tooling tells you what’s firing. It doesn’t tell you why someone built it. Ask the team that works the object every day whether a specific case status, a specific email alert, or a specific field update actually matters to them. Sometimes the answer is “we stopped using that process two reorgs ago and nobody updated Salesforce.” Sometimes it’s “yes, that one’s load-bearing, leave it alone.” You need both kinds of answers, and only people can give you the second kind.

    Disable before you delete

    Don’t delete automation on day one, even once you’re fairly confident it’s dead. Deactivate it instead, whether that means unchecking “active” on a process builder, deactivating a flow version, or wrapping an Apex trigger’s logic behind a feature toggle. Leave it deactivated for a full business cycle if you can, long enough to hit month-end, quarter-end, and any seasonal process that only runs a few times a year.

    If nothing breaks and nobody notices, you’ve got your proof. Document what you deactivated, when, and why, then schedule the actual deletion for a later release. That gap between “disabled” and “deleted” is what turns this from a guess into a defensible decision. It’s also the step most teams skip, because deleting feels more satisfying than waiting.

    Start small, and keep a list

    Don’t try to clear a decade of automation debt in one release. Pick the most isolated piece first, something like a validation rule with no dependencies, or a workflow rule that only sends an email nobody reads. Prove the process on something low-risk before you go anywhere near a trigger that fifteen other pieces of logic depend on.

    Keep a running kill list as part of the backlog, not a separate cleanup project that gets deprioritised the moment a client asks for something new. Retire one thing most sprints. It won’t show up in a demo, and nobody’s going to thank you for it in a stakeholder meeting. But six months of steady retirement does more for the health of an org than any single new feature, because it’s the difference between every future enhancement fighting through a decade of accumulated logic and every future enhancement landing on a clean surface.

    What it actually buys you

    The payoff isn’t visible in a single release. It shows up over time: fewer “why did this fire twice” tickets, faster impact analysis when someone proposes a change, and a release review that takes an hour instead of an afternoon because the team can actually hold the automation model in their heads. None of that is glamorous. All of it is what separates an org that gets harder to change every year from one that stays workable no matter how long it’s been in production.

  • Multi-cloud Salesforce: keeping Service Cloud and Marketing Cloud in sync without a mess

    Every org I’ve worked on that runs both Service Cloud and Marketing Cloud eventually hits the same wall. Someone asks why a customer got a renewal email three days after their case was closed as churned, and nobody has a clean answer. The two clouds aren’t lying to each other. They’re just updating on different clocks, owned by different teams, built for different jobs.

    Service Cloud lives in Salesforce’s core data model: Accounts, Contacts, Cases, all transactional and largely real-time. Marketing Cloud runs on its own data extensions, built for send volume at scale, syncing back through Marketing Cloud Connector or an ETL job on a schedule someone set once and forgot about. Put the two together without a plan and you get the classic failure: a customer gets marketed to after opting out through a support case, or a case gets opened for someone who left the business three months ago.

    Work out where the truth actually lives

    Before picking a sync pattern, decide which system owns which field. Not “both update it.” One owns it, the other reads it. In most orgs I’ve set up, Salesforce core owns identity: who the person is, their account status, whether they’ve opted out, whether they have an open case. Marketing Cloud owns engagement: open rates, click history, journey state, send cadence. The moment both sides think they own “is this contact active,” you’ve built a conflict engine, not a sync.

    Write that ownership down somewhere real, not just in your head. A one-page field matrix does it: field name, source of truth, direction of sync, what happens on conflict. I’ve seen teams skip this step and spend three sprints later untangling which system’s opt-out flag actually wins.

    The subscriber vs contact trap

    Marketing Cloud’s Subscriber and Salesforce’s Contact or Lead are not the same record, even when Connector makes them look like it. A Subscriber Key tied to a Contact ID works fine until someone merges two Contacts in Service Cloud, or a Lead converts into a Contact, and the Subscriber Key doesn’t move with it. Now a subscriber is sending to a dead record while the live one gets nothing. Test your merge and conversion paths specifically for this. It rarely shows up in the happy-path demo.

    Pick your sync mechanism on purpose

    There are three real options here, and most orgs end up running a mix of all three.

    Marketing Cloud Connector

    Fine for straightforward orgs: a handful of data extensions, predictable volume, no need for sub-minute freshness. It’s declarative, it’s supported, and it fails loudly enough that you’ll notice when it breaks. Where it struggles is complex filtering logic and high record volume. Watch your synchronized data extension row counts closely. Connector performance degrades in ways that aren’t obvious until you’re already past the point where re-architecting is cheap.

    Platform Events and real-time triggers

    When a case closure needs to suppress a marketing journey within seconds, not hours, Connector’s batch cadence won’t cut it. Publish a Platform Event when the case closes, and consume it on the Marketing Cloud side through a REST call or a journey entry event. This costs more to build and more to maintain, so save it for the handful of scenarios where timing genuinely matters: opt-outs, churn, anything regulatory. Don’t build real-time sync for data nobody’s waiting on.

    Middleware or an iPaaS layer

    Once you’re syncing more than two systems, or the transformation logic between them gets complicated enough that it doesn’t belong in either platform’s native tooling, bring in middleware. MuleSoft, Boomi, whatever your org standardises on. The win isn’t speed. It’s having one place to see the whole data flow instead of debugging two black boxes that each assume the other is wrong.

    Design for the conflict, not just the happy path

    Sync design fails most often at the edges: both systems updating the same field in the same five-minute window, a sync job failing halfway through a batch, a Contact getting deleted mid-journey. Build and test these scenarios on purpose rather than discovering them in production. A contact-centre team I worked with ran a monthly reconciliation report comparing opt-out status between Marketing Cloud and Service Cloud. It caught drift within a day instead of a customer complaint catching it six weeks later.

    Error handling matters more here than in most integration work, because the failure mode isn’t a stack trace. It’s a customer getting the wrong experience, and nobody finding out until they complain. Log every sync failure somewhere a human actually looks. A failed Connector job nobody checks for two weeks isn’t a resilient integration. It’s a silent one.

    Keep the operating model as tidy as the data model

    Technical sync patterns only hold up if the org around them matches. If the marketing team can add fields to data extensions without talking to the Salesforce admin, your field matrix is out of date within a month. Put a lightweight governance step in place: any new field crossing the Service Cloud/Marketing Cloud boundary gets reviewed against the ownership matrix before it ships. It’s a five-minute conversation that saves a reconciliation project later.

    None of this needs to be elaborate. A typical enterprise org doesn’t need a real-time event mesh for every field. It needs a clear answer to who owns what, a sync mechanism matched to how urgent each piece of data actually is, and a way to notice when the two clouds drift apart before a customer does. Get those three right and multi-cloud Salesforce stops being a liability. It starts being what it’s supposed to be: one customer view, built from two platforms that are each good at their own job.

  • What Agentforce needs from your data model before it can work well

    Most Agentforce pilots stall in the same place. Not the agent builder, not the topics and actions configuration, but the data sitting underneath it. An agent is only as good as what it can see, and most Salesforce orgs I’ve worked in weren’t built with an AI reasoning over every field in mind. They were built for humans who knew which picklist value actually meant what, which custom field was deprecated three years ago but never deleted, and which record type nobody uses anymore.

    Agentforce doesn’t have that institutional memory. It reads the data model as it exists today, and if that model is inconsistent, duplicated, or poorly labelled, the agent gives confident, wrong answers instead of admitting it doesn’t know.

    Start with what the agent can actually see

    Every topic and action you configure in Agentforce ultimately resolves to data: a record, a field, a related list, a flow that queries something. Before touching the builder, I ask a blunt question on every engagement: if a new hire read this object’s field list with no tribal knowledge, would they understand what each field means and when it’s current?

    On a typical enterprise Service Cloud org, the answer is usually no. There’s a Status field with twelve values, four of which haven’t been used since a process changed two reorgs ago. There’s a Priority field and a separate Priority_Legacy__c field that nobody migrated off. An agent asked to summarise case urgency will happily average across both, because nothing in the schema tells it one is dead.

    The fix isn’t glamorous

    Field audits. Deprecating unused fields, or at minimum marking them clearly in help text. Collapsing duplicate meaning into a single source of truth. It’s the kind of work that never makes it onto a roadmap on its own merits, but it’s the actual prerequisite for an agent that doesn’t hallucinate case status to a customer.

    Record-level security is the agent’s permission boundary

    Agentforce runs under a user context, which means sharing rules, permission sets, and field-level security all apply to what the agent can retrieve and act on. I’ve seen teams assume the agent gets broader access than a normal user by default. It doesn’t, and it shouldn’t. If your sharing model is already a tangle of manual sharing rules bolted onto a role hierarchy that outgrew its original design, the agent hits the same gaps your human users do: able to see a case but not the account, able to read a contact but not update it.

    Before any Agentforce rollout, map which persona the agent runs as, then actually test what that persona can and can’t reach. Don’t assume it. I’ve watched a pilot fail in its first week because the agent was configured under a service account with read access to Opportunities but no access to the custom object holding renewal terms, so every renewal-related question came back empty.

    Data Cloud makes this worse before it makes it better

    If you’re layering Data Cloud underneath Agentforce to unify Service Cloud and Marketing Cloud data, the harmonisation step matters more than the AI step does. Identity resolution rules decide which records represent the same person. Get those rules wrong and you get an agent confidently merging two different customers’ order histories, or splitting one customer into three partial profiles depending on which system last touched their record.

    A typical enterprise org I’ve supported had three separate systems feeding contact records, each with its own idea of what counts as a unique customer. Before connecting Agentforce to that, the right move is weeks of validating match rules on a sample set, checking false merges and false splits by hand. That validation work is slower than the Data Cloud setup itself, and skipping it is the single most common reason agent answers come back wrong with total confidence.

    What good looks like

    • A documented identity resolution strategy, tested against real duplicate and near-duplicate records, not just the clean demo set
    • Calculated insights and segments that are actually maintained after go-live, not abandoned once the initial Data Cloud project closed
    • A named owner for data quality who isn’t the same person racing to ship the next Agentforce use case

    Actions and flows are the agent’s real API surface

    Topics get the attention in Agentforce demos, but actions are where the technical risk lives. Every action you expose is effectively an API endpoint the agent can call on its own, built on a flow or an Apex invocable method that was probably written for a human-triggered process. Those flows often have assumptions baked in: a specific picklist value gets set earlier in the user’s manual sequence, a validation rule gets bypassed because the UI never lets a person reach that state in the first place.

    An agent doesn’t share those assumptions. It calls the action with whatever inputs the conversation produced, and if the flow wasn’t built to handle unexpected or missing inputs gracefully, you get a DML error surfaced straight to a customer, or worse, a half-completed transaction sitting in the database. Every action exposed to Agentforce needs the same defensive design you’d put around a public API: input validation, bulkification, and a failure mode that doesn’t leave records in an inconsistent state.

    Sequencing the work

    The orgs that get real value from Agentforce did the data work first, usually in this order: field and object audit, sharing model validation, Data Cloud identity resolution where it applies, then action hardening. The ones that struggle configured the agent first and are now retrofitting data quality under pressure, often because a demo to leadership is already booked on the calendar.

    If you’re scoping an Agentforce rollout, weight the estimate toward data model readiness, not the agent configuration itself. I’d rather spend three weeks cleaning up a Case object and one week configuring topics than the other way round. The configuration is fast once the data underneath it can be trusted. Reversing that order just means doing the same data work later, under worse conditions, with an agent already live in front of real users.

  • Change management for Salesforce releases: why “it works in sandbox” isn’t enough

    I’ve watched a deployment pass every test in sandbox, get signed off by UAT, go to production on a Friday afternoon, and break order processing for six hours. Nobody did anything wrong in the testing phase. The sandbox just wasn’t production, and the gap between the two is where most Salesforce release failures actually live.

    This isn’t an argument against testing in sandbox. It’s an argument for treating “it passed in sandbox” as the start of your confidence, not the end of it.

    What sandbox actually proves

    A sandbox proves your code compiles, your Apex tests pass, your Flow doesn’t throw an error on the records you fed it, and the three people who clicked through UAT didn’t find anything obviously wrong. That’s real value. It’s also a narrow slice of what production will throw at your release.

    Data shape and volume

    Most sandboxes are partial copies, refreshed months ago, seeded with a fraction of production’s record count. A trigger that handles 200 test records cleanly can hit governor limits against 50,000 real ones. A report that renders instantly against a thin dataset can time out against three years of case history. I’ve seen validation rules pass clean in sandbox because the sandbox simply didn’t have the edge-case data combinations that exist in a mature production org, duplicate record types from an old migration, fields left blank by an integration that used to populate them, status values nobody uses anymore but that are still sitting on live records.

    Integration behaviour

    Sandbox integrations are usually pointed at test endpoints, mocked responses, or nothing at all. Your middleware might behave completely differently when it’s talking to the real payment gateway, the real marketing platform, or the real legacy system on the other end. Callout timeouts, rate limits, and authentication token refreshes are exactly the kind of thing that only shows up under real traffic patterns, not a UAT script run by three people over two days.

    User behaviour under real load

    UAT testers follow the happy path you gave them. Real users don’t. They open fifteen tabs, paste data from Excel with hidden characters, hit buttons twice because the page felt slow, and use the org in ways the requirements document never anticipated. None of that shows up until go-live, and by then it’s not a bug report, it’s a support queue.

    Build a release process, not a hope

    The fix isn’t more sandbox testing. It’s treating release management as its own discipline, with its own checks, separate from “did the feature work.”

    Get your environment strategy right first

    If you’re still deploying from a single developer sandbox straight to production, that’s the first thing to fix. A reasonable baseline for an enterprise org looks like: source-tracked developer sandboxes or scratch orgs for build, an integration sandbox for combined testing across teams, a full or partial copy sandbox refreshed regularly for UAT and performance testing, and production. Each stage exists to catch a different class of problem. Skipping a stage doesn’t save time, it just moves the discovery of the problem later and more expensive.

    Automate what should never depend on memory

    Manual change sets, clicked together by whoever’s free that week, are where deployments silently drop a permission set or forget a custom label. A CI/CD pipeline, even a modest one built on Salesforce DX and a free GitHub Actions runner, forces every deployment through the same validated steps: run all tests, check code coverage, validate against the target org before deploying, and keep a record of exactly what went out and when. The point isn’t tooling for its own sake. It’s removing the chance that Friday’s deployment depends on someone remembering a manual step from three releases ago.

    The checks that catch what sandbox misses

    Test against real data volume

    Before a release that touches triggers, flows, or batch logic, run it against a full or recent partial copy sandbox, not a seeded one. If that’s not available, at minimum export enough representative production data to simulate real volume and genuinely messy field values. A load test that takes an afternoon is cheaper than an incident that takes a night.

    Check permission and profile parity

    A feature can work perfectly for the system administrator profile used in sandbox testing and fail completely for the actual end-user profile in production, because a field-level security setting, a permission set assignment, or a sharing rule differs between the two environments. Test with a cloned user matching the real target profile, not your own admin login.

    Write the rollback plan before you deploy, not after it breaks

    Know in advance exactly how you’d undo this release: which metadata to retract, whether any data migration needs reversing, who has the authority to pull the trigger on a rollback at 2pm on a Tuesday. If you can’t answer that in one sentence before deployment, you’re not ready to deploy, regardless of how clean sandbox looked.

    Watch the first hour in production like it’s still testing

    The deployment finishing isn’t the finish line. Monitor error logs, check that scheduled jobs and integrations fired correctly, and have someone specifically watching for the first hour rather than assuming silence means success. Most production incidents I’ve seen were visible in the debug logs within minutes. Nobody was looking.

    What this looks like in practice

    On a typical enterprise Salesforce org I’ve worked with, the releases that went smoothly weren’t the ones with the most UAT sign-offs. They were the ones where someone had deliberately asked “what does production have that sandbox doesn’t,” and then tested against that gap specifically: real data volume, real integration endpoints, real user profiles, and a rollback plan written down before the deployment window opened.

    “Works in sandbox” is a necessary condition for a safe release. It was never a sufficient one. Treat the gap between the two environments as a known risk to manage, not a surprise to react to, and most of your worst release days stop happening.