<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Maintask</title>
    <description>The latest articles on DEV Community by Maintask (@maintask).</description>
    <link>https://dev.to/maintask</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3690118%2Fa71b34cf-5829-43d5-820e-96a967d8452f.png</url>
      <title>DEV Community: Maintask</title>
      <link>https://dev.to/maintask</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/maintask"/>
    <language>en</language>
    <item>
      <title>Salesforce Winter ’27 Regression Testing: What a Green Sandbox Can Still Miss</title>
      <dc:creator>Maintask</dc:creator>
      <pubDate>Wed, 02 Sep 2026 11:53:38 +0000</pubDate>
      <link>https://dev.to/maintask/salesforce-winter-27-regression-testing-what-a-green-sandbox-can-still-miss-l1i</link>
      <guid>https://dev.to/maintask/salesforce-winter-27-regression-testing-what-a-green-sandbox-can-still-miss-l1i</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftcwx6ep01cpgxlyypfuu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ftcwx6ep01cpgxlyypfuu.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A green sandbox test can be completely accurate and still give you the wrong level of confidence.&lt;/p&gt;

&lt;p&gt;The Flow completed.&lt;/p&gt;

&lt;p&gt;The Apex tests passed.&lt;/p&gt;

&lt;p&gt;The page opened.&lt;/p&gt;

&lt;p&gt;The integration returned a successful response.&lt;/p&gt;

&lt;p&gt;Everything looks ready.&lt;/p&gt;

&lt;p&gt;Then production upgrades and a user reports that an approval behaves differently, an integration can no longer authenticate, or code that worked perfectly in the preview sandbox fails against limits production still has.&lt;/p&gt;

&lt;p&gt;Nothing necessarily went wrong with the test.&lt;/p&gt;

&lt;p&gt;The problem is that the test proved less than the team thought it proved.&lt;/p&gt;

&lt;p&gt;That is the interesting part of &lt;strong&gt;Salesforce Winter ’27&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The September 2 revision of the release notes also changed the preparation list again. Salesforce now has &lt;strong&gt;five Release Updates enforced with Winter ’27&lt;/strong&gt;, not the four many August checklists still show. The new addition is the SOAP &lt;code&gt;login()&lt;/code&gt; permission requirement.&lt;/p&gt;

&lt;p&gt;For nonprofit Salesforce teams, this release isn't mainly about chasing the longest possible list of new features. It is about finding the quiet platform changes that can behave differently inside an org with years of Flows, Apex, permissions, integrations, Experience Cloud pages, NPSP packages, and fundraising processes.&lt;/p&gt;

&lt;p&gt;This article stays focused on those regression risks.&lt;/p&gt;

&lt;p&gt;For the complete release calendar, all five enforced Release Updates, integration deadlines, NPSP impact, Agentforce Nonprofit features, and the broader upgrade checklist, see the &lt;a href="https://www.maintask.com/articles/salesforce-winter-27-for-nonprofit-admins" rel="noopener noreferrer"&gt;Salesforce Winter ’27 guide for nonprofit admins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The question here is narrower:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What can still go wrong when the sandbox looks green?&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Winter ’27 regression testing in one minute
&lt;/h2&gt;

&lt;p&gt;If there isn't time to read the whole article yet, start here.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Winter ’27 area&lt;/th&gt;
&lt;th&gt;What a green test can miss&lt;/th&gt;
&lt;th&gt;What to test&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Profile Filtering&lt;/td&gt;
&lt;td&gt;Admin access hides permission differences&lt;/td&gt;
&lt;td&gt;Run the same process as real user personas&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SOAP &lt;code&gt;login()&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;An old integration user lacks &lt;code&gt;Use Any API Auth&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Inventory SOAP identities and authenticate end to end&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flow record-lock retries&lt;/td&gt;
&lt;td&gt;Salesforce succeeds after the external caller times out&lt;/td&gt;
&lt;td&gt;Measure duration, retries, and duplicate behavior&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Apex heap limits&lt;/td&gt;
&lt;td&gt;Winter ’27 sandbox has more heap than Summer ’26 production&lt;/td&gt;
&lt;td&gt;Re-test against the production-era limit&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accessibility reflow&lt;/td&gt;
&lt;td&gt;The page loads, but important actions move or become hard to reach&lt;/td&gt;
&lt;td&gt;Test critical pages at 200% and 400% zoom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flow metadata&lt;/td&gt;
&lt;td&gt;Git shows XML changes that aren't business-logic changes&lt;/td&gt;
&lt;td&gt;Save, retrieve, and review representative Flows&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Integrations&lt;/td&gt;
&lt;td&gt;Authentication, endpoint, API version, or ownership is unknown&lt;/td&gt;
&lt;td&gt;Inventory and test every important connection end to end&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Those checks don't replace a complete release test.&lt;/p&gt;

&lt;p&gt;They tell you where a successful test can be misleading.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Winter ’27 needs regression testing instead of another feature checklist
&lt;/h2&gt;

&lt;p&gt;Release preparation often starts with the release notes and ends with a spreadsheet.&lt;/p&gt;

&lt;p&gt;Feature.&lt;/p&gt;

&lt;p&gt;Applicable?&lt;/p&gt;

&lt;p&gt;Owner.&lt;/p&gt;

&lt;p&gt;Tested?&lt;/p&gt;

&lt;p&gt;Done.&lt;/p&gt;

&lt;p&gt;That works until the most important change isn't really a feature.&lt;/p&gt;

&lt;p&gt;Profile Filtering is a good example.&lt;/p&gt;

&lt;p&gt;The Salesforce org doesn't suddenly stop working.&lt;/p&gt;

&lt;p&gt;A user simply loses visibility into profile information they could previously see.&lt;/p&gt;

&lt;p&gt;That difference may matter only when a particular Flow, formula, report, approval, Apex class, or component evaluates another user.&lt;/p&gt;

&lt;p&gt;The same is true of the Flow record-lock change.&lt;/p&gt;

&lt;p&gt;A transaction that previously failed can now wait and succeed.&lt;/p&gt;

&lt;p&gt;That is an improvement.&lt;/p&gt;

&lt;p&gt;But if an external system stops waiting before Salesforce finishes, the overall business process can still fail.&lt;/p&gt;

&lt;p&gt;Winter ’27 has several changes like this.&lt;/p&gt;

&lt;p&gt;The release doesn't necessarily produce a dramatic red error.&lt;/p&gt;

&lt;p&gt;It changes an assumption.&lt;/p&gt;

&lt;p&gt;And mature Salesforce orgs are full of assumptions.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure pattern #1: Everything works because you tested as System Administrator
&lt;/h2&gt;

&lt;p&gt;Winter ’27 enforces &lt;strong&gt;Profile Filtering&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A user without the &lt;code&gt;View All Profiles&lt;/code&gt; permission can now see only their own profile name.&lt;/p&gt;

&lt;p&gt;On paper, that sounds like a straightforward permissions change.&lt;/p&gt;

&lt;p&gt;In a real org, the more useful question is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What business logic assumes that a user can inspect somebody else's profile?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That assumption can hide in more places than expected:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flows&lt;/li&gt;
&lt;li&gt;formulas&lt;/li&gt;
&lt;li&gt;Apex&lt;/li&gt;
&lt;li&gt;approvals&lt;/li&gt;
&lt;li&gt;assignments&lt;/li&gt;
&lt;li&gt;reports&lt;/li&gt;
&lt;li&gt;custom Lightning components&lt;/li&gt;
&lt;li&gt;user selectors&lt;/li&gt;
&lt;li&gt;internal admin utilities&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Consider a donation-routing Flow.&lt;/p&gt;

&lt;p&gt;It checks the owner of an Opportunity, looks at that user's profile, and uses the result to choose a path.&lt;/p&gt;

&lt;p&gt;Nothing about the Opportunity changed.&lt;/p&gt;

&lt;p&gt;Nothing about the owner changed.&lt;/p&gt;

&lt;p&gt;Nothing about the Flow definition necessarily changed.&lt;/p&gt;

&lt;p&gt;But the running user's visibility did.&lt;/p&gt;

&lt;p&gt;Now test the process while logged in as System Administrator.&lt;/p&gt;

&lt;p&gt;Everything works.&lt;/p&gt;

&lt;p&gt;That result can be technically correct and operationally useless.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use a small persona matrix instead
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Persona&lt;/th&gt;
&lt;th&gt;Useful processes to test&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Fundraising user&lt;/td&gt;
&lt;td&gt;Donation entry, ownership, reports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Program or operations user&lt;/td&gt;
&lt;td&gt;Cross-team automation and approvals&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Privileged non-admin&lt;/td&gt;
&lt;td&gt;Elevated processes without full admin visibility&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System Administrator&lt;/td&gt;
&lt;td&gt;Baseline only&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Run the same process as each persona.&lt;/p&gt;

&lt;p&gt;For a nonprofit org, that can mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;creating or updating a donation;&lt;/li&gt;
&lt;li&gt;changing record ownership;&lt;/li&gt;
&lt;li&gt;submitting an approval;&lt;/li&gt;
&lt;li&gt;running a report;&lt;/li&gt;
&lt;li&gt;triggering assignment logic;&lt;/li&gt;
&lt;li&gt;opening a custom component;&lt;/li&gt;
&lt;li&gt;completing a Screen Flow.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If something breaks, don't solve the regression by immediately giving &lt;code&gt;View All Profiles&lt;/code&gt; to everyone.&lt;/p&gt;

&lt;p&gt;First identify why the process depends on that visibility and give the permission only where there is a documented need.&lt;/p&gt;

&lt;h3&gt;
  
  
  The pitfall isn't Profile Filtering itself
&lt;/h3&gt;

&lt;p&gt;It's treating:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It worked for me"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;as equivalent to:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"It works for the people who actually use it."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If every regression test was run as System Administrator, Profile Filtering hasn't really been tested yet.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure pattern #2: The Flow succeeds after the caller has already given up
&lt;/h2&gt;

&lt;p&gt;Anyone who has worked with busy Salesforce automation has probably met this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;UNABLE_TO_LOCK_ROW
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two transactions need the same record.&lt;/p&gt;

&lt;p&gt;One holds the lock.&lt;/p&gt;

&lt;p&gt;The other can't get it.&lt;/p&gt;

&lt;p&gt;Winter ’27 changes part of that behavior for Flow.&lt;/p&gt;

&lt;p&gt;When Flow encounters record-lock contention during transaction initialization, Salesforce can now wait &lt;strong&gt;10 seconds&lt;/strong&gt; and retry instead of immediately failing.&lt;/p&gt;

&lt;p&gt;That can turn a failure into a successful transaction.&lt;/p&gt;

&lt;p&gt;Good.&lt;/p&gt;

&lt;p&gt;But now imagine the caller isn't another Salesforce process.&lt;/p&gt;

&lt;p&gt;Imagine it is:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a donation platform;&lt;/li&gt;
&lt;li&gt;middleware;&lt;/li&gt;
&lt;li&gt;an event-registration system;&lt;/li&gt;
&lt;li&gt;an accounting connector;&lt;/li&gt;
&lt;li&gt;a custom API client.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A simplified sequence might look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00s  External system sends request
01s  Salesforce starts transaction
02s  Target record is locked
02s  Salesforce waits
10s  External caller reaches its timeout
12s  Salesforce retry succeeds
13s  Salesforce commits
14s  External caller retries the original request
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Salesforce says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The caller says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TIMEOUT — RETRYING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both can be correct.&lt;/p&gt;

&lt;p&gt;And now the nonprofit may have a duplicate-processing problem.&lt;/p&gt;

&lt;p&gt;This is especially worth testing around records that many transactions can touch:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;household Accounts;&lt;/li&gt;
&lt;li&gt;donors;&lt;/li&gt;
&lt;li&gt;campaigns;&lt;/li&gt;
&lt;li&gt;gifts;&lt;/li&gt;
&lt;li&gt;program records;&lt;/li&gt;
&lt;li&gt;cases;&lt;/li&gt;
&lt;li&gt;summary records;&lt;/li&gt;
&lt;li&gt;high-volume imports.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Don't measure only success or failure
&lt;/h3&gt;

&lt;p&gt;For high-contention processes, record:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Salesforce result
Total execution time
Caller timeout
Caller retry behavior
Duplicate protection
Downstream side effects
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The useful question isn't:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did Salesforce finish?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Did the whole process finish once, within the expectations of every system involved?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Flow passed. Great. Now check whether the caller was still listening when it finished.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure pattern #3: An old SOAP integration stops authenticating
&lt;/h2&gt;

&lt;p&gt;This is the important addition in the September 2 release notes.&lt;/p&gt;

&lt;p&gt;Salesforce now lists &lt;strong&gt;Assign Use Any API Auth Permission for SOAP login()&lt;/strong&gt; under the Release Updates enforced with Winter ’27.&lt;/p&gt;

&lt;p&gt;If an integration authenticates using the SOAP API &lt;code&gt;login()&lt;/code&gt; call, the user making that call needs the &lt;code&gt;Use Any API Auth&lt;/code&gt; system permission.&lt;/p&gt;

&lt;p&gt;That makes this a real Winter ’27 regression test.&lt;/p&gt;

&lt;p&gt;Not a future retirement to put on a backlog.&lt;/p&gt;

&lt;h3&gt;
  
  
  Start by finding the identities that actually use SOAP &lt;code&gt;login()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Don't grant the permission to everyone because one integration needs it.&lt;/p&gt;

&lt;p&gt;Inventory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Integration
SOAP version
Salesforce user
Authentication method
Owner
Last successful test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For every identity that genuinely calls SOAP &lt;code&gt;login()&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;identify the integration user;&lt;/li&gt;
&lt;li&gt;assign &lt;code&gt;Use Any API Auth&lt;/code&gt; through a narrowly scoped permission set;&lt;/li&gt;
&lt;li&gt;test authentication in nonproduction;&lt;/li&gt;
&lt;li&gt;run an actual end-to-end transaction.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Setup path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Setup
→ Permission Sets
→ SOAP integration permission set
→ System Permissions
→ Use Any API Auth
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There is a separate org-level detail worth knowing.&lt;/p&gt;

&lt;p&gt;SOAP &lt;code&gt;login()&lt;/code&gt; is disabled by default in orgs created from Winter ’26 onward. That is not the same thing as the new user permission.&lt;/p&gt;

&lt;p&gt;The permission doesn't magically enable an org-level authentication method that is otherwise disabled.&lt;/p&gt;

&lt;h3&gt;
  
  
  The pitfall isn't SOAP itself
&lt;/h3&gt;

&lt;p&gt;It's discovering during release week that nobody knows whether an old vendor job authenticates through SOAP at all.&lt;/p&gt;

&lt;p&gt;The oldest integrations deserve the earliest inventory.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure pattern #4: The sandbox has a governor limit production doesn't have yet
&lt;/h2&gt;

&lt;p&gt;Winter ’27 increases Apex heap limits.&lt;/p&gt;

&lt;p&gt;Synchronous Apex moves from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;6 MB → 10 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Asynchronous Apex moves from:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;12 MB → 25 MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is genuinely useful for code working with large collections, files, API payloads, serialization, or async processing.&lt;/p&gt;

&lt;p&gt;The regression problem appears during the mixed-version window.&lt;/p&gt;

&lt;p&gt;A preview sandbox can already be running Winter ’27 while production is still on Summer ’26.&lt;/p&gt;

&lt;p&gt;That means this can happen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Winter ’27 preview sandbox

Heap used: 8.3 MB
Heap limit: 10 MB
Result: PASS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Summer ’26 production

Heap required: 8.3 MB
Heap limit: 6 MB
Result: FAIL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing is wrong with the sandbox.&lt;/p&gt;

&lt;p&gt;It simply answered a different question.&lt;/p&gt;

&lt;p&gt;The sandbox proved:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This code fits inside Winter ’27.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The deployment team needed to know:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does this code fit inside production today?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Check the active limit
&lt;/h3&gt;

&lt;p&gt;Apex already exposes it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;Heap used:  '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Limits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeapSize&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;Heap limit: '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Limits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLimitHeapSize&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For a reusable check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HeapCheck&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;logCurrentUsage&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Limits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getHeapSize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;limitSize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Limits&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getLimitHeapSize&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;Heap usage: '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;used&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt; / '&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;limitSize&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Winter ’27 nonproduction orgs also provide the &lt;strong&gt;Enforce the Summer ’26 Apex heap limit&lt;/strong&gt; setting.&lt;/p&gt;

&lt;p&gt;The September 2 notes clarify that it remains available in Winter ’27 nonproduction orgs throughout the release, including sandboxes, Developer Edition orgs, and scratch orgs.&lt;/p&gt;

&lt;p&gt;Use it while production is still on the previous release.&lt;/p&gt;

&lt;p&gt;In Spring ’27, the higher limits apply globally regardless of this setting.&lt;/p&gt;

&lt;h3&gt;
  
  
  Which code deserves another look?
&lt;/h3&gt;

&lt;p&gt;Start with code processing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;large JSON responses;&lt;/li&gt;
&lt;li&gt;files and attachments;&lt;/li&gt;
&lt;li&gt;generated documents;&lt;/li&gt;
&lt;li&gt;large maps or lists;&lt;/li&gt;
&lt;li&gt;serialized objects;&lt;/li&gt;
&lt;li&gt;Batch Apex;&lt;/li&gt;
&lt;li&gt;Queueables;&lt;/li&gt;
&lt;li&gt;integration payloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The higher limit is extra room.&lt;/p&gt;

&lt;p&gt;It shouldn't redefine "safe to deploy" before production receives the same room.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure pattern #5: The UI works until someone zooms to 400%
&lt;/h2&gt;

&lt;p&gt;Three of the five Release Updates enforced with Winter ’27 concern accessibility and Lightning reflow above &lt;strong&gt;200% zoom&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This is easy to file under:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Accessibility testing.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's correct, but incomplete.&lt;/p&gt;

&lt;p&gt;When standard Salesforce components reflow differently, the custom components around them can move too.&lt;/p&gt;

&lt;p&gt;The page can remain technically functional while the task becomes harder to complete.&lt;/p&gt;

&lt;p&gt;Salesforce calls out areas including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;page headers;&lt;/li&gt;
&lt;li&gt;modal windows;&lt;/li&gt;
&lt;li&gt;date pickers;&lt;/li&gt;
&lt;li&gt;popovers;&lt;/li&gt;
&lt;li&gt;bottom utility bars;&lt;/li&gt;
&lt;li&gt;record headers;&lt;/li&gt;
&lt;li&gt;cards;&lt;/li&gt;
&lt;li&gt;docked containers;&lt;/li&gt;
&lt;li&gt;menus;&lt;/li&gt;
&lt;li&gt;panels.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A nonprofit shouldn't test a random Account page just because it's convenient.&lt;/p&gt;

&lt;p&gt;Test the pages people cannot avoid:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gift entry;&lt;/li&gt;
&lt;li&gt;program management;&lt;/li&gt;
&lt;li&gt;case management;&lt;/li&gt;
&lt;li&gt;grant review;&lt;/li&gt;
&lt;li&gt;reports;&lt;/li&gt;
&lt;li&gt;quick actions;&lt;/li&gt;
&lt;li&gt;Experience Cloud portals.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  A useful test is intentionally simple
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;200% zoom
↓
400% zoom
↓
Keyboard navigation
↓
Mouse navigation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Can the primary action still be reached?&lt;/li&gt;
&lt;li&gt;Does keyboard focus make sense?&lt;/li&gt;
&lt;li&gt;Are labels still clear?&lt;/li&gt;
&lt;li&gt;Can modal buttons be reached?&lt;/li&gt;
&lt;li&gt;Do custom components overlap standard Salesforce UI?&lt;/li&gt;
&lt;li&gt;Can the user actually finish the task?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last question matters most.&lt;/p&gt;

&lt;p&gt;A page that technically renders isn't necessarily a page that still works for the user.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure pattern #6: Git shows a Flow change nobody intentionally made
&lt;/h2&gt;

&lt;p&gt;This one probably won't cause an outage.&lt;/p&gt;

&lt;p&gt;It can still waste a surprising amount of engineering time.&lt;/p&gt;

&lt;p&gt;Winter ’27 changes how Flow Builder stores &lt;strong&gt;End elements&lt;/strong&gt; for auto-layout Flows.&lt;/p&gt;

&lt;p&gt;Saving an existing auto-layout Flow now stores End elements explicitly in the XML.&lt;/p&gt;

&lt;p&gt;So a normal workflow can look like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sf project retrieve start &lt;span class="nt"&gt;--metadata&lt;/span&gt; &lt;span class="s2"&gt;"Flow:Donation_Intake"&lt;/span&gt;
git diff
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And Git suddenly reports changes.&lt;/p&gt;

&lt;p&gt;The developer didn't intentionally add business logic.&lt;/p&gt;

&lt;p&gt;The admin doesn't remember changing the end of the Flow.&lt;/p&gt;

&lt;p&gt;The XML still changed.&lt;/p&gt;

&lt;p&gt;All three statements can be true.&lt;/p&gt;

&lt;h3&gt;
  
  
  Calibrate the deployment process before the first real change
&lt;/h3&gt;

&lt;p&gt;Take one representative auto-layout Flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;save it in the Winter ’27 sandbox;&lt;/li&gt;
&lt;li&gt;retrieve it;&lt;/li&gt;
&lt;li&gt;run the diff;&lt;/li&gt;
&lt;li&gt;identify the expected End-element changes;&lt;/li&gt;
&lt;li&gt;make sure reviewers know what the new metadata looks like.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If the team uses CI/CD, metadata normalization, automated review, or Flow-comparison tooling, run those too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A metadata diff and a behavioral diff are not the same thing.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Knowing which one you're looking at makes the first Winter ’27 deployment much less mysterious.&lt;/p&gt;




&lt;h2&gt;
  
  
  Two optional Flow changes deserve separate tests
&lt;/h2&gt;

&lt;p&gt;Not everything in Winter ’27 changes existing Flows automatically.&lt;/p&gt;

&lt;p&gt;That distinction matters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Running-user permissions
&lt;/h3&gt;

&lt;p&gt;Screen and autolaunched Flows on runtime API 68.0 can opt into more consistent enforcement of the running user's permissions.&lt;/p&gt;

&lt;p&gt;This does &lt;strong&gt;not&lt;/strong&gt; automatically change the security context of every existing Flow.&lt;/p&gt;

&lt;p&gt;If the option is enabled, regression-test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the actual user permissions;&lt;/li&gt;
&lt;li&gt;record access;&lt;/li&gt;
&lt;li&gt;fault handling;&lt;/li&gt;
&lt;li&gt;total runtime;&lt;/li&gt;
&lt;li&gt;callouts;&lt;/li&gt;
&lt;li&gt;any subflows or actions involved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Don't combine a runtime-version change, a new security context, and unrelated Flow edits into one deployment if they can be tested separately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic batch sizing
&lt;/h3&gt;

&lt;p&gt;Scheduled Flows can also use dynamic batch sizing.&lt;/p&gt;

&lt;p&gt;If Salesforce hits certain CPU, SOQL, or heap-limit errors, processing can retry with a smaller batch.&lt;/p&gt;

&lt;p&gt;That can turn a failing scheduled job into a successful one.&lt;/p&gt;

&lt;p&gt;But, again, success isn't the only metric.&lt;/p&gt;

&lt;p&gt;A Flow that finishes after splitting work into more transactions may have different:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;total runtime;&lt;/li&gt;
&lt;li&gt;callout behavior;&lt;/li&gt;
&lt;li&gt;downstream load;&lt;/li&gt;
&lt;li&gt;transaction counts.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If dynamic batch sizing solves a real limit problem, great.&lt;/p&gt;

&lt;p&gt;Measure what changed around that success.&lt;/p&gt;




&lt;h2&gt;
  
  
  Failure pattern #7: The integration nobody owns becomes the release risk
&lt;/h2&gt;

&lt;p&gt;The newest Salesforce integration is usually not the one worth worrying about.&lt;/p&gt;

&lt;p&gt;Someone remembers the new one.&lt;/p&gt;

&lt;p&gt;It has a Jira ticket.&lt;/p&gt;

&lt;p&gt;Probably documentation.&lt;/p&gt;

&lt;p&gt;Definitely a person who worked on it.&lt;/p&gt;

&lt;p&gt;The more interesting integration is the job that has run quietly for four years.&lt;/p&gt;

&lt;p&gt;Maybe it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;imports donations;&lt;/li&gt;
&lt;li&gt;pushes data to accounting;&lt;/li&gt;
&lt;li&gt;synchronizes an email platform;&lt;/li&gt;
&lt;li&gt;creates backups;&lt;/li&gt;
&lt;li&gt;sends files to another system;&lt;/li&gt;
&lt;li&gt;subscribes to a platform event.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nobody touches it because it works.&lt;/p&gt;

&lt;p&gt;Eventually that becomes:&lt;/p&gt;

&lt;p&gt;Nobody knows why it works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the boring table
&lt;/h3&gt;

&lt;p&gt;"Boring" is a compliment here.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Integration&lt;/th&gt;
&lt;th&gt;User&lt;/th&gt;
&lt;th&gt;Auth&lt;/th&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Endpoint&lt;/th&gt;
&lt;th&gt;Owner&lt;/th&gt;
&lt;th&gt;Last test&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Donation platform&lt;/td&gt;
&lt;td&gt;Integration user&lt;/td&gt;
&lt;td&gt;OAuth&lt;/td&gt;
&lt;td&gt;Known&lt;/td&gt;
&lt;td&gt;My Domain&lt;/td&gt;
&lt;td&gt;Fundraising Ops&lt;/td&gt;
&lt;td&gt;Aug 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Accounting sync&lt;/td&gt;
&lt;td&gt;Finance user&lt;/td&gt;
&lt;td&gt;OAuth&lt;/td&gt;
&lt;td&gt;Known&lt;/td&gt;
&lt;td&gt;My Domain&lt;/td&gt;
&lt;td&gt;Finance&lt;/td&gt;
&lt;td&gt;Aug 2026&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Legacy export&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;td&gt;?&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The third row is the useful one.&lt;/p&gt;

&lt;p&gt;For every connection, confirm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;application or vendor;&lt;/li&gt;
&lt;li&gt;authentication flow;&lt;/li&gt;
&lt;li&gt;API version;&lt;/li&gt;
&lt;li&gt;endpoint;&lt;/li&gt;
&lt;li&gt;integration user;&lt;/li&gt;
&lt;li&gt;monitoring;&lt;/li&gt;
&lt;li&gt;owner;&lt;/li&gt;
&lt;li&gt;last successful test;&lt;/li&gt;
&lt;li&gt;replacement deadline, if one exists.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And Winter ’27 gives several concrete reasons to do this now.&lt;/p&gt;




&lt;h3&gt;
  
  
  Don't confuse February 2027 OAuth work with upgrade weekend
&lt;/h3&gt;

&lt;p&gt;The OAuth 2.0 username-password flow is &lt;strong&gt;not&lt;/strong&gt; retired when the org upgrades to Winter ’27.&lt;/p&gt;

&lt;p&gt;Salesforce moved that retirement to &lt;strong&gt;February 20, 2027&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The OAuth user-agent and hybrid user-agent flows now retire on the same date.&lt;/p&gt;

&lt;p&gt;If an integration still uses one of those flows, that's real technical debt.&lt;/p&gt;

&lt;p&gt;Give it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an owner;&lt;/li&gt;
&lt;li&gt;a replacement design;&lt;/li&gt;
&lt;li&gt;a test environment;&lt;/li&gt;
&lt;li&gt;a migration date.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But don't confuse a February 2027 remediation project with a September or October 2026 production-upgrade regression test.&lt;/p&gt;

&lt;p&gt;Different deadline.&lt;/p&gt;

&lt;p&gt;Different workstream.&lt;/p&gt;




&lt;h3&gt;
  
  
  Don't wait for a hard-coded instance URL to break
&lt;/h3&gt;

&lt;p&gt;Old instance-specific Salesforce URLs are another integration problem worth finding before enforcement does it for you.&lt;/p&gt;

&lt;p&gt;Search:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;middleware
scripts
CI/CD
Named Credentials
vendor configuration
custom settings
environment variables
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;for stale instance-specific Salesforce hosts.&lt;/p&gt;

&lt;p&gt;Where appropriate, move integrations to the org's My Domain login URL.&lt;/p&gt;

&lt;p&gt;A useful sandbox control is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Setup
→ My Domain
→ Redirections
→ Block API traffic that uses an incorrect instanced URL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the control in a sandbox and run affected integrations end to end.&lt;/p&gt;

&lt;p&gt;The current Salesforce schedule places sandbox enforcement between &lt;strong&gt;October 22 and November 5, 2026&lt;/strong&gt;, and production enforcement in phases between &lt;strong&gt;January 12 and March 25, 2027&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The surrounding Salesforce documentation has shifted more than once.&lt;/p&gt;

&lt;p&gt;The practical solution hasn't.&lt;/p&gt;

&lt;p&gt;Don't wait for enforcement to discover the hostname was hard-coded.&lt;/p&gt;




&lt;h3&gt;
  
  
  Device Flow has its own November deadline
&lt;/h3&gt;

&lt;p&gt;Device Flow also doesn't belong on the Winter ’27 production-upgrade weekend checklist unless the org actually uses it.&lt;/p&gt;

&lt;p&gt;Its important date is &lt;strong&gt;November 30, 2026&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After that, Device Flow becomes restricted to eligible local External Client Apps using a localhost callback.&lt;/p&gt;

&lt;p&gt;If the org uses that authentication pattern, identify the affected Connected Apps and plan the appropriate External Client App design.&lt;/p&gt;

&lt;p&gt;Again:&lt;/p&gt;

&lt;p&gt;real work, different date.&lt;/p&gt;




&lt;h3&gt;
  
  
  Standard-volume platform events deserve special attention
&lt;/h3&gt;

&lt;p&gt;Standard-volume platform events retire on &lt;strong&gt;December 15, 2026&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;After that date, publishing and subscribing stop working.&lt;/p&gt;

&lt;p&gt;Winter ’27 adds a migration path to high-volume platform events, but publishing pauses during the migration.&lt;/p&gt;

&lt;p&gt;That means this is maintenance-window work, not something to click during a busy fundraising afternoon.&lt;/p&gt;

&lt;p&gt;After migrating, retest every dependent:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flow;&lt;/li&gt;
&lt;li&gt;Apex trigger;&lt;/li&gt;
&lt;li&gt;Streaming API client;&lt;/li&gt;
&lt;li&gt;integration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For an established nonprofit org, this is exactly the kind of feature that can sit underneath an old integration nobody thinks about until it stops.&lt;/p&gt;




&lt;h2&gt;
  
  
  One Winter ’27 project you may be able to remove completely
&lt;/h2&gt;

&lt;p&gt;Earlier Winter ’27 checklists often included &lt;strong&gt;Adopt Authorized Email Domains&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Salesforce canceled that Release Update.&lt;/p&gt;

&lt;p&gt;It has been replaced by &lt;strong&gt;Maintain Your Email Verification Exception&lt;/strong&gt;, enforced on &lt;strong&gt;December 1, 2026&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The replacement is much narrower.&lt;/p&gt;

&lt;p&gt;It appears only in orgs where Salesforce Support previously disabled user email verification for a domain.&lt;/p&gt;

&lt;p&gt;Check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Setup
→ Release Updates
→ Maintain Your Email Verification Exception
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it appears, configure the authorized domain and test users and return addresses.&lt;/p&gt;

&lt;p&gt;If it doesn't appear, there is no generic Winter ’27 email-domain project for that org.&lt;/p&gt;

&lt;p&gt;Release plans should get smaller when Salesforce removes work.&lt;/p&gt;

&lt;p&gt;Not just bigger every time somebody publishes another checklist.&lt;/p&gt;

&lt;p&gt;For the current deadline table—including which items were canceled, postponed, or moved outside the upgrade window—the &lt;a href="https://www.maintask.com/articles/salesforce-winter-27-for-nonprofit-admins" rel="noopener noreferrer"&gt;full Winter ’27 release guide&lt;/a&gt; tracks the broader timeline.&lt;/p&gt;




&lt;h2&gt;
  
  
  Use nonprofit-shaped test data, not clean demo records
&lt;/h2&gt;

&lt;p&gt;A brand-new Contact and a &lt;code&gt;$10&lt;/code&gt; Opportunity can prove that Salesforce saves a record.&lt;/p&gt;

&lt;p&gt;It does not prove that the nonprofit's real operating model survived the release.&lt;/p&gt;

&lt;p&gt;Production is messier.&lt;/p&gt;

&lt;p&gt;Useful regression scenarios may include:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;One-time gift
Recurring gift
Multi-payment pledge
Household with multiple contacts
Organization donor
Soft credit
High-volume donation import
Program enrollment
Grant workflow
Experience Cloud user
Integration transaction
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not every nonprofit needs every scenario.&lt;/p&gt;

&lt;p&gt;The point is to use data shapes that actually exercise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;sharing;&lt;/li&gt;
&lt;li&gt;relationships;&lt;/li&gt;
&lt;li&gt;automation;&lt;/li&gt;
&lt;li&gt;rollups;&lt;/li&gt;
&lt;li&gt;integrations;&lt;/li&gt;
&lt;li&gt;user permissions;&lt;/li&gt;
&lt;li&gt;reporting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If several donations can update the same household during a campaign, test that.&lt;/p&gt;

&lt;p&gt;If accounting sync only runs after a particular Opportunity change, test that.&lt;/p&gt;

&lt;p&gt;If a portal user has very different permissions from staff, test as the portal user.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Production data has edge cases. Regression data needs them too.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What should an NPSP team actually test?
&lt;/h2&gt;

&lt;p&gt;This is where release coverage can easily create unnecessary work.&lt;/p&gt;

&lt;p&gt;Winter ’27 affects an &lt;strong&gt;NPSP org&lt;/strong&gt; because NPSP runs on Salesforce.&lt;/p&gt;

&lt;p&gt;The platform changes matter.&lt;/p&gt;

&lt;p&gt;But Winter ’27 does &lt;strong&gt;not&lt;/strong&gt; announce:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;NPSP retirement;&lt;/li&gt;
&lt;li&gt;an NPSP migration deadline;&lt;/li&gt;
&lt;li&gt;an automatic migration;&lt;/li&gt;
&lt;li&gt;a forced move to Agentforce Nonprofit.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The September 2 release notes do not change that.&lt;/p&gt;

&lt;p&gt;So for an NPSP-only org, the useful question is not:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How quickly do we migrate because Winter ’27 arrived?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It is:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Does the Salesforce environment we operate today still work correctly after Winter ’27?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Test:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;donation paths;&lt;/li&gt;
&lt;li&gt;household processing;&lt;/li&gt;
&lt;li&gt;recurring-gift automation;&lt;/li&gt;
&lt;li&gt;Profile Filtering;&lt;/li&gt;
&lt;li&gt;Apex;&lt;/li&gt;
&lt;li&gt;Flow;&lt;/li&gt;
&lt;li&gt;high-zoom pages;&lt;/li&gt;
&lt;li&gt;Experience Cloud;&lt;/li&gt;
&lt;li&gt;installed packages;&lt;/li&gt;
&lt;li&gt;payment integrations;&lt;/li&gt;
&lt;li&gt;accounting integrations;&lt;/li&gt;
&lt;li&gt;email integrations;&lt;/li&gt;
&lt;li&gt;backup or ETL jobs.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If an NPSP-to-Agentforce Nonprofit migration is already on the roadmap, that is valid work.&lt;/p&gt;

&lt;p&gt;It deserves its own architecture, data, integration, and change-management plan.&lt;/p&gt;

&lt;p&gt;Winter ’27 doesn't set that timetable.&lt;/p&gt;




&lt;h2&gt;
  
  
  Agentforce Nonprofit teams have a different second test
&lt;/h2&gt;

&lt;p&gt;Salesforce now refers to &lt;strong&gt;Nonprofit Cloud as Agentforce Nonprofit&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That is a product-name transition.&lt;/p&gt;

&lt;p&gt;It is not a rename of NPSP and doesn't move NPSP data into the newer platform.&lt;/p&gt;

&lt;p&gt;For organizations already using Agentforce Nonprofit, Winter ’27 has product-specific work in areas such as Program, Case, and Outcome Management setup, Fundraising, and Grantmaking.&lt;/p&gt;

&lt;p&gt;But the order still matters.&lt;/p&gt;

&lt;p&gt;First:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;prove that the existing system survived the release.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Then:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;evaluate the new functionality the org is actually licensed to use.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;For Fundraising, one useful regression path is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Gift
→ Donor
→ Household
→ Relationships
→ Naming
→ Hard / soft credit
→ Household totals
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Winter ’27 adds automatic household creation and household naming functionality for eligible Agentforce Nonprofit orgs with the required add-on.&lt;/p&gt;

&lt;p&gt;Don't stop at confirming that a household record exists.&lt;/p&gt;

&lt;p&gt;Check:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the right household;&lt;/li&gt;
&lt;li&gt;the right relationships;&lt;/li&gt;
&lt;li&gt;naming and greetings;&lt;/li&gt;
&lt;li&gt;hard credit;&lt;/li&gt;
&lt;li&gt;soft credit;&lt;/li&gt;
&lt;li&gt;household totals;&lt;/li&gt;
&lt;li&gt;API behavior if gifts enter through an integration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For Grantmaking, Compliant Data Sharing deserves persona-based testing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Reviewer A → assigned sections only
Reviewer B → their assigned sections only
Process owner → full evaluation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once again, security should be tested as the user who is supposed to see &lt;strong&gt;less&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The complete object, field, licensing, NPSP, and Agentforce Nonprofit breakdown is in the &lt;a href="https://www.maintask.com/articles/salesforce-winter-27-for-nonprofit-admins" rel="noopener noreferrer"&gt;Salesforce Winter ’27 guide for nonprofit admins&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  One more automatic change worth knowing about: Agentforce Platform
&lt;/h2&gt;

&lt;p&gt;The September 2 notes also add a platform change that is easy to miss if you're only reading the nonprofit section.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentforce Platform is enabled by default when new Winter ’27 orgs are created, and eligible existing orgs are being enabled on a rolling basis starting in early September.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That doesn't mean users suddenly have a live agent.&lt;/p&gt;

&lt;p&gt;Permissions and activation still matter.&lt;/p&gt;

&lt;p&gt;But an admin may find Agentforce Builder available without having deliberately enabled the platform.&lt;/p&gt;

&lt;p&gt;So treat this as a permissions check, not an AI emergency.&lt;/p&gt;

&lt;p&gt;If the org isn't planning to use Agentforce yet:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;review who has relevant permissions;&lt;/li&gt;
&lt;li&gt;confirm whether Einstein generative AI is enabled;&lt;/li&gt;
&lt;li&gt;make sure no agent has been activated unintentionally.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The presence of the platform and the presence of an active production agent are not the same thing.&lt;/p&gt;




&lt;h2&gt;
  
  
  A 45-minute Winter ’27 regression pass
&lt;/h2&gt;

&lt;p&gt;A complete regression cycle obviously takes longer than 45 minutes.&lt;/p&gt;

&lt;p&gt;But 45 minutes can expose where the next few hours should go.&lt;/p&gt;

&lt;h3&gt;
  
  
  0–10 minutes: users and permissions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Choose one critical nonprofit process&lt;/li&gt;
&lt;li&gt;[ ] Run it as an ordinary user&lt;/li&gt;
&lt;li&gt;[ ] Repeat it as a privileged non-admin&lt;/li&gt;
&lt;li&gt;[ ] Run it as admin&lt;/li&gt;
&lt;li&gt;[ ] Check any logic that reads another user's profile&lt;/li&gt;
&lt;li&gt;[ ] Record differences instead of immediately widening permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  10–15 minutes: SOAP authentication
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Identify any job or vendor that uses SOAP &lt;code&gt;login()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Confirm the integration user&lt;/li&gt;
&lt;li&gt;[ ] Confirm &lt;code&gt;Use Any API Auth&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Authenticate in nonproduction&lt;/li&gt;
&lt;li&gt;[ ] Run a real end-to-end transaction&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  15–25 minutes: Flow and Apex
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Run a process likely to encounter record contention&lt;/li&gt;
&lt;li&gt;[ ] Measure Flow duration, not only final status&lt;/li&gt;
&lt;li&gt;[ ] Check caller timeout and retry behavior&lt;/li&gt;
&lt;li&gt;[ ] Check for duplicate downstream work&lt;/li&gt;
&lt;li&gt;[ ] Check &lt;code&gt;Limits.getLimitHeapSize()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Re-test relevant Apex under the Summer ’26 limit while production is still there&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  25–30 minutes: UI
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Open a critical page at 200% zoom&lt;/li&gt;
&lt;li&gt;[ ] Repeat at 400%&lt;/li&gt;
&lt;li&gt;[ ] Navigate with a keyboard&lt;/li&gt;
&lt;li&gt;[ ] Check custom components beside standard Salesforce UI&lt;/li&gt;
&lt;li&gt;[ ] Confirm the task can actually be completed&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  30–35 minutes: metadata
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] Save one existing auto-layout Flow&lt;/li&gt;
&lt;li&gt;[ ] Retrieve it&lt;/li&gt;
&lt;li&gt;[ ] Run &lt;code&gt;git diff&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;[ ] Identify expected End-element changes&lt;/li&gt;
&lt;li&gt;[ ] Make sure reviewers understand the new metadata&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  35–45 minutes: integrations
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;[ ] List active connections&lt;/li&gt;
&lt;li&gt;[ ] Confirm authentication method&lt;/li&gt;
&lt;li&gt;[ ] Confirm API version&lt;/li&gt;
&lt;li&gt;[ ] Confirm endpoint&lt;/li&gt;
&lt;li&gt;[ ] Confirm integration user&lt;/li&gt;
&lt;li&gt;[ ] Name an owner&lt;/li&gt;
&lt;li&gt;[ ] Check for instance-specific URLs&lt;/li&gt;
&lt;li&gt;[ ] Run one real end-to-end transaction&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Forty-five minutes won't make an org release-ready.&lt;/p&gt;

&lt;p&gt;It can tell you where the real risk is.&lt;/p&gt;

&lt;p&gt;That is usually much more useful than spending those forty-five minutes reading another "Top Winter ’27 Features" roundup.&lt;/p&gt;




&lt;h2&gt;
  
  
  A green sandbox is evidence, not proof
&lt;/h2&gt;

&lt;p&gt;Most Winter ’27 problems won't come from forgetting one headline feature.&lt;/p&gt;

&lt;p&gt;They'll come from testing the right thing in the wrong context.&lt;/p&gt;

&lt;p&gt;Testing Profile Filtering as admin.&lt;/p&gt;

&lt;p&gt;Testing SOAP without the integration user that actually uses it.&lt;/p&gt;

&lt;p&gt;Testing Salesforce without the external caller.&lt;/p&gt;

&lt;p&gt;Testing Winter ’27 heap limits for a Summer ’26 production deployment.&lt;/p&gt;

&lt;p&gt;Testing a page only at default zoom.&lt;/p&gt;

&lt;p&gt;Reviewing a metadata diff without knowing serialization changed.&lt;/p&gt;

&lt;p&gt;Testing a perfect &lt;code&gt;$10&lt;/code&gt; donation instead of the data shape that puts pressure on the org.&lt;/p&gt;

&lt;p&gt;Testing an integration without knowing who owns it.&lt;/p&gt;

&lt;p&gt;Each gap looks small.&lt;/p&gt;

&lt;p&gt;Together, they explain why a green preview sandbox doesn't automatically mean release-ready.&lt;/p&gt;

&lt;p&gt;A useful regression plan isn't trying to prove that everything in Salesforce works.&lt;/p&gt;

&lt;p&gt;That would be impossible.&lt;/p&gt;

&lt;p&gt;It is trying to prove something narrower:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The processes this organization cannot afford to lose still work for the real users, with representative data, through the real integrations.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If production upgrade weekend becomes boring after that, the testing did its job.&lt;/p&gt;




&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;For the broader release picture—including the current production dates, all five enforced Release Updates, NPSP impact, Agentforce Nonprofit changes, SOAP and OAuth deadlines, platform-event retirement, hard-coded endpoint enforcement, and the full nonprofit testing plan—read the &lt;a href="https://www.maintask.com/articles/salesforce-winter-27-for-nonprofit-admins" rel="noopener noreferrer"&gt;Salesforce Winter ’27 Release guide for nonprofit admins&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Winter ’27 documentation has changed several times during the release cycle. Recheck the current Salesforce Release Notes and the Trust maintenance event for your own instance before turning a preview date into a production commitment.&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>nonprofit</category>
      <category>salesforcewinter27</category>
      <category>agentforce</category>
    </item>
    <item>
      <title>How to Plan an NPSP to Nonprofit Cloud Migration Without Breaking Donor Data</title>
      <dc:creator>Maintask</dc:creator>
      <pubDate>Wed, 15 Jul 2026 09:54:35 +0000</pubDate>
      <link>https://dev.to/maintask/how-to-plan-an-npsp-to-nonprofit-cloud-migration-without-breaking-donor-data-93n</link>
      <guid>https://dev.to/maintask/how-to-plan-an-npsp-to-nonprofit-cloud-migration-without-breaking-donor-data-93n</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00l7twkpr8aur3s4am0g.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F00l7twkpr8aur3s4am0g.jpeg" alt=" " width="800" height="528"&gt;&lt;/a&gt;If you administer a Salesforce org for a nonprofit, you've probably noticed that new features keep landing in &lt;strong&gt;Nonprofit Cloud&lt;/strong&gt; while your &lt;strong&gt;NPSP&lt;/strong&gt; (Nonprofit Success Pack) org sits still. This post is a practical look at what that actually means for your data model and how to plan a migration before you're forced into a rushed one.&lt;/p&gt;

&lt;p&gt;No shutdown date has been announced for NPSP. That's precisely why this is worth planning for now, while you can do it calmly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;NPSP is a &lt;strong&gt;managed package&lt;/strong&gt; layered on top of core Salesforce. It reshapes standard objects into a fundraising-friendly model:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Account&lt;/strong&gt; → household/organization accounts&lt;br&gt;
&lt;strong&gt;Opportunity&lt;/strong&gt; → donations, with npsp_&lt;em&gt;Primary_Contact&lt;/em&gt;&lt;em&gt;c, soft credits, and recurring donations (npe03&lt;/em&gt;&lt;em&gt;Recurring_Donation&lt;/em&gt;_c)&lt;br&gt;
A stack of NPSP settings, triggers, rollups (Customizable Rollups / CRLP), and batch jobs&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu7nttokuu7dkgfi9l157.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fu7nttokuu7dkgfi9l157.png" alt=" " width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nonprofit Cloud uses a different fundraising data model built around objects such as GiftTransaction, GiftCommitment, GiftCommitmentSchedule, and related designation and donor records.&lt;/p&gt;

&lt;p&gt;The core problem: your donor history, recurring gifts, and reporting logic are all expressed in NPSP's model, and they need to be &lt;strong&gt;re-mapped&lt;/strong&gt;, not just copied.&lt;/p&gt;
&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Treat it as a data-modeling project in four phases.&lt;br&gt;
&lt;em&gt;1. Inventory what you actually have.&lt;/em&gt;&lt;br&gt;
Pull the real footprint before you plan anything. A quick way to list your custom fields per object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Requires Salesforce CLI (sf)
sf sobject describe --sobject Opportunity --target-org myOrg \
  | jq '.fields[] | select(.custom == true) | .name'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do the same for Account, Contact, npe03_&lt;em&gt;Recurring_Donation&lt;/em&gt;_c, and any custom objects. Then check what's genuinely used — a field with 2% of the population across ten years is a migration candidate for deletion, not migration.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;2. Map old model → new model.&lt;/em&gt; Build an explicit field-mapping sheet: source object/field → target object/field → transformation rule. This is where you decide what's a "must move" (donation history, recurring commitments, core reports) vs. "leave behind" (that 2019 workaround field).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;3. Clean before you move.&lt;/em&gt; De-dupe households, close dead integrations, resolve ownerless records. A migration will faithfully replicate every bit of mess unless you clear it first.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;4. Migrate to a sandbox first, phased, away from year-end.&lt;/em&gt; Never do the first run in production. Full sandbox → validate → dry-run the load → reconcile counts → then plan the production cutover on a date you choose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example
&lt;/h2&gt;

&lt;p&gt;A minimal reconciliation check after a trial load — verify totals match so a broken mapping doesn't silently drop gifts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Source (NPSP): total closed-won donation amount
SELECT SUM(Amount) total, COUNT(Id) cnt
FROM Opportunity
WHERE IsWon = true

-- Target (Nonprofit Cloud): same population, new model
-- Compare total + count. A mismatch = a mapping or filter bug,
-- NOT "close enough". Investigate before cutover.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If SUM(Amount) and COUNT(Id) don't line up between source and target, stop and find out why. "Off by a few thousand dollars" in a trial load becomes a wrong number on the board report after cutover.&lt;/p&gt;

&lt;p&gt;The same principle applies to recurring donations: reconcile the count of active npe03_&lt;em&gt;Recurring_Donation&lt;/em&gt;_c records and their expected annual value against the migrated commitments before anyone trusts the new system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Treating it as an upgrade.&lt;/strong&gt; It's a different product and data model. Planning it like a version bump is how projects derail.&lt;br&gt;
&lt;strong&gt;Migrating the mess.&lt;/strong&gt; Skipping the cleanup step just gives you more expensive versions of the same problems.&lt;br&gt;
&lt;strong&gt;No reconciliation step.&lt;/strong&gt; If you don't verify record counts and totals against the source, silent data loss ships to production.&lt;br&gt;
&lt;strong&gt;Testing only the happy path.&lt;/strong&gt; Split gifts, in-kind donations, multi-payment pledges, and soft credits are where mappings break — test them explicitly.&lt;br&gt;
&lt;strong&gt;Ignoring adoption.&lt;/strong&gt; The data can migrate perfectly and the project still fails if staff aren't retrained on the new screens.&lt;br&gt;
&lt;strong&gt;Waiting to feel forced.&lt;/strong&gt; Every new custom field you add before migrating makes the eventual move harder, not easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;If you'd rather map your specific org's model and timeline with people who've done this migration before, that's the kind of planning &lt;a href="https://www.maintask.com/" rel="noopener noreferrer"&gt;we help nonprofits with at Maintask.&lt;/a&gt;&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>crm</category>
      <category>nonprofit</category>
      <category>datamigration</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Maintask</dc:creator>
      <pubDate>Wed, 17 Jun 2026 09:57:23 +0000</pubDate>
      <link>https://dev.to/maintask/-4b9</link>
      <guid>https://dev.to/maintask/-4b9</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/maintask/your-apex-might-return-fewer-records-in-summer-26-and-not-throw-a-single-error-26de" class="crayons-story__hidden-navigation-link"&gt;Your Apex Might Return Fewer Records in Summer '26 — and Not Throw a Single Error&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/maintask" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3690118%2Fa71b34cf-5829-43d5-820e-96a967d8452f.png" alt="maintask profile" class="crayons-avatar__image" width="340" height="340"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/maintask" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Maintask
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Maintask
                
              
              &lt;div id="story-author-preview-content-3922772" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/maintask" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3690118%2Fa71b34cf-5829-43d5-820e-96a967d8452f.png" class="crayons-avatar__image" alt="" width="340" height="340"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Maintask&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/maintask/your-apex-might-return-fewer-records-in-summer-26-and-not-throw-a-single-error-26de" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Jun 17&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/maintask/your-apex-might-return-fewer-records-in-summer-26-and-not-throw-a-single-error-26de" id="article-link-3922772"&gt;
          Your Apex Might Return Fewer Records in Summer '26 — and Not Throw a Single Error
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ai"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ai&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/salesforce"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;salesforce&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/apex"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;apex&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/security"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;security&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/maintask/your-apex-might-return-fewer-records-in-summer-26-and-not-throw-a-single-error-26de" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/multi-unicorn-b44d6f8c23cdd00964192bedc38af3e82463978aa611b4365bd33a0f1f4f3e97.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;5&lt;span class="hidden s:inline"&gt;&amp;nbsp;reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/maintask/your-apex-might-return-fewer-records-in-summer-26-and-not-throw-a-single-error-26de#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              

              &lt;span class="hidden s:inline"&gt;Add&amp;nbsp;Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            6 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success crayons-icon c-btn__icon"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>Your Apex Might Return Fewer Records in Summer '26 — and Not Throw a Single Error</title>
      <dc:creator>Maintask</dc:creator>
      <pubDate>Wed, 17 Jun 2026 09:57:09 +0000</pubDate>
      <link>https://dev.to/maintask/your-apex-might-return-fewer-records-in-summer-26-and-not-throw-a-single-error-26de</link>
      <guid>https://dev.to/maintask/your-apex-might-return-fewer-records-in-summer-26-and-not-throw-a-single-error-26de</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j5g7kctveabkwdwdz2c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4j5g7kctveabkwdwdz2c.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you maintain custom Apex on a Salesforce org, Summer '26 (API v67.0) ships a change that can quietly alter what your code returns — no exception, no failed deploy, just &lt;em&gt;different results&lt;/em&gt;. It's a good change. It's also the kind of change that surprises a production org if nobody saw it coming.&lt;/p&gt;

&lt;p&gt;Here's what's happening and how to get ahead of it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem: Apex used to ignore the running user's permissions
&lt;/h2&gt;

&lt;p&gt;For most of Apex's history, database operations ran in &lt;strong&gt;system mode&lt;/strong&gt; by default. That means SOQL, SOSL, DML, and &lt;code&gt;Database&lt;/code&gt; methods ignored the running user's:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;object permissions,
&lt;/li&gt;
&lt;li&gt;field-level security (FLS), and
&lt;/li&gt;
&lt;li&gt;sharing rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A query in a controller would happily return records the logged-in user was never supposed to see, and write to fields they couldn't access — unless you explicitly added guards. In a nonprofit org, that's exactly how a volunteer with a restricted profile ends up able to read every donor's giving history through a custom Lightning component nobody pen-tested.&lt;/p&gt;

&lt;p&gt;The platform gave us tools to opt &lt;em&gt;into&lt;/em&gt; security (&lt;code&gt;WITH SECURITY_ENFORCED&lt;/code&gt;, &lt;code&gt;Security.stripInaccessible()&lt;/code&gt;, &lt;code&gt;with sharing&lt;/code&gt; on classes), but they were opt-in. If you forgot, your code ran wide open. Secure was the thing you had to remember; insecure was the default.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution: Summer '26 flips the default to user mode
&lt;/h2&gt;

&lt;p&gt;In &lt;strong&gt;API version 67.0&lt;/strong&gt;, that default inverts. Database operations now run in &lt;strong&gt;user mode&lt;/strong&gt; unless you explicitly say otherwise. Concretely, on v67.0:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;SOQL / SOSL / DML / &lt;code&gt;Database&lt;/code&gt; methods enforce the running user's object permissions, FLS, and sharing rules by default.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A class with no sharing declaration now defaults to &lt;code&gt;with sharing&lt;/code&gt;&lt;/strong&gt; (previously an omitted declaration effectively inherited the caller's mode — often &lt;code&gt;without sharing&lt;/code&gt;).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;WITH SECURITY_ENFORCED&lt;/code&gt; is removed&lt;/strong&gt; and no longer compiles — you migrate to &lt;code&gt;WITH USER_MODE&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Triggers always run in system mode&lt;/strong&gt; and can't declare a sharing or access mode.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The important nuance: &lt;strong&gt;this is keyed to the API version of the Apex class.&lt;/strong&gt; Existing classes stay on their current API version and behave as before until &lt;em&gt;you&lt;/em&gt; bump them to 67.0. So this is a migration you control — not an overnight change forced on every line of code you own. New classes you create on 67.0 get the new behavior immediately.&lt;/p&gt;

&lt;p&gt;You opt back into system mode explicitly, per operation, when you genuinely need it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="c1"&gt;// SOQL — inline&lt;/span&gt;
&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Opportunity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;gifts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;USER_MODE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;   &lt;span class="c1"&gt;// enforce user access (now the default)&lt;/span&gt;
&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Opportunity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;SYSTEM_MODE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt; &lt;span class="c1"&gt;// intentional escape hatch&lt;/span&gt;

&lt;span class="c1"&gt;// DML&lt;/span&gt;
&lt;span class="k"&gt;insert&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="n"&gt;newGifts&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;     &lt;span class="c1"&gt;// respect the user's create/FLS&lt;/span&gt;
&lt;span class="k"&gt;update&lt;/span&gt; &lt;span class="n"&gt;as&lt;/span&gt; &lt;span class="n"&gt;system&lt;/span&gt; &lt;span class="n"&gt;staleRows&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// intentional system-mode write&lt;/span&gt;

&lt;span class="c1"&gt;// Database methods — pass the AccessLevel enum&lt;/span&gt;
&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Opportunity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;dyn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;SELECT Id, Amount FROM Opportunity'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;AccessLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;SYSTEM_MODE&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;newGifts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;AccessLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;USER_MODE&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mental model: &lt;strong&gt;user mode is the safe default; &lt;code&gt;SYSTEM_MODE&lt;/code&gt; / &lt;code&gt;without sharing&lt;/code&gt; is now the thing you write down on purpose, in the open, where a reviewer can see it.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: a donor-giving controller before and after
&lt;/h2&gt;

&lt;p&gt;Say you have a Lightning component that shows a contact's giving history. Pre-v67, a common (and quietly unsafe) version looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pre-Summer '26 — no sharing declaration, system-mode query&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DonorGivingController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@AuraEnabled(&lt;/span&gt;&lt;span class="n"&gt;cacheable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Opportunity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getGifts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="n"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Runs in system mode: returns gifts regardless of who is asking,&lt;/span&gt;
        &lt;span class="c1"&gt;// and exposes Amount even if the user has no FLS on it.&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CloseDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StageName&lt;/span&gt;
            &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt;
            &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Primary_Contact__c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;contactId&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On v67.0, the same class — with no code changes — now behaves as &lt;code&gt;with sharing&lt;/code&gt; in user mode. If a restricted user opens the component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;gifts on records they can't see via sharing are filtered out, and
&lt;/li&gt;
&lt;li&gt;if they lack FLS on &lt;code&gt;Amount&lt;/code&gt;, the query throws instead of leaking the field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's the behavior you almost certainly wanted all along. The explicit, reviewer-friendly version makes the intent obvious:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Summer '26 — intent is explicit and secure by default&lt;/span&gt;
&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="kd"&gt;sharing&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DonorGivingController&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@AuraEnabled(&lt;/span&gt;&lt;span class="n"&gt;cacheable&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Opportunity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;getGifts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="n"&gt;contactId&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CloseDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StageName&lt;/span&gt;
            &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt;
            &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Primary_Contact__c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;contactId&lt;/span&gt;
            &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;USER_MODE&lt;/span&gt;
        &lt;span class="p"&gt;];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And when you &lt;em&gt;do&lt;/em&gt; have a legitimate system job — a nightly rollup that must see every gift regardless of the running user — you say so, out loud:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;without&lt;/span&gt; &lt;span class="kd"&gt;sharing&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;NightlyGivingRollup&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;Batchable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;SObject&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;QueryLocator&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;BatchableContext&lt;/span&gt; &lt;span class="n"&gt;bc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getQueryLocator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;SELECT Id, Amount, Primary_Contact__c FROM Opportunity'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;AccessLevel&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;SYSTEM_MODE&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Pitfalls: what actually breaks, and how to catch it
&lt;/h2&gt;

&lt;p&gt;The reason this deserves a real migration pass — not just a version bump — is that the failures are often &lt;em&gt;silent&lt;/em&gt;. Here are the ones to watch.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Queries return fewer rows, with no error.&lt;/strong&gt; A query that returned 500 records can return 40 once user access is enforced, and nothing throws. Downstream logic that assumed a full result set produces wrong numbers — bad rollups, missing list items, under-counted reports. To confirm a row drop is access-related, compare the two modes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;asUser&lt;/span&gt;   &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nf"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;USER_MODE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;asSystem&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nf"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;SYSTEM_MODE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;debug&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;user='&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;asUser&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s2"&gt;  system='&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;asSystem&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A large gap means the running user simply can't see those rows — decide whether that's correct (it usually is) or whether this code is a true system context that needs &lt;code&gt;SYSTEM_MODE&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;WITH SECURITY_ENFORCED&lt;/code&gt; won't compile.&lt;/strong&gt; If you bump a class to 67.0 and it still contains the old clause, deployment fails. Find-and-replace it with &lt;code&gt;WITH USER_MODE&lt;/code&gt;, which is stricter (it also enforces sharing, not just FLS/object perms), so re-test afterward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Before (fails to compile on v67.0)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Contact&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;SECURITY_ENFORCED&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="c1"&gt;// After&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Contact&lt;/span&gt; &lt;span class="k"&gt;WITH&lt;/span&gt; &lt;span class="n"&gt;USER_MODE&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Integration and automation users hit walls.&lt;/strong&gt; This is the big one for nonprofits with connected apps (Click &amp;amp; Pledge, FormAssembly, ETL/data loads, scheduled jobs). Code that runs as an integration user and assumed system-level reach will now be filtered to that user's permissions. The fix is a deliberate choice per context: either grant the integration user the object/field/sharing access it legitimately needs, &lt;strong&gt;or&lt;/strong&gt; mark the genuinely-system code paths &lt;code&gt;without sharing&lt;/code&gt; + &lt;code&gt;SYSTEM_MODE&lt;/code&gt;. Don't blanket-&lt;code&gt;SYSTEM_MODE&lt;/code&gt; everything to make red tests green — that just recreates the old wide-open default you're trying to leave behind.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. The sharing-declaration default flips.&lt;/strong&gt; A class you &lt;em&gt;relied on&lt;/em&gt; to run unrestricted (because an omitted declaration used to behave like &lt;code&gt;without sharing&lt;/code&gt; in many call paths) may now enforce sharing as &lt;code&gt;with sharing&lt;/code&gt;. Audit every class with no explicit declaration and write the intent down — &lt;code&gt;with sharing&lt;/code&gt; or &lt;code&gt;without sharing&lt;/code&gt; — so behavior no longer depends on who called it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Triggers no longer enforce sharing.&lt;/strong&gt; All triggers now run in system mode, period. If any logic quietly leaned on sharing being enforced inside a trigger, move it into a handler class that declares &lt;code&gt;with sharing&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight apex"&gt;&lt;code&gt;&lt;span class="n"&gt;trigger&lt;/span&gt; &lt;span class="n"&gt;OpportunityTrigger&lt;/span&gt; &lt;span class="n"&gt;on&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;OpportunityTriggerHandler&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;afterUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;new&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Trigger&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="py"&gt;oldMap&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="kd"&gt;sharing&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;OpportunityTriggerHandler&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;afterUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Opportunity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;updated&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;oldMap&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// sharing is enforced here because the class declares it&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A pragmatic migration order
&lt;/h2&gt;

&lt;p&gt;You don't have to do this all at once — the API version is per-class, so migrate deliberately:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Inventory first.&lt;/strong&gt; Search your codebase for &lt;code&gt;WITH SECURITY_ENFORCED&lt;/code&gt; (must change) and for classes with no sharing declaration (behavior may change).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bump high-risk classes in a sandbox&lt;/strong&gt;, one batch at a time: controllers, &lt;code&gt;@AuraEnabled&lt;/code&gt; methods, REST endpoints, and anything an integration user runs.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run your tests, but don't trust green alone&lt;/strong&gt; — user-mode bugs are about &lt;em&gt;data visibility&lt;/em&gt;, which weak test setups miss. Add tests that run as a restricted user via &lt;code&gt;System.runAs()&lt;/code&gt;.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decide each escape hatch on purpose.&lt;/strong&gt; Every &lt;code&gt;SYSTEM_MODE&lt;/code&gt; / &lt;code&gt;without sharing&lt;/code&gt; should be a conscious, commented decision, not a reflex to silence a failure.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only then bump the API version in production&lt;/strong&gt;, class group by class group.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Done this way, a change that could have been a silent production incident becomes a routine, secure-by-default upgrade — which is exactly what it's meant to be.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;p&gt;If you want the rest of the Summer '26 picture — key dates, the enforced updates, Flow and reporting changes, and a release checklist — we put together a full breakdown here: &lt;a href="https://www.maintask.com/articles/salesforce-summer-26-release-updates" rel="noopener noreferrer"&gt;Salesforce Summer '26 Release: Key Dates, Features, Flow Updates &amp;amp; Release Checklist&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Keep focusing on your priorities. We take care of your CRM.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>salesforce</category>
      <category>apex</category>
      <category>security</category>
    </item>
    <item>
      <title>Five Salesforce Reports Every Nonprofit Leadership Team Should Have</title>
      <dc:creator>Maintask</dc:creator>
      <pubDate>Mon, 25 May 2026 10:44:35 +0000</pubDate>
      <link>https://dev.to/maintask/five-salesforce-reports-every-nonprofit-leadership-team-should-have-nl9</link>
      <guid>https://dev.to/maintask/five-salesforce-reports-every-nonprofit-leadership-team-should-have-nl9</guid>
      <description>&lt;p&gt;Nonprofit leadership teams often rely on Salesforce for fundraising, program tracking, grant management, and operational reporting.&lt;/p&gt;

&lt;p&gt;But having the data in Salesforce is not the same as having useful reports.&lt;/p&gt;

&lt;p&gt;A report is only valuable if it helps someone make a decision.&lt;/p&gt;

&lt;p&gt;For nonprofit leaders, that usually means answering questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Are we on track to meet our fundraising goal?
&lt;/li&gt;
&lt;li&gt;Which donors are at risk of lapsing?
&lt;/li&gt;
&lt;li&gt;Are our programs reaching the right people?
&lt;/li&gt;
&lt;li&gt;Which grant deadlines are coming up?
&lt;/li&gt;
&lt;li&gt;Can we trust the data in this dashboard?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article walks through five Salesforce reports that can help nonprofit leadership teams make better decisions.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwb2wi6mohcg01i8p6zj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpwb2wi6mohcg01i8p6zj.png" alt=" " width="800" height="724"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Problem&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;A nonprofit may have thousands of records in Salesforce. The data exists, but leadership still struggles to answer basic questions.&lt;/p&gt;

&lt;p&gt;Common symptoms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The board asks for a fundraising forecast, but only closed donations are reported.
&lt;/li&gt;
&lt;li&gt;Program impact numbers are pulled manually from spreadsheets.
&lt;/li&gt;
&lt;li&gt;Grant deadlines live in inboxes or individual calendars.
&lt;/li&gt;
&lt;li&gt;Lapsed donors are noticed months too late.
&lt;/li&gt;
&lt;li&gt;Dashboards look accurate, but the underlying data is incomplete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core problem is not usually Salesforce itself. The problem is that reports are often built around available fields instead of leadership decisions.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Solution&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Build a small set of reports around the questions leadership asks most often. A practical nonprofit leadership reporting set should include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fundraising Pipeline Report
&lt;/li&gt;
&lt;li&gt;Donor Retention and Lapsed Donor Report
&lt;/li&gt;
&lt;li&gt;Program Impact Report
&lt;/li&gt;
&lt;li&gt;Grant Tracking and Compliance Report
&lt;/li&gt;
&lt;li&gt;Data Quality and Operational Health Report&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each report should have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A clear owner
&lt;/li&gt;
&lt;li&gt;A clear audience
&lt;/li&gt;
&lt;li&gt;A clear business question
&lt;/li&gt;
&lt;li&gt;A defined update frequency
&lt;/li&gt;
&lt;li&gt;A next action when something looks wrong&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The goal is not to create more reports. The goal is to create reports people actually trust.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Example 1: Fundraising Pipeline Report&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What it answers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Are we likely to meet our fundraising target?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Many nonprofit teams report only on closed donations. That shows what already happened, but it does not show what is likely to happen next. &lt;/p&gt;

&lt;p&gt;A pipeline report gives leadership visibility into expected revenue.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Useful fields&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Opportunity Name
&lt;/li&gt;
&lt;li&gt;Donor or Account
&lt;/li&gt;
&lt;li&gt;Amount
&lt;/li&gt;
&lt;li&gt;Stage
&lt;/li&gt;
&lt;li&gt;Probability
&lt;/li&gt;
&lt;li&gt;Expected Close Date
&lt;/li&gt;
&lt;li&gt;Campaign
&lt;/li&gt;
&lt;li&gt;Owner
&lt;/li&gt;
&lt;li&gt;Next Step
&lt;/li&gt;
&lt;li&gt;Last Activity Date&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Suggested grouping&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Group by Stage
&lt;/li&gt;
&lt;li&gt;Then group by Expected Close Month&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Why it matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If many major gifts are stuck in “Proposal Sent”, the issue may not be the fundraising strategy. It may be the follow-up.&lt;/p&gt;

&lt;p&gt;If several grants are expected to close this quarter but have no next step, leadership can act before the forecast becomes a surprise.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Simple report logic&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Object: Opportunities
Filter:
  IsClosed = False
  Amount &amp;gt; 0
  CloseDate = Current Fiscal Year

Group:
  Stage
  Close Month

Show:
  Sum of Amount
  Sum of Expected Revenue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  &lt;strong&gt;Example 2: Donor Retention and Lapsed Donor Report&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What it answers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Which donors are at risk of disappearing?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A donor may not actively cancel or complain. They may simply stop giving.&lt;/p&gt;

&lt;p&gt;That makes retention reporting important, especially for recurring donors and major donors.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Useful fields&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Donor Name
&lt;/li&gt;
&lt;li&gt;Last Gift Date
&lt;/li&gt;
&lt;li&gt;Last Gift Amount
&lt;/li&gt;
&lt;li&gt;Total Giving
&lt;/li&gt;
&lt;li&gt;Current Year Giving
&lt;/li&gt;
&lt;li&gt;Previous Year Giving
&lt;/li&gt;
&lt;li&gt;Giving Frequency
&lt;/li&gt;
&lt;li&gt;Recurring Donation Status
&lt;/li&gt;
&lt;li&gt;Last Activity Date
&lt;/li&gt;
&lt;li&gt;Owner or Relationship Manager&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Simple report logic&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Object: Contacts or Accounts with Opportunities

Filter:
  Previous Year Giving &amp;gt; 0
  Current Year Giving = 0

Optional:
  Last Activity Date older than 90 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Why it matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This report turns donor retention into an operational process.&lt;/p&gt;

&lt;p&gt;Instead of asking “Why is donor retention down?” leadership can ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which donors need follow-up?
&lt;/li&gt;
&lt;li&gt;Who owns the relationship?
&lt;/li&gt;
&lt;li&gt;What action should happen next?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Example 3: Program Impact Report&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What it answers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;What work are we delivering, and what outcomes are we seeing?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Fundraising reports show how money comes in. Program impact reports show what that money supports.&lt;/p&gt;

&lt;p&gt;For nonprofits, this is often the report leadership needs for board updates, grant reporting, and strategic planning.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Useful fields&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Program Name
&lt;/li&gt;
&lt;li&gt;Participant or Beneficiary
&lt;/li&gt;
&lt;li&gt;Service Type
&lt;/li&gt;
&lt;li&gt;Service Date
&lt;/li&gt;
&lt;li&gt;Status
&lt;/li&gt;
&lt;li&gt;Outcome
&lt;/li&gt;
&lt;li&gt;Location
&lt;/li&gt;
&lt;li&gt;Funding Source
&lt;/li&gt;
&lt;li&gt;Owner
&lt;/li&gt;
&lt;li&gt;Completion Date&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Useful metrics&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Participants served
&lt;/li&gt;
&lt;li&gt;Programs completed
&lt;/li&gt;
&lt;li&gt;Open cases or active enrollments
&lt;/li&gt;
&lt;li&gt;Completion rate
&lt;/li&gt;
&lt;li&gt;Outcome achievement rate
&lt;/li&gt;
&lt;li&gt;Services delivered by location
&lt;/li&gt;
&lt;li&gt;Demand by program area&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Simple report logic&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Object: Program Participation or Custom Program Object

Filter:
  Service Date = Current Fiscal Year

Group:
  Program Name
  Status
  Outcome

Show:
  Record Count
  Completion Rate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Why it matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Counting records is not enough. A useful impact report should separate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Started
&lt;/li&gt;
&lt;li&gt;In progress
&lt;/li&gt;
&lt;li&gt;Completed
&lt;/li&gt;
&lt;li&gt;Dropped
&lt;/li&gt;
&lt;li&gt;Outcome achieved
&lt;/li&gt;
&lt;li&gt;Outcome not achieved&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This prevents teams from reporting activity as impact.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Example 4: Grant Tracking and Compliance Report&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What it answers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;What grant deadlines, deliverables, or reports are at risk?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Grant tracking often breaks when key information lives outside Salesforce.&lt;/p&gt;

&lt;p&gt;Common places where grant details get lost:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Email threads
&lt;/li&gt;
&lt;li&gt;Individual calendars
&lt;/li&gt;
&lt;li&gt;Spreadsheets
&lt;/li&gt;
&lt;li&gt;PDF award letters
&lt;/li&gt;
&lt;li&gt;Staff memory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A Salesforce grant tracking report creates a shared source of visibility.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1gl1acujh1t6pok972f.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu1gl1acujh1t6pok972f.jpeg" alt=" " width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Useful fields&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Grant Name
&lt;/li&gt;
&lt;li&gt;Funder
&lt;/li&gt;
&lt;li&gt;Amount Requested
&lt;/li&gt;
&lt;li&gt;Amount Awarded
&lt;/li&gt;
&lt;li&gt;Stage
&lt;/li&gt;
&lt;li&gt;Application Deadline
&lt;/li&gt;
&lt;li&gt;Award Date
&lt;/li&gt;
&lt;li&gt;Reporting Deadline
&lt;/li&gt;
&lt;li&gt;Restricted Purpose
&lt;/li&gt;
&lt;li&gt;Program Funded
&lt;/li&gt;
&lt;li&gt;Responsible Owner
&lt;/li&gt;
&lt;li&gt;Deliverable Status
&lt;/li&gt;
&lt;li&gt;Next Step&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Simple report logic&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Object: Grants or Opportunities

Filter:
  Grant Status = Active
  Reporting Deadline = Next 90 Days

Group:
  Reporting Deadline Month
  Responsible Owner

Show:
  Grant Amount
  Deliverable Status
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Why it matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;This report reduces reliance on memory. Leadership can see what is due, who owns it, and where risk is building before a deadline is missed.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Example 5: Data Quality and Operational Health Report&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;What it answers&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Can we trust the data behind the dashboard?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This report is usually less exciting than fundraising or impact reporting, but it is also one of the most important.&lt;/p&gt;

&lt;p&gt;If Salesforce data is incomplete or inconsistent, every dashboard built on top of it becomes questionable.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Simple report logic&lt;/strong&gt;
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Object: Contacts

Filter:
  Email = Blank
  Created Date = Last 30 Days

Group:
  Created By
  Owner

Show:
  Record Count
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Object: Opportunities

Filter:
  Campaign = Blank
  Close Date = Current Fiscal Year

Group:
  Owner
  Stage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  &lt;strong&gt;Why it matters&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Data quality reporting helps teams identify process issues.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If many gifts are missing campaigns, the donation entry process may need adjustment.
&lt;/li&gt;
&lt;li&gt;If many contacts are missing emails, form mapping may be incomplete.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This report should not be used to blame users.&lt;/p&gt;

&lt;p&gt;It should be used to improve the system.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Pitfalls&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pitfall 1: Creating too many reports&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Leadership does not need 80 reports.&lt;/p&gt;

&lt;p&gt;They need a small number of reports that answer important questions.&lt;/p&gt;

&lt;p&gt;Too many reports often create confusion because different teams may use different filters, definitions, or date ranges.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pitfall 2: Reporting on fields no one updates&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;A dashboard is only as reliable as the fields behind it.&lt;/p&gt;

&lt;p&gt;If “Stage”, “Status”, “Outcome”, or “Next Step” fields are not maintained consistently, leadership reports will become unreliable.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pitfall 3: Confusing activity with impact&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;For program reporting, avoid treating every record count as an outcome.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;“1,000 participants registered” is not the same as “1,000 participants completed the program”.&lt;/p&gt;

&lt;p&gt;The report should make that distinction clear.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Pitfall 4: Building dashboards before fixing data quality&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Dashboards can make bad data look official.&lt;/p&gt;

&lt;p&gt;Before relying on dashboards, build data quality reports that show whether key fields are complete and consistent.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Practical Checklist&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Before publishing a Salesforce leadership report, confirm:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this report answer a leadership question?
&lt;/li&gt;
&lt;li&gt;Is the audience clear?
&lt;/li&gt;
&lt;li&gt;Are field definitions consistent?
&lt;/li&gt;
&lt;li&gt;Is there a known action when numbers look wrong?
&lt;/li&gt;
&lt;li&gt;Can users trust the underlying data?
&lt;/li&gt;
&lt;li&gt;Should this be part of a dashboard?&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Further Reading&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;For Salesforce CRM reporting, automation, and nonprofit implementation support, see &lt;a href="https://maintask.com/" rel="noopener noreferrer"&gt;Maintask&lt;/a&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  &lt;strong&gt;Final Thought&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Good Salesforce reporting is not about having more dashboards.&lt;/p&gt;

&lt;p&gt;It is about creating a small set of reports that leadership can trust.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpz3lk5622doadchka11t.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpz3lk5622doadchka11t.jpeg" alt=" " width="800" height="529"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For many nonprofits, these five reports are a strong starting point:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fundraising Pipeline
&lt;/li&gt;
&lt;li&gt;Donor Retention
&lt;/li&gt;
&lt;li&gt;Program Impact
&lt;/li&gt;
&lt;li&gt;Grant Tracking
&lt;/li&gt;
&lt;li&gt;Data Quality&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once those are in place, board meetings become less about reconciling numbers and more about making decisions.&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>crm</category>
      <category>nonprofit</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Reporting Failure Patterns We Keep Seeing in Nonprofit Salesforce Orgs</title>
      <dc:creator>Maintask</dc:creator>
      <pubDate>Mon, 04 May 2026 15:20:17 +0000</pubDate>
      <link>https://dev.to/maintask/reporting-failure-patterns-we-keep-seeing-in-nonprofit-salesforce-orgs-kp6</link>
      <guid>https://dev.to/maintask/reporting-failure-patterns-we-keep-seeing-in-nonprofit-salesforce-orgs-kp6</guid>
      <description>&lt;p&gt;If you have ever inherited a nonprofit Salesforce org with eighty reports, three dashboards, and zero confidence in any of them, you already know the real problem: the report is usually not broken. The reporting design is.&lt;/p&gt;

&lt;p&gt;Most of the time, the bad number on the dashboard is just the last visible symptom. The real issue sits deeper: no one agreed on stage definitions, recurring-donation exceptions are mixed into retention reporting, programme counts are based on row volume instead of unique participants, or critical grant deadlines still live in someone's calendar.&lt;/p&gt;

&lt;p&gt;This post is not a list of "reports leaders should have." It is a list of the reporting patterns that keep making leadership stop trusting the CRM in the first place.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A quick note before the examples: names ending in &lt;code&gt;__c&lt;/code&gt; indicate custom objects or custom fields — adapt those names to your own org. In NPSP and nonprofit orgs generally, package fields and custom models vary, and Salesforce recommends checking the actual object schema in the target org before hard-coding anything.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Pattern 1 — Stale pipeline disguised as forecast
&lt;/h2&gt;

&lt;p&gt;When someone asks, &lt;em&gt;"what is likely to close this quarter?"&lt;/em&gt;, they are not asking for a sum of every open opportunity in the system. They are asking for a forecast that excludes deals nobody has touched in weeks.&lt;/p&gt;

&lt;p&gt;The first fix is to separate &lt;strong&gt;forecast reporting&lt;/strong&gt; from &lt;strong&gt;stale-opportunity cleanup&lt;/strong&gt;. For a quarter view, use a grouped query or report that looks at stage and close-date timing together.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;StageName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CALENDAR_MONTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CloseDate&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;COUNT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;SUM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;IsClosed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;CloseDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THIS_QUARTER&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;StageName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CALENDAR_MONTH&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CloseDate&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then pair it with a stale-opportunity view:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Amount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;StageName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CloseDate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;OwnerId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LastActivityDate&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Opportunity&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;IsClosed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;FALSE&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;CloseDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THIS_QUARTER&lt;/span&gt;
  &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;LastActivityDate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;NULL&lt;/span&gt; &lt;span class="k"&gt;OR&lt;/span&gt; &lt;span class="n"&gt;LastActivityDate&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;LAST_N_DAYS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;CloseDate&lt;/span&gt; &lt;span class="k"&gt;ASC&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;COUNT_DISTINCT&lt;/code&gt;, date grouping functions like &lt;code&gt;CALENDAR_MONTH()&lt;/code&gt;, and relative date literals in &lt;code&gt;WHERE&lt;/code&gt; clauses are all documented parts of SOQL, so this pattern is technically sound. The pitfall is not the query. It is trying to "clean the pipeline" before the team has written down what each stage actually means. Skip that conversation, and the team will move records around for weeks while quietly redefining the stages on the next call.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 2 — Recurring revenue drift hiding inside donor retention
&lt;/h2&gt;

&lt;p&gt;In NPSP, recurring donations are their own object, and Salesforce distinguishes between open-ended and fixed-length recurring gifts. That matters because &lt;em&gt;"lapsed donor"&lt;/em&gt; and &lt;em&gt;"recurring gift stopped processing"&lt;/em&gt; are not the same operational problem — and if the org also relies on soft credits to attribute gifts to multiple contacts, the reporting layer gets messier still (&lt;a href="https://www.maintask.com/articles/understanding-soft-credits-in-salesforce-npsp" rel="noopener noreferrer"&gt;more on that pattern here&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Merging the two into a single report usually produces a noisy list nobody owns. The cleaner approach is to split it into two views:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a classic retention report for donors who gave in the previous period but not the current one&lt;/li&gt;
&lt;li&gt;a recurring-donation exception report for open recurring gifts with no recent successful payment or no expected next-payment movement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The exact field names depend on the org, the NPSP version, and whether the team is on classic NPSP or has moved to the &lt;a href="https://www.maintask.com/articles/from-npsp-to-nonprofit-cloud" rel="noopener noreferrer"&gt;newer Nonprofit Cloud product line&lt;/a&gt; — which Salesforce has been increasingly pushing as the primary path for new implementations. Official NPSP source code confirms package fields such as &lt;code&gt;npe03__Amount__c&lt;/code&gt;, &lt;code&gt;npe03__Contact__c&lt;/code&gt;, and &lt;code&gt;npe03__Open_Ended_Status__c&lt;/code&gt;, but reporting should stay field-agnostic until the exact schema in the target org is confirmed. A field name that fails on day one undermines the whole report — and the trust attached to it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 3 — Participant numbers inflated by reporting design
&lt;/h2&gt;

&lt;p&gt;This one shows up constantly: a team says it served 1,200 participants, but the report is really counting enrolment rows, not people. Duplicates, dropouts, reopened cases, and migration artefacts quietly inflate the number.&lt;/p&gt;

&lt;p&gt;The fastest fix is to stop treating raw row count as impact. In Salesforce reports, &lt;strong&gt;unique count&lt;/strong&gt; on a column and &lt;strong&gt;row-level formulas&lt;/strong&gt; (or other report formulas) handle per-record logic — the supported way to do "completed vs dropped vs active" style analysis inside reporting.&lt;/p&gt;

&lt;p&gt;If the org uses a custom enrolment object, the aggregate logic can be simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;Program__c&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;COUNT_DISTINCT&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Contact__c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;Program_Enrollment__c&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;Enrollment_Date__c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;THIS_FISCAL_YEAR&lt;/span&gt;
&lt;span class="k"&gt;GROUP&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;Program__c&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What should not be published as runnable SOQL is SQL-style &lt;code&gt;SUM(CASE WHEN ...)&lt;/code&gt;. Salesforce's documented SOQL syntax does not include standard &lt;code&gt;CASE&lt;/code&gt; expressions, so conditional completed/dropped metrics belong in report formulas, formula fields, or separate grouped reports. The query above gives unique participants per programme; everything beyond that — completed, dropped, in-progress — belongs in the reporting layer or a custom formula field, not in the SOQL itself.&lt;/p&gt;

&lt;p&gt;The pitfall here is not technical. It is that the program team has to commit to a written definition of "completed." Otherwise the report gets rebuilt three times in six months as definitions drift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 4 — Grant obligations stored outside the CRM
&lt;/h2&gt;

&lt;p&gt;A grant can be in Salesforce and still be operationally unsafe if the reporting obligations live in email or in one person's calendar. The opportunity exists, but the actual compliance work is invisible.&lt;/p&gt;

&lt;p&gt;This is where a separate obligation model works better. Not because Salesforce ships a standard &lt;code&gt;Grant_Obligation__c&lt;/code&gt; object — it does not; names ending in &lt;code&gt;__c&lt;/code&gt; indicate a custom object — but because the reporting requirement is operationally different from the revenue record.&lt;/p&gt;

&lt;p&gt;A simple custom object pattern is enough:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Grant_Obligation__c
- Grant__c           (Lookup -&amp;gt; Opportunity)
- Obligation_Type__c (Picklist: Interim Report, Final Report, Deliverable, Compliance Doc)
- Due_Date__c        (Date, required)
- OwnerId            (User, required)
- Status__c          (Picklist: Not Started, In Progress, Submitted, Late)
- Days_Until_Due__c  (Formula: Due_Date__c - TODAY())
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A single dashboard component over &lt;code&gt;Status__c != 'Submitted'&lt;/code&gt; and &lt;code&gt;Days_Until_Due__c &amp;lt;= 60&lt;/code&gt;, grouped by owner, gives leadership a clean early-warning view.&lt;/p&gt;

&lt;p&gt;The mistake that keeps showing up here is trying to auto-generate every obligation from the grant itself. In real organisations, grant requirements vary too much, and the auto-generated records will be wrong often enough that the grant manager will quietly stop trusting them — and go back to the inbox. A manual but mandatory creation step is usually more reliable than a clever automation nobody believes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pattern 5 — Dashboards without a health layer
&lt;/h2&gt;

&lt;p&gt;Every leadership dashboard depends on an assumption about the data underneath it. If duplicates are rising, campaign attribution is patchy, or overdue tasks are piling up, the dashboard can look polished while the organisation slowly loses trust in it.&lt;/p&gt;

&lt;p&gt;A small admin-facing "org health" dashboard sitting next to the executive one solves this. It does not need to be fancy — and in &lt;a href="https://www.maintask.com/case-studies/internal-administrator-services" rel="noopener noreferrer"&gt;long-running admin engagements that function as a nonprofit's internal Salesforce team&lt;/a&gt;, this is one of the first things to set up. The cost is low, the trust dividend is enormous.&lt;/p&gt;

&lt;p&gt;The dashboard just needs to answer questions like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;how many duplicate contacts are being carried?&lt;/li&gt;
&lt;li&gt;how many won opportunities are missing campaign attribution?&lt;/li&gt;
&lt;li&gt;how many donor records are missing critical contact data?&lt;/li&gt;
&lt;li&gt;where are overdue operational tasks accumulating?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Each component should have an explicit target — &lt;em&gt;duplicates &amp;lt; 2%&lt;/em&gt;, &lt;em&gt;missing campaign attribution &amp;lt; 5%&lt;/em&gt; — so that "good" and "bad" stop being subjective.&lt;/p&gt;

&lt;p&gt;The pitfall here is cultural, not technical. The moment a data-quality dashboard gets framed as a tool for blaming staff, the data quietly gets &lt;em&gt;worse&lt;/em&gt;. People stop logging things honestly. Frame it as a process indicator instead: &lt;em&gt;"if duplicates are climbing, the intake form needs revisiting,"&lt;/em&gt; not &lt;em&gt;"Sarah created twelve duplicates last month."&lt;/em&gt; The goal is traceability, not accountability theatre.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pattern behind the patterns
&lt;/h2&gt;

&lt;p&gt;Trace all five back and they share one thing: &lt;strong&gt;the report was built around the fields that were available, not around the decision somebody needed to make.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;That is the underlying technical debt in most nonprofit Salesforce orgs. It is not the schema, it is not the automation, it is not the integration — it is the gap between &lt;em&gt;"what data do we have?"&lt;/em&gt; and &lt;em&gt;"what decision needs to be made?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Closing that gap is mostly a scoping skill, not a Salesforce skill. The five patterns above are just the most common places it shows up.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://trailhead.salesforce.com/content/learn/modules/fundraising-reports-and-dashboards-with-nonprofit-success-pack" rel="noopener noreferrer"&gt;Reports and Dashboards with Nonprofit Success Pack&lt;/a&gt; — Salesforce's own Trailhead module on the topic&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql_select_agg_functions.htm" rel="noopener noreferrer"&gt;SOQL aggregate functions reference&lt;/a&gt; — for the SOQL syntax used above&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.maintask.com/solutions/nonprofit-cloud" rel="noopener noreferrer"&gt;Maintask — Salesforce work for US nonprofits&lt;/a&gt; — a consultancy behind many of the implementations these patterns come from&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>salesforce</category>
      <category>crm</category>
      <category>nonprofit</category>
      <category>automation</category>
    </item>
    <item>
      <title>Avoiding the “$100K Spreadsheet” Trap in Nonprofit Salesforce Projects</title>
      <dc:creator>Maintask</dc:creator>
      <pubDate>Mon, 23 Feb 2026 15:37:29 +0000</pubDate>
      <link>https://dev.to/maintask/avoiding-the-100k-spreadsheet-trap-in-nonprofit-salesforce-projects-1j8p</link>
      <guid>https://dev.to/maintask/avoiding-the-100k-spreadsheet-trap-in-nonprofit-salesforce-projects-1j8p</guid>
      <description>&lt;p&gt;Salesforce projects in nonprofits rarely fail because of the platform.&lt;/p&gt;

&lt;p&gt;They fail because unclear processes, inconsistent definitions, and messy data get automated instead of resolved.&lt;/p&gt;

&lt;p&gt;When that happens, Salesforce becomes what many teams privately call it: "A very expensive spreadsheet."&lt;/p&gt;

&lt;p&gt;This post breaks down why that happens and how to avoid it from an implementation perspective.&lt;/p&gt;

&lt;h2&gt;
  
  
  Problem: Automating Without Structural Clarity
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjdaznogqw431t7swztk.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjdaznogqw431t7swztk.jpeg" alt=" " width="800" height="473"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nonprofits typically move to Salesforce when operational complexity increases. More donors. More grants. More reporting pressure. More cross-team collaboration.&lt;/p&gt;

&lt;p&gt;Spreadsheets stop scaling. Manual reconciliation becomes painful. Leadership loses visibility.&lt;/p&gt;

&lt;p&gt;The problem is not the decision to adopt Salesforce. The problem is how the implementation starts.&lt;/p&gt;

&lt;p&gt;Many projects begin with configuration:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating custom objects&lt;/li&gt;
&lt;li&gt;Building automation flows&lt;/li&gt;
&lt;li&gt;Designing dashboards&lt;/li&gt;
&lt;li&gt;Importing legacy data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the foundational questions are often unresolved:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What exactly qualifies as a "major donor"?&lt;/li&gt;
&lt;li&gt;When is a fundraising opportunity officially "closed won"?&lt;/li&gt;
&lt;li&gt;How are grant stages defined?&lt;/li&gt;
&lt;li&gt;What metrics matter to the board?&lt;/li&gt;
&lt;li&gt;Who owns data quality?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If these definitions vary across teams, automation will only make inconsistencies permanent.&lt;/p&gt;

&lt;p&gt;Technology scales whatever it is given — clarity or confusion.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution: Sequence Matters More Than Features
&lt;/h2&gt;

&lt;p&gt;A stable Salesforce implementation in a nonprofit context follows a predictable sequence. It is less about technical complexity and more about order of operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Define Outcomes Before Building Objects
&lt;/h3&gt;

&lt;p&gt;Before touching configuration, define success.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What decisions need better visibility?&lt;/li&gt;
&lt;li&gt;What reporting gaps currently exist?&lt;/li&gt;
&lt;li&gt;What operational friction are you trying to reduce?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If leadership cannot articulate the outcomes, dashboards will not fix that later.&lt;/p&gt;

&lt;p&gt;Salesforce is a system of record. It should reflect decisions, not create them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 2: Standardize Process Definitions
&lt;/h3&gt;

&lt;p&gt;Most CRM inconsistencies are semantic.&lt;/p&gt;

&lt;p&gt;If one fundraiser marks a donor as "major" at $5,000 and another at $25,000, segmentation breaks immediately. If grant tracking stages differ across departments, reporting becomes unreliable.&lt;/p&gt;

&lt;p&gt;Before building automation, document and align on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fundraising lifecycle stages&lt;/li&gt;
&lt;li&gt;Opportunity definitions&lt;/li&gt;
&lt;li&gt;Program tracking structure&lt;/li&gt;
&lt;li&gt;Grant state transitions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This alignment reduces downstream rework significantly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 3: Treat Data Migration as a Data Project
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmvfk2u0nvpn4kugs96m.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmvfk2u0nvpn4kugs96m.jpeg" alt=" " width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Legacy nonprofit databases often contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Duplicate records (sometimes 20–30%)&lt;/li&gt;
&lt;li&gt;Inconsistent formatting&lt;/li&gt;
&lt;li&gt;Missing emails&lt;/li&gt;
&lt;li&gt;Partial donation histories&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this data is imported without cleansing, Salesforce will reflect those flaws at scale.&lt;/p&gt;

&lt;p&gt;User trust erodes quickly when reports do not match expectations.&lt;/p&gt;

&lt;p&gt;Clean data is not a cosmetic improvement. It is a prerequisite for adoption.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 4: Resist Early Over-Customization
&lt;/h3&gt;

&lt;p&gt;Nonprofit Cloud includes a strong baseline architecture — household data model, campaign tracking, standard opportunity handling, and built-in reporting.&lt;/p&gt;

&lt;p&gt;It is tempting to customize everything to replicate legacy workflows. But over-customization introduces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Technical debt&lt;/li&gt;
&lt;li&gt;Upgrade friction&lt;/li&gt;
&lt;li&gt;Increased admin burden&lt;/li&gt;
&lt;li&gt;Dependency on one "system expert."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Start with standard functionality. Customize only when process requirements truly demand it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Example: Two Implementation Paths
&lt;/h2&gt;

&lt;p&gt;Consider two mid-sized nonprofits implementing Salesforce.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Path A: Configuration-First&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The organization imports 50,000 records without deduplication. Each department keeps its own opportunity definitions. Custom objects are created to match historical spreadsheets. Dashboards are built late in the process.&lt;/p&gt;

&lt;p&gt;Two months later:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Segmentation produces inconsistent counts&lt;/li&gt;
&lt;li&gt;Grant reporting requires manual adjustment&lt;/li&gt;
&lt;li&gt;Leadership questions data accuracy&lt;/li&gt;
&lt;li&gt;Admin workload increases&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The platform functions technically, but it does not create operational clarity.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Path B: Process-First&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Before configuration begins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fundraising stages are standardized&lt;/li&gt;
&lt;li&gt;Grant lifecycle definitions are aligned&lt;/li&gt;
&lt;li&gt;Duplicate records are reduced&lt;/li&gt;
&lt;li&gt;Reporting requirements are mapped&lt;/li&gt;
&lt;li&gt;Governance ownership is assigned&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Configuration then reflects those agreements.&lt;/p&gt;

&lt;p&gt;Result: reliable dashboards, higher adoption, lower long-term maintenance, reduced reporting friction.&lt;/p&gt;

&lt;p&gt;Same platform. Different discipline.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pitfalls to Avoid
&lt;/h2&gt;

&lt;p&gt;Most nonprofit Salesforce failures share similar patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Treating CRM implementation as an IT task instead of an organizational alignment exercise&lt;/li&gt;
&lt;li&gt;Importing legacy data without validation or deduplication&lt;/li&gt;
&lt;li&gt;Allowing uncontrolled field creation post go-live&lt;/li&gt;
&lt;li&gt;Building automation before definitions are finalized&lt;/li&gt;
&lt;li&gt;Skipping governance planning&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of these are technical limitation. There are sequencing issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;Salesforce does not inherently create clarity. It scales it.&lt;/p&gt;

&lt;p&gt;If your nonprofit has agreed-upon definitions, clean and structured data, documented processes, and clear governance ownership, Salesforce becomes a strategic asset.&lt;/p&gt;

&lt;p&gt;If not, it becomes a powerful system delivering spreadsheet-level value.&lt;/p&gt;

&lt;p&gt;The difference is rarely about advanced automation or complex architecture. It is about implementation order and organizational alignment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Further Reading
&lt;/h2&gt;

&lt;p&gt;If you're exploring a structured, strategy-first approach to nonprofit Salesforce implementation, you can see how Maintask approaches CRM delivery here:&lt;a href="https://maintask.com" rel="noopener noreferrer"&gt;https://maintask.com&lt;/a&gt;&lt;/p&gt;

</description>
      <category>salesforce</category>
      <category>crm</category>
      <category>nonprofit</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
