Post History
The core idea in that design is that the program shouldn't have error handling all over the place, and it should also not make important state decisions all over the place. Those two things should ...
#1: Initial revision
The core idea in that design is that the program shouldn't have error handling all over the place, and it should also not make important state decisions all over the place. Those two things should both be centralized somewhere and they are often related. How to design the internals is highly project-specific. In the linked example I had moved the decision maker outside the error handler, but it could as well be integrated into one function. All of it depends on how complex the application is, how many states and errors there are etc. An error handler typically needs to do 3 things: - **Evaluate errors reported.** Is this error critical or something that can be ignored, or maybe somewhere in between? If the error handler is the only code "weighing" the errors, then we do not need to rewrite large amounts of code when regarding one kind of error as more/less serious than previously. - **Take action accordingly.** Is there something that needs to be stopped or aborted? Should the program revert to a safe state? Are there functions for that, which resets variables etc? Or in some cases the proper action might be the reset and reboot the MCU itself. Since the error handler sits at the top of the application tiers, it will have access to most drivers and modules etc. - **Report the error somewhere.** Maybe post it on some bus or display, maybe log it in memory, maybe lit some LED etc etc. In a system where you also have a state machine of some sort, you can integrate change of state with the actions that might be taken. It might be wise/necessary to also let the error handler know where the error is coming from, especially when the same kind of error can occur in many different places. --- For example if you have a safe state, then the only place where you can revert from it is inside the error handler. Then there is only one place in the code where errors are evaluated, and only one place where the state changes happen. Take the alternative design: if there are 10+ places where errors are reported and state changes happen in those 10+ places, then maintaining the code becomes burdensome. If we decide that "error x" isn't critical, we would have to track down all those places reporting "error x" and rewrite how the program reacts to that error. Having error handling all over the place also tends to clutter down the code significantly and distract a lot from what the code is actually doing when it functions as it should. Not to mention the most serious problem with that design: the program changes states and execution path from all over the place - aka spaghetti/"stateghetti" programming. Similarly, it is also spaghetti programming to have a big collection of various global status flags that are changed from all over the place, and which the program acts upon all over the place. It becomes very hard to follow the program flow and as complexity grows, the program flow can even start to take unpredicted ways. One metric sometimes used when speaking of bad code is "cyclomatic complexity", which is basically the number of paths any given code can take given certain circumstances. The more it increases, the harder the program flow becomes to follow, let alone to prove that all possible execution paths are actually taken, or that all use-cases that the program should cover are actually covered. Complexity always leads to more bugs too. If we can instead split up decision-making, program flow, error handling and the actual algorithms in different places, everything becomes so much maintainable than if all of that is merged together in one big unmaintainable blob. One sign of code smell is where every driver ends up with some function named "handler", which... handles stuff... often in vague ways. Could be error handling, could be error reporting, could be state changes, could be calls to unrelated parts of the program. When the programmer who wrote the code can't easily summarize what a certain function does, then that's a bad sign. That function should most likely be removed and replaced with centralized code.
