Series Note: This article is Part 7 of our hands-on engineering series: Building the Salesforce Appointment Scheduler. Follow along as we design and implement a multi-location scheduling platform on Salesforce using enterprise patterns and developer-first tooling.
Introduction: The Fragmented Gauntlet of Salesforce DX
Throughout Parts 1 through 6, we designed and built the complete Salesforce Appointment Scheduler from the ground up:
- Native geolocation data schema and CLI scaffolding (Part 1)
- Least-privilege permission sets without profile sprawl (Part 2)
- Dual in-memory and database test factories (Part 3)
- High-velocity Apex service engines with row-locking concurrency (Part 4)
- Trigger-driven Task generation and email notifications (Part 5)
- Modern 4-step Lightning Web Component booking wizard (Part 6)
At this point, our codebase is feature-complete. But there is a glaring question every development team faces on a Monday morning, when onboarding a new engineer, or when reviewing a pull request:
“How long does it take to spin up a clean, working environment from scratch?”
In the modern web ecosystem (e.g. Next.js, Vite), spinning up a local environment is virtually instantaneous:
git clone <repo>
npm install
npm run dev
In standard Salesforce DX, however, setting up a fresh development scratch org has traditionally required running a tedious gauntlet of disconnected commands:
- Kindle the Org:
(Wait 60-90 seconds while monitoring terminal spinners…)sf org create scratch -f config/project-scratch-def.json -d 7 -a appointment-dev - Deploy Source Metadata:
(Wait another 60 seconds while objects, classes, and LWCs compile…)sf project deploy start - Assign Permission Sets:
sf org assign permset -n Appointment_Scheduler_Operator - Seed Demo Data:
sf apex run --file scripts/apex/seed.apex - Run Apex Unit Tests:
sf apex run test --test-level RunLocalTests --code-coverage - Open the Org in the Browser:
sf org open
The Cost of Fragmented Workflows
This manual chain introduces real friction into engineering teams:
- Context Switching & Idle Waiting: Developers can’t simply run a command and walk away; they must baby-sit each step, waiting for one command to exit before pasting the next.
- The “Forgotten Step” Bug: If an engineer forgets Step 3 (permission set), custom objects and tabs are hidden. If they forget Step 4 (seeding), the booking wizard renders empty dropdowns and zero service slots. Diagnosing these missing steps burns valuable engineering time.
- Dead Ash & Scratch Org Sprawl: Over weeks of development, developers create dozens of scratch orgs that expire or are abandoned. Local DX authorization files accumulate like dead ashes, cluttering
sf org listand consuming Dev Hub limits.
In this final chapter, we explore how Hob: The Salesforce House-Elf turns this fragmented gauntlet into a single, cohesive developer workflow:
- Dev Hub Hearth Care (
hob hearth) - The 5-in-1 Pipeline (
hob scratch new) - Formatted Test Summaries (
hob test) - Automated Scratch Org Purging (
hob scratch purge)
1. Tending the Dev Hub: Lighting & Sweeping the Hearth
In the lore of Hob, the Dev Hub is the Hearth of your development household. It is the central fire from which all temporary scratch orgs are kindled.
( ) ( ) )
) ( ) ( (
( ) ( ) )
┌─────────────┐
│ HEARTH │ <-- Your Dev Hub (Salesforce Org)
│ (Dev Hub) │
└──────┬──────┘
│
Kindles Scratch Orgs
│
┌──────────┴──────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ Scratch #1 │ │ Scratch #2 │
│ (Feature A) │ │ (Bugfix 102) │
└──────────────┘ └──────────────┘
Hob provides dedicated commands to manage, inspect, and tidy your hearth.
Lighting the Hearth (hob hearth)
To connect a new Dev Hub to your machine:
hob hearth --alias devhub
Hob opens your browser to the Salesforce OAuth login portal, links the Dev Hub to your local environment, and sets it as the default kindling source.
Surveying the Hearths (hob hearth -l)
To inspect all authorized Dev Hubs:
hob hearth -l
Terminal Output
🧙 Hob — The Salesforce House-Elf 🧦
"Loyal, quiet, and tireless assistance for your Salesforce Development."
- Hob is checking the hearths...
🧦 Hob found the following hearths (Dev Hubs):
Alias Default Status Org Id Username
────────────── ────────── ────────────── ────────────────── ──────────────────────────────
open yes Connected 00DgK00000XmXDBUA3 keesjan.e9473c8f35ea@agentforce.com
Sweeping the Hearth: Pruning Dead Ashes (hob hearth -c)
When scratch orgs expire after their 7-day or 30-day lifetime, Salesforce deletes the org in the cloud, but the local CLI retains stale authorization tokens. Running standard sf org list warns:
Warning: You have 2 expired or deleted local scratch org authorizations.
To remove authorizations for inactive orgs, run "sf org list --clean".
Hob sweeps away the dead ashes automatically with a single command:
hob hearth -c -p
🧙 Hob — The Salesforce House-Elf 🧦
"Loyal, quiet, and tireless assistance for your Salesforce Development."
- Hob is sweeping the hearth: clearing inactive and expired scratch orgs...
√ The hearth is swept clean! Removed inactive scratch org authorizations.
🧦 Hob kept the hearth tidy and free of dead ashes 🧦
With --clean (-c) and --no-prompt (-p), the hearth is kept pristine with zero interactive prompts.
2. Kindling a Complete Environment: The 5-in-1 Pipeline
Instead of running 6 separate commands, Hob consolidates scratch org creation, metadata deployment, security assignment, data seeding, and browser launching into a single automated pipeline:
hob scratch new appointment-dev \
-p Appointment_Scheduler_Operator \
-s scripts/apex/seed.apex
The Orchestration Pipeline
sequenceDiagram
autonumber
actor Dev as Developer
participant Hob as Hob CLI (House-Elf)
participant Hub as Dev Hub (Hearth)
participant Org as Scratch Org (appointment-dev)
participant Browser as Default Browser
Dev->>Hob: hob scratch new appointment-dev -p ... -s ...
Hob->>Hub: [1/5] Kindle scratch org (duration: 7 days)
Hub-->>Hob: Org provisioned (00DAw00000HKh2DMAT)
Hob->>Org: [2/5] Carry source code into org (force-app)
Org-->>Hob: Deployment successful (100% components)
Hob->>Org: [3/5] Bestow permission sets (Appointment_Scheduler_Operator)
Org-->>Hob: Permset assigned
Hob->>Org: [4/5] Seed test fixtures (scripts/apex/seed.apex)
Org-->>Hob: Seed records created (Locations, Staff, Customers, Slots)
Hob->>Browser: [5/5] Open scratch org in browser
Browser-->>Dev: Ready to test Appointment Scheduler Wizard!
The Live Terminal Output
🧙 Hob — The Salesforce House-Elf 🧦
"Loyal, quiet, and tireless assistance for your Salesforce Development."
🧦 Hob is preparing to kindle scratch org 'appointment-dev' with a full pipeline...
[1/5] Kindling scratch org 'appointment-dev' (duration: 7 days)...
- Hob is requesting scratch org 'appointment-dev' from the Dev Hub...
√ Scratch org 'appointment-dev' provisioned successfully! (Org ID: 00DAw00000HKh2DMAT)
[2/5] Carrying source code into the scratch org...
- Hob is deploying local source files into the scratch org...
√ Source code deployed successfully to scratch org.
[3/5] Granting permissions in the scratch org...
- Hob is assigning permission set 'Appointment_Scheduler_Operator'...
√ Assigned permission set 'Appointment_Scheduler_Operator'.
[4/5] Sowing test data seeds into the scratch org...
- Hob is executing test data seed 'scripts/apex/seed.apex'...
√ Test data seed 'scripts/apex/seed.apex' applied.
[5/5] Welcoming you into the scratch org...
- Hob is opening your scratch org in the browser...
√ Org opened!
✔ Scratch org 'appointment-dev' is fully kindled and ready!
Username: test-qbatsqozonab@example.com
Org ID: 00DAw00000HKh2DMAT
🧦 All chores complete. Hob retreats to the shadows until you need him next 🧦
What Just Happened?
- Org Kindling: Hob validated
config/project-scratch-def.jsonand requested the org with the aliasappointment-devset as default. - Metadata Deployment: Hob pushed the entire
force-appdirectory (objects, tabs, applications, classes, triggers, and LWCs). - Security Provisioning: The
Appointment_Scheduler_Operatorpermission set was assigned immediately, making all tabs, objects, and field-level security available to the admin user. - Data Seeding: Hob executed
scripts/apex/seed.apex, creating realistic compound geolocation branch coordinates, staff shift schedules, and customer contacts. - Instant Access: The browser launched directly into the authenticated Lightning session.
The developer had to type one command, press enter, and return 90 seconds later to an environment fully ready for feature work or QA.
3. Verifying Application Health: Instant Testing with hob test
Once an environment is provisioned, verifying test health is essential. Salesforce CLI’s standard sf apex run test produces extensive raw tables or verbose JSON output that requires scrolling through hundreds of lines.
Hob streamlines test execution with human-friendly, color-coded summaries:
hob test
The Terminal Output
🧙 Hob — The Salesforce House-Elf 🧦
"Loyal, quiet, and tireless assistance for your Salesforce Development."
🧦 Hob is preparing to cast unit test spells for 'Local Tests'...
- Hob is executing Apex tests in Salesforce...
🧪 Test Execution Results:
Status Test Name Time
────── ──────────────────────────────────────────────────────────────────────── ──────
✔ Pass AvailabilityServiceTest.testGetAvailableSlotsExcludesBookedTimes 301ms
✔ Pass AvailabilityServiceTest.testNoShiftsReturnsEmptyList 57ms
✔ Pass AvailabilityServiceTest.testNullArgumentsThrowException 7ms
✔ Pass AppointmentTriggerTest.testCancellationClosesOpenTask 747ms
✔ Pass AppointmentTriggerTest.testDefaultStatusEnforced 313ms
✔ Pass AppointmentTriggerTest.testDeleteCompletedAppointmentPrevented 134ms
✔ Pass AppointmentTriggerTest.testTaskCreatedOnAppointmentInsert 327ms
✔ Pass CustomerServiceTest.testCreateCustomerMissingLastName 14ms
✔ Pass CustomerServiceTest.testCreateCustomerSuccess 84ms
✔ Pass CustomerServiceTest.testGetCustomerById 37ms
✔ Pass CustomerServiceTest.testSearchCustomersByPostalCode 35ms
✔ Pass CustomerServiceTest.testSearchCustomersByTerm 40ms
✔ Pass AppointmentServiceTest.testBookAppointmentSuccess 513ms
✔ Pass AppointmentServiceTest.testCancelAppointment 534ms
✔ Pass AppointmentServiceTest.testCustomerDoubleBookingPrevented 514ms
✔ Pass AppointmentServiceTest.testInvalidTimeThrowsException 62ms
✔ Pass AppointmentServiceTest.testStaffDoubleBookingPrevented 315ms
✔ Pass LocationServiceTest.testCalculateHaversineDistanceKm 18ms
✔ Pass LocationServiceTest.testGetNearestLocationsProximity 128ms
✔ Pass LocationServiceTest.testGetNearestLocationsServiceFiltering 82ms
✔ Pass LocationServiceTest.testNullServiceTypeThrowsException 9ms
📊 Apex Code Coverage:
Class Name Coverage Bar % Lines (Cov/Total) Uncovered Lines
────────────────────────── ────────────────────── ───── ──────────────── ────────────────────
AppointmentService [█████████████████░] 92% (44/48) None
CustomerService [█████████████████░] 95% (42/44) None
LocationService [█████████████████░] 92% (81/88) None
AppointmentTriggerHandler [█████████████████░] 93% (111/120) None
AvailabilityService [█████████████████░] 93% (79/85) None
AppointmentTrigger [██████████████████] 100% (4/4) None
Test Run Coverage: 93% (Minimum requirement: 75%) ✔
Org-Wide Coverage: 92%
────────────────────────────────────────────────────────────
Total: 26 | Passed: 26 | Failed: 0 | Skipped: 0 | Duration: 2.1s
────────────────────────────────────────────────────────────
✔ All Apex tests passed successfully in 2.1s!
🧦 Hob tidied up the test suite and polished your code coverage reports! 🧦
To run a specific test suite or drill down into uncovered lines:
hob test CustomerServiceTest --detailed
4. Lifecycle Hygiene: Scratch Org Purging (hob scratch purge)
Scratch orgs are meant to be ephemeral. Leaving dozens of stale scratch orgs active consumes the daily active scratch org limit of your Dev Hub (typically 3 for Developer Edition hubs, 40 for Enterprise).
Hob includes a built-in purge command to sweep away expired and unwanted scratch orgs safely:
1. Dry Run Inspection (--dry-run)
Before deleting anything, preview what would be removed:
hob scratch purge --dry-run
🧙 Hob — The Salesforce House-Elf 🧦
"Loyal, quiet, and tireless assistance for your Salesforce Development."
- Hob is scanning the hearth for disposable scratch orgs...
Found 2 expired scratch orgs:
- test-q34k1 (Expired 2 days ago, Org ID: 00DAw00000HKa01)
- temp-fix-2 (Expired yesterday, Org ID: 00DAw00000HKb02)
[Dry Run] No orgs were deleted. Run without --dry-run to purge.
2. Purging Expired Orgs
hob scratch purge --expired-only --no-prompt
- Hob is purging expired scratch orgs from the Dev Hub...
√ Deleted scratch org 'test-q34k1'
√ Deleted scratch org 'temp-fix-2'
🧦 The hearth is clean and uncluttered! 🧦
5. Architectural Retrospective: The Complete Journey
Across this 7-part series, we took a blank Git repository and built an enterprise-grade appointment booking platform on Salesforce.
Here is how each layer of the application maps to the Hob development lifecycle:
| Layer | Component | Architecture Highlights | Scaffolding Command |
|---|---|---|---|
| 1. Data Model | Appointment__c, Service_Type__c, Staff_Working_Hours__c | Native geolocation compound fields (ShippingAddress), AutoNumber APT-{0000}, Shift Time fields. | hob create objecthob create field |
| 2. Security | Appointment_Scheduler_Operator | Strict least-privilege permission set, zero profile bloat, explicit FLS, RecordType visibility. | hob create permset |
| 3. Test Hygiene | *DataFactory.cls, seed.apex | Dual in-memory (build) vs database (create) pattern to avoid governor limits, demo seeding fixtures. | hob create factoryhob seed init |
| 4. Domain Services | CustomerService, LocationService, AvailabilityService, AppointmentService | Transactional concurrency with FOR UPDATE row-locking, Haversine proximity calculations, TDD test companions. | hob create apex --with-test |
| 5. Automation | AppointmentTrigger, AppointmentTriggerHandler | Logic-less triggers, System.TriggerOperation dispatching, standard Task generation, customer emails. | hob create trigger |
| 6. Lightning UI | appointmentWizard + 4 child components, App & Tabs | Container/subcomponent state machine, live context pills, modern SLDS layout, Lightning App bundle. | hob create lwc -t app tab |
| 7. Org Lifecycle | Dev Hub Hearth, Scratch Pipeline, Testing, Purge | End-to-end kindling pipeline, automatic permset assignment, seed script execution, test summaries. | hob hearthhob scratch newhob testhob scratch purge |
6. DX Verdict & Tool Feedback
What Shined
- Frictionless Zero-to-App Setup:
hob scratch new <alias> -p <permset> -s <script>solves the single largest pain point in Salesforce DX: the multi-command orchestration barrier. Being able to run one command and come back to a fully deployed and seeded org is a massive productivity boost. - Cohesive Metaphor & Delightful DX: The House-Elf theme (lighting the hearth, sweeping dead ashes, carrying source files, kindling orgs) brings personality to what is traditionally dry DevOps plumbing. The terminal output is clean, informative, and engaging.
- Built-in Quality Gates:
Pairing
hob scratch newwithhob testprovides an immediate verification loop that can be integrated directly into local developer routines or CI/CD pipelines.
Opportunities for Hob’s Backlog
- Project Configuration File (
.hobrcorhob.config.json):- Currently, developers must pass
-p Appointment_Scheduler_Operatorand-s scripts/apex/seed.apexon every invocation. - Allowing a
.hobrc.jsonfile in the project root:
would allow developers to simply run{ "scratch": { "defaultDuration": 7, "permissionSets": ["Appointment_Scheduler_Operator"], "seedScript": "scripts/apex/seed.apex", "defaultApp": "Appointment_Scheduler" } }hob scratch new appointment-devwith zero additional flags.
- Currently, developers must pass
- Shorthand Flag Conflict with Root
--version(-v):- Commander.js assigns
-vto--versionat the root CLI level. Whenhob scratch new -v devhubis executed, the root-vflag can intercept the parameter and output the version number (0.1.0) instead of parsing--target-dev-hub. - Recommending
--target-dev-hubor aliasing Dev Hub flag to-V/-H(--hub) in Hob CLI avoids this Commander parser collision.
- Commander.js assigns
- Post-Kindling Hooks:
- Supporting custom post-creation hooks (such as running
npm testor a custom script after seeding) would provide complete CI/CD automation flexibility.
- Supporting custom post-creation hooks (such as running
7. Conclusion
Modern Salesforce development does not have to mean wrestling with XML files, clicking through Setup menus, or babysitting 6-step terminal commands.
With clear architectural separation of concerns and an intelligent CLI companion like Hob: The Salesforce House-Elf, building enterprise applications on the Salesforce platform can be as fast, modular, and enjoyable as any modern web stack.
Every line of code, configuration, test factory, and automation created throughout this series is available in the Salesforce Appointment Scheduler repository.
Happy coding, and may your hearth always burn bright! 🧦
The Appointment Scheduler Series
- Series Index: Building the Salesforce Appointment Scheduler: Architecture & Series Index
- ← Part 1: Eliminating Salesforce XML Fatigue — CLI Schema Scaffolding with Hob
- ← Part 2: Instant Least-Privilege Access — Generating Permission Sets with Hob
- ← Part 3: Standardizing Test Hygiene — In-Memory vs DML Factories with Hob
- ← Part 4: High-Velocity TDD — Companion Test Scaffolding with Hob
- ← Part 5: Architecture by Default — Separation of Concerns with Hob Triggers
- ← Part 6: Building Modern Lightning UIs Fast — Scaffolded LWCs with Hob
- Part 7: One Command to Rule Them All — The Complete Dev Org Setup with Hob (Current Article)