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.
{% @mermaid/diagram content="graph LR A[Fetch Environment] --> B[Pull Latest] B --> C[Make Changes] C --> D[Push Changes] D --> E[Test Locally] E --> F[Create PR] F --> G[CI/CD Validation] G --> H[Review Environment] H --> I[Merge]" %}
Prerequisites
DevHub Access Required
Before starting development with sfp, ensure you have:
# Check your DevHub connectionsforgdisplay--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 Server-Managed Pools
Create a New Sandbox (if needed)
Authenticate to Your Environment
Once you have your environment:
2. Development Cycle
Pull Latest Metadata
Before making changes, ensure you have the latest metadata from your org:
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:
Modify existing metadata in package directories
Create new components using SF CLI or your IDE
Add new packages if needed:
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:
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:
Run Apex Tests
Execute tests in your development org to validate your changes. sfp follows a package-centric testing approach:
For detailed information on test levels, coverage validation, output formats, and CI/CD integration, see Running Apex Tests.
Install to Your Org (Optional)
While developers rarely need to install built artifacts to their own orgs, you can test the installation:
3. Dependency Management
As you develop, you may need to manage package dependencies:
Analyze Dependencies
4. Submitting Your Work
Create a Pull Request
Once your feature is complete:
CI/CD Pipeline Takes Over
When you create a PR, the automated pipeline will:
Run sfp validate to verify your changes:
Create a review environment for acceptance testing:
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:
Artifacts are built from the main branch
Published to artifact repository
Ready for release to higher environments
Common Workflows
Working with Aliasified Packages
When working with environment-specific metadata:
Using Text Replacements
For configuration values that change per environment:
Then push/pull will automatically handle replacements:
Handling Destructive Changes
When you need to delete metadata:
Move components to pre-destructive/ or post-destructive/ folders
Push changes normally:
The destructive changes are automatically processed.
Troubleshooting
Pool is Empty
Pool provisioning and replenishment is managed by the sfp server. If pools are consistently empty, contact your administrator to adjust pool configuration via sfp server pool config update.
# List available instances (works for both scratch orgs and sandboxes)
sfp pool list \
--tag dev-pool
# Fetch an org from the pool (scratch or sandbox)
sfp pool fetch \
--tag dev-pool
# Extend org expiration if needed
sfp pool extend \
--tag dev-pool
# Unassign and return to pool when done
sfp pool unassign \
--tag dev-pool
# Create a new sandbox directly
sfp sandbox create --name feature-sandbox \
--type Developer \
--source-org production \
--alias my-feature-sandbox
# Open the org to verify access
sfp org open --targetusername my-feature-org
# Open in a specific browser
sfp org open --targetusername my-feature-org --browser chrome
# Set as default for convenience
sfp config set target-org my-feature-org
# Set globally
sfp config set target-org my-feature-org --global
# 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 pull --targetusername my-feature-org --json
# Create a new source package
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"
# 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 push --targetusername my-feature-org --json
# 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
# Test a specific package (recommended)
sfp apextests trigger -o my-feature-org -l RunAllTestsInPackage -n sales-core
# Test all packages in a domain
sfp apextests trigger -o my-feature-org -l RunAllTestsInDomain \
-r config/release-config.yaml
# Quick test during development
sfp apextests trigger -o my-feature-org -l RunSpecifiedTests \
--specifiedtests PaymentProcessorTest
# Test with code coverage validation
sfp apextests trigger -o my-feature-org -l RunAllTestsInPackage \
-n sales-core -c -p 80
# 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
# 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"
# This runs automatically in CI/CD
sfp validate org --target-org validation-org \
--mode thorough \
--coverageThreshold 75
# 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
# This happens automatically in CI/CD
sfp build --branch main
sfp publish --artifacts artifacts \
--npm-registry https://your-registry.com
# Pull from a specific environment
sfp pull --targetusername dev-sandbox
# The correct variant is automatically selected
# src-env-specific/main/dev/* contents are used
# 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"
# Push replaces placeholders with environment values
sfp push --targetusername my-feature-org
# Pull reverses replacements back to placeholders
sfp pull --targetusername my-feature-org
sfp push --targetusername my-feature-org
# Check pool status
sfp pool list --tag dev-pool
# Replenish the pool
sfp pool probe --tag dev-pool
# Ignore conflicts during pull
sfp pull --targetusername my-feature-org --ignore-conflicts
# Ignore conflicts during push
sfp push --targetusername my-feature-org --ignore-conflicts
# Check for issues in specific package
sfp build --devhubalias mydevhub \
--package problematic-package \
--loglevel DEBUG
# Validate dependencies
sfp dependency explain --package problematic-package
# 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