Copado Quality Gates: Static Analysis and Compliance Checks for Salesforce

Copado Quality Gates: Static Analysis and Compliance Checks for Salesforce

A deployment pipeline is only as good as the quality of code it promotes. Apex code that compiles and deploys successfully can still be a time bomb — unused variables, SOQL queries inside loops, hardcoded IDs, and security vulnerabilities that pass all tests but degrade performance or create compliance gaps. Copado's Quality Gates feature adds a layer of automated static analysis that can block deployments before bad code ever reaches a higher environment.

What Quality Gates Are in Copado

A Quality Gate in Copado is an automated check that must pass before a promotion can proceed. Unlike test execution (which verifies that code behaves correctly at runtime), quality gates analyze the code itself — its structure, patterns, and compliance with defined rules — without executing it.

Copado implements quality gates as part of the pipeline stage configuration. When a user story is promoted from one stage to the next, Copado:

  1. Extracts the metadata from the promotion package
  2. Passes it to one or more configured analysis tools
  3. Collects the results (violations, severity levels, counts)
  4. Evaluates the results against your defined thresholds
  5. Either allows the promotion to proceed or blocks it with a detailed report

Quality gates run automatically — no manual trigger required, no opportunity for someone to skip the check because they're in a hurry.

PMD Static Analysis Integration

PMD (Programming Mistake Detector) is the most widely used static analysis tool for Apex code. It analyzes your Apex classes and triggers against a set of rules and reports violations by category and severity.

What PMD Detects in Apex

PMD's Apex ruleset catches issues in several categories:

Performance issues:

  • SOQL queries inside loops (a major cause of governor limit violations)
  • DML operations inside loops
  • Inefficient list operations
  • Unbounded queries without WHERE clauses or LIMIT

Code quality:

  • Unused variables and methods (dead code)
  • Empty catch blocks (swallowing exceptions silently)
  • Overly complex methods (high cyclomatic complexity)
  • Methods that are too long

Best practices:

  • Hardcoded IDs (IDs differ between orgs and will cause failures)
  • Missing @isTest annotations on test helper methods
  • Test methods without assertions (tests that can never fail)
  • Using debug() statements excessively

Security (ApexSec rules):

  • SOQL injection vulnerabilities
  • Sharing violations (queries that bypass org-wide defaults)

Configuring PMD in Copado

Copado integrates PMD through its Quality Gate Rules configuration. Navigate to Quality Gate Rules > New in the Copado app.

Select PMD as the tool type and configure:

  • Ruleset — which PMD rule categories to enforce
  • Severity threshold — block deployments on Critical only, or also on High
  • Maximum violation count — allow a deployment with up to N violations, block above that

A typical production-grade PMD configuration:

<!-- custom-ruleset.xml — committed to your Git repository -->
<?xml version="1.0"?>
<ruleset name="Copado Standard Rules"
    xmlns="http://pmd.sourceforge.net/ruleset/2.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://pmd.sourceforge.net/ruleset/2.0.0 
                        https://pmd.sourceforge.io/ruleset_2_0_0.xsd">

    <description>HelpMeTest Salesforce Apex Standards</description>

    <!-- Performance: Never allow SOQL in loops -->
    <rule ref="category/apex/performance.xml/AvoidSoqlInLoops">
        <priority>1</priority>
    </rule>

    <!-- Performance: Never allow DML in loops -->
    <rule ref="category/apex/performance.xml/AvoidDmlStatementsInLoops">
        <priority>1</priority>
    </rule>

    <!-- Best Practices: No hardcoded IDs -->
    <rule ref="category/apex/bestpractices.xml/AvoidHardcodingId">
        <priority>2</priority>
    </rule>

    <!-- Security: Prevent SOQL injection -->
    <rule ref="category/apex/security.xml/ApexSharingViolations">
        <priority>1</priority>
    </rule>

    <!-- Code Quality: Empty catch blocks -->
    <rule ref="category/apex/errorprone.xml/EmptyCatchBlock">
        <priority>2</priority>
    </rule>

    <!-- Code Quality: Cognitive complexity limit -->
    <rule ref="category/apex/design.xml/CyclomaticComplexity">
        <priority>3</priority>
        <properties>
            <property name="methodReportLevel" value="15"/>
            <property name="classReportLevel" value="80"/>
        </properties>
    </rule>
</ruleset>

Store this file in your Git repository. In Copado's Quality Gate Rule configuration, reference the path to this file in the repository so it's versioned alongside your Salesforce metadata.

Setting Severity Thresholds

PMD uses a 1-5 priority scale:

Priority Level Typical action
1 Critical Always block deployment
2 High Block in most configurations
3 Medium Warn but allow (or block in strict configs)
4 Low Informational
5 Info No action

A recommended starting configuration blocks deployments when any Priority 1 violation is found, and allows Priority 2-3 violations with a warning. As your team improves code quality over time, tighten the threshold to block on Priority 2 violations as well.

Salesforce Code Scanner Integration

The Salesforce Code Analyzer (formerly known as Salesforce Scanner) is Salesforce's own static analysis tool, available as a Salesforce CLI plugin. It bundles multiple analysis engines:

  • PMD — the same PMD rules described above
  • ESLint — for Lightning Web Component JavaScript code
  • RetireJS — detects JavaScript dependencies with known vulnerabilities
  • Salesforce Graph Engine — a deeper analysis engine that traces data flow across method calls to detect complex vulnerabilities like SOQL injection that simple pattern-matching misses

Why Use Salesforce Code Analyzer Instead of PMD Directly?

The Salesforce Code Analyzer adds value over standalone PMD in two ways:

  1. LWC support — PMD doesn't analyze JavaScript. If your team builds Lightning Web Components, Code Analyzer's ESLint integration catches JavaScript quality issues that PMD ignores.
  2. Graph Engine for security — PMD catches simple SOQL injection patterns like Database.query(input). The Graph Engine traces data flow across multiple method calls and catches cases where user input travels through several layers before reaching a vulnerable query.

Configuring Salesforce Code Analyzer in Copado

Copado supports Salesforce Code Analyzer through its Function framework. Create a Function that:

  1. Installs the Salesforce CLI and Code Analyzer plugin
  2. Runs the scan on the promotion's metadata files
  3. Exports results in JSON format
  4. Evaluates the results against your thresholds
  5. Returns a pass/fail exit code that Copado reads
#!/bin/bash
# Copado Function: Salesforce Code Analyzer

# Install dependencies
npm install -g @salesforce/cli
sf plugins install @salesforce/sfdx-scanner

# Run the scan on the metadata in the current workspace
sf scanner run \
  --target "$COPADO_WORKSPACE/force-app/**/*.cls" \
  --target "$COPADO_WORKSPACE/force-app/**/*.trigger" \
  --target "$COPADO_WORKSPACE/force-app/**/*.js" \
  --format json \
  --outfile /tmp/scan-results.json \
  --engine pmd,eslint-lwc,retire-js

# Parse results and check for critical violations
CRITICAL_COUNT=$(jq '[.[] | select(.severity == 1)] | length' /tmp/scan-results.json)
HIGH_COUNT=$(jq '[.[] | select(.severity == 2)] | length' /tmp/scan-results.json)

echo "Critical violations: $CRITICAL_COUNT"
echo "High violations: $HIGH_COUNT"

# Write results to Copado result file for display in UI
echo "{\"criticalCount\": $CRITICAL_COUNT, \"highCount\": $HIGH_COUNT}" > $COPADO_RESULT_FILE

# Block deployment if critical violations found
if [ "$CRITICAL_COUNT" -gt "0" ]; then
    echo "BLOCKING: $CRITICAL_COUNT critical violation(s) found"
    exit 1
fi

exit 0

Attach this Function to your pipeline stage as a Quality Gate Step. Copado executes it during the promotion process and reads the exit code to determine whether to proceed.

Compliance Rules for Regulated Industries

Beyond code quality, Copado's Quality Gates can enforce business-level compliance rules. This is particularly important in financial services, healthcare, and government — industries where non-compliant deployments can trigger regulatory penalties.

Common Compliance Rules

Change Management Compliance:

  • All promotions to production must have an approved Change Request
  • Production deployments are only allowed during approved maintenance windows
  • Every deployment must have a documented rollback plan

Security Compliance:

  • No Apex class may have without sharing unless explicitly approved
  • No custom permissions grant access to regulated data objects without security review
  • All new API integrations must use Named Credentials (no hardcoded endpoints)

Data Compliance:

  • No new fields on regulated objects (PII, financial data) without a data classification tag
  • Triggers on patient records must include audit logging

Implementing Compliance Rules as Quality Gates

Compliance rules that can be expressed as static checks go into your PMD or Code Analyzer configuration. For example, to detect without sharing:

<!-- Custom PMD rule: detect without sharing -->
<rule name="NoWithoutSharing"
      language="apex"
      message="Classes must use 'with sharing' or 'inherited sharing'. 
               'without sharing' requires explicit security review."
      class="net.sourceforge.pmd.lang.apex.rule.security.ApexSharingViolationsRule">
    <priority>1</priority>
</rule>

Compliance rules that require contextual knowledge (e.g., "does this change have an approved Change Request?") are implemented as Copado Functions that query your ITSM system (ServiceNow, Jira Service Management) and verify that an approved ticket exists before allowing promotion.

#!/bin/bash
# Compliance Function: Verify Change Request exists

USER_STORY_ID="${COPADO_USER_STORY_ID}"

# Query ServiceNow for approved change request linked to this user story
CR_STATUS=$(curl -s \
  -H "Authorization: Bearer $SERVICENOW_TOKEN" \
  "https://your-instance.service-now.com/api/now/table/change_request\
?sysparm_query=u_copado_user_story=${USER_STORY_ID}^state=approved" \
  | jq -r '.result[0].state // "not_found"')

if [ "$CR_STATUS" != "approved" ]; then
    echo "BLOCKING: No approved Change Request found for User Story ${USER_STORY_ID}"
    echo "Create and approve a Change Request in ServiceNow before promoting to production."
    exit 1
fi

echo "Change Request verified: approved"
exit 0

Custom Rule Creation

For rules that go beyond what PMD and Salesforce Code Analyzer support out of the box, Copado allows fully custom rules implemented as shell scripts or JavaScript functions.

Example: Enforcing Naming Conventions

Your organization might require that all trigger handlers follow the naming pattern <Object>TriggerHandler.cls:

#!/bin/bash
# Custom rule: trigger handler naming convention

VIOLATIONS=0

# Find all trigger files in the deployment package
for TRIGGER_FILE in $(find $COPADO_WORKSPACE -name "*.trigger"); do
    TRIGGER_NAME=$(basename "$TRIGGER_FILE" .trigger)
    EXPECTED_HANDLER="${TRIGGER_NAME}Handler.cls"
    
    # Check if the handler class exists and follows naming convention
    HANDLER_EXISTS=$(find $COPADO_WORKSPACE -name "$EXPECTED_HANDLER" | wc -l)
    
    if [ "$HANDLER_EXISTS" -eq "0" ]; then
        echo "VIOLATION: $TRIGGER_NAME trigger has no handler class named $EXPECTED_HANDLER"
        VIOLATIONS=$((VIOLATIONS + 1))
    fi
done

if [ "$VIOLATIONS" -gt "0" ]; then
    echo "BLOCKING: $VIOLATIONS naming convention violation(s) found"
    exit 1
fi

exit 0

Example: Detecting Forbidden Patterns

Block deployments that include direct references to production org IDs:

#!/bin/bash
# Custom rule: no hardcoded production IDs

PROD_ORG_ID="00D000000000001"  # Your production org ID
VIOLATIONS=$(grep -r "$PROD_ORG_ID" $COPADO_WORKSPACE/force-app/ | wc -l)

if [ "$VIOLATIONS" -gt "0" ]; then
    echo "BLOCKING: Found $VIOLATIONS reference(s) to hardcoded production org ID"
    grep -r "$PROD_ORG_ID" $COPADO_WORKSPACE/force-app/
    exit 1
fi

exit 0

Audit Trails for Regulated Industries

Every Quality Gate execution in Copado is recorded on the Promotion record with:

  • Which rules ran
  • The violation count per rule
  • Whether the gate passed or failed
  • Timestamp and executing user
  • Link to the full scan output

This creates an immutable audit trail that compliance teams can query via Salesforce reports. For a financial services firm, you can produce a report showing every deployment in the past year, which quality gates ran, and whether any violations were found — evidence that your change management controls are operating effectively.

Creating Compliance Reports

Build a Salesforce report on Promotions with the following fields:

  • Promotion Name
  • Target Environment
  • Promotion Date
  • Quality Gate Status
  • Critical Violations Found
  • Approved By
  • User Stories Included

Export this report quarterly for compliance reviews. Unlike manual change logs, this data is automatically populated by Copado — there's no reliance on engineers manually recording what they deployed.

Phasing In Quality Gates

Don't try to enforce strict quality gates on an existing codebase immediately. A legacy Salesforce org may have thousands of PMD violations accumulated over years of development. Blocking all deployments until the backlog is cleared will paralyze your team.

Phase 1 — Measure only. Configure PMD and Salesforce Code Analyzer in report mode. Run them on every promotion but don't block deployments. Collect baseline metrics for 2-4 weeks to understand your starting violation count.

Phase 2 — Block on new critical violations. Configure the quality gate to run a differential scan — compare violations in the deployment package against the existing baseline. Block only new critical violations introduced by the current change. This prevents the codebase from getting worse without requiring the backlog to be cleared first.

Phase 3 — Tighten progressively. Once the team is comfortable with the workflow and the violation backlog is being reduced, lower the threshold. Block on high violations, then medium, as code quality improves.

Phase 4 — Full enforcement. All critical and high violations block deployments. The codebase meets your defined quality standard. New violations can only enter if someone explicitly creates an exception (documented in the Quality Gate record).

Quality gates shift code quality from a periodic audit activity to a continuous enforcement mechanism. When every developer knows their code will be scanned before it reaches QA, standards improve proactively — because it's faster to write clean code than to resolve a blocked deployment and explain the violation to your team lead.

Read more

Start now free