/sac-optimize
Analyze SAC scripts for performance optimization opportunities
$ npx -y skills add secondsky/sap-skills --agent claude-codeHow it fires
How this command gets triggered: by you, by Claude, or both.
- Fires itselfClaude auto-loads it when your prompt matches the work.
- You can call itInvoke it directly when you want it.
- Slash command
/sac-optimize
Context preview
What this command does when you run it.
Analyze SAC scripts for performance optimization opportunities
Command definition
sac-optimize.mdname: sac-optimize
description: Analyze SAC scripts for performance optimization opportunities
allowed-tools:
- Read
- Grep
- Glob
argument-hint: "[script-file-or-symptom]"
Shell Snippet Notes
- Shell snippets assume Bash on Linux/macOS, WSL2, or Git Bash.
- Install the command-specific tooling shown near each snippet before running it.
- Confirm before running commands that delete files, change ownership, deploy, or modify remote systems.
Output Contract
Return performance findings, backend-call risks, caching or batching suggestions, and validation steps. Default to read-only analysis and do not edit scripts.
Analyze SAC scripts for performance issues and provide optimization recommendations.
Performance Analysis Workflow
Step 1: Collect Script Information
Ask the user to provide: 1. **Scripts to analyze** - paste code or describe functionality 2. **Performance symptoms** - slow load, laggy interactions, timeouts? 3. **Number of data points** - how much data is being processed? 4. **User complaints** - specific slow operations?
Step 2: Performance Anti-Patterns Checklist
High-Impact Issues (Fix First)
**1. getMembers() without accessMode**
// SLOW: Hits backend every time
var members = ds.getMembers("Location");
// FAST: Uses cached booked values
var members = ds.getMembers("Location", {
accessMode: MemberAccessMode.BookedValues
});**Impact**: Each call = 1 backend roundtrip (~200-500ms)
**2. Missing setRefreshPaused() Batching**
// SLOW: 3 separate backend calls
ds.setDimensionFilter("Dim1", value1);
ds.setDimensionFilter("Dim2", value2);
ds.setDimensionFilter("Dim3", value3);
// FAST: 1 backend call
ds.setRefreshPaused(true);
ds.setDimensionFilter("Dim1", value1);
ds.setDimensionFilter("Dim2", value2);
ds.setDimensionFilter("Dim3", value3);
ds.setRefreshPaused(false);**Impact**: Reduces N calls to 1 call
**3. Heavy onInitialization Code**
// BAD: Slow startup
// onInitialization
var members = Chart_1.getDataSource().getMembers("Location");
members.forEach(function(m) {
// Heavy processing
});
// GOOD: Empty or minimal initialization
// onInitialization
// (keep empty - defer to lazy loading)**Impact**: Blocks initial render
Medium-Impact Issues
**4. getData() Instead of getResultSet()**
// SLOWER: May hit backend
var data = ds.getData();
// FASTER: Uses cached result set
var resultSet = ds.getResultSet();
**5. Repeated getDataSource() Calls**
// INEFFICIENT: Multiple lookups
Chart_1.getDataSource().setDimensionFilter("A", "1");
Chart_1.getDataSource().setDimensionFilter("B", "2");
// BETTER: Cache reference
var ds = Chart_1.getDataSource();
ds.setDimensionFilter("A", "1");
ds.setDimensionFilter("B", "2");**6. Backend Calls in Loops**
// TERRIBLE: N backend calls
for (var i = 0; i < items.length; i++) {
ds.getMembers(items[i]); // Backend call each iteration!
}
// BETTER: Single call, process in memory
var resultSet = ds.getResultSet();
// Process resultSet in memoryStep 3: Performance Optimization Patterns
Pattern 1: Efficient Data Access
// For reading dimension values
var ds = Chart_1.getDataSource();
// Option A: From result set (fastest, cached)
var resultSet = ds.getResultSet();
var uniqueValues = {};
resultSet.forEach(function(row) {
uniqueValues[row["Location"].id] = row["Location"].description;
});
// Option B: Booked values only (fast, cached)
var members = ds.getMembers("Location", {
accessMode: MemberAccessMode.BookedValues
});
// Option C: All master data (slowest, hits backend)
// Only use when you need unbooked values
var allMembers = ds.getMembers("Location", {
accessMode: MemberAccessMode.MasterData
});Pattern 2: Batched Filter Updates
function applyMultipleFilters(ds, filters) {
ds.setRefreshPaused(true);
try {
for (var dim in filters) {
if (filters[dim] === null) {
ds.removeDimensionFilter(dim);
} else {
ds.setDimensionFilter(dim, filters[dim]);
}
}
} finally {
ds.setRefreshPaused(false); // Always unpause
}
}
// Usage
applyMultipleFilters(Chart_1.getDataSource(), {
"Location": "US",
"Product": ["A", "B", "C"],
"Year": null // Remove this filter
});Pattern 3: Lazy Loading
// Script variable to track initialization
var initialized = false;
// In onResultChanged or first interaction
if (!initialized) {
// Perform one-time heavy setup
initialized = true;
}Pattern 4: Debounced Updates
// For rapid user input (e.g., typing in search)
var debounceTimer = null;
function onSearchInput(value) {
if (debounceTimer) {
clearTimeout(debounceTimer);
}
debounceTimer = setTimeout(function() {
// Actual filter operation
ds.setDimensionFilter("Search", value);
}, 300); // Wait 300ms after last input
}Step 4: Performance Metrics
Enable Performance Logging
Add URL parameter: `?APP_PERFORMANCE_LOGGING=true`
This shows:
- Script execution times
- Backend call durations
- Widget render times
Manual Timing
console.time("operationName");
// ... operation ...
console.timeEnd("operationName"); // Logs durationStep 5: Optimization Report Template
**Current State Analysis:**
- onInitialization: [Empty/Light/Heavy]
- Backend calls per interaction: [Count]
- Main bottlenecks: [List]
**Recommendations:**
| Priority | Issue | Current Impact | Fix | Expected Improvement | |----------|-------|---------------|-----|---------------------| | High | getMembers without accessMode | 5 calls/action | Add BookedValues | -80% calls | | High | No refresh batching | 3 calls/filter | Add setRefreshPaused | -66% calls | | Medium | Heavy onInit | 2s startup | Move to lazy load | -1.5s start
Read more
name: sac-optimize description: Analyze SAC scripts for performance optimization opportunities allowed-tools: - Read - Grep - Glob argument-hint: "[script-file-or-symptom]"
Shell Snippet Notes
- Shell snippets assume Bash on Linux/macOS, WSL2, or Git Bash.
- Install the command-specific tooling shown near each snippet before running it.
- Confirm before running commands that delete files, change ownership, deploy, or modify remote systems.
Output Contract
Return performance findings, backend-call risks, caching or batching suggestions, and validation steps. Default to read-only analysis and do not edit scripts.
Analyze SAC scripts for performance issues and provide optimization recommendations.
Performance Analysis Workflow
Step 1: Collect Script Information
Ask the user to provide: 1. **Scripts to analyze** - paste code or describe functionality 2. **Performance symptoms** - slow load, laggy interactions, timeouts? 3. **Number of data points** - how much data is being processed? 4. **User complaints** - specific slow operations?
Step 2: Performance Anti-Patterns Checklist
High-Impact Issues (Fix First)
**1. getMembers() without accessMode**
// SLOW: Hits backend every time
var members = ds.getMembers("Location");
// FAST: Uses cached booked values
var members = ds.getMembers("Location", {
accessMode: MemberAccessMode.BookedValues
});**Impact**: Each call = 1 backend roundtrip (~200-500ms)
**2. Missing setRefreshPaused() Batching**
// SLOW: 3 separate backend calls
ds.setDimensionFilter("Dim1", value1);
ds.setDimensionFilter("Dim2", value2);
ds.setDimensionFilter("Dim3", value3);
// FAST: 1 backend call
ds.setRefreshPaused(true);
ds.setDimensionFilter("Dim1", value1);
ds.setDimensionFilter("Dim2", value2);
ds.setDimensionFilter("Dim3", value3);
ds.setRefreshPaused(false);**Impact**: Reduces N calls to 1 call
**3. Heavy onInitialization Code**
// BAD: Slow startup
// onInitialization
var members = Chart_1.getDataSource().getMembers("Location");
members.forEach(function(m) {
// Heavy processing
});
// GOOD: Empty or minimal initialization
// onInitialization
// (keep empty - defer to lazy loading)**Impact**: Blocks initial render
Medium-Impact Issues
**4. getData() Instead of getResultSet()**
// SLOWER: May hit backend var data = ds.getData(); // FASTER: Uses cached result set var resultSet = ds.getResultSet();
**5. Repeated getDataSource() Calls**
// INEFFICIENT: Multiple lookups
Chart_1.getDataSource().setDimensionFilter("A", "1");
Chart_1.getDataSource().setDimensionFilter("B", "2");
// BETTER: Cache reference
var ds = Chart_1.getDataSource();
ds.setDimensionFilter("A", "1");
ds.setDimensionFilter("B", "2");**6. Backend Calls in Loops**
// TERRIBLE: N backend calls
for (var i = 0; i < items.length; i++) {
ds.getMembers(items[i]); // Backend call each iteration!
}
// BETTER: Single call, process in memory
var resultSet = ds.getResultSet();
// Process resultSet in memoryStep 3: Performance Optimization Patterns
Pattern 1: Efficient Data Access
// For reading dimension values
var ds = Chart_1.getDataSource();
// Option A: From result set (fastest, cached)
var resultSet = ds.getResultSet();
var uniqueValues = {};
resultSet.forEach(function(row) {
uniqueValues[row["Location"].id] = row["Location"].description;
});
// Option B: Booked values only (fast, cached)
var members = ds.getMembers("Location", {
accessMode: MemberAccessMode.BookedValues
});
// Option C: All master data (slowest, hits backend)
// Only use when you need unbooked values
var allMembers = ds.getMembers("Location", {
accessMode: MemberAccessMode.MasterData
});Pattern 2: Batched Filter Updates
function applyMultipleFilters(ds, filters) {
ds.setRefreshPaused(true);
try {
for (var dim in filters) {
if (filters[dim] === null) {
ds.removeDimensionFilter(dim);
} else {
ds.setDimensionFilter(dim, filters[dim]);
}
}
} finally {
ds.setRefreshPaused(false); // Always unpause
}
}
// Usage
applyMultipleFilters(Chart_1.getDataSource(), {
"Location": "US",
"Product": ["A", "B", "C"],
"Year": null // Remove this filter
});Pattern 3: Lazy Loading
// Script variable to track initialization
var initialized = false;
// In onResultChanged or first interaction
if (!initialized) {
// Perform one-time heavy setup
initialized = true;
}Pattern 4: Debounced Updates
// For rapid user input (e.g., typing in search)
var debounceTimer = null;
function onSearchInput(value) {
if (debounceTimer) {
clearTimeout(debounceTimer);
}
debounceTimer = setTimeout(function() {
// Actual filter operation
ds.setDimensionFilter("Search", value);
}, 300); // Wait 300ms after last input
}Step 4: Performance Metrics
Enable Performance Logging
Add URL parameter: `?APP_PERFORMANCE_LOGGING=true`
This shows:
- Script execution times
- Backend call durations
- Widget render times
Manual Timing
console.time("operationName");
// ... operation ...
console.timeEnd("operationName"); // Logs durationStep 5: Optimization Report Template
**Current State Analysis:**
- onInitialization: [Empty/Light/Heavy]
- Backend calls per interaction: [Count]
- Main bottlenecks: [List]
**Recommendations:**
| Priority | Issue | Current Impact | Fix | Expected Improvement | |----------|-------|---------------|-----|---------------------| | High | getMembers without accessMode | 5 calls/action | Add BookedValues | -80% calls | | High | No refresh batching | 3 calls/filter | Add setRefreshPaused | -66% calls | | Medium | Heavy onInit | 2s startup | Move to lazy load | -1.5s start
40 SAP development plugins with evidence-tracked verification SAP development plugins for AI coding assistants, with public-source or package-registry verification tracked where available.
Repo: secondsky/sap-skills
Other commands on sap-skills.
- /abap-cds-model-check
Optional intended usage such as analytical, transactional, reuse, value-help, or extraction
Open command - /abap-cloud-review
Optional target ABAP platform or SAP BTP ABAP Environment release
Open command - /ai-core-deployment-check
Optional runtime or scenario name, such as orchestration, serving, training, or batch
Open command - /api-style-review
Optional API style lens such as REST, OData, OpenAPI, or SDK
Open command - /btp-architecture-review
Optional scenario lens such as extension, integration, analytics, or AI
Open command - /work-zone-content-check
Optional Work Zone edition or content type
Open command

