Never have a dynamically derived status flag
An obscure title for a very niche topic? Possibly.
It is, however, the cause of subtle errors and inconsistencies in many systems I've had the 'pleasure' of working on in the past.
Imagine if you will a billing application. There are various values that can change over time, all of which contribute towards determining the current account status:
- Is it a test account?
- Is it in a trial period?
- Do they have a payment method on file?
- Do they have an outstanding balance?
- When was their last invoice generated?
- Do they have an active dispute on file?
- Did a previous payment attempt fail?
- Do they have a chargeback to handle?
- Do they have a credit that needs applying?
And there are many more variables that could apply.
So now the following happens:
- The billing system developers decide that the way to ascertain whether or not a payment is due is to perform logic based on the combinations and permutations of the above.
- The business intelligence/data analysis/reporting/data warehouse bods (or whoever) also perform logic to see how things stand.
- The call centre development team also perform logic to present the current situation to the call handlers.
This is convoluted logic, existing in several places, possibly written in different languages (eg Go or C# for the app and SQL for the BI tool).
Some of the possible outcomes:
- The two sets of logic are in sync, and everything works out.
- There are implementation differences (algorithm, rounding, logic flow).
- There have been changes made that don't exist in both places.
- The configuration values differ across systems.
- Systems run their updates at different times giving different answers.
- There are gaps in the permutation coverage leading to an undefined state.
So now you're left with uncertainty and possible differences that lead to customer complaints when one system says the status is one thing and another behaves as if it is something different.
Systems change state when actions occur whether that be (for example) a chargeback being input, a payment going through, an order being placed, a complaint raised, or a credit applied.
- Maintain a clear set of flags.
- Update them whenever an action occurs.
- Expose these flags to other systems.
This gives the most important thing a conglomeration of interacting systems and processes can have - predictability.
- There is a single source of truth. All systems can check those flags and get the exact same answer.
- A record always has a state. As the flags change when actions occur, they progress through known states and can never be in an indeterminate stage.
- Different systems running at different times will always see the same fixed state on the record, not one calculated on the fly.
- A known set of states to transition through gives visibility to the process and makes it amenable to documenting.
There are other benefits. And there are 'reasons' why you may be tempted to work out some kind of status based on an easy combination of known values.
No matter how easy it seems to have something derived on the fly - for instance a quick little public property with it's own internal logic, or a disinclination to add state-setting code for every action - do not be tempted.
A single source of truth, consistent across all systems, is a kind of holy grail. Avoiding dynamic status logic can help hugely.
Never have a dynamically derived status flag.