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.
- Is
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.
Example 1
- 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
Answer: null
is probably not appropriate
Rationale: 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).
Example 2
- 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
NULL
- The default and safe value of the
char *
member for your API can be indicated by a single value of NULL
- Upon your user's struct initialization, you would like to provide the user the possibility of not explicitly initializing the
char *
member.
Answer: 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.
- Is
null
indicative of a potential client side error? For JSON in JavaScript, this is probably a no as null
isn't necessarily used as an indication of allocation failure. In JavaScript, it is used as an explicit indication of object absence from a reference to be set problematically. However, there are non-javascript parsers and serializers out there that map JSON 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.
General Advice
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?
if( value === null) { use parent value; }
However, if you set the child value, even to an empty string (e.g. override the default parent value with blank), then how do you "re-inherit" the value? To me, setting it as null would mean "unset this value so we know to use the parent value."null
(it is true thatnull
is avoided as such), the questioner means "Return non-null" [Object] (i.e: empty String, empty Array, etc) when they write "avoid".