agentsclimarketplace

Salesforce performance tuning

Skill jeremylongshore/claude-code-plugins-plus-skills/plugins/saas-packs/salesforce-pack/skills/salesforce-performance-tuning

425 plugins, 2,810 skills, 200 agents for Claude Code. Open-source marketplace at tonsofskills.com with the ccpi CLI package manager.

Install
npx -y skills add jeremylongshore/claude-code-plugins-plus-skills --skill salesforce-performance-tuning

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

What its author says it does

Copied from the file, not written here

'Optimize Salesforce API performance with SOQL tuning, Composite API batching, and caching.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

6.5 KB, as published. Nobody here has run it

Salesforce Performance Tuning

Overview

Optimize Salesforce API performance: tune SOQL queries, minimize API calls using Composite/Collections APIs, implement metadata caching, and handle large result sets efficiently.

Prerequisites

  • jsforce connection configured
  • Understanding of SOQL query plans
  • Redis or in-memory cache available (optional)
  • Access to Setup > API usage monitoring

Instructions

Step 1: SOQL Query Optimization

// BAD: SELECT * equivalent — fetches all fields
const result = await conn.query('SELECT FIELDS(ALL) FROM Account LIMIT 100');

// GOOD: Only select fields you need
const result = await conn.query(`
  SELECT Id, Name, Industry, AnnualRevenue
  FROM Account
  WHERE Industry = 'Technology'
  LIMIT 100
`);

// BAD: Non-selective WHERE clause (full table scan)
const result = await conn.query("SELECT Id FROM Contact WHERE Title LIKE '%Engineer%'");

// GOOD: Use indexed fields in WHERE (Id, Name, CreatedDate, RecordType, lookup fields)
const result = await conn.query(`
  SELECT Id, Name, Title
  FROM Contact
  WHERE AccountId = '001xxxxxxxxxxxx'
    AND CreatedDate >= LAST_N_DAYS:30
  LIMIT 200
`);

// Use relationship queries to avoid N+1 pattern
// BAD: Query Accounts, then query Contacts for each (N+1 API calls)
const accounts = await conn.query('SELECT Id FROM Account LIMIT 50');
for (const acct of accounts.records) {
  await conn.query(`SELECT Id FROM Contact WHERE AccountId = '${acct.Id}'`);
  // 50 extra API calls!
}

// GOOD: Single relationship query (1 API call)
const accountsWithContacts = await conn.query(`
  SELECT Id, Name,
    (SELECT Id, FirstName, LastName, Email FROM Contacts LIMIT 20)
  FROM Account
  WHERE Industry = 'Technology'
  LIMIT 50
`);

Step 2: Reduce API Call Count

// STRATEGY 1: sObject Collections — 200 records per API call
// Instead of 100 individual creates = 100 API calls
const contacts = Array.from({ length: 100 }, (_, i) => ({
  FirstName: `User${i}`,
  LastName: `Test`,
  Email: `user${i}@test.com`,
}));
await conn.sobject('Contact').create(contacts); // 1 API call

// STRATEGY 2: Composite API — 25 mixed operations per API call
// Create Account + Contact + Opportunity = 1 API call instead of 3
// See salesforce-core-workflow-b

// STRATEGY 3: queryMore for pagination — FREE (doesn't count as extra call)
let result = await conn.query('SELECT Id, Name FROM Contact');
let allRecords = [...result.records];
while (!result.done) {
  result = await conn.queryMore(result.nextRecordsUrl!);
  allRecords.push(...result.records);
}

Step 3: Cache Metadata (Describe Calls)

import { LRUCache } from 'lru-cache';

// Describe calls are expensive and metadata rarely changes
const describeCache = new LRUCache<string, any>({
  max: 50,                // Cache up to 50 sObject describes
  ttl: 1000 * 60 * 60,   // 1 hour TTL (metadata changes are rare)
});

async function cachedDescribe(sObjectType: string) {
  const cached = describeCache.get(sObjectType);
  if (cached) return cached;

  const conn = await getConnection();
  const describe = await conn.sobject(sObjectType).describe();
  describeCache.set(sObjectType, describe);
  return describe;
}

// Cache SOQL query results for frequently-accessed reference data
const queryCache = new LRUCache<string, any>({
  max: 100,
  ttl: 1000 * 60 * 5,    // 5 minute TTL for query results
});

async function cachedQuery<T>(soql: string): Promise<T[]> {
  const cached = queryCache.get(soql);
  if (cached) return cached;

  const conn = await getConnection();
  const result = await conn.query<T>(soql);
  queryCache.set(soql, result.records);
  return result.records;
}

Step 4: Stream Large Result Sets

// For large exports (100K+ records), use Bulk API 2.0 query
// Streams results to avoid loading everything into memory

const queryResults = await conn.bulk2.query(
  'SELECT Id, Name, Email FROM Contact WHERE CreatedDate >= LAST_YEAR'
);

// Process as async iterator — constant memory usage
let count = 0;
for await (const record of queryResults) {
  await processContact(record);
  count++;
  if (count % 10000 === 0) {
    console.log(`Processed ${count} records...`);
  }
}

Step 5: Connection Optimization

// Reuse connections across requests (singleton pattern)
// jsforce handles keep-alive internally

// Pin API version to avoid version negotiation overhead
const conn = new jsforce.Connection({
  loginUrl: process.env.SF_LOGIN_URL,
  version: '59.0',         // Skip version detection call
  maxRequest: 10,           // Max concurrent requests
});

Performance Benchmarks

OperationTypical LatencyOptimization
Single SOQL query100-300msUse selective filters on indexed fields
sObject Create (single)150-400msBatch with Collections (up to 200)
Describe call200-500msCache for 1 hour
Bulk API job creation500ms-2sUse for 10K+ records
Composite (25 subrequests)500ms-3sReplaces 25 individual calls

Error Handling

IssueCauseSolution
NON_SELECTIVE_QUERYWHERE clause too broadAdd indexed field filters
QUERY_TOO_COMPLICATEDToo many joins/subqueriesSimplify or split into multiple queries
50,001 row limitToo many resultsAdd LIMIT, or use Bulk API for exports
Cache stampedeTTL expired, all threads missUse stale-while-revalidate pattern

Resources

Next Steps

For cost optimization, see salesforce-cost-tuning.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.