Hardware side of things:
Fit only passive and power semiconductors initially, no active elements where possible.
Confirm your application actually matches your design schematic.
Check polarities of all electrolytic capacitors, diodes and other polarity sensitive devices for proper orientation before powering up.
Check for obvious short circuits, especially on power supply rails, clock lines and outputs.
Power up and check your power supply rails and decoupling, if all seems as expected only then power down and fit the remaining semiconductors.
Check the other possible causes of simple troubles, like clocking, don't forget the crystal decoupling caps for example, although that should already have been caught by verifying your implementation.
Check for proper operation of master reset circuitry.
Firmware side of things:
Ensure that the proper device configuration bits are set to perform the functions you require.
Disable any device peripheral or feature that you are not using, do not rely on default power up-reset conditions to perform this, as it may not always work as expected under all circumstances.
Always set your input and output directions, clear junk from output latches and initialise used registers and interrupt flags to a known state.
If using a watchdog timer, ensure your code has a way to handle it in a timely fashion, attempt to find a way to reduce the number of places within your code where the timer is reset, ideally, in only one place, because there is less chance of a runaway process or loop resetting it accidentally.
Check that your program has defined entry and exit points for all interrupts and loops, always make sure your code can return from an interrupt or loop when required to.
Also ensure that you save and restore the status of flags and essential registers used or shared within any interrupt service routine.
Watch your calls and loops for stack overflow problems, especially if using a small or mid range PIC as they have limited stack space and no management features like push/pop.
When using a processor that uses banking and paging, like a PIC for example, ensure that you handle these in a graceful way, always know where your code is executing from.
It's always helpful to flowchart any process, in all but the simplest case, that you are attempting to achieve, it makes the code implementation side easier and highlights potential problems in advance.
Break down complicated processes into smaller, easier dealt with, logical blocks, and thoroughly debug each block of code before moving on.
Document everything that you do, add notes to yourself to help you remember why you done something this way rather than that way.
Always back up your work, never have only one copy of your code, keep one copy for adding new code blocks, and a copy immediately preceding the changes, when you verify that all is working as expected, make that file the back-up.
Always take a back-up whenever you make major revisions to your code.
Store back-ups off the machine you are working on, just in case of a drive failure.
There are loads of approaches to this subject, but this is the general methodology I try to stick to, I say try, because it's much harder to do than say
rgds