Slide 1

Slide 1 text

Defensive Programming X FinTech

Slide 2

Slide 2 text

Defensive Programming X FinTech Tato Kutalia, eMoney/MyCoins Android Dev fb.com/tatocaster github.com/tatocaster soundcloud.com/debugger-podcast

Slide 3

Slide 3 text

Defend against the impossible, because the impossible will happen.

Slide 4

Slide 4 text

Multiple ways to plug Example of extremely bad design

Slide 5

Slide 5 text

Only one way to plug Example of good design

Slide 6

Slide 6 text

No content

Slide 7

Slide 7 text

No content

Slide 8

Slide 8 text

Unexpected bug

Slide 9

Slide 9 text

Add validation

Slide 10

Slide 10 text

Add error handling

Slide 11

Slide 11 text

Add logging

Slide 12

Slide 12 text

= code that should have been there in the first place

Slide 13

Slide 13 text

Suggestions

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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!!!

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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.

Slide 24

Slide 24 text

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!

Slide 25

Slide 25 text

Assertions - Use assertions to document assumptions and to highlight “impossible” conditions (dev env)

Slide 26

Slide 26 text

fun register(facebookAccessToken : String) { val response = facebookSdkClient.getUserByToken(facebookAccessToken) if (response.email.isNullOrEmpty()) throw IllegalArgumentException("Invalid response from Facebook") // Register the user }

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

Standardize error handling - “Normal errors” vs “Expected errors” - Not all errors are errors

Slide 31

Slide 31 text

authorizeUserWithUsernameAndPassword(user, pass) .subscribe({ when { it.errorCode != 0 -> { // API error } else -> { getUserInfo(it.accessToken) } } }, { // Any other error. Throwable })

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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.

Slide 36

Slide 36 text

// 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*/ } }

Slide 37

Slide 37 text

// RECOMMENDED fun process(str: String?) { if (str != null) // Do Something } // Decompiled JAVA public final void process(@Nullable String str) { if (str != null) /*Do something*/ }

Slide 38

Slide 38 text

“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

Slide 39

Slide 39 text

Don’t go too far

Slide 40

Slide 40 text

registerUsingEmail(EmailAddress("[email protected]"))

Slide 41

Slide 41 text

- Defensive code is code – all code has bugs Misc

Slide 42

Slide 42 text

- Defensive code is code – all code has bugs - Understanding what conditions to check for and how much defensive coding is needed takes experience Misc

Slide 43

Slide 43 text

Don’t reinvent the wheel

Slide 44

Slide 44 text

(!frameworks.active) ???

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

- 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

Slide 48

Slide 48 text

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 */ }

Slide 49

Slide 49 text

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") } }

Slide 50

Slide 50 text

“just to be safe” aka overly defensive code

Slide 51

Slide 51 text

- Passing empty objects instead of crash Overly Defensive Code

Slide 52

Slide 52 text

- Passing empty objects instead of crash - Traverse nested object/array to check the key Overly Defensive Code

Slide 53

Slide 53 text

*,body,body * { font-family: DejaVu Serif, serif !important; }

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

// DON'T! @Throws(Exception::class) fun doNotSpecifyException() { doSomething() } // DO THIS! @Throws(NumberFormatException::class, IllegalArgumentException::class) fun doSomething() { // do something }

Slide 56

Slide 56 text

Money in Fintech

Slide 57

Slide 57 text

double?

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Roundings?

Slide 60

Slide 60 text

- ROUND_CEILING - ROUND_DOWN - ROUND_FLOOR - ROUND_HALF_DOWN - ROUND_HALF_EVEN - ROUND_HALF_UP - ROUND_UP Roundings

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

- 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

Slide 63

Slide 63 text

Questions?