Run prettier everywhere
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
|
||||
# Table of Contents
|
||||
|
||||
- [Error handling anti-patterns](#error-handling-anti-patterns)
|
||||
@@ -12,8 +13,7 @@
|
||||
|
||||
# Error handling anti-patterns
|
||||
|
||||
Hiding exceptions
|
||||
-----------------
|
||||
## Hiding exceptions
|
||||
|
||||
There are multiple variations of this anti-pattern:
|
||||
|
||||
@@ -40,9 +40,9 @@ def toast(bread):
|
||||
|
||||
It depends on the context but in most cases this is a bad pattern:
|
||||
|
||||
* **Debugging** those silent errors will be really difficult, because they won't show up in logs and exception reporting tool such as Sentry. Say you have an undefined variable in `Toaster.insert()`: it will raise `NameError`, which will be caught, and ignored, and you will never know about this developer error.
|
||||
* **The user experience** will randomly degrade without anybody knowing about it, including the user.
|
||||
* **Identifying** those errors will be impossible. Say `do_stuff` does an HTTP request to another service, and that service starts misbehaving. There won't be any exception, any metric that will let you identify it.
|
||||
- **Debugging** those silent errors will be really difficult, because they won't show up in logs and exception reporting tool such as Sentry. Say you have an undefined variable in `Toaster.insert()`: it will raise `NameError`, which will be caught, and ignored, and you will never know about this developer error.
|
||||
- **The user experience** will randomly degrade without anybody knowing about it, including the user.
|
||||
- **Identifying** those errors will be impossible. Say `do_stuff` does an HTTP request to another service, and that service starts misbehaving. There won't be any exception, any metric that will let you identify it.
|
||||
|
||||
An article even named this [the most diabolical Python antipattern](https://realpython.com/blog/python/the-most-diabolical-python-antipattern/).
|
||||
|
||||
@@ -84,14 +84,14 @@ __main__.ToastException: Could not toast bread
|
||||
Sometime it's tempting to think that graceful degradation is about silencing
|
||||
exception. It's not.
|
||||
|
||||
* Graceful degradation needs to happen at the **highest level** of the code, so that the user can get a very explicit error message (e.g. "we're having issues with X, please retry in a moment"). That requires knowing that there was an error, which you can't tell if you're silencing the exception.
|
||||
* You need to know when graceful degradation happens. You also need to be
|
||||
- Graceful degradation needs to happen at the **highest level** of the code, so that the user can get a very explicit error message (e.g. "we're having issues with X, please retry in a moment"). That requires knowing that there was an error, which you can't tell if you're silencing the exception.
|
||||
- You need to know when graceful degradation happens. You also need to be
|
||||
alerted if it happens too often. This requires adding monitoring (using
|
||||
something like statsd) and logging (Python's `logger.exception` automatically
|
||||
adds the exception stacktrace to the log message for instance). Silencing an
|
||||
exception won't make the error go away: all things being equal, it's better
|
||||
for something to break hard, than for an error to be silenced.
|
||||
* It is tempting to confound silencing the exception and fixing the exception. Say you're getting sporadic timeouts from a service. You might thing: let's ignore those timeouts and just do something else, like return an empty response. But this is very different from (1) actually finding the root cause for those timeouts (e.g. maybe a specific edge cases impacting certain objects) (2) doing proper graceful degradation (e.g. asking users to retry later because the request failed).
|
||||
- It is tempting to confound silencing the exception and fixing the exception. Say you're getting sporadic timeouts from a service. You might thing: let's ignore those timeouts and just do something else, like return an empty response. But this is very different from (1) actually finding the root cause for those timeouts (e.g. maybe a specific edge cases impacting certain objects) (2) doing proper graceful degradation (e.g. asking users to retry later because the request failed).
|
||||
|
||||
In other words, ask yourself: would it be a problem if every single action was failing? If you're silencing the error, how would you know it's happening for every single action?
|
||||
|
||||
@@ -171,8 +171,7 @@ def main():
|
||||
toast('brioche')
|
||||
```
|
||||
|
||||
Raising unrelated/unspecific exception
|
||||
--------------------------------------
|
||||
## Raising unrelated/unspecific exception
|
||||
|
||||
Most languages have predefined exceptions, including Python. It is important to make sure that the right exception is raised from a semantic standpoint.
|
||||
|
||||
@@ -201,7 +200,6 @@ def validate(toast):
|
||||
|
||||
`TypeError` is here perfectly meaningful, and clearly convey the context around the error.
|
||||
|
||||
|
||||
## Unconstrained defensive programming
|
||||
|
||||
While defensive programming can be a very good technique to make the code more resilient, it can seriously backfire when misused. This is a very similar anti-pattern to carelessly silencing exceptions (see about this anti-pattern in this document).
|
||||
@@ -219,13 +217,12 @@ def get_user_name(user_id):
|
||||
|
||||
While this may look like a very good example of defensive programming (we're returning `unknown` when we can't find the user), this can have terrible repercussions, very similar to the one we have when doing an unrestricted bare `try... except`:
|
||||
|
||||
* A new developer might not know about this magical convention, and assume that `get_user_name` is guaranteed to return a true user name.
|
||||
* The external service that we're getting user name from might start failing, and returning 404. We would silently return 'unknown' as a user name for all users, which could have terrible repercussions.
|
||||
- A new developer might not know about this magical convention, and assume that `get_user_name` is guaranteed to return a true user name.
|
||||
- The external service that we're getting user name from might start failing, and returning 404. We would silently return 'unknown' as a user name for all users, which could have terrible repercussions.
|
||||
|
||||
A much cleaner way is to raise an exception on 404, and let the caller decide how it wants to handle users that are not found.
|
||||
|
||||
Unnecessarily catching and re-raising exceptions
|
||||
------------------------------------------------
|
||||
## Unnecessarily catching and re-raising exceptions
|
||||
|
||||
Bad:
|
||||
|
||||
@@ -299,7 +296,7 @@ Another problem with this pattern is that you can consider it quite useless to d
|
||||
|
||||
> Error handling, and recovery are best done at the outer layers of your code base. This is known as the end-to-end principle. The end-to-end principle argues that it is easier to handle failure at the far ends of a connection than anywhere in the middle. If you have any handling inside, you still have to do the final top level check. If every layer atop must handle errors, so why bother handling them on the inside?
|
||||
|
||||
*[Write code that is easy to delete, not easy to extend](http://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to)*
|
||||
_[Write code that is easy to delete, not easy to extend](http://programmingisterrible.com/post/139222674273/write-code-that-is-easy-to-delete-not-easy-to)_
|
||||
|
||||
A better way:
|
||||
|
||||
@@ -328,4 +325,4 @@ def call_3():
|
||||
|
||||
More resources:
|
||||
|
||||
* [Hiding exceptions](https://github.com/charlax/antipatterns/blob/master/code-antipatterns.md#hiding-exceptions)) anti-pattern.
|
||||
- [Hiding exceptions](https://github.com/charlax/antipatterns/blob/master/code-antipatterns.md#hiding-exceptions)) anti-pattern.
|
||||
|
||||
Reference in New Issue
Block a user