Home » Blog » High Quality Apex: Code Review #2

High Quality Apex: Code Review #2

Let’s continue our look at the sample code in the Apex Quick Start. I’m sure Salesforce would never claim this to be production-quality code, but these snippets are what many developers use as a model for writing their own code. Why not demonstrate really great code? Let’s consider a few ways it could be improved… My comments are the shaded green text, inserted directly below the line they refer to.

Apex Developer Guide: Apex Quick Start: Primitive Data Types

First, here’s the code without comments:
Object obj = new MyApexClass();
// Cast the object to the MyApexClass custom type.
MyApexClass mc = (MyApexClass)obj;
// Access a method on the user-defined class.
mc.someClassMethod();
trigger t on Solution (before insert) { 
  Trigger.new[0].SolutionNote ='hello'; 
}
Integer x, sign;
// Your code
if (x <= 0) if (x == 0) sign = 0; else sign = -1;
global class CustomIterable 
   implements Iterator{ 

   List accs {get; set;} 
   Integer i {get; set;} 

   public CustomIterable(){ 
       accs = 
       [SELECT Id, Name, 
       NumberOfEmployees 
       FROM Account 
       WHERE Name = 'false']; 
       i = 0; 
   }   

   global boolean hasNext(){ 
       if(i >= accs.size()) {
           return false; 
       } else {
           return true; 
       }
   }    

   global Account next(){ 
       // 8 is an arbitrary 
       // constant in this example
       // that represents the 
       // maximum size of the list.
       if(i == 8){return null;} 
       i++; 
       return accs[i-1]; 
   } 
}
global class batchClass implements Database.batchable{ 
   global Iterable start(Database.batchableContext info){ 
       return new example(); 
   }     
   global void execute(Database.batchableContext info, List scope){ 
       List accsToUpdate = new List(); 
       for(Account a : scope){ 
           a.Name = 'true'; 
           a.NumberOfEmployees = 69; 
           accsToUpdate.add(a); 
       } 
       update accsToUpdate; 
   }     
   global void finish(Database.batchableContext info){     
   } 
}

And now, with review notes:

Object obj = new MyApexClass();
// For readability, variable names should be descriptive; it's a little
// tough in this case because the class name (MyApexClass) is also not
// descriptive

   // Cast the object to the MyApexClass custom type.
// Avoid adding comments that have no value, such as this one
   MyApexClass mc = (MyApexClass)obj;
// Casting without checking the actual type is dangerous. If "obj" is
// not of type "MyApexClass", an exception will be thrown. This should
// probably be wrapped in a try/catch block, or "instanceof" should be
// used to check the type

   // Access a method on the user-defined class.
// This comment also doesn't add enough value to justify its existence
   mc.someClassMethod();
// This should be changed to a more descriptive method name (in the
// declaration as well)
trigger t on Solution (before insert) {
// For readability, the trigger name should be more descriptive.
// "Solutions" or "SolutionsTrigger"?
// Handle all events in each trigger. This will be a subtle reminder
// later - to use this trigger, instead of creating a new one on the
// same object. It also forces you to check the event type when
// executing the business logic below, which ads clarity
            Trigger.new[0].SolutionNote ='hello';
// We believe this will always work, but indexing an array without
// first checking the size is a bad habit

// Constant should be moved to a custom label, or at least to the top
// of the file

// This is not bulkified. If 2 or more records are being inserted, only
// the first one will be handled here

// White space is inconsistent. A 12 space indent is probably not your
// team's standard, and putting a space both before and after the "="
// is easier to read

// Finally, all business logic should be put in an Apex class. This
// can't be tested without inserting records, which is not ideal
}
   Integer x, sign;
// "X" may need to be more descriptive, depending on the context
   // Your code
   if (x <= 0) if (x == 0) sign = 0; else sign = -1;
// Placing the constant first (0 == x) is recommended so that typos
// (0 = x) will be caught by the compiler

// Best practice is to include curly brackets around all block
// statements. (e.g. if (condition) { statements })

// This doesn't cover all cases; what is "sign" when x > 0?
// Another clearer option would be a ternary operator
global class CustomIterable 
// "Global" is often used in Salesforce example code, when usually,
// public is a better option. A good practice is to make code only as
// accessible as it needs to be. No more
   implements Iterator{ 
// A class comment would help the reader understand the purpose and
// responsibilities of this class

   List accs {get; set;} 
// "accounts" would be a clearer property name
// This syntax really doesn't buy us anything. "List accounts;" would
// be a bit simpler to follow
   Integer i {get; set;} 

   public CustomIterable(){ 
// Should the "next" and "hasNext" methods really be globally
// accessible, but not the constructor? Probably not. My preference
// would be to make the class and methods all "public"
       accs = 
       [SELECT Id, Name, 
       NumberOfEmployees 
       FROM Account 
       WHERE Name = 'false']; 
       i = 0;
// Consider making this more selective, adding a limit, or at least
// commenting on why there will never be too many matching records 

// Consider putting the SOQL query all on one line. This is easy to
// read and requires less vertical scrolling

// There is never a need to query the Id of a record. It's included
// implicitly
   }   

   global boolean hasNext(){ 
       if(i >= accs.size()) {
// Use consistent whitespace
           return false; 
       } else {
           return true; 
       }
// Consider simplifying to "return (i < accs.size());"
   }    

   global Account next(){ 
       // 8 is an arbitrary 
       // constant in this example
       // that represents the 
       // maximum size of the list.
       if(i == 8){return null;}
// Why use an arbitrary constant and the 4-line comment above? The
// correct code is "if (!hasNext())"

// Put the constant first in comparisons to avoid a bug due to a type
// (e.g. "8 == i")

// Put each statement on a new line to improve readability, debugging,
// etc.
       i++; 
       return accs[i-1]; 
   }
// Better: public Account next() { return hasNext() ? accs[i++] : null; }
}

// A class comment would add to maintainability
global class batchClass implements Database.batchable{
// "Global" is often used in Salesforce example code, when usually,
// public is a better option. A good practice is to make code only as
// accessible as it needs to be. No more

// Use consistent capitalization. Salesforce's usual pattern is to
// capitalize classes and interfaces (i.e. "BatchClass, Batchable")

// Consider using a more descriptive class name (i.e.
// "AccountUpdateBatch")
   global Iterable start(Database.batchableContext info){
// A more meaningful parameter name might be "context"
       return new example(); 
   }
// Whitespace - a blank line between methods is common practice and adds to readability
   global void execute(Database.batchableContext info, List scope){
// Capitalize names of all classes (i.e. "BatchableContext")
       List accsToUpdate = new List();
// "accountsToUpdate" is easier to read and remember
       for(Account a : scope){ 
           a.Name = 'true'; 
           a.NumberOfEmployees = 69;
// Consider moving constants to custom labels, custom settings, custom metadata, or top-of-file constants 
// The code isn't clear on this point, but make sure we're not updating records that aren't changing (i.e. Name is already 'true' and NumberOfEmployees is already 69)
           accsToUpdate.add(a); 
       } 
       update accsToUpdate;
// Error handling should be discussed here. Is this the handling desired by the client?
   }     
   global void finish(Database.batchableContext info){     
   } 
}

Primary Takeaways

In any code review, there will a range of comment severities, from “trivial” to “showstopper”. Severities will depend somewhat on the specifics of your project and priorities. To me, these are the most important ways the code above could be improved:

  1. The trigger has several issues. First, it should be bulkified. Second, the business logic should be moved to Apex class. Finally, other improvements described above should be considered.
  2. Error handling should be considered in the type casting example as well as the batch process that updates accounts.
  3. Lots of small, low-risk changes could be made to improve readability and maintainability. Software developers are responsible for much more than just the moment in time in which they write a piece of code. Writing great code includes making it easy to read, understand, debug, and enhance.

Again, just to be clear, the severity of these issues and whether or not you address them depends entirely on the quality goals of your project, the life expectancy of the code, and your team’s standards. In my opinion, Salesforce needs to up its “sample code” game and provide much more responsible examples of how to write world-class code.

Thanks for following along! Writing high-quality code is important to your customers, to your company, and to yourself. Please leave comments and suggestions, or let us know if you’ve got some code you’d like us to review!