Ce diaporama a bien été signalé.
Le téléchargement de votre SlideShare est en cours. ×

Error handling

Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Publicité
Prochain SlideShare
Redefining the unit
Redefining the unit
Chargement dans…3
×

Consultez-les par la suite

1 sur 77 Publicité

Error handling

Télécharger pour lire hors ligne

Handling error conditions is a core part of the software we write. However, we often treat it as a second class citizen, obscuring our intent through abuse of null values and exceptions that make our code hard to understand and maintain. In the functional programming community, it is common to use datatypes such as Option, Either or Validated to make our intentions explicit when dealing with errors. We can leverage the compiler to verify that we are handling them instead of hoping for the best at runtime. This results in code that is clearer, without hidden path flows. We’ll show how we have been doing this in Kotlin, with the help of the Arrow library.

Handling error conditions is a core part of the software we write. However, we often treat it as a second class citizen, obscuring our intent through abuse of null values and exceptions that make our code hard to understand and maintain. In the functional programming community, it is common to use datatypes such as Option, Either or Validated to make our intentions explicit when dealing with errors. We can leverage the compiler to verify that we are handling them instead of hoping for the best at runtime. This results in code that is clearer, without hidden path flows. We’ll show how we have been doing this in Kotlin, with the help of the Arrow library.

Publicité
Publicité

Plus De Contenu Connexe

Similaire à Error handling (20)

Plus par Thoughtworks (20)

Publicité

Plus récents (20)

Error handling

  1. 1. Moving away from null and exceptions: An alternative way of error handling Mario Fernandez Andrei Bechet © 2020 ThoughtWorks
  2. 2. What to expect © 2020 ThoughtWorks
  3. 3. The two takeaways © 2020 ThoughtWorks
  4. 4. Let’s stop using null © 2020 ThoughtWorks
  5. 5. Let’s stop abusing exceptions © 2020 ThoughtWorks
  6. 6. Some Context © 2020 ThoughtWorks
  7. 7. JS API API DbLegacy Backend Big German Corp
  8. 8. Null © 2020 ThoughtWorks
  9. 9. Did you have a NullPointerException lately in a production system? © 2020 ThoughtWorks
  10. 10. © 2020 ThoughtWorks
  11. 11. public static boolean isAdmin(List<Scope> scopes) { if(scopes == null) { return false; } Scope adminScope = findAdminScope(scopes); if(adminScope == null) { return false; } return adminScope.isValid(); }
  12. 12. Ad-hoc Error Handling and it pollutes © 2020 ThoughtWorks
  13. 13. Null values sidestep the type system © 2020 ThoughtWorks
  14. 14. Runtime instead of compile time feedback © 2020 ThoughtWorks
  15. 15. Nullable Types © 2020 ThoughtWorks
  16. 16. Authorization: Bearer bGciOi...JIUzI1NiIs fun String.extractToken(): String? = if (startsWith("Bearer")) split(" ").last() else null
  17. 17. header.extractToken() ?.let { token -> doStuff(token) }
  18. 18. What about more complex flows © 2020 ThoughtWorks
  19. 19. Get Authorization header Extract Token Verify Token Set Security Context Null?
  20. 20. request.getHeader(Headers.AUTHORIZATION) ?.let { header -> header.extractToken() ?.let { jwt -> verifier.verify(jwt) ?.let { token -> SecurityContextHolder.getContext().authentication = token } } }
  21. 21. Data Types A digression about Functional Programming © 2020 ThoughtWorks
  22. 22. A data type is an abstraction that encapsulates one reusable coding pattern © 2020 ThoughtWorks
  23. 23. Think of containers holding your data © 2020 ThoughtWorks
  24. 24. Provide clear semantics and an interface to manipulate the data © 2020 ThoughtWorks
  25. 25. Option Either Validated IO Examples © 2020 ThoughtWorks
  26. 26. arrow-kt.io/docs/0.10/datatypes/intro/ © 2020 ThoughtWorks
  27. 27. Option © 2020 ThoughtWorks
  28. 28. A value that might be absent © 2020 ThoughtWorks
  29. 29. Option<T> NoneSome<T> T
  30. 30. sealed class Option<out T> { data class Some<out T>(val a: T): Option<T>() object None: Option<Nothing>() }
  31. 31. How to use it? © 2020 ThoughtWorks
  32. 32. fun String.extractToken(): Option<String> = startsWith("Bearer ") .maybe { split(" ").last() }
  33. 33. when (val token = header.extractToken()) { is Option.None -> ResponseEntity.status(401).build() is Option.Some -> ResponseEntity.ok(result.t) }
  34. 34. A container is not just a holder of data © 2020 ThoughtWorks
  35. 35. interface Operations { fun <A, B> Option<A>.map(f: (A) -> B): Option<B> fun <A, B> Option<A>.flatMap(f: (A) -> Option<B>): Option<B> }
  36. 36. Exceptions The hidden GOTO © 2020 ThoughtWorks
  37. 37. Did you get a 500 lately in a production system? © 2020 ThoughtWorks
  38. 38. … pretty common to throw © 2020 ThoughtWorks
  39. 39. © 2020 ThoughtWorks
  40. 40. At first Exceptions may sound like a good idea © 2020 ThoughtWorks
  41. 41. … but they easily lead to bad decisions © 2020 ThoughtWorks
  42. 42. Why © 2020 ThoughtWorks
  43. 43. Easy to ignore or miss © 2020 ThoughtWorks
  44. 44. Exceptions oftenly end up being used as flow control © 2020 ThoughtWorks
  45. 45. Breaks encapsulation © 2020 ThoughtWorks
  46. 46. © 2020 ThoughtWorks Controller API clientService layer Happy path Error path Happy path
  47. 47. interface Verifier { /** * @param jwt a jwt token * @return authentication credentials */ fun verify(jwt: String): TokenAuthentication } © 2020 ThoughtWorks
  48. 48. /** * Perform the verification against the given Token * * @param token to verify. * @return a verified and decoded JWT. * @throws AlgorithmMismatchException * @throws SignatureVerificationException * @throws TokenExpiredException * @throws InvalidClaimException */ public DecodedJWT verifyByCallingExternalApi(String token);
  49. 49. A typical way of handling this © 2020 ThoughtWorks
  50. 50. @ExceptionHandler(JWTVerificationException::class) fun handleException(exception: JWTVerificationException): ResponseEntity<ErrorMessage> { return ResponseEntity .status(HttpStatus.BAD_GATEWAY) .body(ErrorMessage.fromException(exception)) }
  51. 51. What is an Exception? © 2020 ThoughtWorks
  52. 52. A cluster going down 🔥 is an exception © 2020 ThoughtWorks
  53. 53. Not really an exception © 2020 ThoughtWorks
  54. 54. As the context grows it becomes harder to test and reason about © 2020 ThoughtWorks
  55. 55. Either © 2020 ThoughtWorks
  56. 56. Two different values depending on the result of the computation © 2020 ThoughtWorks
  57. 57. sealed class Either<out L, out R> { data class Left<out L, out R>(val a: L) : Either<L, R>() data class Right<out L, out R>(val b: R) : Either<L, R>() }
  58. 58. How to use it? © 2020 ThoughtWorks
  59. 59. interface Verifier { fun verify(token: String): Either<TokenEx, TokenAuth> }
  60. 60. fun Verifier.unsafeVerify(jwt: String): Either<TokenEx, TokenAuth> = try { verifyByCallingExternalApi(jwt).right() } catch (e: JWTVerificationException) { e.left() }
  61. 61. An evolving computation © 2020 ThoughtWorks
  62. 62. interface Operations { fun <T, A, B> Either<T, A>.map(f: (A) -> B): Either<T, B> fun <T, A, B> Either<T, A>.flatMap(f: (A) -> Either<T, B>): Either<T, B> }
  63. 63. verifier .unsafeVerify(jwt) .map { it.asToken() }
  64. 64. What about more complex flows © 2020 ThoughtWorks
  65. 65. Get Authorization header Extract Token Verify Token Set Security Context Null?
  66. 66. request.getHeader(Headers.AUTHORIZATION) .toEither() .flatMap { header -> header.extractToken() .flatMap { jwt -> verifier .verify(jwt) .map { token -> SecurityContextHolder.getContext().authentication = token } } }
  67. 67. Non-Nested Syntax © 2020 ThoughtWorks
  68. 68. Similar to async/await © 2020 ThoughtWorks
  69. 69. Either.fx { val (header) = request.getHeader(Headers.AUTHORIZATION).toEither() val (jwt) = header.extractToken() val (token) = verifier.verify(jwt) SecurityContextHolder.getContext().authentication = token }
  70. 70. thoughtworks.com/insights/blog/either-data-type-alternative-throwing-exceptions © 2020 ThoughtWorks
  71. 71. Summary © 2020 ThoughtWorks
  72. 72. Null and exceptions can lead to flaky, hard to understand code © 2020 ThoughtWorks
  73. 73. Let’s stop using null © 2020 ThoughtWorks
  74. 74. Let’s stop abusing exceptions © 2020 ThoughtWorks
  75. 75. Continue the conversation on Slack © 2020 ThoughtWorks XConfEurope2020 xconfeurope2020.slack.com #talk4-alternative-to-null-and-exceptions #XConfOnline

×