Code review can be like eating vegetables. It can be boring, tedious, and sometimes the source of bitterness. Plus the entire time you're tempted to just half-ass it because "Oh I'm sure QA will catch any errors" (you do have a QA process, right?). 

But just like vegetables I think it's safe to say that code review is not only necessary but also good for you. The time required to do is greatly outweighed by the benefits:
  • Increases the Bus Factor of the code base by exposing more team members to code
  • Provides learning opportunities to both the reviewer and reviewee
  • Just the fear of public humiliation discourages developers from cutting corners
Plus there's much more, but that's not really the point here. You know that you need to do code review but how do you even start? Here's a few tips for doing code review with your team:

Choose a Format

Ok, everyone stab as many bugs as you can

Round table session:
A small team gets together and reviews the each of the commits done during the day. The author of the commit explains what's going on in the code while the rest of the teams listens. Generally I think the code should speak for itself, but we were in consulting working on different projects so a little context helped figure out what was trying to be done. However, this can be really disruptive because you basically need to scheduled out an hour or so everyday to have this meeting. Also, some people may be reluctant to speak up at a group meeting.

Gatekeeper:
A designated person (or people) are assigned the task of doing code review for the team. I only recommend this style of coding if you're using outsourced developers and you are having trouble getting them to do code review. You lose a lot of the benefits of code review this way, but it's better than nothing. Also if the gatekeeper is doing any coding then who, uh, gatekeeps(?) the gatekeeper?

Individual Commit Review:
At least two other members of the team individually reviews your code . This allows people to do code review on their own time. However, going through EACH commit can be a little tedious. 

Pull Request Review:
Your pull request is reviewed by at least two individuals. This one is my favorite, as it allows for something cohesive be reviewed as opposed to just chunks of code. However, don't let too much time pass between pull requests or you're going to have MASSIVE amounts of code to review, allowing for great opportunities of things to slip through unnoticed.

Lay Some Ground Rules

The first rule of code review is no fighting.
Fight club is Tuesday after the requirements gathering meeting.
Now that you've chosen a format, make sure to lay some ground rules to help keep code review focused on what's important. Here are a few suggestions:

DO NOT make any personal comments about the author of the code
Nothing constructive comes out of comments like, "This is so dumb, why would you do it like that?" Instead, ask constructive questions like, "Can you restructure this to use X?" This opens the door to a discussion instead of an argument, allowing the author to explain themselves or see the error.

DO NOT take any criticism personally
On the flip side, don't immediately get defensive when someone criticizes your code. The team doesn't don't (or at least shouldn't) care who wrote the code; they just want it to be done well. Take criticism as an opportunity to become a better programmer.

There must be reasoning behind your criticism
Vague complaints like "I don't like it" or "It doesn't feel right" are not valid criticisms. This is not to say you should just stay quiet if something seems awry, but you need to give some direction as to what needs to be done. I can't do anything with just "I don't like it." Also, saying something is not "best practice" is not enough; you need to say why isn't it a best practice. In fact, check out this delightfully cute book on fallacious arguments to help you figure out what's a good argument.

Decide what's in the scope of code review
This is a little more subjective, but your team will need to agree upon what's subject to code review or you may find yourself squabbling over trivial issues. Do you care about whitespace? (you should) Do all web pages need to be mobile friendly? These things come down to establishing coding standards (a whole different discussion), that serve as a (flexible) baseline as to what will be discussed. 

Code Review Checklist

This is definitely not all-inclusive, but it's generally what I look for while doing code reviews (for Salesforce projects)

Triggers
  • Is the trigger bulk safe?
  • Is it safe from recursion?
  • Has the been logic abstracted out of the trigger?
  • Does the trigger use the correct trigger context?
    • e.g. Are updates to non-trigger objects done in the after context?
  • Have all of the trigger contexts been tested?
  • Can the trigger be done with a workflow instead?
Visualforce
  • Have repetitive layouts been abstracted out into a component?
  • Has Javascript/CSS been moved to a public static resource to take advantage of caching?
    • There are exceptions to this such as needing to use merge fields
  • Does the CSS use !important (they shouldn't)
  • Are custom labels used as opposed to hard coded text? (Especially important for multi-language orgs)
Governor Limits
  • Are all SOQL calls outside of loops?
    • Remember to check if methods that contain SOQL are not called in loops
  • Are all future calls outside of loops?
  • Are all callouts outside of loops?
  • Are all DML calls outside of loops?
Tests
  • Do the tests have valid assertions?
  • Have the creation of test records been abstracted out?
  • Are all public methods tested?
Miscellaneous
  • Are all comments necessary and valid?
  • Is the code as modular as possible?
    • If you have to scroll to see what a method does, it's probably too long
  • Have inputs been sanitized as necessary?
    • String.escapeSingleQuotes is your friend!
  • Is everything only as visible as needed?
    • I default to private before making something public
And for our Managed Package friends
  • Are any namespaced items called by a string?
    • Salesforce will change record.MyField__c to record.myNamespace__MyField__c, but it will not be able to pick up record.put('MyField__c', aValue) and you'll get an exception at run time.
  • Are you sure you want that method/class to be global?
  • Are new fields the correct data type?
    • This is always important, but in a Managed Package it's forever.
Again, this is not all inclusive and it should be treated as a guideline, not as a strict rule. There are exceptions to everything and sometimes you have to skip things and take the technical debt in order to get something out in time. 

So get out there and start that code review! Because while it's not always fun to eat your veggies, you'll wished you did when things blowing up and you are all backed up.