null Usage is Application/Language Dependent
Ultimately the choice of whether or not to use
null as a valid application value is largely determined by your application and programming language/interface/edge.
At a fundamental level, I'd recommend trying to use distinct types if there are distinct classes of values.
null may be an option if your interface allows it and there are only two classes of a property you're trying to represent. Omitting a property may be an option if your interface or format allows it. A new aggregate type (class, object, message type) may be another option.
For your string example, if this is in the programming language, I'd ask myself a couple questions.
- Do I plan on adding future types of values? If so, an
Option will probably be better for your interface design.
- When do I need to validate consumers calls? Statically? Dynamically? Before? After? At all? If your programming language supports it, use the benefits of static typing as it avoids the amount of code you have to create for validation.
Option probably fills this case best if your string was non-nullable. However, you're probably going to have to check user input for a
null string value anyways though, so I'd probably defer back to the first line of questioning: how many types of values do/will I want to represent.
null indicative of a programmer error in my programming language? Unfortunately,
null is often the default value for uninitialized (or non-explicitly initialized) pointers or references in some languages. Is
null as a value acceptable as the default value? Is it safe as a default value? Sometimes
null is indicative of deallocated values. Should I provide consumers of my interface with an indication of these potential memory management or initialization problems in their program? What's the failure mode of such a call in the face of such problems? Is the caller in the same process or thread as mine so that such errors are a high risk to my application?
Depending on your answers to these questions, you'll probably be able to hone in on whether or not
null is right for your interface.
- Your application is safety critical
- You're using some type of heap initialization on startup and
null is a possible string value passed back upon failure to allocate space for a string.
- There's a a potential such a string hits your interface
null is probably not appropriate
null in this case is actually used to indicate two different types of values. The first may be a default value which the user of your interface may want to set. Unfortunately, the second value is a flag to indicate that your system is not functioning correctly. In such cases, you probably want to fail as safely as possible (whatever that means for your system).
- You're using a C struct which has a
char * member.
- Your system does not use heap allocation and you're using MISRA checking.
- Your interface accepts this struct as a pointer and checks to make sure that struct does not point to
- The default and safe value of the
char * member for your API can be indicated by a single value of
- Upon your user's struct initialization, you would like to provide the user the possibility of not explicitly initializing the
char * member.
NULL may be appropriate
Rationale: There is a little chance that your struct passes the
NULL check but is uninitialized. However, your API may not be able to account for this unless you have some sort of checksum on the struct value and/or range checking of the address of the struct. MISRA-C linters may help users of your API by flagging usage of structs before their initialization. However, as for the
char * member, if the pointer to struct points to an initialized struct,
NULL is the default value of an unspecified member in a struct initializer. Therefore,
NULL may serve as a safe, default value for the
char * struct member in your application.
If it's on a serialization interface, I'd ask myself the following questions about whether or not to use null on a string.
null to the native
null type. If this is the case, this begs the discussion of whether or not native
null usage is okay for your particular language, parser and serializer combination.
- Does explicit absence of a property value impact more than a single property value? Sometimes a
null is actually indicating that you have a new message type entirely. It may be cleaner for your consumers of the serialization format to just specify a completely different message type. This ensures that their validation and application logic can have clean separation between the two distinctions of messages that your web interface provides.
null can not be a value on an edge or interface that does not support it. If you're using something that's extremely loose in nature on the typing of values of properties (i.e. JSON), try to push some form of schema or validation on the consumers edge software (e.g. JSON Schema) if you can. If it's a programming language API, validate the user input statically if possible (via typing) or as loud as is sensible in runtime (a.k.a. practice defensive programming on consumer facing interfaces). As importantly, document or define the edge so there's no question as to:
- What type(s) of value a given property accepts
- What ranges of value are valid for a given property.
- How an aggregate type should be structured. What properties must/should/can be present in an aggregate type?
- If it's some type of container, how many items can or should the container hold, and what are the types of values the container holds?
- What order, if any, are properties or instances of a container or aggregates type returned?
- What side-effects are there of setting particular values and what are the side effects of reading those values?