5 Mejores Prácticas de Apex que Todo Desarrollador Salesforce Debe Seguir
Aprende las mejores prácticas esenciales de Apex que te ayudarán a escribir código más limpio, eficiente y escalable en Salesforce. Desde bulkificación hasta manejo de errores.

Como Arquitecto Salesforce con más de 10 años de experiencia, he visto innumerables implementaciones de Apex, algunas elegantes, otras no tanto. En este artículo, compartiré las cinco mejores prácticas más importantes que todo desarrollador Salesforce debería seguir para crear aplicaciones escalables y robustas.
1. Siempre Bulkifica Tu Código
Los governor limits en Salesforce están diseñados para garantizar el uso justo de recursos compartidos. El error más común que veo es escribir código que funciona para un solo registro pero falla al procesar múltiples registros. El código que no está bulkificado fallará eventualmente en producción.
// ❌ Mal: Query dentro de un bucle
for (Account acc : Trigger.new) {
List<Contact> contacts = [SELECT Id FROM Contact WHERE AccountId = :acc.Id];
}
// ✅ Bien: Recoger IDs primero, luego hacer una sola query
Set<Id> accountIds = new Set<Id>();
for (Account acc : Trigger.new) {
accountIds.add(acc.Id);
}
Map<Id, List<Contact>> contactsByAccount = new Map<Id, List<Contact>>();
for (Contact c : [SELECT Id, AccountId FROM Contact WHERE AccountId IN :accountIds]) {
if (!contactsByAccount.containsKey(c.AccountId)) {
contactsByAccount.put(c.AccountId, new List<Contact>());
}
contactsByAccount.get(c.AccountId).add(c);
}
2. Usa Maps para Acceso Eficiente a Datos
Los Maps son tu mejor aliado cuando necesitas acceder a registros por su ID o cualquier campo único. Proporcionan tiempo de búsqueda O(1) que es significativamente más rápido que iterar a través de una lista (O(n)) para encontrar un registro coincidente.
Map<Id, Account> accountMap = new Map<Id, Account>(
[SELECT Id, Name, Industry FROM Account WHERE Id IN :accountIds]
);
for (Opportunity opp : opportunities) {
// Acceso instantáneo sin bucles adicionales
Account relatedAccount = accountMap.get(opp.AccountId);
if (relatedAccount != null) {
// Procesar con datos de la cuenta
}
}
3. Implementa Manejo de Errores Adecuado
Nunca dejes excepciones sin manejar. Usa bloques try-catch de forma estratégica y asegúrate de manejar excepciones específicas como DmlException en lugar de una Exception genérica.
public class AccountService {
public static void updateAccounts(List<Account> accounts) {
try {
update accounts;
} catch (DmlException e) {
// Manejar errores DML específicos
for (Integer i = 0; i < e.getNumDml(); i++) {
accounts[e.getDmlIndex(i)].addError(
'Error al actualizar: ' + e.getDmlMessage(i)
);
}
}
}
}
4. Separa Responsabilidades con Clases Handler
Mantén tus triggers limpios delegando la lógica a clases handler. Los triggers solo deben ser responsables de enviar el contexto al método handler correcto. Esto hace tu código más testeable, reutilizable y mantenible.
// El trigger es mínimo
trigger AccountTrigger on Account (before insert, before update, after insert) {
AccountTriggerHandler.handle(Trigger.new, Trigger.oldMap, Trigger.operationType);
}
// La clase handler contiene la lógica real
public class AccountTriggerHandler {
public static void handle(List<Account> newList, Map<Id, Account> oldMap, TriggerOperation operation) {
switch on operation {
when BEFORE_INSERT {
validateAccounts(newList);
}
when BEFORE_UPDATE {
handleFieldChanges(newList, oldMap);
}
when AFTER_INSERT {
createRelatedRecords(newList);
}
}
}
}
5. Escribe Tests Significativos
Los tests deben cubrir no solo el código, sino la lógica de negocio real. Siempre prueba:
- Escenarios masivos (200+ registros)
- Casos positivos (comportamiento esperado)
- Casos negativos (manejo de errores)
- Permisos de usuario (runAs)
@isTest
private class AccountServiceTest {
@TestSetup
static void setup() {
// Crear suficientes datos para probar límites
List<Account> accounts = TestDataFactory.createAccounts(200);
insert accounts;
}
@isTest
static void testBulkUpdate() {
List<Account> accounts = [SELECT Id, Name FROM Account];
Test.startTest();
AccountService.updateAccounts(accounts);
Test.stopTest();
List<Account> updatedAccounts = [SELECT Id, LastModifiedDate FROM Account];
System.assertEquals(200, updatedAccounts.size(), 'Todas las cuentas deben actualizarse');
}
}
Conclusión
Seguir estas mejores prácticas te ayudará a construir soluciones Salesforce más robustas y escalables. Recuerda: el código que funciona para un registro debe funcionar para miles.
¿Necesitas ayuda con Salesforce?

Ayudo a empresas a diseñar y construir soluciones Salesforce escalables.