Update Blog “2020-12-09-guide-on-error-handling”
This commit is contained in:
parent
087d7e8d69
commit
fec377d8b2
@ -7,7 +7,7 @@ tags:
|
|||||||
- Development
|
- Development
|
||||||
- Guide
|
- Guide
|
||||||
---
|
---
|
||||||
Having a good _error handling_ practice is very important when building complex applications.
|
Having good _error handling_ practice is very important when building complex applications.
|
||||||
Applications which consist of multiple applications such as web applications,
|
Applications which consist of multiple applications such as web applications,
|
||||||
where you have multiple parts like _client_, _server_ and _database_ create a system where
|
where you have multiple parts like _client_, _server_ and _database_ create a system where
|
||||||
errors can happen in any individual parts or in communication between them.
|
errors can happen in any individual parts or in communication between them.
|
||||||
@ -18,7 +18,7 @@ Let me show you some examples of what kind of errors there might happen and how
|
|||||||
|
|
||||||
Don't pay attention to the coding style here.
|
Don't pay attention to the coding style here.
|
||||||
There might be a different kind of libraries and conventions used in applications.
|
There might be a different kind of libraries and conventions used in applications.
|
||||||
Code samples are written just for demonstration of the various scenarios and what should happen in those scenarios.
|
Code samples are written just for the demonstration of the various scenarios and what should happen in those scenarios.
|
||||||
Please read through the comments as they explain how errors should be treated.
|
Please read through the comments as they explain how errors should be treated.
|
||||||
|
|
||||||
### Back-end application - Single run script or a CRON job
|
### Back-end application - Single run script or a CRON job
|
||||||
@ -40,7 +40,7 @@ It is necessary to pass the error context to the logger so the error can be trac
|
|||||||
|
|
||||||
### Back-end application - Responding to client requests
|
### Back-end application - Responding to client requests
|
||||||
|
|
||||||
This is usually how web applications are built and most common scenario where errors should be properly handled as it might be a security threat.
|
This is usually how web applications are built and the most common scenario where errors should be properly handled as it might be a security threat.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
app.post('/message', (req, res) => {
|
app.post('/message', (req, res) => {
|
||||||
@ -53,7 +53,7 @@ This is usually how web applications are built and most common scenario where er
|
|||||||
// They are caused by a bad input from the user.
|
// They are caused by a bad input from the user.
|
||||||
// There is no need to log these errors as they don't cause a system crash.
|
// There is no need to log these errors as they don't cause a system crash.
|
||||||
|
|
||||||
// Client should be informed on the matter what went wrong with the request.
|
// Client should be informed on the matter of what went wrong with the request.
|
||||||
res.status(400).json({
|
res.status(400).json({
|
||||||
message: `Invalid input. ${err.message}`
|
message: `Invalid input. ${err.message}`
|
||||||
})
|
})
|
||||||
@ -68,16 +68,16 @@ This is usually how web applications are built and most common scenario where er
|
|||||||
res.status(200).json(data)
|
res.status(200).json(data)
|
||||||
} else {
|
} else {
|
||||||
// This is an example of an **unexpected** error.
|
// This is an example of an **unexpected** error.
|
||||||
// It doesn't only apply to an `Database`. You can replace the `Database` with any other 3rd party system.
|
// It doesn't only apply to a `Database`. You can replace the `Database` with any other 3rd party system.
|
||||||
// These type of errors can be programmers fault of not handling certain scenarios correctly,
|
// These type of errors can be programmers fault for not handling certain scenarios correctly,
|
||||||
// but also be caused by an unexpected events happening in the system
|
// but also be caused by unexpected events happening in the system
|
||||||
// (ex. Database server is down, usage of wrong ID's of relations in SQL queries)
|
// (ex. Database server is down, usage of the wrong ID of relations in SQL queries)
|
||||||
|
|
||||||
// In this case we want to `log` this error with full context,
|
// In this case we want to `log` this error with full context,
|
||||||
// so it can be found and fixed if it is an programmers fault.
|
// so it can be found and fixed if it is a programmer's fault.
|
||||||
logger.error('Unexpected error happened', err)
|
logger.error('Unexpected error happened', err)
|
||||||
|
|
||||||
// Client should be informed of such an error but without the context as it might reveal proprietary information about the system
|
// Client should be informed of such an error but without the context, as it might reveal proprietary information about the system
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
message: 'Unexpected error happened'
|
message: 'Unexpected error happened'
|
||||||
})
|
})
|
||||||
@ -90,12 +90,12 @@ It is very important to **keep all proprietary information** about the system **
|
|||||||
It is a **security threat** and it might be abused by malicious hackers when any of the information gets leaked.
|
It is a **security threat** and it might be abused by malicious hackers when any of the information gets leaked.
|
||||||
It doesn't matter if the information is shown to the users or not, while it's being sent to the client it can be discovered. I
|
It doesn't matter if the information is shown to the users or not, while it's being sent to the client it can be discovered. I
|
||||||
|
|
||||||
However if there is an additional error handling functionality in place which
|
However, if there is an additional error handling functionality in place which
|
||||||
is able to filter out the error context in the `production` environment,
|
is able to filter out the error context in the `production` environment,
|
||||||
it might be a good practice to include the error context in the `development` environment,
|
it might be a good practice to include the error context in the `development` environment,
|
||||||
as it will speed up the error discoverability.
|
as it will speed up the error discoverability.
|
||||||
|
|
||||||
To implement such feature I'd recommend to split `message` types sent to the client,
|
To implement such a feature I'd recommend splitting `message` types sent to the client,
|
||||||
so there isn't a chance that someone would accidentally send proprietary information to the client.
|
so there isn't a chance that someone would accidentally send proprietary information to the client.
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
@ -114,24 +114,24 @@ so there isn't a chance that someone would accidentally send proprietary informa
|
|||||||
|
|
||||||
### Front-end application - Handle an error from back-end
|
### Front-end application - Handle an error from back-end
|
||||||
|
|
||||||
When an error happens on the back-end we should show this error on the client so the user knows that his action was not successfully fulfilled.
|
When an error happens on the back-end we should show this error to the client so the user knows that his action was not successfully fulfilled.
|
||||||
|
|
||||||
If back-end handles errors correctly and sends client an appropriate message, client should be able to just show the informative description of the error that occurred.
|
If the back-end handles errors correctly and sends the client an appropriate message, the client should be able to just show the informative description of the error that occurred.
|
||||||
|
|
||||||
#### Translating errors
|
#### Translating errors
|
||||||
|
|
||||||
If your application is built with any **internationalization** framework or with a translation system in general,
|
If your application is built with any **internationalization** framework or with a translation system in general,
|
||||||
there has to be some kind of mapping from error sent from back-end to the translated message.
|
there has to be some kind of mapping from error sent from the back-end to the translated message.
|
||||||
|
|
||||||
Simple mapping could be done with having a table of error codes for any application error that might happen.
|
Simple mapping could be done by having a table of error codes for any application error that might happen.
|
||||||
These codes don't have to be strictly numeric. They can consist of some rules like having separators to distinguish
|
These codes don't have to be strictly numeric. They can consist of some rules like having separators to distinguish
|
||||||
services or parts which fail and which way. Examples `RESOURCE-404`, `POST-404`.
|
services or parts that fail and which way. Examples `RESOURCE-404`, `POST-404`.
|
||||||
It's a good practice to have always a default message for unexpected errors which often do happen.
|
It's a good practice to have always a default message for unexpected errors which often do happen.
|
||||||
|
|
||||||
### Front-end application - Network Error
|
### Front-end application - Network Error
|
||||||
|
|
||||||
These type of errors are least expected by developers but they happen regularly.
|
These types of errors are least expected by developers but they happen regularly.
|
||||||
Users usually lose connection when they browse while traveling or are connected to unreliable mobile connection.
|
Users usually lose connection when they browse while traveling or are connected to an unreliable mobile connection.
|
||||||
It should always be anticipated that these errors can happen and the application should recover
|
It should always be anticipated that these errors can happen and the application should recover
|
||||||
from the failure and allowed to retry their action.
|
from the failure and allowed to retry their action.
|
||||||
|
|
||||||
@ -139,20 +139,20 @@ from the failure and allowed to retry their action.
|
|||||||
|
|
||||||
Errors which happen only on front-end are often a coding error.
|
Errors which happen only on front-end are often a coding error.
|
||||||
Most common examples would be not covering all possible cases of application logic or accessing properties
|
Most common examples would be not covering all possible cases of application logic or accessing properties
|
||||||
that are undefined because of unexpected shape of response data.
|
that are undefined because of the unexpected shape of response data.
|
||||||
While back-end applications can recover from their errors by restarting the process,
|
While back-end applications can recover from their errors by restarting the process,
|
||||||
it doesn't apply to client applications where these type of **errors might cause a crash**, or a **freeze** of an application.
|
it doesn't apply to client applications where these types of **errors might cause a crash** or a **freeze** of an application.
|
||||||
Some frameworks allow to **recover from these crashes** by implementing an **error boundaries**.
|
Some frameworks allow to **recover from these crashes** by implementing an **error boundaries**.
|
||||||
See [_React_ Error Boundaries for example](https://reactjs.org/docs/error-boundaries.html).
|
See [_React_ Error Boundaries for example](https://reactjs.org/docs/error-boundaries.html).
|
||||||
I can only highly recommend of using such type of recovery.
|
I can only highly recommend using such a type of recovery.
|
||||||
|
|
||||||
If you use an external error monitoring tool like [Sentry](https://sentry.io/welcome/) or [bugsnag](https://www.bugsnag.com/), don't forget to log these errors from the error boundary.
|
If you use an external error monitoring tool like [Sentry](https://sentry.io/welcome/) or [bugsnag](https://www.bugsnag.com/), don't forget to log these errors from the error boundary.
|
||||||
|
|
||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
To summarize this we can create an a list of rules:
|
To summarize this we can create a list of rules:
|
||||||
|
|
||||||
- Error messages have to be informative for client, but they **can't reveal private and proprietery information** about the system architecture
|
- Error messages have to be informative for the client, but they **can't reveal private and proprietary information** about the system architecture
|
||||||
- Errors should be logged only when they require additional action
|
- Errors should be logged only when they require additional action
|
||||||
- Errors that are recoverable should not crash the system
|
- Errors that are recoverable should not crash the system
|
||||||
- Unexpected errors should turn into expected errors and be handled properly
|
- Unexpected errors should turn into expected errors and be handled properly
|
||||||
|
Loading…
Reference in New Issue
Block a user