Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Favorite (Clever) Defensive Programming Best Practices [closed]

People also ask

What is meant by defensive programming?

Defensive programming is a form of defensive design intended to ensure the continuing function of a piece of software under unforeseen circumstances. Defensive programming practices are often used where high availability, safety, or security is needed.

What is defensive programming example?

Defensive programming is the practice of writing software to enable continuous operation after and while experiencing unplanned issues. A simple example is checking for NULL after calling malloc() , and ensuring that the program gracefully handles the case.

Why is defensive programming essential?

Defensive programming can be tough to write source code, but it results in high-quality foolproof code. Without Defensive programming, your code will still run normally. However, it can easily break or give incorrect output depending on the condition or user input.

What are the key features of defensive coding?

The authors recommend developers follow these five defensive programing techniques: design by contract, respect that dead programs tell no lies, implement assertive programming, learn how to balance resources and don't outrun your headlights.


In c++, I once liked redefining new so that it provided some extra memory to catch fence-post errors.

Currently, I prefer avoiding defensive programming in favor of Test Driven Development. If you catch errors quickly and externally, you don't need to muddy-up your code with defensive maneuvers, your code is DRY-er and you wind-up with fewer errors that you have to defend against.

As WikiKnowledge Wrote:

Avoid Defensive Programming, Fail Fast Instead.

By defensive programming I mean the habit of writing code that attempts to compensate for some failure in the data, of writing code that assumes that callers might provide data that doesn't conform to the contract between caller and subroutine and that the subroutine must somehow cope with it.


SQL

When I have to delete data, I write

select *    
--delete    
From mytable    
Where ...

When I run it, I will know if I forgot or botched the where clause. I have a safety. If everything is fine, I highlight everything after the '--' comment tokens, and run it.

Edit: if I'm deleting a lot of data, I will use count(*) instead of just *


Allocate a reasonable chunk of memory when the application starts - I think Steve McConnell referred to this as a memory parachute in Code Complete.

This can be used in case something serious goes wrong and you are required to terminate.

Allocating this memory up-front provides you with a safety-net, as you can free it up and then use the available memory to do the following:

  • Save all the persistent data
  • Close all the appropriate files
  • Write error messages to a log file
  • Present a meaningful error to the user

In every switch statement that doesn't have a default case, I add a case that aborts the program with an error message.

#define INVALID_SWITCH_VALUE 0

switch (x) {
case 1:
  // ...
  break;
case 2:
  // ...
  break;
case 3:
  // ...
  break;
default:
  assert(INVALID_SWITCH_VALUE);
}

When you're handling the various states of an enum (C#):

enum AccountType
{
    Savings,
    Checking,
    MoneyMarket
}

Then, inside some routine...

switch (accountType)
{
    case AccountType.Checking:
        // do something

    case AccountType.Savings:
        // do something else

    case AccountType.MoneyMarket:
        // do some other thing

    default:
-->     Debug.Fail("Invalid account type.");
}

At some point I'll add another account type to this enum. And when I do, I'll forget to fix this switch statement. So the Debug.Fail crashes horribly (in Debug mode) to draw my attention to this fact. When I add the case AccountType.MyNewAccountType:, the horrible crash stops...until I add yet another account type and forget to update the cases here.

(Yes, polymorphism is probably better here, but this is just an example off the top of my head.)


When printing out error messages with a string (particularly one which depends on user input), I always use single quotes ''. For example:

FILE *fp = fopen(filename, "r");
if(fp == NULL) {
    fprintf(stderr, "ERROR: Could not open file %s\n", filename);
    return false;
}

This lack of quotes around %s is really bad, because say filename is an empty string or just whitespace or something. The message printed out would of course be:

ERROR: Could not open file

So, always better to do:

fprintf(stderr, "ERROR: Could not open file '%s'\n", filename);

Then at least the user sees this:

ERROR: Could not open file ''

I find that this makes a huge difference in terms of the quality of the bug reports submitted by end users. If there is a funny-looking error message like this instead of something generic sounding, then they're much more likely to copy/paste it instead of just writing "it wouldn't open my files".


SQL Safety

Before writing any SQL that will modify the data, I wrap the whole thing in a rolled back transaction:

BEGIN TRANSACTION
-- LOTS OF SCARY SQL HERE LIKE
-- DELETE FROM ORDER INNER JOIN SUBSCRIBER ON ORDER.SUBSCRIBER_ID = SUBSCRIBER.ID
ROLLBACK TRANSACTION

This prevents you from executing a bad delete/update permanently. And, you can execute the whole thing and verify reasonable record counts or add SELECT statements between your SQL and the ROLLBACK TRANSACTION to make sure everything looks right.

When you're completely sure it does what you expected, change the ROLLBACK to COMMIT and run for real.