Development Workflow

This guide walks through the complete development workflow using sfp in a modular Salesforce project following the Flxbl framework.

Overview

The development workflow in an sfp-powered project follows an iterative approach where developers work in isolated environments, make changes using source-driven development, and submit their work through pull requests that trigger automated validation and review environments.

Prerequisites

DevHub Access Required

Before starting development with sfp, ensure you have:

  1. DevHub access - Required for:

    • Building packages (all types)

    • Creating scratch orgs

    • Managing unlocked packages

  2. DevHub user setup:

    • Your user must be added to the DevHub org

    • Follow Salesforce's guide: Add DevHub License Users

    • Authenticate to your DevHub:

    sf org login web --alias mydevhub --set-default-dev-hub
  3. Verify DevHub connection:

    # Check your DevHub connection
    sf org display --target-dev-hub

1. Starting a New Feature

Fetch a Development Environment

Every feature or story begins with a developer fetching a fresh environment from a pre-prepared pool. The frequency depends on your team's practice:

  • Scratch Orgs: Can be fetched for every story or feature

  • Sandboxes: Typically fetched at the start of an iteration or sprint

Fetch from Scratch Org Pool (Community Edition - Local Pools)

# List available scratch orgs in pool (alias: pool:list)
sfp pool scratch list --tag dev-pool

# Fetch a scratch org from the pool (alias: pool:fetch)
sfp pool scratch fetch --tag dev-pool --alias my-feature-org

# Initialize a pool if empty (aliases: prepare, pool:prepare)
sfp pool scratch init --tag dev-pool \
  --targetdevhubusername mydevhub \
  --config config/project-scratch-def.json \
  --count 5

Fetch from Pool using sfp Server (sfp-pro - Server-Managed Pools)

# List available instances (works for both scratch orgs and sandboxes)
sfp server pool instance list \
  --repository myorg/myrepo \
  --tag dev-pool

# Fetch an org from the pool (scratch or sandbox)
sfp server pool instance fetch \
  --repository myorg/myrepo \
  --tag dev-pool \
  --assignment-id feature-123

# Extend org expiration if needed
sfp server pool instance extend \
  --repository myorg/myrepo \
  --tag dev-pool \
  --assignment-id feature-123 \
  --expiration-hours 48

# Unassign and return to pool when done
sfp server pool instance unassign \
  --repository myorg/myrepo \
  --tag dev-pool \
  --assignment-id feature-123

Create a New Sandbox (if needed)

# Create a new sandbox directly
sfp sandbox create --name feature-sandbox \
  --type Developer \
  --source-org production \
  --alias my-feature-sandbox

Authenticate to Your Environment

Once you have your environment:

# Open the org to verify access (sfp-pro)
sfp org open --targetusername my-feature-org

# Open in a specific browser (sfp-pro)
sfp org open --targetusername my-feature-org --browser chrome

# For community edition, use Salesforce CLI
sf org open --target-org my-feature-org

# Set as default for convenience (sfp-pro)
sfp config set target-org my-feature-org

# Set globally (sfp-pro)
sfp config set target-org my-feature-org --global

2. Development Cycle

Pull Latest Metadata

Before making changes, ensure you have the latest metadata from your org:

# Pull all changes from the org (using aliases: pull, source:pull, project:pull)
sfp pull --targetusername my-feature-org

# Pull with conflict resolution
sfp pull --targetusername my-feature-org --ignore-conflicts

# Pull a specific package
sfp pull --targetusername my-feature-org --package my-package

# Pull and see what replacements were reversed (sfp-pro)
sfp pull --targetusername my-feature-org --json

The pull command will:

  • Retrieve metadata changes from your org

  • Apply reverse text replacements to convert environment-specific values back to placeholders

  • Update your local source files

Make Your Changes

Now you can work on your feature using your preferred IDE:

  1. Modify existing metadata in package directories

  2. Create new components using SF CLI or your IDE

  3. Add new packages if needed:

# Create a new source package (sfp-pro)
sfp package create source -n "feature-payment" \
  -r "src/payment-processing" \
  --domain

# Create an unlocked package
sfp package create unlocked -n "feature-payment" \
  -r "src/payment-processing" \
  -v mydevhub

# Create a data package
sfp package create data -n "reference-data" \
  -r "data/reference-data"

# For community edition, manually add to sfdx-project.json
  1. Organize packages into logical groups using release configs (domains are conceptual, not explicit commands)

Push Changes to Your Org

Deploy your local changes to the development org:

# Push all changes (using aliases: push, source:push, project:push)
sfp push --targetusername my-feature-org

# Push a specific package
sfp push --targetusername my-feature-org --package my-package

# Push ignoring conflicts
sfp push --targetusername my-feature-org --ignore-conflicts

# Push and see what replacements were applied (sfp-pro)
sfp push --targetusername my-feature-org --json

The push command will:

  • Apply text replacements for environment-specific values

  • Deploy metadata to your org

  • Run tests if specified

Build and Test Locally

Build Artifacts

Test that your packages can be built successfully:

# Build all packages (DevHub required)
sfp build --devhubalias mydevhub

# Build a specific domain
sfp build --devhubalias mydevhub --domain sales

# Build a specific package
sfp build --devhubalias mydevhub --package payment-processing

# Build with different options
sfp build --devhubalias mydevhub \
  --branch feature/payment \
  --buildnumber 123 \
  --diffcheck

# Note: DevHub is required even for source packages to resolve dependencies

Run Apex Tests

Execute tests in your development org:

# Trigger all local tests
sfp apextests trigger --target-org my-feature-org \
  --testlevel RunLocalTests

# Run specific test classes
sfp apextests trigger --target-org my-feature-org \
  --testlevel RunSpecifiedTests \
  --specified-tests PaymentTest,InvoiceTest

# Run with code coverage
sfp apextests trigger --target-org my-feature-org \
  --testlevel RunLocalTests \
  --coverage 75

Install to Your Org (Optional)

While developers rarely need to install built artifacts to their own orgs, you can test the installation:

# Install a single package
sfp install --target-org my-feature-org \
  --artifacts artifacts \
  --package payment-processing

# Install with skip testing for faster deployment
sfp install --target-org my-feature-org \
  --artifacts artifacts \
  --skipifalreadyinstalled

3. Dependency Management

As you develop, you may need to manage package dependencies:

Analyze Dependencies

# Understand package dependencies
sfp dependency explain --package payment-processing

# Expand all transitive dependencies (for troubleshooting)
sfp dependency expand --target-devhub mydevhub

# Clean up redundant dependencies
sfp dependency shrink --target-devhub mydevhub

4. Submitting Your Work

Create a Pull Request

Once your feature is complete:

# Commit your changes
git add .
git commit -m "feat: implement payment processing module"

# Push to your feature branch
git push origin feature/payment-processing

# Create PR using GitHub CLI (optional)
gh pr create --title "Payment Processing Module" \
  --body "Implements new payment gateway integration"

CI/CD Pipeline Takes Over

When you create a PR, the automated pipeline will:

  1. Run sfp validate to verify your changes:

# This runs automatically in CI/CD
sfp validate org --target-org validation-org \
  --mode thorough \
  --coverageThreshold 75
  1. Create a review environment for acceptance testing:

# CI/CD creates an ephemeral environment
sfp pool scratch fetch --pool review-pool \
  --alias pr-123-review

# Install the changes
sfp install --target-org pr-123-review \
  --artifacts artifacts
  1. Run quality checks:

    • Code coverage validation

    • Dependency validation

    • Package structure verification

Review Environment Testing

The review environment URL is posted to your PR for stakeholders to test:

  • Product owners can validate functionality

  • QA can run acceptance tests

  • Other developers can review the implementation

5. Post-Merge

After your PR is approved and merged:

  1. Artifacts are built from the main branch

  2. Published to artifact repository

  3. Ready for release to higher environments

# This happens automatically in CI/CD
sfp build --branch main
sfp publish --artifacts artifacts \
  --npm-registry https://your-registry.com

Common Workflows

Working with Aliasified Packages

When working with environment-specific metadata:

# Pull from a specific environment
sfp pull --targetusername dev-sandbox

# The correct variant is automatically selected
# src-env-specific/main/dev/* contents are used

Using Text Replacements

For configuration values that change per environment:

# Create preDeploy/replacements.yml in your package
replacements:
  - name: "API Endpoint"
    glob: "**/*.cls"
    pattern: "%%API_URL%%"
    environments:
      default: "https://api.dev.example.com"
      prod: "https://api.example.com"

Then push/pull will automatically handle replacements:

# Push replaces placeholders with environment values
sfp push --targetusername my-feature-org

# Pull reverses replacements back to placeholders
sfp pull --targetusername my-feature-org

Handling Destructive Changes

When you need to delete metadata:

  1. Move components to pre-destructive/ or post-destructive/ folders

  2. Push changes normally:

sfp push --targetusername my-feature-org

The destructive changes are automatically processed.

Troubleshooting

Pool is Empty

Community Edition (Local Pools)

# Check pool status
sfp pool scratch list --tag dev-pool

# Replenish the pool (aliases: prepare, pool:prepare)
sfp pool scratch init --tag dev-pool \
  --targetdevhubusername mydevhub \
  --count 5

sfp-pro (Server-Managed Pools)

# Check pool status
sfp server pool status --repository myorg/myrepo --tag dev-pool

# Replenish pool (works for both scratch orgs and sandboxes)
sfp server pool replenish \
  --repository myorg/myrepo \
  --tag dev-pool

Push/Pull Conflicts

# Ignore conflicts during pull
sfp pull --targetusername my-feature-org --ignore-conflicts

# Ignore conflicts during push
sfp push --targetusername my-feature-org --ignore-conflicts

Build Failures

# Check for issues in specific package
sfp build --devhubalias mydevhub \
  --package problematic-package \
  --loglevel DEBUG

# Validate dependencies
sfp dependency explain --package problematic-package

DevHub Connection Issues

# Re-authenticate to DevHub
sf org login web --alias mydevhub --set-default-dev-hub

# Verify DevHub is enabled
sf org display --target-dev-hub

# Check DevHub limits
sf limits api display --target-org mydevhub

Last updated