AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Perf Optimise

skill-korchard333-claude-power-platform-community-perf-optimise · by korchard333

Power Platform performance optimization. Use when: performance, slow, optimization, delegation, caching, indexing, query performance, API limits, throttling, virtual scrolling, lazy loading, concurrent, batch operations, form load.

No reviews yet
0 installs
41 views
0.0% view→install

Install

$ agentstack add skill-korchard333-claude-power-platform-community-perf-optimise

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access Used
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-korchard333-claude-power-platform-community-perf-optimise)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
5mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Perf Optimise? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Skill: Performance Optimization

When to Use

Trigger when diagnosing or optimizing performance for Canvas Apps, Model-Driven Apps, Code Apps, Power Automate flows, Dataverse queries, or plugins. Also trigger when addressing API throttling (429 errors) or capacity limits.


Canvas App Performance

Delegation

Delegation pushes query logic to the data source. Non-delegable operations process only the first 500-2000 records locally.

| Delegable to Dataverse | NOT Delegable | |---|---| | Filter with =, <>, , = | Distinct | | Filter with in (choice columns) | CountRows (use CountIf for delegable) | | Sort (single column) | GroupBy | | Search (Dataverse only) | AddColumns in a Filter | | StartsWith | Nested Filter | | LookUp | First applied to non-delegable |

Key Canvas Optimizations

// BAD: Non-delegable — loads all records, filters locally
Filter(Contacts, Text(Phone, "[$-en-US]#") = "555-0100")

// GOOD: Delegable — server-side filter
Filter(Contacts, Phone = "555-0100")

// BAD: Loading all records then counting
CountRows(Filter(LargeTable, Status = "Active"))

// GOOD: CountIf is delegable to Dataverse
CountIf(LargeTable, Status = "Active")

Concurrent() for Parallel Data Loading

// BAD: Sequential loading
ClearCollect(colAccounts, Accounts);
ClearCollect(colContacts, Contacts);
ClearCollect(colOrders, Orders);
// Total time = T1 + T2 + T3

// GOOD: Parallel loading
Concurrent(
    ClearCollect(colAccounts, Accounts),
    ClearCollect(colContacts, Contacts),
    ClearCollect(colOrders, Orders)
);
// Total time = max(T1, T2, T3)

Named Formulas (Pre-Computation)

// App.Formulas — computed once, cached
ActiveAccounts = Filter(Accounts, statuscode = 1);
UserDepartment = Office365Users.MyProfile().Department;
TodayDate = Today();

// These are evaluated once and cached — no re-computation on screen navigation

Collection Pre-Loading

// App.OnStart — load reference data once
Concurrent(
    ClearCollect(colStatusOptions, Choices('Accounts'.Status)),
    ClearCollect(colCategoryOptions, Choices('Accounts'.Category)),
    ClearCollect(colCountries, Filter(Countries, IsActive = true))
);

// Galleries and dropdowns use local collections (fast) instead of Dataverse calls

Dataverse Query Performance

$select — Only Request Needed Columns

# BAD: Returns ALL columns (slow, wastes bandwidth)
GET /api/data/v9.2/contacts

# GOOD: Only requested columns returned
GET /api/data/v9.2/contacts?$select=firstname,lastname,emailaddress1

$filter — Server-Side Filtering

# BAD: Get all, filter client-side
GET /api/data/v9.2/contacts  # Then filter in JS

# GOOD: Server-side filter
GET /api/data/v9.2/contacts?$filter=statecode eq 0 and contains(lastname,'Smith')&$top=50

$top — Limit Result Set

# Always use $top for paged results
GET /api/data/v9.2/contacts?$top=50&$select=fullname,emailaddress1

# For next page, use @odata.nextLink from response

Indexing

Dataverse automatically indexes primary key and common columns. For custom columns used in filters:

Power Platform Admin Center → Environments → [env] → Settings
  → Administration → System Settings → Administration tab
  → Performance: Entity-specific index recommendations

Or request custom indexes via support for high-volume tables

FetchXML Aggregate vs OData

For aggregations, FetchXML is significantly faster than OData:


  
    
    
    
      
    
  
# Equivalent OData (less efficient for aggregation)
GET /api/data/v9.2/contacts?$apply=filter(statecode eq 0)/aggregate(contactid with countdistinct as count, annual_revenue with sum as total_revenue)

Alternate Keys for Upsert

# Instead of: GET to check existence, then POST or PATCH
# Use: PATCH with alternate key (single call, upsert)
PATCH /api/data/v9.2/contacts(emailaddress1='john@contoso.com')
  -H "If-Match: *"
  -d '{"firstname": "John", "lastname": "Doe"}'

Plugin Performance

Sandbox Constraints

| Constraint | Limit | |---|---| | Execution timeout | 2 minutes | | Memory | 256 MB | | CPU | Shared (no guarantee) | | External HTTP calls | Allowed in async, risky in sync |

Optimization Patterns

// BAD: Query inside a loop
foreach (var contactId in contactIds)
{
    var contact = service.Retrieve("contact", contactId, new ColumnSet("fullname"));
    // N+1 query problem
}

// GOOD: Single query with IN filter
var query = new QueryExpression("contact")
{
    ColumnSet = new ColumnSet("fullname"),
    Criteria = new FilterExpression
    {
        Conditions = {
            new ConditionExpression("contactid", ConditionOperator.In, contactIds.ToArray())
        }
    }
};
var results = service.RetrieveMultiple(query);
// BAD: Synchronous HTTP call in sync plugin (blocks user, risks timeout)
var client = new HttpClient();
var response = await client.GetAsync("https://external-api.com/validate");

// GOOD: Use Service Bus or async plugin for external calls
// Or use pre-validated data from Dataverse
// BAD: Processing all records in bulk operation trigger
// Plugin fires once per record in a CreateMultiple — 1000 records = 1000 plugin executions

// GOOD: Batch-aware plugin using IPluginExecutionContext4
var context4 = (IPluginExecutionContext4)context;
// Check if running in bulk context and optimize accordingly

Code App Performance

Tree-Shaking and Bundle Size

// BAD: Import entire library
import _ from "lodash";

// GOOD: Import only what you need
import debounce from "lodash/debounce";

Lazy Routes (Code Splitting)

// React Router with lazy loading
import { lazy, Suspense } from "react";

const Dashboard = lazy(() => import("./pages/Dashboard"));
const Settings = lazy(() => import("./pages/Settings"));

function App() {
  return (
    }>
      
        } />
        } />
      
    
  );
}

TanStack Query (Stale/Cache)

import { useQuery } from "@tanstack/react-query";

function useAccounts() {
  return useQuery({
    queryKey: ["accounts"],
    queryFn: fetchAccounts,
    staleTime: 5 * 60 * 1000,     // 5 min — don't refetch if fresh
    gcTime: 30 * 60 * 1000,        // 30 min — keep in cache
    refetchOnWindowFocus: false,    // Don't refetch on tab switch
  });
}

Virtual Scrolling for Large Lists

import { useVirtualizer } from "@tanstack/react-virtual";

function VirtualList({ items }: { items: Item[] }) {
  const parentRef = useRef(null);
  const virtualizer = useVirtualizer({
    count: items.length,
    getScrollElement: () => parentRef.current,
    estimateSize: () => 40,
  });

  return (
    
      
        {virtualizer.getVirtualItems().map((virtual) => (
          
            {items[virtual.index].name}
          
        ))}
      
    
  );
}

Power Automate Performance

Apply to Each Concurrency

Apply to each → Settings → Concurrency Control
  → Turn on: Yes
  → Degree of parallelism: 50 (maximum)

| Setting | Behavior | |---|---| | Off (default) | Sequential — 1 at a time | | On, parallelism = 20 | 20 concurrent iterations | | On, parallelism = 50 | Maximum concurrency |

> Warning: High concurrency can cause API throttling (429). Monitor and adjust.

Pagination

Action: List rows (Dataverse)
  → Settings → Pagination → On
  → Threshold: 100,000 (max records to retrieve)

Default without pagination: 5,000 rows max

Child Flows

Break large flows into child flows for:

  • Reusability across multiple parent flows
  • Cleaner flow design
  • Independent error handling
  • Parallel execution of independent branches

Avoid Deep Nesting

BAD: Apply to each → Condition → Apply to each → Condition → Apply to each
  (> 8 levels of nesting = flow failure)

GOOD: Use child flows, Select/Filter expressions, or batch operations

Model-Driven App Performance

Form Load Optimization

| Optimization | Impact | |---|---| | Fewer tabs and sections | Less HTML to render | | Lazy subgrids | Load on tab expand, not on form load | | Fewer columns on form | Less data fetched | | Minimize OnLoad JavaScript | Defer non-critical logic | | Quick View forms instead of subgrids | Lighter weight for related data |

JavaScript Best Practices

// BAD: Heavy processing in OnLoad
function onLoad() {
    // Fetches data, updates 20 fields, calls external API
    var result = Xrm.WebApi.retrieveMultipleRecords("contact", "?$top=1000");
    // ...
}

// GOOD: Minimal OnLoad, defer heavy work
function onLoad(context) {
    // Only essential logic
    setFieldVisibility(context);

    // Defer non-critical work
    setTimeout(function() {
        loadSecondaryData(context);
    }, 100);
}

API Request Limits

Protection Limits (Hard Throttle)

| Limit | Value | Scope | |---|---|---| | Per user | 6,000 requests / 5-minute window | Per user | | Per web server | 60,000 / 5 min | Per web server (shared) | | Concurrent requests | 52 per user | Simultaneous connections |

When Throttled (HTTP 429)

# Response includes Retry-After header
HTTP/1.1 429 Too Many Requests
Retry-After: 30

# Always honor Retry-After
sleep $(curl -sI ... | grep Retry-After | awk '{print $2}')

Strategies to Avoid Throttling

| Strategy | Implementation | |---|---| | Batch operations | Use CreateMultiple/UpdateMultiple instead of individual calls | | $select | Reduce payload size, faster responses | | Exponential backoff | On 429, wait 1s → 2s → 4s → 8s | | Connection pooling | Reuse HTTP connections | | Off-peak scheduling | Run bulk operations during low-usage hours | | Service protection limit monitoring | Track via Admin Center analytics |


Performance Checklist

## Performance Review Checklist

### Canvas App
- [ ] All gallery queries are delegable
- [ ] Using Concurrent() for parallel data loading
- [ ] Named Formulas for pre-computation
- [ ] Collections for reference data (not repeated server calls)
- [ ] No non-delegable functions on tables > 500 rows

### Dataverse Queries
- [ ] $select on every query (no select-all)
- [ ] $filter for server-side filtering
- [ ] $top for pagination
- [ ] FetchXML for aggregations
- [ ] Alternate keys for upsert operations

### Code App
- [ ] Tree-shaking (no unused library imports)
- [ ] Lazy routes for code splitting
- [ ] TanStack Query with staleTime configured
- [ ] Virtual scrolling for lists > 100 items
- [ ] Dynamic imports for heavy components

### Power Automate
- [ ] Apply to Each concurrency enabled (where safe)
- [ ] Pagination enabled for large queries
- [ ] Child flows for reusable/nested logic
- [ ] No nesting deeper than 4-5 levels
- [ ] Batch operations instead of loop + individual API calls

### Plugins
- [ ] No N+1 query patterns
- [ ] No sync HTTP calls in sync plugins
- [ ] Using early-bound types (faster than late-bound)
- [ ] ColumnSet specified (never ColumnSet(true))

### API Limits
- [ ] Retry-After handling for 429 responses
- [ ] Batch operations for bulk processing
- [ ] Off-peak scheduling for heavy operations
- [ ] Monitoring API usage in Admin Center

Anti-Patterns

  • Querying all columns ($select omitted) — wastes bandwidth, slower response
  • Non-delegable functions on large datasets — silent data truncation
  • Synchronous HTTP calls in sync plugins — blocks user, risks timeout
  • Apply to Each without concurrency — sequential processing of 1000 items
  • No caching in Code Apps — refetching data on every render
  • Full page loads instead of virtual scrolling — DOM bloat, slow rendering
  • Plugin executing inside loops — N+1 query pattern
  • Ignoring 429 errors — retrying immediately without backoff makes it worse
  • Loading all reference data on app start — slow startup, use lazy loading
  • No $top on OData queries — potentially returning millions of rows

Related Skills

  • dataverse-web-api — Query optimization, batch operations
  • canvas-apps — Delegation patterns, performance techniques
  • code-apps — React performance, bundling
  • plugins — Sandbox constraints, execution optimization
  • power-automate — Flow concurrency, pagination
  • observability — Performance monitoring and alerting

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.