We use a technique that we call "assert programming" that is an easy way to test and document all those assumptions that you take as a programmer. As a programmer, if you at a certain point in the code knows that x
is greater than zero then you will probably not add the following code:
if (x <= 0) then throw new ApplicationException(<message>)
It's tedious and ugly as you know that it should never happen. Furthermore if you do add the test, there is now an exception that needs to be taken care of, and you also known that you ought to log this before throwing the exception.
If you instead added the following line of code:
FulcrumAssert.AreNotEqual(0, x);
You would now not only catch the errors; it would get logged, a standardized exception would be thrown and you would also have documented your assumption in the code.
We believe that the vast majority of all bugs in programs are about assumptions that fail. If you are wrong about such an assumption, they are often hard to find and they can easily lead to severe consequences that might not be visible until days later.
Based on this, we think that assumptions deserves a high grad of attention. Our technique uses a combination of assertions and guards that are both simple to implement and provides a coherent way of dealing with failing assumptions. It could be assumptions made by a developer about his or her own code or it could be assumptions about parameters that are passed into methods.
The basic idea is to add code that lets the application verify the assumptions in run-time.
The way we do this has a number nice properties:
- Very compact way of adding error checks - i.e. the developers don't find them a burden
- Errors are logged and handled in a standardized way
- Low performance penalty
- Is a way of documenting the code
- Invaluable in finding the source of a problem, often pinpointing the exact location
- Problems are often found earlier in the development cycle
- In the production environment, they usually interrupt the program before there are serious consequences
We have three different kind of assertions and guards; FulcrumAssert
, InternalContract
and ServiceContract
.
FulcrumAssert
Whenever you as a developer think that you are really sure about something, then you should add a FulcrumAssert
that both documents that assumption and guards against it ever being wrong. If the assertion proves to not hold, this will be logged and a FulcrumAssertionFailedException
will be thrown.
Example 1:
The developer wants to use the 5 last characters of a name and for some reason he or she is dead certain that the name contains at least 5 characters.
var position = name.Length-5;
FulcrumAssert.IsGreaterThanOrEqualTo(0, position);
var suffix = name.Substring(position, 5);
Example 2:
The developer knows that a method (according to its documentation) will never return null.
var manager = person.GetManager();
FulcrumAssert.IsNotNull(manager);
var managerName = manager.Name;
There are quite a few different assertions provided, such as FulcrumAssert.Fail()
, FulcrumAssert.IsTrue(bool condition)
, etc.
ServiceContract and InternalContract
These are assertions that you make about the incoming data to guard the logic of the methods that you write. By setting these guards at the beginning of every method, you make sure that callers that breaks the contract are stopped before they can do any harm. You can find the same kind of assertions as for FulcrumAssert
, but these are called requirements and starts with Require
.
For public API methods you use ServiceContract
. If your requirement fail it will log and throw a FulcrumServiceContractException
which should eventually result in an HTTP response with status code 400 Bad Request
. For other methods you should use InternalContract
as your guard. It will log, throw a FulcrumContractException
which should eventually result in an HTTP response with status code 500 Internal Server Error
.
Example:
The developer assumes that the person
parameter is never null.
public void Add(Person person) {
InternalContract.RequireNotNull(person, nameof(person));
...