Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Defensive Programming x Fintech

Defensive Programming x Fintech

What is defensive programming? How to master this discipline. common mistakes and tips for better code

45c9a9c3c539129f03e4fd07e3aa4ccb?s=128

Merab Tato Kutalia

September 08, 2019
Tweet

Transcript

  1. Defensive Programming X FinTech

  2. Defensive Programming X FinTech Tato Kutalia, eMoney/MyCoins Android Dev fb.com/tatocaster

    github.com/tatocaster soundcloud.com/debugger-podcast
  3. Defend against the impossible, because the impossible will happen.

  4. Multiple ways to plug Example of extremely bad design

  5. Only one way to plug Example of good design

  6. None
  7. None
  8. Unexpected bug

  9. Add validation

  10. Add error handling

  11. Add logging

  12. = code that should have been there in the first

    place
  13. Suggestions

  14. Safe zones - Protect code from invalid data coming from

    “outside”
  15. Safe zones - Protect code from invalid data coming from

    “outside” - Everything outside of the boundary is dangerous
  16. Safe zones - Protect code from invalid data coming from

    “outside” - Everything outside of the boundary is dangerous - Double check for limits and bounds
  17. Safe zones - Protect code from invalid data coming from

    “outside” - Everything outside of the boundary is dangerous - Double check for limits and bounds - Do whitelists, not blacklists
  18. Handling - Defensive Programming is NOT about swallowing errors or

    hiding bugs
  19. Handling - Defensive Programming is NOT about swallowing errors or

    hiding bugs - Return an error and stop (fail fast)
  20. Handling - Defensive Programming is NOT about swallowing errors or

    hiding bugs - Return an error and stop (fail fast) - Return a neutral value
  21. Handling - Defensive Programming is NOT about swallowing errors or

    hiding bugs - Return an error and stop (fail fast) - Return a neutral value - Don’t be afraid to fail!!!
  22. Code doesn’t work as advertised - Don’t assume that a

    function call outside of code will work as advertised.
  23. Code doesn’t work as advertised - Don’t assume that a

    function call outside of code will work as advertised. - Always wrap 3rd party libraries with your own gateways.
  24. Code doesn’t work as advertised - Don’t assume that a

    function call outside of code will work as advertised. - Always wrap 3rd party libraries with your own gateways. - ALWAYS!
  25. Assertions - Use assertions to document assumptions and to highlight

    “impossible” conditions (dev env)
  26. fun register(facebookAccessToken : String) { val response = facebookSdkClient.getUserByToken(facebookAccessToken) if

    (response.email.isNullOrEmpty()) throw IllegalArgumentException("Invalid response from Facebook") // Register the user }
  27. Logging - Add logging and tracing to help explain what’s

    going on at run-time, especially if you run into a problem.
  28. Logging - Add logging and tracing to help explain what’s

    going on at run-time, especially if you run into a problem. - Use 3rd party systems to analyze logs and maintain the same level
  29. Standardize error handling - “Normal errors” vs “Expected errors”

  30. Standardize error handling - “Normal errors” vs “Expected errors” -

    Not all errors are errors
  31. authorizeUserWithUsernameAndPassword(user, pass) .subscribe({ when { it.errorCode != 0 -> {

    // API error } else -> { getUserInfo(it.accessToken) } } }, { // Any other error. Throwable })
  32. authorizeUserWithUsernameAndPassword(user, pass) .subscribe({ result: ApiResponse<LoginResponse> -> when { result.errorCode !=

    0 -> { // API error } else -> { getUserInfo(result.accessToken) } } }, { it: Throwable // Any other error. Throwable })
  33. Misc - Never ever wait forever on an external call,

    especially a remote call.
  34. Misc - Never ever wait forever on an external call,

    especially a remote call. - Forever can be a long time when something goes wrong. Use time-out/retry logic
  35. Misc - Never ever wait forever on an external call,

    especially a remote call. - Forever can be a long time when something goes wrong. Use time-out/retry logic. - When just checking null for an immutable variable, don’t use LET.
  36. // NOT RECOMMENDED fun process(str: String?) { str?.let { /*Do

    something*/ } } // Decompiled JAVA public final void process(@Nullable String str) { if (str != null) { boolean var4 = false; /*Do something*/ } }
  37. // RECOMMENDED fun process(str: String?) { if (str != null)

    // Do Something } // Decompiled JAVA public final void process(@Nullable String str) { if (str != null) /*Do something*/ }
  38. “A public method is like a child: once you've written

    it, you are going to maintain it for the rest of its life!” Stefan Priebsch
  39. Don’t go too far

  40. registerUsingEmail(EmailAddress("kutaliatato@gmail.com"))

  41. - Defensive code is code – all code has bugs

    Misc
  42. - Defensive code is code – all code has bugs

    - Understanding what conditions to check for and how much defensive coding is needed takes experience Misc
  43. Don’t reinvent the wheel

  44. (!frameworks.active) ???

  45. - Well you like doing extra work for no reason,

    congratulations! ⭐ Frameworks
  46. - Well you like doing extra work for no reason,

    congratulations! ⭐ - Well tested, trusted by thousands of developers and stable Frameworks
  47. - Well you like doing extra work for no reason,

    congratulations! ⭐ - Well tested, trusted by thousands of developers and stable - That’s what is used to call intelligent code reuse Frameworks
  48. fun doSomething(count: Int){ if (count < 1 || count >

    100) throw IllegalArgumentException("Invalid count") /* Do something */ } fun doSomethingElse(count: Int) { if (count < 1 || count > 100) throw IllegalArgumentException("Invalid count") /* Do something else */ }
  49. fun doSomething(count: Count) { /* Do something */ } fun

    doSomethingElse(count: Count) { /* Do something else */ } class Count(var value: Int) { init { if (value < 1 || value > 100) throw IllegalArgumentException("Invalid count") } }
  50. “just to be safe” aka overly defensive code

  51. - Passing empty objects instead of crash Overly Defensive Code

  52. - Passing empty objects instead of crash - Traverse nested

    object/array to check the key Overly Defensive Code
  53. *,body,body * { font-family: DejaVu Serif, serif !important; }

  54. (this.state.stuff && this.state.stuff.things && this.state.stuff.things.meta) || {}

  55. // DON'T! @Throws(Exception::class) fun doNotSpecifyException() { doSomething() } // DO

    THIS! @Throws(NumberFormatException::class, IllegalArgumentException::class) fun doSomething() { // do something }
  56. Money in Fintech

  57. double?

  58. val result = 0.1 + 0.2 - 0.3 println("0.1 +

    0.2 - 0.3=$result") => 0.1 + 0.2 - 0.3 = 5.551115123125783E-17
  59. Roundings?

  60. - ROUND_CEILING - ROUND_DOWN - ROUND_FLOOR - ROUND_HALF_DOWN - ROUND_HALF_EVEN

    - ROUND_HALF_UP - ROUND_UP Roundings
  61. val result = 0.1 + 0.2 - 0.3 val resultRounded

    = BigDecimal(result).setScale(2, BigDecimal.ROUND_HALF_UP) println("0.1 + 0.2 - 0.3=$resultRounded") => 0.1 + 0.2 - 0.3 = 0.00
  62. - BigDecimal(String) constructor should always be preferred over BigDecimal(Double) because

    using BigDecimal(double)is unpredictable due to the inability of the double to represent 0.1 as exact 0.1. - If double must be used for initializing a BigDecimal, use BigDecimal.valueOf(double), which converts the Double value to String using Double.toString(double) method - Rounding mode should be provided while setting the scale - StripTrailingZeros chops off all the trailing zeros - toString() may use scientific notation but, toPlainString() will never return exponentiation in its result - BigDecimal
  63. Questions?