AgentSkillsCN

Sfb2b Review

为 {{clientName}} Salesforce B2B Commerce 迁移项目制定代码评审标准与质量保证流程。触发条件:评审、代码评审、PR、拉取请求、质量、标准、最佳实践、Lint、静态分析。

SKILL.md
--- frontmatter
title: sfb2b-review
description: >
  Code review standards and quality assurance procedures for the {{clientName}}
  Salesforce B2B Commerce migration project. Triggers on: review, code review, PR,
  pull request, quality, standards, best practices, lint, static analysis.
triggers:
  - review
  - code review
  - pr
  - pull request
  - quality
  - standards
  - best practices
  - lint
  - static analysis

sfb2b-review Skill

Code review standards, quality checks, and best practices for {{clientName}} B2B Commerce project.

Review Checklist

Comprehensive checklist for all code reviews. Use this systematically for every PR.

Architecture Alignment

  • Components follow Service, Selector, Domain (SSD) pattern
  • Correct layer usage:
    • Service Layer: Business logic, orchestration, external integrations
    • Selector Layer: SOQL queries, data retrieval only
    • Domain Layer: Business rules, validation, data manipulation
    • Controller Layer: API exposure only
  • LWC components don't contain business logic (logic in Apex service layer)
  • No circular dependencies between modules
  • Integration points use Named Credentials (no hardcoded endpoints)
  • Async operations use proper patterns (queueable, batch, scheduled)

Example of correct layering:

apex
// CONTROLLER LAYER - Apex Controller
public with sharing class {{projectPrefix}}_ProductController {
    @AuraEnabled(cacheable=true)
    public static List<Product2> getProductsForStore(String storeId) {
        // Immediately delegates to service layer
        return {{projectPrefix}}_ProductService.getProductsForStore(storeId);
    }
}

// SERVICE LAYER - Business Logic
public with sharing class {{projectPrefix}}_ProductService {
    public static List<Product2> getProductsForStore(String storeId) {
        // Complex logic, integrations
        Set<Id> validProductIds = {{projectPrefix}}_ProductSelector.getActiveProductsForStore(storeId);
        List<Product2> products = {{projectPrefix}}_ProductSelector.getProductsByIds(validProductIds);

        // Apply business rules
        {{projectPrefix}}_ProductDomain.applyDiscounts(products);
        {{projectPrefix}}_ProductDomain.validateInventory(products);

        return products;
    }
}

// SELECTOR LAYER - Data Access Only
public with sharing class {{projectPrefix}}_ProductSelector {
    public static Set<Id> getActiveProductsForStore(String storeId) {
        return new Map<Id, Product2>([
            SELECT Id FROM Product2
            WHERE IsActive = true
            AND StoreId__c = :storeId
        ]).keySet();
    }
}

// DOMAIN LAYER - Business Rules
public with sharing class {{projectPrefix}}_ProductDomain {
    public static void applyDiscounts(List<Product2> products) {
        // Apply buyer-group-specific discounts
        for (Product2 prod : products) {
            // Discount logic
        }
    }
}

Naming Conventions

  • All custom Apex classes/interfaces start with {{projectPrefix}}_ prefix
  • LWC component naming:
    • Use camelCase with leading lowercase: {{projectPrefixLower}}ProductCard
    • Parent folder same as component name
    • Descriptive names reflecting component purpose
  • Class naming follows pattern:
    • Service: {{projectPrefix}}_[Entity]Service
    • Selector: {{projectPrefix}}_[Entity]Selector
    • Domain: {{projectPrefix}}_[Entity]Domain
    • Controller: {{projectPrefix}}_[Entity]Controller
    • Test: {{projectPrefix}}_[Entity][Type]Test
  • Variables use camelCase
  • Constants use UPPER_SNAKE_CASE
  • Boolean variables start with is or has
apex
// Correct naming
public class {{projectPrefix}}_OrderService {
    private static final Integer MAX_ORDER_ITEMS = 500;
    private static final String ERROR_MSG_INVALID_ORDER = 'Order is invalid';

    public static Boolean isOrderValid(Order order) { ... }
    private Boolean hasDiscountApplied = false;
}

// Incorrect naming
public class OrderService { ... } // Missing {{projectPrefix}}_ prefix
public class getOrderInfo() { ... } // Should be camelCase for methods
private String maxItems; // Should be MAX_ITEMS for constant

Test Coverage Requirements

  • Apex: >85% code coverage for submitted class
  • Jest: >80% coverage for LWC component
  • Both statement AND branch coverage verified
  • All public methods have tests
  • All error paths tested
  • Coverage report attached to PR

Verification commands:

bash
# Apex coverage
sf apex run test --class-names {{projectPrefix}}_OrderServiceTest --code-coverage

# Jest coverage
npm test -- --coverage --collectCoverageFrom='force-app/**/component.js'

Security Checks

  • No hardcoded credentials (API keys, passwords, endpoints)

  • CRUD/FLS checks present in all queries:

    apex
    public class {{projectPrefix}}_OrderSelector {
        public static List<Order> getOrdersByAccount(Id accountId) {
            // CRUD check
            if (!Order.sObjectType.getDescribe().isAccessible()) {
                throw new SecurityException('No access to Order');
            }
    
            // FLS check
            SObjectAccessDecision decision = Security.stripInaccessible(
                AccessLevel.USER_MODE,
                [SELECT Id, OrderNumber, Amount FROM Order WHERE AccountId = :accountId]
            );
            return (List<Order>)decision.getRecords();
        }
    }
    
  • LWC prevents XSS: uses textContent instead of innerHTML

    javascript
    // Secure
    element.textContent = userInput;
    
    // Vulnerable
    element.innerHTML = userInput;
    
  • All external API calls use Named Credentials

  • Query parameters sanitized

  • Sharing rules respected in all operations

  • No with sharing omitted without justification

  • PII fields not logged or exposed

  • Third-party integrations validate responses

Performance Checks

  • No SOQL queries in loops:

    apex
    // BAD
    for (Order order : orders) {
        Order fullOrder = [SELECT Id, Amount FROM Order WHERE Id = :order.Id]; // Query in loop!
    }
    
    // GOOD
    Set<Id> orderIds = new Map<Id, Order>(orders).keySet();
    List<Order> fullOrders = [SELECT Id, Amount FROM Order WHERE Id IN :orderIds];
    
  • Bulkified operations (handles 200+ records)

  • Lazy loading implemented for lists/details

  • No synchronous callouts in critical paths

  • Proper indexing on frequently queried fields

  • Collection operations optimized (avoid nested loops)

  • Governor limits monitored

Performance anti-patterns to flag:

apex
// BAD: Multiple separate queries
for (Account acc : accounts) {
    List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
    List<Opportunity> opps = [SELECT Id FROM Opportunity WHERE AccountId = :acc.Id];
}

// GOOD: Single bulkified query
Map<Id, List<Contact>> contactsByAccountId = new Map<Id, List<Contact>>();
for (Contact con : [SELECT AccountId FROM Contact WHERE AccountId IN :accountIds]) {
    if (!contactsByAccountId.containsKey(con.AccountId)) {
        contactsByAccountId.put(con.AccountId, new List<Contact>());
    }
    contactsByAccountId.get(con.AccountId).add(con);
}

Accessibility (WCAG 2.1 AA)

  • ARIA labels on interactive elements

    html
    <!-- Good -->
    <lightning-button aria-label="Add product to cart" variant="brand">
      Add to Cart
    </lightning-button>
    
    <!-- Bad -->
    <button>+</button>
    
  • Semantic HTML used (buttons for actions, links for navigation)

  • Color not sole means of conveying information

  • Form labels associated with inputs

  • Keyboard navigation fully supported (Tab, Enter, Escape)

  • Focus visible on all interactive elements

  • Images have alt text

  • Headings use correct hierarchy (h1 -> h2 -> h3)

Accessibility test template:

javascript
test('has proper ARIA labels for accessibility', async () => {
  element.product = { Name: 'Test Product', Id: '001xx000003DHY' };
  await element.updateComplete;

  const button = element.shadowRoot.querySelector('[data-test-id="add-to-cart"]');
  expect(button.getAttribute('aria-label')).toBe('Add Test Product to cart');
});

test('is fully keyboard navigable', async () => {
  // Tab through all interactive elements
  const button = element.shadowRoot.querySelector('button');
  button.focus();
  expect(document.activeElement).toBe(element.shadowRoot.host);

  // Fire keyboard events
  const event = new KeyboardEvent('keydown', { key: 'Enter' });
  button.dispatchEvent(event);
  // Verify action occurred
});

Integration Safety

  • Named Credentials used for all external endpoints
  • Retry logic present for callouts (exponential backoff)
  • Error handling graceful (no stack traces exposed)
  • Timeout values appropriate (not too long, not too short)
  • Integration points have circuit breaker pattern (fail gracefully)
  • Webhook/callback validations in place
  • Webhook signatures verified

Named Credential usage:

apex
// CORRECT: Using Named Credential
HttpRequest req = new HttpRequest();
req.setEndpoint('callout:{{projectPrefix}}_Middleware_Prod/orders');
req.setMethod('POST');
Http http = new Http();
HttpResponse res = http.send(req);

// INCORRECT: Hardcoded endpoint
req.setEndpoint('https://api.internal.com/orders?key=abc123');

Documentation

  • All public methods have JSDoc/Apex comments:
    apex
    /**
     * @description Retrieves active products for the specified store
     * @param storeId The WebStore ID
     * @return List of active products filtered by store
     * @throws SecurityException if user lacks Order access
     */
    public static List<Product2> getProductsForStore(String storeId) {
        // Implementation
    }
    
  • Complex logic explained with inline comments
  • Edge cases documented
  • PR description is comprehensive
  • Linked issues/tickets have clear context
  • Breaking changes clearly noted

Automated Checks

PMD Apex Rules Configuration

.pmd/rulesets/apex.xml

xml
<?xml version="1.0" encoding="UTF-8"?>
<ruleset name="{{projectPrefix}} Apex Rules"
  xmlns="http://pmd.sf.net/ruleset/2.0.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://pmd.sf.net/ruleset/2.0.0
    http://pmd.sf.net/ruleset_2_0_0.xsd">

  <description>PMD Rules for {{clientName}} B2B Commerce</description>

  <!-- Code style -->
  <rule ref="category/apex/style.xml/FieldNamingConventions">
    <properties>
      <property name="prefix" value="m_"/>
      <property name="exclusions" value="serialVersionUID"/>
    </properties>
  </rule>

  <!-- Design -->
  <rule ref="category/apex/design.xml/ExcessiveParameterList">
    <properties>
      <property name="threshold" value="4"/>
    </properties>
  </rule>

  <rule ref="category/apex/design.xml/ExcessiveClassLength">
    <properties>
      <property name="threshold" value="1000"/>
    </properties>
  </rule>

  <!-- Performance -->
  <rule ref="category/apex/performance.xml/OperationWithLimitsInLoop">
    <priority>1</priority>
  </rule>

  <rule ref="category/apex/performance.xml/AvoidSoqlInLoops">
    <priority>1</priority>
  </rule>

  <!-- Best Practices -->
  <rule ref="category/apex/bestpractices.xml/AvoidGlobalModifier">
    <priority>3</priority>
  </rule>

  <rule ref="category/apex/bestpractices.xml/ApexAssertionsShouldIncludeMessage">
    <priority>3</priority>
  </rule>

</ruleset>

Run PMD:

bash
sf scanner run --engine pmdapex --target force-app/ --format json > pmd-report.json

ESLint Rules for LWC

.eslintrc.json

json
{
  "extends": ["eslint:recommended", "plugin:@lwc/recommended"],
  "parserOptions": {
    "ecmaVersion": 2020
  },
  "env": {
    "browser": true,
    "es2020": true,
    "jest": true
  },
  "rules": {
    "no-var": "error",
    "prefer-const": "error",
    "no-console": "warn",
    "no-unused-vars": ["error", { "argsIgnorePattern": "^_" }],
    "@lwc/lwc/no-invalid-dom-query-selector": "error",
    "@lwc/lwc/valid-api": "error",
    "@lwc/lwc/no-document-query": "error",
    "no-restricted-globals": ["error", "window"],
    "@lwc/lwc/no-dupe-class-fields": "error"
  }
}

Run ESLint:

bash
npm run lint
npm run lint -- --fix  # Auto-fix issues

Prettier Configuration

.prettierrc

json
{
  "singleQuote": true,
  "trailingComma": "es5",
  "tabWidth": 2,
  "semi": true,
  "printWidth": 100,
  "arrowParens": "always",
  "overrides": [
    {
      "files": "*.apex",
      "options": {
        "parser": "apex",
        "tabWidth": 4
      }
    }
  ]
}

Run Prettier:

bash
npm run format
npm run format:check

SFDX Scanner Rules

Create configuration: .sfdx-scanner-config.json

json
{
  "rules": [
    {
      "engine": "pmdapex",
      "enabled": true,
      "categories": ["Performance", "Security", "Design"]
    },
    {
      "engine": "retire",
      "enabled": true
    }
  ],
  "target": ["force-app/"]
}

Run scanner:

bash
sf scanner run --config .sfdx-scanner-config.json --format json

PR Template

Create .github/pull_request_template.md

markdown
## Summary

[Brief description of what this PR accomplishes - 1-2 sentences]

## Workstream

- Epic/Feature: [e.g., "B2B Cart Management"]
- Story: [e.g., "As a buyer, I want to modify cart quantities"]
- Task: [e.g., "Implement cart update endpoint"]

## Gap Analysis Items

[List any outstanding tasks or considerations]

- [ ] Integration testing pending
- [ ] Performance benchmarking required
- [ ] Third-party API validation needed

## Type of Change

- [ ] Bug fix (non-breaking change)
- [ ] New feature (non-breaking change)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Documentation update
- [ ] Performance improvement
- [ ] Refactoring (no functional change)
- [ ] Security fix

## Testing

**Manual Testing Steps:**

1. Navigate to product listing page
2. Select a product and add to cart
3. Update quantity in cart
4. Verify total price recalculates correctly
5. Proceed to checkout and complete order

**Test Coverage:**

- Jest Coverage: 85% (statements, branches, lines)
- Apex Coverage: 92% (includes error paths)
- Manual QA: [Pass/Fail]

## Screenshots

[For UI changes, include before/after screenshots]

## Checklist

### Code Quality

- [ ] Code follows project style guide
- [ ] No hardcoded credentials or sensitive data
- [ ] Comments explain complex logic
- [ ] Variable/method names are clear and descriptive
- [ ] No console.log or debug statements left

### Testing

- [ ] Unit tests written and passing
- [ ] Integration tests passing
- [ ] All test scenarios documented
- [ ] Negative test cases included
- [ ] Code coverage >80% (LWC) / >85% (Apex)

### Security & Performance

- [ ] CRUD/FLS checks included in queries
- [ ] No SOQL in loops
- [ ] Bulkified for 200+ records
- [ ] No hardcoded endpoints (using Named Credentials)
- [ ] Accessibility checks (WCAG 2.1 AA)

### Documentation

- [ ] JSDoc/Apex comments on public methods
- [ ] README updated if new setup steps required
- [ ] Edge cases documented
- [ ] Related issues/tickets linked

### Review Requirements

- [ ] 2+ code reviewers assigned
- [ ] No merge conflicts
- [ ] Builds passing in CI/CD
- [ ] Performance benchmarks met (if applicable)

---

## Related Issues

Closes #[issue number]
Related to #[issue number]

## Additional Context

[Add any other context about the PR here]

Review Response Format

Severity Levels

Structure all review comments with severity levels:

CRITICAL - Must fix before merge

markdown
**CRITICAL: Security Issue**
This query doesn't check CRUD permissions before accessing Account data.

Suggested fix:
\`\`\`apex
if (!Account.sObjectType.getDescribe().isAccessible()) {
throw new SecurityException('No access to Account');
}
\`\`\`

MAJOR - Should fix before merge

markdown
**MAJOR: Performance Issue**
This SOQL query inside a loop will cause governor limit errors with 200+ records.

Suggested fix:

- Move query outside loop
- Use bulk API instead
- See sfb2b-test skill for bulk testing examples

MINOR - Nice to have, not blocking

markdown
**MINOR: Code Style**
Method name should follow camelCase convention: `getProductInfo` instead of `getproductinfo`

Reference: {{projectPrefix}}_ naming conventions in sfb2b-review skill

SUGGESTION - Consider for future

markdown
**SUGGESTION: Maintainability**
Consider extracting this calculation into a domain class for reusability.

This is optional but would improve code reusability across the system.

Response Template

markdown
## Summary

This PR implements the cart quantity update feature for the B2B Commerce platform.

## Code Quality Review

- Architecture follows SSD pattern correctly
- Test coverage: 87% (exceeds 85% requirement)
- MAJOR: See performance concern below

## Issues Found

### 1. **CRITICAL: Security - CRUD Check Missing**

Lines 42-45 in `{{projectPrefix}}_CartService.cls`

The `updateCartItem` method queries CartItem without checking CRUD permissions.

**Fix Required:**
\`\`\`apex
public static void updateCartItem(Id cartItemId, Decimal newQuantity) {
if (!CartItem.sObjectType.getDescribe().isAccessible()) {
throw new SecurityException('Insufficient privileges');
}
// ... rest of method
}
\`\`\`

### 2. **MAJOR: Performance - SOQL in Loop**

Lines 58-62 in `{{projectPrefix}}_CartDomain.cls`

This loop will trigger SOQL in loop error:
\`\`\`apex
for (CartItem item : cartItems) {
Product2 prod = [SELECT UnitPrice FROM Product2 WHERE Id = :item.Product2Id]; // Query in loop
}
\`\`\`

**Fix Required:**
\`\`\`apex
Set<Id> productIds = new Map<Id, CartItem>(cartItems).keySet();
Map<Id, Product2> products = new Map<Id, Product2>([
SELECT Id, UnitPrice FROM Product2 WHERE Id IN :productIds
]);
for (CartItem item : cartItems) {
Product2 prod = products.get(item.Product2Id);
}
\`\`\`

### 3. **MINOR: Naming Convention**

Variable `qty` should be `quantity` for clarity.

## Approvals Needed

- [ ] 2+ reviewers
- [ ] Security team (for CRUD implementation)
- [ ] Performance review (verify bulkification)

## Ready to Merge?

**NO** - Critical security issue must be resolved first.

---

Approve after addressing CRITICAL and MAJOR items.

Anti-Patterns to Flag

Review all code for these problematic patterns:

1. God Classes (Single Responsibility Violation)

Pattern to reject:

apex
// BAD: {{projectPrefix}}_OrderManager does everything
public class {{projectPrefix}}_OrderManager {
    public static void createOrder() { }
    public static void updateInventory() { }
    public static void calculateTax() { }
    public static void submitToERP() { }
    public static void sendNotification() { }
    public static void generateReport() { }
}

Correct structure:

apex
// GOOD: Separated concerns
public class {{projectPrefix}}_OrderService { ... }      // Creates/manages orders
public class {{projectPrefix}}_InventoryService { ... }  // Inventory operations
public class {{projectPrefix}}_TaxService { ... }        // Tax calculations
public class {{projectPrefix}}_ERPIntegration { ... }    // ERP submission
public class {{projectPrefix}}_NotificationService { ... } // Notifications

2. Mixed Concerns in LWC

Pattern to reject:

javascript
// BAD: Component with business logic
export default class {{projectPrefixLower}}Order extends LightningElement {
  handleAddToCart() {
    // Complex business logic in component
    const subtotal = this.cartItems.reduce((sum, item) => {
      return sum + item.quantity * item.unitPrice;
    }, 0);

    const taxRate = this.getTaxRate(this.billingState);
    const tax = subtotal * taxRate;
    const total = subtotal + tax;
    // ...
  }
}

Correct pattern:

javascript
// GOOD: Call service, let Apex handle logic
import { {{projectPrefix}}_CartController } from '@salesforce/apex/{{projectPrefix}}_CartController';

export default class {{projectPrefixLower}}Order extends LightningElement {
  async handleAddToCart(cartItem) {
    // Delegate to service
    const cartTotal = await {{projectPrefix}}_CartController.addItemToCart(cartItem);
    this.displayTotal(cartTotal);
  }
}

3. Hardcoded IDs and Endpoints

Pattern to reject:

apex
// BAD: Hardcoded IDs and URLs
public class {{projectPrefix}}_ProductService {
    private static final String STORE_ID = '0ZeQ30000000001CAA'; // Hardcoded ID
    private static final String API_ENDPOINT = 'https://api.internal.com/products'; // Hardcoded

    public static void syncProducts() {
        HttpRequest req = new HttpRequest();
        req.setEndpoint(API_ENDPOINT);
    }
}

Correct pattern:

apex
// GOOD: Use Named Credentials and Custom Metadata
public class {{projectPrefix}}_ProductService {
    public static void syncProducts() {
        {{projectPrefix}}_Config__mdt config = {{projectPrefix}}_Config__mdt.getInstance('{{projectPrefix}}_Prod');

        HttpRequest req = new HttpRequest();
        req.setEndpoint('callout:{{projectPrefix}}_Middleware_Prod/products'); // Named Credential

        WebStore store = {{projectPrefix}}_WebStoreSelector.getDefaultStore(); // Query vs hardcode
    }
}

4. SOQL in Loops

Pattern to reject:

apex
// BAD: Multiple queries in loop
for (Account acc : accounts) {
    List<Contact> contacts = [SELECT Id, Name FROM Contact WHERE AccountId = :acc.Id];
    List<Opportunity> opportunities = [SELECT Id, Amount FROM Opportunity WHERE AccountId = :acc.Id];
    // Process
}

Correct pattern:

apex
// GOOD: Single bulkified query
Set<Id> accountIds = new Map<Id, Account>(accounts).keySet();

Map<Id, List<Contact>> contactsByAccountId = new Map<Id, List<Contact>>();
for (Contact con : [SELECT AccountId, Name FROM Contact WHERE AccountId IN :accountIds]) {
    if (!contactsByAccountId.containsKey(con.AccountId)) {
        contactsByAccountId.put(con.AccountId, new List<Contact>());
    }
    contactsByAccountId.get(con.AccountId).add(con);
}

5. Missing Null Checks

Pattern to reject:

apex
// BAD: No null checks
public static Decimal calculateOrderTotal(Order order) {
    Decimal total = order.Amount + order.Tax; // order or Amount could be null
    return total;
}

Correct pattern:

apex
// GOOD: Defensive null checking
public static Decimal calculateOrderTotal(Order order) {
    if (order == null || order.Amount == null) {
        throw new IllegalArgumentException('Order and Amount required');
    }

    Decimal tax = order.Tax != null ? order.Tax : 0;
    Decimal total = order.Amount + tax;
    return total;
}

6. Synchronous Long-Running Callouts

Pattern to reject:

apex
// BAD: Synchronous callout during transaction
public static void submitOrder(Id orderId) {
    Order order = [SELECT Id, OrderNumber FROM Order WHERE Id = :orderId];

    Http http = new Http();
    HttpRequest req = new HttpRequest();
    req.setEndpoint('callout:{{projectPrefix}}_Middleware_Prod/orders');
    req.setMethod('POST');

    HttpResponse res = http.send(req); // Blocks entire transaction

    update order;
}

Correct pattern:

apex
// GOOD: Queue async callout
public static void submitOrder(Id orderId) {
    Order order = [SELECT Id, OrderNumber FROM Order WHERE Id = :orderId];
    order.Status = 'Processing';
    update order;

    // Queue async job to prevent blocking
    System.enqueueJob(new {{projectPrefix}}_OrderSubmissionQueueable(orderId));
}

// In separate queueable class
public class {{projectPrefix}}_OrderSubmissionQueueable implements Queueable, Database.AllowsCallouts {
    private Id orderId;

    public void execute(QueueableContext context) {
        Http http = new Http();
        HttpRequest req = new HttpRequest();
        req.setEndpoint('callout:{{projectPrefix}}_Middleware_Prod/orders');

        HttpResponse res = http.send(req); // Async, non-blocking
    }
}

7. Unhandled Exceptions

Pattern to reject:

apex
// BAD: No exception handling
public static List<Order> getOrders() {
    return [SELECT Id, OrderNumber FROM Order LIMIT 1000]; // What if SOQL fails?
}

Correct pattern:

apex
// GOOD: Graceful error handling
public static List<Order> getOrders() {
    try {
        return [SELECT Id, OrderNumber FROM Order LIMIT 1000];
    } catch (QueryException e) {
        System.debug(LoggingLevel.ERROR, 'Failed to retrieve orders: ' + e.getMessage());
        throw new ApplicationException('Unable to retrieve orders', e);
    }
}

Pre-Merge Checklist

Final checklist before approving and merging PR:

  • All automated checks passing (ESLint, PMD, Jest, Apex tests)
  • Code review approved by 2+ team members
  • No blocking comments remaining
  • Test coverage meets minimum requirements
  • Security review complete (if applicable)
  • Performance validation passed
  • Accessibility verified (if UI changes)
  • Documentation updated
  • No conflicts with target branch
  • CI/CD pipeline green
  • Linked issues updated to reflect completion

Quick Review Commands

bash
# Run full review suite
npm run lint && npm test -- --coverage && sf scanner run

# Check specific file
npm run lint force-app/main/default/lwc/{{projectPrefixLower}}ProductCard/{{projectPrefixLower}}ProductCard.js

# Run Apex tests with coverage
sf apex run test --class-names {{projectPrefix}}_OrderServiceTest --code-coverage

# Generate PMD report
sf scanner run --engine pmdapex --format json > pmd-report.json

# Check test coverage
npm test -- --coverage --collectCoverageFrom='force-app/main/default/lwc/**/*.js'

Reference: Common Review Comments

markdown
### Must Always Include

- [ ] CRUD/FLS checks in queries
- [ ] SOQL bulkification (no loops with queries)
- [ ] Unit test coverage >80-85%
- [ ] JSDoc comments on public methods
- [ ] {{projectPrefix}}_ prefix on all custom classes

### Must Not Include

- [ ] Hardcoded credentials or endpoints
- [ ] console.log statements
- [ ] Synchronous callouts
- [ ] Commented-out code blocks
- [ ] TODO/FIXME without context