Salesforce Development

(4.9)
2000 Viewers

This article covers Apex programming from scratch, control flow, the three collection types, and a breakdown of shallow vs. deep copy. We have included code examples throughout to perform your hands-on practices.

Salesforce Development
  • Blog Author:
    Kalla SaiKumar
  • Last Updated:
    12 May 2026
  • Views:
    2000
  • Read Time:
    33:57 Minutes
  • Share:
Salesforce Articles

Salesforce is a popular CRM platform that most organizations use to enhance their customer relationships and business productivity.

Salesforce reports that companies across the automotive, communications, education, financial services, and manufacturing sectors use Salesforce to modernize their businesses and reduce costs.

This tutorial helps you learn about the Apex programming language and how to develop Salesforce applications with it.

Table of Contents:

What is Apex Programming?

Apex is the programming language which Salesforce designed for their platform. Since it is object-oriented as well as strongly typed and runs server-side, your code would execute on Salesforce's infrastructure.

It means that your Apex code can talk directly to the database, access the logged-in user's info, read org metadata. It can do all of these without configuring connection strings or standing up middleware. It just works out of the gate, which is honestly one of the nicer things about developing on this platform.

Anyone with a Java background will pick this up quickly. 

The syntax is very similar. Classes, methods, instance variables, loops, try-catch you'll recognize all of it. 

However, it is different in the runtime environment. Salesforce is multi-tenant, meaning your organisation can share server resources with other organisations. Hence, they have put strong limitations on what your code can do within a single execution. It includes elements like 

  • how many SOQL queries you can fire, 
  • how many DML statements you can run, 
  • how much heap memory you can use.

These caps have the name "governor limits," and the whole reason they exist is to stop one user’s weakly optimized code from dragging down performance for others on the same server cluster.

Apex Data Types

Talking about the data type, Apex includes:

  • Integer,
  • String,
  • Boolean,
  • Double,
  • Long,
  • Date,
  • Datetime,
  • and Blob.

So, if you have used any C-based language, you would feel comfortable with these.

The thing that will feel new, and this is genuinely cool if you're coming from Java or Python, is sObjects. 

An sObject is a direct and in-memory representation of an actual Salesforce record. So, when you type Account acc = new Account(); and start setting fields on it, you are building a database row in code. Call insert acc; and that object becomes real data sitting in your org. The first time you do that, it will click immediately why people like building on this platform.

Variable declarations look like this in practice:

apex

Integer headcount = 120;
String companyName = 'Zenith Solutions';
Boolean isVerified = true;
Double totalRevenue = 3400000.50;
Date joinDate = Date.newInstance(2022, 6, 10);

And here's how you work with an sObject:

apex

Account newAccount = new Account();
newAccount. Name = 'Orion Technologies';
newAccount. Industry = 'Healthcare';
newAccount.AnnualRevenue = 5000000;

That Account object is ready to be inserted into the database whenever you run a DML insert statement.

How To Run An Apex Code?

The easiest and quickest way to run Apex code is through the Developer Console.

  • You go to Setup, 
  • Click the gear icon at the top right,
  • Open Developer Console.
  • Next, go to Debug > Open Execute Anonymous Window.
  • You can also just press Ctrl+E once the console is open.
  • Paste your code in and hit Execute. 
  • Then check the Logs tab at the bottom, filter by "DEBUG" to see your output.
apex

String msg = 'First line of Apex - feels good.';
System.debug(msg);

Integer x = 8;
Integer y = 13;
System.debug('Total: '+ (x + y));

System.debug() is how you print output in Apex. There's no console.log or System.out.println here  it's all System.debug(), and you read the results in the debug log.

That covers the basics. If you want a more thorough understanding of Apex fundamentals with hands-on labs, MindMajix's Salesforce Development training program covers this and a lot more.

Now let's see how you can control what your code does and when it does it.

Controlling the Program Flow in Salesforce

Without control flow statements, your code would just run line by line from top to bottom i.e, every single line for every single time. But, that’s not how real efficient programs work.

You need your code to make decisions, i.e., should this discount apply or not? You need your code to repeat things and process every record in this list. That's what control flow gives you.

Apex supports two kinds of control flow.

  1. Conditional statements that decide which code runs
  2. Loop statements that determine how many times each code runs.

1. If-Else Statement

This is the most basic conditional. You check something first. Based on whether it's true or false, different code runs.

Output: Standard shipping fee added

The condition was false, so the “else” block ran.

2. Nested If The If-Else-If Ladder

Most business logic isn't simple true/false. You usually have multiple possible outcomes, and you need to check for each one in order.

Output: Neither  value set to -1

The important thing to remember is that these conditions are tested from top to bottom. The moment one of them is true, its block executes, and everything below it gets skipped. So the order you arrange your conditions in actually matters.

3. Switch Statement

If you have a single variable and need to match it against other specific values, go for the switch statement. It is a cleaner option compared to writing multiple if-else blocks.

Output: Assign to senior engineer

This reads a lot better than a thread of if-else statements doing the same thing.

4. While Loop

The while loop repeats a code block as long as a condition holds true. It checks the condition before each run. So if the condition is false from the start, the loop doesn’t execute at all.

Output: Prints values 1 through 5.

One thing to watch if you forget the j++ part, the condition never becomes false, and you've got yourself an infinite loop. Salesforce will kill it eventually when CPU limits are hit, but that's not how you want to discover the problem.

5. Do-While Loop

This works in a similar way to a while loop, except that the condition is checked after the code block runs. This means the code will surely execute at least once, irrespective of anything.

Output: Prints values from 5 to 14.

The first print happens before the condition e <= 14 is evaluated. That's the whole point  when you need something to run at least once.

6. Traditional For Loop

When you know exactly how many iterations you need, the for loop does initialization, condition checking, and increments to one neat line.

Output: Prints 0 through 9.

Three parts separated by semicolons i.e, declare your counter, set your stopping condition, and define how the counter changes after each pass.

7. For-Each Loop (The One You'll Use Constantly)

This loop was designed for iterating through collections and arrays. Instead of managing an index variable yourself, Apex just hands you each element one at a time.

Output: Prints each element sequentially.

The Apex assigns the current element to the variable e while running the loop. But when it runs out of elements, the loop stops. Also, you don't have to worry about indexes being out of bounds or off-by-one mistakes.

So, if you want to start running from a particular statement based on a condition, conditional statements are important. And, if you want to execute code repeatedly, you can use loops. Together, these control flow tools help you handle any business logic scenario.

Next up are the data structures that hold all the information your code works with.

MindMajix Youtube Channel

Collections in Salesforce: An Overview

Collections are basically containers that hold multiple elements. You can think of them as smarter versions of arrays, which grow and shrink on their own. They come with built-in methods for searching and sorting, and they work seamlessly with Salesforce's database layer.

Salesforce gives you three types:

  • List ordered, allows duplicate values, accessed by index
  • Set unordered, no duplicates allowed
  • Map stores key-value pairs. The keys should be unique.

Let's check these with code examples.

Salesforce Collection List

A List is an ordered collection of elements. It means the elements retain the position you first place them in. Like, first one at index 0, second at index 1, and so on. Lists easily handle duplicates. So, if you add the same value ten times, it stores all ten.

Also, every SOQL query in Salesforce returns a List. This makes it the most-used collection type in all of Apex development.

List elements can be primitive types like String or Integer, sObjects like Account or Contact, user-defined classes, or even other collections. They automatically adjust the size as you add or remove items.

1. Declaring a List

Output: (Mango, Apple, Banana, Apple). Here, both Apple entries are kept. Duplicates are perfectly fine in a List.

Adding Elements and Processing Them

Let's check out a scenario. Consider that you need to track employee salaries and run some operations on them.

Adding values with add():

Output: (45000, 62000, 78000, 51000)

Checking the size with size():

Output: 4

Grabbing a specific element with get():

Output: 62000

Remember indexes start at 0. So index 1 gives you the second element.

Removing an element with remove():

Output: (45000, 62000, 51000)  the element at index 2 (78000) is gone.

Checking if a value exists with contains():

Output: true

Sorting the list with sort():

empSalary.sort(); 
System.debug('Sorted: ' + empSalary);

Output: (45000, 51000, 62000)

Checking if the list is empty with isEmpty():

System.debug('Empty? ' + empSalary.isEmpty());

Output: false

2. Looping Through a List

The for-each loop is the standard way to iterate through every element.

List<String> cities = new List<String>{'Hyderabad', 'Pune', 'Chennai', 'Delhi'}; 

for (String city : cities) { 
System.debug('City: ' + city);

That's the List collection which is ordered, repeat-friendly, index-based. You'll use it in virtually every piece of Salesforce Development code you write.

Salesforce Collection Set

Sets solve a very specific problem: they store elements without allowing any duplicates. You can try adding the same value a second time, and the Set just ignores it. You get no error message or exception. It simply doesn't add the duplicate.

The other thing about Sets is that they are unordered. There's no index. Here, you can't say "give me element number 3." You either check whether something is in the Set or you iterate through the whole thing.

Set elements can be primitive types (String, Integer, etc.), sObjects, or user-defined types.

1. Declaring a Set

Set<Integer> accountNumbers = new Set<Integer>(); 

Set<String> teams = new Set<String>{'Development', 'QA', 'DevOps'};

2. Working With Sets: Step by Step

Here's a scenario straight from the MindMajix source material: adding employee bank account numbers and processing them with various Set operations.

Adding values:

Set<Integer> empBankAcc = new Set<Integer>(); 
empBankAcc.add(111); 
empBankAcc.add(222); 
empBankAcc.add(333); 
empBankAcc.add(444); 

System.debug('The Current Account Numbers = ' + empBankAcc);

Output: {111, 222, 333, 444}

Now try adding a duplicate:

empBankAcc.add(111); 

System.debug('The Current Account Numbers = ' + empBankAcc);

Output: {111, 222, 333, 444}

An interesting thing here is that 111 was already in the Set. So, the second add(111) was completely ignored and the Set didn't expand. This is what makes Sets useful for de-duplication.

Checking whether a value exists:

Boolean checkTheValue = empBankAcc.contains(444); 

System.debug('Does 444 exist? = ' + checkTheValue);

Output: true

Boolean checkAnother = empBankAcc.contains(999); 
System.debug('Does 999 exist? = ' + checkAnother);

Output: false

Getting the size:

Integer length = empBankAcc.size(); 
System.debug('Total accounts = ' + length);

Output: 4

Checking if the Set is empty:

Boolean emptyCheck = empBankAcc.isEmpty(); 
System.debug('Is this set empty? = ' + emptyCheck);

Output: false

Removing a value:

empBankAcc.remove(222); 
System.debug('After removing 222: ' + empBankAcc);
Output: {111, 333, 444}

3. Iterating Over a Set

Since there's no index, you use the for-each loop

Set<String> emailIds = new Set<String>{ 
'ravi@demo.com', 'priya@demo.com', 'kiran@demo.com'
}; 
for (String email : emailIds) { 
System.debug('Email: ' + email); 
}

Practice assignment: Create a set of 10 email IDs. Print each one using System.debug() inside a for-each loop. (Hint: SET + for-each loop.)

Sets 

  • Are unordered, 
  • They reject duplicates, and
  • They are especially handy in trigger code when you need to collect unique record IDs before running a SOQL query.

Salesforce Collection Map

Maps are the most important collection type in real-world Salesforce Development.

A Map contains key-value pairs, where each key needs to be unique. You can not have two entries with the same key, but the values attached to those keys can repeat as many times as you need.

What makes Maps different from Lists? When it comes to a List, you look things up using an index number  0, 1, 2, 3, which doesn’t have a specific meaning. While, when you look things up in a Map, you use a meaningful key like a product name, an Account ID, a country code.

Some key characteristics from the source material:

  • Maps work on pairs of elements, never single values like Lists or Sets.
  • Keys cannot be duplicated (they behave like a Set).
  • Values can be duplicate (they behave like a List).
  • Both keys and values accept any data type  primitives, sObjects, collections, user-defined classes.
  • Maps are specially used when you cannot refer to records easily using meaningless numbers (0, 1, 2, 3...) through a List.

1. Declaring a Map

Map<String, Integer> productStock = new Map<String, Integer>(); 
Map<String, String> countryCodes = new Map<String, String>{ 
'India' => 'I	N', 
'Germany' => 'DE',
'Brazil' => 'BR'
};

Building a Map - The Mobile Price Example

Adding key-value pairs with put():

Map<String, Integer> myMobilePrice = new Map<String, Integer>(); myMobilePrice.put('iphone x', 70000); 
myMobilePrice.put('nokia', 60000); 
myMobilePrice.put('samsung', 50000); 
myMobilePrice.put('motorola', 40000); 
System.debug('My Mobile prices List = ' + myMobilePrice);

Output: {iphone x=70000, motorola=40000, nokia=60000, samsung=50000}

Checking if the Map is empty:

Boolean emptyCheck = myMobilePrice.isEmpty(); 
System.debug('Is the map empty = ' + emptyCheck);

Output: false

Getting all keys with keySet():

Set<String> mobiles = myMobilePrice.keySet(); 
System.debug('List of Mobiles = ' + mobiles);

Output: {iphone x, motorola, nokia, samsung}

Notice something interesting? keySet() returns a Set. That makes sense; keys are always unique, just like Set elements. That's not a coincidence in the design.

Getting all values with values():

List<Integer> prices = myMobilePrice.values(); 
System.debug('List of Mobile Prices = ' + prices);

Output: (40000, 50000, 60000, 70000)

And values() returns a List. Again, it makes sense values can repeat, just like List elements.

Retrieving a specific value by key:

Integer nokiaPrice = myMobilePrice.get('nokia'); 
System.debug('Nokia price: ' + nokiaPrice);

Output: 60000

Checking if a key exists:

System.debug('Has samsung? ' + myMobilePrice.containsKey('samsung'));

Output: true

Getting the total count:

System.debug('Total brands: ' + myMobilePrice.size());

Output: 4

2. Looping Through a Map

You can iterate over the key set and pull each value inside the loop body.

for (String brand : myMobilePrice.keySet()) {
Integer price = myMobilePrice.get(brand);
System.debug(brand + ' costs: ' + price); 
}

3. Why Maps Are Critical in Triggers

This is where Maps prove their value more than anywhere else. In trigger development, you constantly need to access related records without running queries inside loops (which would hit governor limits almost immediately).

Here's the pattern that every serious Salesforce developer knows:

// Step 1  Collect unique IDs using a Set
Set<Id> accountIds = new Set<Id>();
for (Contact con : Trigger.new) {
    if (con.AccountId != null) {
        accountIds.add(con.AccountId);
    }
}

// Step 2  Query once and dump results into a Map
Map<Id, Account> accountMap = new Map<Id, Account>(
    [SELECT Id, Name, Industry FROM Account WHERE Id IN :accountIds]
);

// Step 3  Use the Map inside the loop. Zero additional queries.
for (Contact con : Trigger.new) {
    if (accountMap.containsKey(con.AccountId)) {
        Account parent = accountMap.get(con.AccountId);
        System.debug(con.LastName + ' belongs to ' + parent.Name);
    }
}

This pattern - Set for collecting IDs, Map for storing query results, for-each for processing, is the foundation of bulkification in Salesforce Development. 

Write it on a sticky note and put it on your monitor to remember it often.

Practice assignment: Create a Map with 5 student names and their marks. Print every name-marks pair using a for-each loop.

Comparing the Three Collections

 ListSetMap
Ordered?YesNoNo
Allows duplicates?YesNoKeys: No / Values: Yes
How to access elementsBy index  get(0)Only through iterationBy key  get('keyName')
Nulls allowed?YesYesYes
Best forSOQL results, ordered processingUnique value collection, deduplicationRecord lookups, trigger bulkification

Shallow Copy and Deep Copy in Salesforce

This one challenges users more and the difficult part is that the bug it causes can be hard to track down. While you change a variable in one way and somehow a completely different variable in a completely different part of your code changes too.

Has that ever happened to you? Well, then you might have been hit by a shallow copy.

Let's understand it properly.

What Happens With Shallow Copy

When you use the assignment operator (=) to copy an sObject variable, Apex doesn't actually create a new object. It creates a new reference. This is basically a second name pointing to the same object in memory.

Here, both variables are looking at the same thing. Touch one, and the other feels it.

Output: acc1 Name: ChangedCorp acc2 Name: ChangedCorp

We only changed acc2, but acc1 changed right along with it. This is because the line acc2 = acc1 didn't copy any data; it just made acc2 point to the same object that acc1 was already pointing to.

The shallow copy just copies the handle, not the thing the handle is attached to.

What Happens With Deep Copy

Deep copy creates a totally new, completely independent object with all the same field values. While you modify one, the other is totally unaffected.

In Apex, you have to do this with the .clone() method.

Output: acc1 Name: OriginalCorp acc2 Name: IndependentCorp

Now they live in completely separate chunks of memory. Changing one has zero effect on the other. That's what deep copy gives you true independence.

How This Plays Out With Collections

The shallow-vs-deep issue gets worse with collections, because you might be accidentally sharing references to dozens or hundreds of records without realizing it.

The dangerous version shallow copy of a List:

Output: MUTATED

You only touched shallowCopy, but originalList got changed too. Both Lists contain references to the same Account objects in memory.

The safe version  deep clone of a List:

Output: Original: MUTATED Deep copy: SAFE CHANGE

deepClone() creates new sObject instances for every element in the List. Each copy is completely independent.

Side by Side: When to Use Which

 Shallow CopyDeep Copy
What gets copiedThe reference onlyThe whole object  all field values
Separate memory?No  both point to same objectYes  each has its own
Modifying one changes the other?Yes, alwaysNo, never
How to do it= operator.clone() for single records, .deepClone() for Lists
When to useWhen you deliberately want shared accessWhen you need a truly independent copy

You can use shallow copy when you are passing a record to a helper method which is supposed to modify the original. On the other hand, you use deep copy when you are creating backups, cloning records for insertion or if you want to make changes without side effects.

Understanding this difference will save you from a category of bugs which are genuinely hard to debug. The symptoms are subtle i.e. data changes when it should not, fields update when it should not and triggers behave inconsistently. 90% of the times, a shallow copy somewhere in the code poses an issue.

Wrapping Up

That covers a lot of ground. Let us recap what we walked through in this tutorial:

Apex Programming - this includes the language, the data types, sObjects, and how to run code in the Developer Console using System.debug().

Control Flow - this includes if-else for decisions, switch for multi-value matching, while and do-while for conditional repetition, for loops for counted iteration, and for-each loops for walking through collections.

List ordered collection, allows duplicates, accessed by index. Every SOQL query returns one of these, so you'll use them constantly.

Set unordered collection automatically rejects duplicates. Ideal for gathering unique IDs before running queries.

Map key-value pairs where keys are unique. The most powerful collection type and the backbone of trigger bulkification patterns.

Shallow Copy vs Deep Copy  - it is the difference between copying a reference and copying actual data. Use .clone() and .deepClone() to keep your copies independent.

These are the actual tools you use every day when building on the Salesforce platform. Triggers, batch jobs, integrations, Lightning components - all these rely on these fundamentals.

If you want to go deeper with structured training and gain hands-on experience on real projects along with certification, check out Salesforce Development training at MindMajix. It covers Apex, SOQL, Lightning, Visualforce, and everything else you need to become a production-ready developer.

Frequently Asked Questions

1. Do I need Java experience to learn Apex?

No. Java experience is good to have because the syntax is similar, but it is not a mandatory requirement.Many people pick up Apex as their first real programming language. What is more important is understanding Salesforce-specific patterns like governor limits, SOQL, and bulkification.

2. How long before I can write production-level Apex code?

If you are consistently studying, you can learn the fundamentals in 4 to 6 weeks. Getting comfortable enough to write triggers and batch jobs for a real project usually takes about 3 months of complete practice.

3. Which collection type should I learn first?

Start with List; it's the most commonly used and every SOQL query returns one. Then learn Set for deduplication. Finally, learn Map. It's the most powerful and the most important for writing efficient trigger code.

4. What's the most common mistake with shallow copy?

People assign one sObject variable to another using = and assume they have two separate copies, but they don't. They have two references to the same object. Then they modify one and can't figure out why the other changed. Use .clone() when you need independence.

5. Where can I practice these concepts?

The Developer Console in any Salesforce org works great for testing. You can sign up for a free Salesforce Developer Edition and start writing code right away. For structured learning with projects and mentorship, take a look at MindMajix's Salesforce training.

6. Can I find more learning resources on these topics?

Yes,  MindMajix has several free resources:

logoOn-Job Support Service

Online Work Support for your on-job roles.

jobservice
@Learner@SME

Our work-support plans provide precise options as per your project tasks. Whether you are a newbie or an experienced professional seeking assistance in completing project tasks, we are here with the following plans to meet your custom needs:

  • Pay Per Hour
  • Pay Per Week
  • Monthly
Learn MoreContact us
Course Schedule
NameDates
Salesforce TrainingMay 30 to Jun 14View Details
Salesforce TrainingJun 02 to Jun 17View Details
Salesforce TrainingJun 06 to Jun 21View Details
Salesforce TrainingJun 09 to Jun 24View Details
Last updated: 12 May 2026
About Author

Kalla Saikumar is a technology expert and is currently working as a Marketing Analyst at MindMajix. Write articles on multiple platforms such as Tableau, PowerBi, Business Analysis, SQL Server, MySQL, Oracle, and other courses. And you can join him on LinkedIn and Twitter.

read less