TL;DR
- Real hands-on Salesforce training means the code is typed, run and broken in a real org, and you watch the instructor diagnose and fix the failure.
- Fake hands-on looks like pasted code, slides of finished answers, demos that only ever insert one record, and security left to a final chapter.
- Five signals to check in any course's free preview: typed code, a real error, a debug log on screen, a test with 200 records, and sharing and field access handled inside the build.
- Watching is not enough on its own. You still have to type along in your own org, which is true of my courses too.
Hands-on Salesforce training means the code is typed, run and broken in a real org, and you watch someone diagnose and fix the failure. You should see real errors, real debug logs, tests that insert 200 records, and sharing and field access handled as part of the build, not saved for a final chapter. Fake hands-on looks different: pasted code, slides of finished answers, and demos that only ever insert one record. At the end of this post there's a checklist you can use on any course, mine included, plus a walkthrough of one real debugging moment.
This is the post I'd point to if someone asked how I teach and why. I'm biased, obviously. So I'll try to make the checklist fair enough that you could use it to reject my courses too.
What "hands-on" should mean
"Hands-on" is on nearly every course landing page now, so it's stopped meaning much. Here's the definition I hold myself to:
You see the same things a working developer sees, in the same order they see them. That includes the parts that go wrong.
A working Salesforce developer's day isn't writing perfect code from memory. It's writing something reasonable, running it, getting an error they didn't expect, opening a log, forming a theory, changing one thing, and running it again. Then it's writing a test that proves the fix holds for a data load, not just for one record typed in through the UI. Then it's checking that a user with fewer permissions gets the right result.
Training that skips those steps is teaching the answer without the work. The work is the skill.
Typing the code vs pasting it
When an instructor pastes a finished class into the editor, three things disappear.
The order of decisions. Real code isn't written top to bottom. You write the loop, realise you need the parent records, step back to collect IDs, and add a map. Watching code get typed shows you when each decision gets made, and that's most of what a junior developer is missing.
The small errors. A missing semicolon, a field API name typed wrong, a variable out of scope. They're trivial, but seeing how quickly an experienced developer reads a compile error and moves on teaches you not to panic at them.
The pace. Pasted code arrives at the speed of light, so your brain files it as "too complicated for me". Typed code arrives at the speed of thought, so you can follow the reasoning.
There's a cost to typing on camera. Videos get longer, and some viewers want the answer faster. I think that trade is worth it. And every course still has a GitHub repo, so you can diff your version against mine when you're stuck.
Hitting real errors, on camera
Most course content is edited to remove mistakes. I think that's backwards for programming.
The errors you'll meet in your first year are predictable: System.LimitException, System.NullPointerException, MIXED_DML_OPERATION, FIELD_CUSTOM_VALIDATION_EXCEPTION, a callout that isn't allowed after uncommitted work, and a test that passes alone but fails in a full run. If the first time you see one is in production, at a client, under pressure, that's the worst possible classroom.
When a course hits these on camera and works through them, you build a mental index: I've seen this, it usually means that, check here first. That index is a large part of what separates a junior developer from a mid-level one.
Reading debug logs like a working developer
A Salesforce debug log is long, noisy and intimidating, and most courses never open one. That's a shame, because reading logs is one of the most useful skills you can have on the platform.
Hands-on training should show you how to:
- Set a trace flag on the right user and choose sensible log levels.
- Jump to the
FATAL_ERRORline instead of scrolling. - Filter for
SOQL_EXECUTE_BEGINorDML_BEGINto see what actually ran, and from which line. - Follow the order of execution: which trigger, flow or validation rule fired, and when.
- Read the cumulative limit usage near the end of the log.
You don't learn this from a description of a log. You learn it by watching someone use a log to answer a specific question, then doing it yourself.
Bulk testing with 200 records
If I could make one change to every Salesforce development course, it would be this: every trigger demo ends with a test that inserts 200 records.
The reason is mechanical. Apex triggers receive records in chunks of up to 200, and Data Loader sends records in batches by default. A trigger that works for one record typed into a page layout can fail completely on its first real data load. A 200-record test reproduces that path in seconds.
A demo that only ever creates one record through the UI is the single most common sign of fake hands-on. It looks like a working build, but it hasn't been tested against the case that breaks things. There's more on this in Apex bulkification explained and Apex test classes beyond 75% coverage.
Security and sharing belong inside the build
Many courses put security in its own chapter near the end. Then every example before it quietly runs with full access, and the student learns that as normal.
Real code has to answer security questions every time:
- Does this class run
with sharing? If not, why not, and is that written down? - Does the query enforce the running user's field and object permissions, for example with
WITH USER_MODE? - Does the LWC rely on Lightning Data Service, which respects sharing and field-level security, or on custom Apex that has to enforce them itself?
- Has the test checked behaviour as a user with fewer permissions, using
System.runAs?
Hands-on training makes these decisions in every build, out loud, including the cases where system mode is the correct answer and why.
Why slides fail for Salesforce development
Slides are good at one thing: showing a finished, correct idea. "Avoid SOQL inside loops." "Use a trigger handler." "Bulkify your code." All true, and all forgettable.
Salesforce development fails at runtime, in context. The same code is fine for one record and fatal for 200. It works as an admin and fails for a community user. It runs alone but fails when a flow on the same object fires first. A slide can state the rule. It can't show you the moment the rule matters, and that moment is what makes it stick.
I've never used a slide deck in a course. That isn't an aesthetic preference. It's because the platform's hardest lessons only show up when code runs.
A real debugging moment: "Too many SOQL queries: 101"
Here's what this looks like in practice. It's a scenario I've debugged in real orgs more times than I can count.
The requirement
When a contact is created under an account and has no mailing country, copy the account's billing country onto it. It's a small, sensible piece of defaulting.
The first version
trigger ContactTrigger on Contact (before insert) {
for (Contact c : Trigger.new) {
if (c.AccountId != null && String.isBlank(c.MailingCountry)) {
Account parent = [SELECT BillingCountry FROM Account WHERE Id = :c.AccountId];
c.MailingCountry = parent.BillingCountry;
}
}
}
Create one contact in the UI and it works. Most demos stop here. That's the trap.
The failure
Now load contacts with Data Loader, or run a test that inserts 200 of them, and the whole insert fails:
System.LimitException: Too many SOQL queries: 101
Reading the log
Set a trace flag, run the insert again and open the log. Filter for SOQL_EXECUTE_BEGIN and you'll see the same SELECT BillingCountry FROM Account query over and over, every entry pointing at the same line of the trigger. Then jump to FATAL_ERROR: the exception is thrown on that same line.
The diagnosis takes less than a minute once you know where to look. A synchronous transaction allows 100 SOQL queries, and this trigger runs one query per contact. At 101 queries the platform stops the transaction. LimitException can't be caught, so nothing in the trigger can rescue it.
The fix
Move the logic into a handler class, collect the account IDs first, query once, and look each parent up in a map:
trigger ContactTrigger on Contact (before insert) {
ContactDefaults.copyAccountCountry(Trigger.new);
}
public with sharing class ContactDefaults {
public static void copyAccountCountry(List<Contact> contacts) {
Set<Id> accountIds = new Set<Id>();
for (Contact c : contacts) {
if (c.AccountId != null && String.isBlank(c.MailingCountry)) {
accountIds.add(c.AccountId);
}
}
if (accountIds.isEmpty()) {
return;
}
Map<Id, Account> accounts = new Map<Id, Account>(
[SELECT Id, BillingCountry FROM Account WHERE Id IN :accountIds WITH USER_MODE]
);
for (Contact c : contacts) {
Account parent = accounts.get(c.AccountId);
if (parent != null && String.isBlank(c.MailingCountry)) {
c.MailingCountry = parent.BillingCountry;
}
}
}
}
It's one query whether the chunk has 1 contact or 200. It's a before insert trigger, so there's no DML at all; the field is set before the record is saved. And WITH USER_MODE means the query enforces the running user's permissions and sharing. Whether defaulting logic like this should run in user or system mode is a real design decision. In a course, I'd make that decision out loud rather than leave it to chance.
The test that proves it
@IsTest
private class ContactDefaultsTest {
@IsTest
static void copiesAccountCountryForBulkInsert() {
Account parent = new Account(Name = 'Bulk Test Account', BillingCountry = 'India');
insert parent;
List<Contact> contacts = new List<Contact>();
for (Integer i = 0; i < 200; i++) {
contacts.add(new Contact(LastName = 'Bulk ' + i, AccountId = parent.Id));
}
Test.startTest();
insert contacts;
Test.stopTest();
Integer copied = [SELECT COUNT() FROM Contact WHERE AccountId = :parent.Id AND MailingCountry = 'India'];
Assert.areEqual(200, copied, 'Every contact should inherit the account billing country');
}
}
Run this test against the first version and it fails with the same LimitException. Run it against the fix and it passes. That's the point of the test. It isn't there for coverage. It encodes the exact failure, so it can never come back unnoticed.
What to take away
The fix isn't the lesson. Anyone can memorise "no SOQL in loops". The lesson is the sequence: a demo that passed, a realistic load that failed, a log that pointed to the line, a fix based on how the platform actually counts limits, and a test that locks the fix in. That sequence is what I mean by hands-on.
How to spot fake hands-on: a checklist for any course
Use this on any free preview: a Udemy course's preview lectures, a YouTube playlist, a classroom institute's demo class, or one of my free courses. It's meant to be fair, so it rewards good teaching wherever it is.
- Is the code typed or pasted? Watch ten minutes. If finished blocks keep appearing fully formed, be cautious.
- Do you ever see a real error? And is it diagnosed, or quietly edited out?
- Does a debug log ever appear on screen?
- Do the tests insert around 200 records? And do they assert on outcomes, or just call the code for coverage?
- Is security part of the build? Look for
with sharing, user-mode queries orstripInaccessible, andrunAsin tests. - Is the syntax current — or does the instructor call out what has changed since recording? Template directives, the
Assertclass in tests and user-mode queries have all moved in recent releases. Older recordings are fine if the changes are flagged. - Can you get the code? A repo lets you compare your version when yours breaks.
- Where do questions go? A discussion group, a Q&A board, an instructor who answers. Something.
- Do you have to build anything yourself? Credit where it's due: Trailhead's hands-on challenges and superbadges make you build in a real org and check your work automatically, which videos can't. Its challenges are mostly guided steps, though, so pair it with material that shows messy real-world debugging. More on that in Salesforce Casts vs Trailhead.
A course doesn't need all nine to be good. If it fails the first five, though, it's probably teaching you to pass a demo, not to ship code.
Where my own courses fall short
Since I asked you to apply the checklist to me, here's what you'll find.
- There are no automated exercises. Videos show you the build. You have to type along in your own org to make it stick. Nothing grades you.
- There are no scheduled classes or 1:1 mentoring. Questions go to the discussion group.
- Videos stream only.
- There are no refunds. That's why several complete courses are free, so you can run this checklist before paying anything.
Try the checklist on me
Pick any of the free courses and hold it to the nine points above:
Apex Triggers Framework 101
8 episodes · 0.5 hours
Javascript for Lightning Web Components [FREE]
22 episodes · 2.6 hours
Salesforce Administration Training
40 episodes · 6.2 hours · 8 modules
If it passes, the paid courses are built the same way: Salesforce Development (Visualforce & Apex), Apex Test Fest, Lightning Web Components Development and the rest. If it doesn't, you've lost nothing, and you now have a checklist that works on every course you'll ever consider.
Learn it by building it
Every course, every code repository, the discussion group and certificates of completion — one subscription.
Frequently asked questions
What does hands-on Salesforce training mean?
It means you learn by building in a real org: writing code, running it, hitting real errors such as governor limits, reading debug logs, writing bulk tests and handling security, rather than watching slides or pasted code.
How can I tell if a Salesforce course is really hands-on?
Watch a free preview and check five things: whether the instructor types the code, whether you ever see a real error get diagnosed, whether a debug log appears, whether tests use around 200 records, and whether sharing and field-level security are part of the build.
Is Trailhead hands-on?
Yes, in an important way. Trailhead gives you real orgs, hands-on challenges and superbadges, and it is excellent for fundamentals. Its challenges are mostly guided steps that are checked automatically, so pair it with material that shows messy, real-world debugging and design reasoning.
Why do Salesforce tests use 200 records?
Apex triggers process records in chunks of up to 200, and tools such as Data Loader send records in batches. A test with 200 records exercises the same path a real data load does, which is where code with SOQL or DML inside loops fails.
Can watching videos really be hands-on?
Only if you type along. A video can show you real errors and how to read them, which slides cannot, but the skill only sticks when you build the same thing in your own org and break it yourself.
Keep going
Blog
Apex Bulkification Explained: From 'Too Many SOQL Queries: 101' to Bulk-Safe Code
Blog
Apex Test Classes: Stop Chasing 75% Coverage
Comparison
Salesforce Casts vs Trailhead: Why You Should Use Both
Comparison
Salesforce Casts vs Udemy: Which Is Better for Learning Salesforce in 2026?
Topic guide
Learn Apex: A Hands-On Salesforce Apex Course for Developers
Blog
Learn JavaScript Before LWC: The 12 Concepts That Matter