There are a number of "defensive programming" things that I have done in my code. Most of them force the programmer to do the right thing in terms of how the objects work together.
For classes that should not be instantiated into an object, I make their constructor's protected so that any such attempt will result in a compilation error.
Another thing is to disallow certain operations that don't make sense for the objects.
For example, in my Land Surveying project, I have several types of coordinates - Plane, Assumed & Geodetic. There are several types of each of those as well.
Firstly it doesn't make sense for them to be added to each other because they have different meanings, (Even though they look very similar - they all have East, North & Height ordinates) so I don't provide operators to do this. I need to figure out how to prevent assignment of them as well, preferably to produce a compile error, not some kind of runtime error.
Secondly, when dealing with absolute (world) coordinates of the same type, it still doesn't make sense to add them - a typical coordinate might be 300000.000, 7000000.000, 200.000. So I have another class called Delta, and I provide operators to add Delta's to each of the other Coord tpyes.
In surveying there are a lot things that look very similar but are actually rather different. A good example is distances. I have several types of distances - Horizontal, Vertical and Slope. Again it doesn't make sense to do operations on these, and I need to be able to use them for overloaded functions. So I create a class for each one, which is a bit of a pain because each class has only one member variable, and I have to create an object for each one rather than just using a double. I use them in functions calls like this:
CreateNewPt(ExistingPt, Bearing, ZenithAng, SlopeDist);
CreateNewPt(ExistingPt, Bearing, HorizDist, VertDist);
Bearing and ZenithAng are examples of different types of angle - horizontal & vertical planes respectively.
Further complicating things, I have a whole inheritance tree which does unit conversion for distances, angles, areas etc. Angles are converted to radians and stored and used as such, while distances are stored in metres. I structure the code so that the programmer is forced to use the units, very few of the functions allow doubles as arguments.
The Geodetic Coordinates provide the biggest nightmare, because the formulae are long and fairly complex - there are lot's of variables.This is because Geodetic Coordinates are calculated on an ellipsoidal surface. If you have lots of spare time you can read about it here:
The other thing I do is to make the documentation in the code crystal clear, so that when I go back to it 6 months later, I don't confuse myself. Obviously this is great if someone else has to read your code.
Some other more trivial and fairly obvious things I do, are to have a variable for each term in a series to avoid complication of the code for complex formulae. I put braces around single line statements in if, loops, switches etc in case I add more code later.
As well as all that, there is the ever present mentality of, "What are all the possible ways this can go wrong?", and writing code to defend against all these possibilities.
Finally, there is testing. I write code to test my code. It can be quite tricky to make sure you have tested all the boundary conditions. Sometimes I have just as much test code as I do original code. I make heavy use of Git to achieve this, I found that easier than duplicating the whole project like I used to do in the past.
There you go - all that might produce some debate, do you think?