In Salesforce, security is not optional—it is architectural. One of the most misunderstood (and misused) aspects of Apex development is sharing context. Many Salesforce implementations fail security audits, leak data, or behave unpredictably, not because of missing features, but because sharing rules were assumed rather than explicitly enforced.

At Winobell, we often encounter Salesforce orgs where Apex code unintentionally bypasses record visibility, leading to compliance risks, data exposure, and inconsistent user experiences. This article explains how Apex sharing really works, why defaults are dangerous, and what best practices enterprises should follow to build secure, future-proof Salesforce solutions.

Understanding Apex Sharing Context: The Basics

By default, all Apex classes run in without sharing mode unless a sharing declaration is explicitly specified. This default behavior is powerful—but risky.

Sharing context in Apex controls record-level access only. It does not automatically enforce:

  • Object-level permissions (CRUD)

  • Field-level security (FLS)

These must always be handled explicitly using:

  • Schema.sObjectType permission checks

  • WITH SECURITY_ENFORCED in SOQL

  • User-mode DML where applicable

Relying on defaults is one of the most common root causes of Salesforce security issues.

Apex Sharing Keywords Explained

  1. with sharing

    • Enforces the organization’s sharing rules for the current user
    • Users can only access records they are entitled to see
    • Applies record-level security only
    • Recommended for most user-facing business logic
      Best suited for:
    • UI controllers (LWC, Aura)
    • Business services invoked by end users
    • Data access layers where visibility matters
  2. without sharing

    • Ignores record-level sharing rules
    • Allows access to all records, regardless of user visibility
    • Still respects object permissions unless bypassed explicitly
      Best suited for:
    • System-level operations
    • Background processing
    • Administrative or data correction logic
      ⚠️ Use sparingly. Overuse creates hidden security risks.
  3. inherited sharing

    • The class inherits the sharing context of the calling code
    • Allows the same class to behave differently based on how it is invoked
    • If no sharing is declared, Apex defaults to without sharing
      Best suited for:
    • Shared utility or service classes
    • Logic reused across UI, batch, REST, and triggers
    • Large enterprise orgs with layered architectures

Apex Entry Points and Their Default Sharing Behavior

Different Apex entry points apply different default sharing contexts:

Apex Entry Point Default Sharing
Aura component controller with sharing
@AuraEnabled method (LWC) with sharing
Visualforce controller without sharing
Apex REST service without sharing
Asynchronous Apex (Batch, Queueable, Future, Scheduled) without sharing
Other Apex entry points without sharing

Important: The calling context is only honored if the called class is declared as inherited sharing.

Why Explicit Sharing Declaration Is Critical

When sharing is not declared:

  • Code behavior becomes unpredictable
  • Security issues surface only during audits or incidents
  • Future enhancements can unintentionally expose data
  • Salesforce security audit fails

Explicit sharing declarations:

  • Make intent clear
  • Improve maintainability
  • Reduce regression risks
  • Align code with Salesforce’s Secure by Design principles

Winobell’s Recommendations

For New Apex Classes Always declare sharing explicitly at creation time:

  • Default to with sharing
    • When unsure
    • For user-driven operations
  • Use inherited sharing
    • For reusable service or utility classes
    • When behavior should depend on the caller
  • Use without sharing
    • Only when business logic truly requires unrestricted access
      Rule of thumb: If you must ask 'should this bypass sharing?'—it probably shouldn’t.

For Existing Apex Classes Introducing sharing into legacy code is not risk-free:

  • Existing logic may rely on unrestricted access
  • Automation and integrations may break
  • Regression testing is mandatory Best approach:
  • Introduce sharing incrementally
  • Apply during enhancements or refactoring
  • Validate downstream automation, integrations, and reports

At Winobell, we often include this as part of a Salesforce ROI Recovery or Security Hardening engagement.

Apex Triggers and Sharing: What Architects Must Know

  • Triggers always run in without sharing by default
  • If a trigger calls an Apex class:
    • The class runs according to its declared sharing
  • If a trigger fires due to a CRUD operation:
    • User-mode DML (as user) → trigger runs with sharing
    • System-mode DML → trigger runs without sharing
  • If a trigger is invoked by a with sharing Apex class:
    • The trigger inherits with sharing

Sharing vs Security: A Common Misconception (Sharing ≠ Security) To build secure Apex:

  • Use with sharing or inherited sharing
  • Enforce CRUD using Schema.describe
  • Enforce FLS using WITH SECURITY_ENFORCED
  • Prefer user-mode DML and queries where possible
  • Never assume UI-level security applies to Apex

Final Thoughts

Salesforce provides powerful security tools—but it is up to architects and developers to use them correctly. The safest Apex code is explicit, intentional, and defensive.

At Winobell, we help organizations:

  • Identify insecure Apex patterns
  • Refactor legacy code safely
  • Pass Salesforce security reviews
  • Restore trust and ROI from underperforming Salesforce implementations

Key takeaway:

Never rely on defaults. Explicit sharing + explicit security enforcement leads to predictable, scalable, and compliant Salesforce solutions.

👉 Talk to a Winobell Salesforce expert. Schedule a free 30-minute discovery call with our team by emailing us at support@winobell.com or call us at +1 (437) 253-5469