Two Orgs. Same Code. One Breaks.
What the CSP wall taught me about front-end that actually travels.
I stared at the console error for longer than I want to admit.
The component worked. I had tested it on my dev org, tested it on a sandbox, handed it off with confidence. Three days later, I get a message. A red wall of security violations on a customer's org. The kind that make your stomach drop because nothing in the error tells you what YOU did wrong.
Here's the thing. I didn't do anything wrong. The code was correct. That almost made it worse.
Salesforce has a concept most front-end developers learn about the hard way. The platform enforces its own Content Security Policy at the platform level, not at the app level. There are things the browser won't let your code do, not because of anything you configured, but because Salesforce configured it for you.
CSP on the open web is something you set. A header. A policy. You control what sources are allowed, what scripts can run, what the browser is permitted to trust. On Salesforce, the policy is already written. You build inside it.
I knew this, in theory. Most developers do. What I didn't fully understand, at the time, was that the policy itself was different depending on the security engine running underneath the org.
And that's where the dual-runtime problem lives.
Salesforce has run two distinct front-end security architectures in production at the same time. The older one, Locker Service, sandboxes your code through a layer of JavaScript proxies. The newer one, Lightning Web Security, takes a different approach, using compartmentalized evaluation to isolate each component's execution context.
They behave differently. The rules about what your code can and cannot do are not identical between them. A pattern that passes through one runtime hits a wall in the other. And until you know which one is running, debugging feels like fighting a ghost.
My org was on one architecture. Their org was on the other. The error told me what the code had attempted. It didn't tell me which engine had stopped it.
I spent a good chunk of an afternoon narrowing this down. Not because the problem was buried deep. Because I was looking for a bug in my logic when the real question was about the environment itself.
That's a specific kind of humbling.
The moment it clicked was when I stopped treating the org as a constant.
I had been reasoning about it like this: the code runs on Salesforce, Salesforce has CSP, therefore the CSP is a fixed constraint. That reasoning is almost right. The CSP is enforced. The sandboxing is enforced. But which specific behaviors get through and which get stopped? That depends on which security engine is active.
Two orgs. Both Salesforce. Both running the same platform, broadly speaking. Different engines underneath.
Once I saw that, the path forward got cleaner. The question stopped being why is this failing and became what does each engine actually allow, and what's in the intersection.
The intersection is what you write to.
Not the more permissive of the two. Not the capabilities of one while assuming the other. The overlap. What is legal, and works correctly, under both. That's where portable Salesforce front-end lives.
What changed was how I thought about certain patterns before I wrote them. Not in the sense of memorizing a rulebook. More like developing a nose for the kinds of things that tend to behave differently across runtimes: anything that leans on the global scope in an unusual way, anything that relies on the host environment to behave as it does on the open web, anything that felt like a shortcut.
Shortcuts on a constrained platform are always a gamble. You're borrowing against an assumption about the environment that may not hold on the next org over.
The flip side is that writing to the intersection isn't that hard once you know it's the goal. It requires more care up front. It requires testing against both runtime behaviors, not just the one you happen to have available. But the code that comes out of that process is genuinely portable. It ships and it stays shipped.
The dopamine when something works across every org, without a special case, without a caveat, without a "works on my machine" asterisk: that's the real payoff.
Here's what I took from it.
Platform constraints are easy to treat as friction. Things you can't do. CSP violations, sandboxing errors, security exceptions. All of those feel like walls.
But there's another way to read them. Those walls tell you where the platform is genuinely opinionated. Where the vendor decided that safety matters more than developer convenience. Where shortcuts will always cost something, eventually.
When you build to the intersection of what every runtime allows, you stop fighting the platform. You stop writing code that passes the test of did it work on my org today and start writing code that holds up on every org, in every configuration, over time.
The CSP wall wasn't a bug in my project. It was a signal. I was building to an assumption, not to a principle.
The code that passes on every org isn't the code that found the clever workaround. It's the code that didn't need one.
Top comments (0)