Slide 1

Slide 1 text

22.03.23 Fixing the Billion Dollar Mistake (Java Lens) Richard Gross (he/him) IT-Tailor Archaeologist Auditor richargh.de/ speakerdeck.com/richargh @arghrich

Slide 2

Slide 2 text

Slides by richargh.de and 5 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in Java

Slide 3

Slide 3 text

Slides by richargh.de and What is the most dangerous error programmers make? 6

Slide 4

Slide 4 text

Slides by richargh.de and 2022 CWE Top 20 Most Dangerous1 Software Weaknesses 7 1„most common and impactful issues experienced over the previous two calendar years.” https://cwe.mitre.org/top25/archive/2022/2022_cwe_top25.html & Comment by Jeff Atwood #11 Null Pointer Dereference Input Validation â€ș ‚Cross-Site Scripting‘ â€ș ‚SQL Injection‘ Auth â€ș Missing Authentication / Authorization Manual Memory â€ș Out-of-bounds read/write â€ș Use after Free

Slide 5

Slide 5 text

Slides by richargh.de and Handling null is important but not the most reported mistake 8

Slide 6

Slide 6 text

Slides by richargh.de and How often does null cause business problems? 9

Slide 7

Slide 7 text

Slides by richargh.de and 97% of All Logged Errors are Caused by only 10 Unique Errors 10 https://www.overops.com/blog/we-crunched-1-billion-java-logged-errors-heres-what-causes-97-of-them-2/ https://www.overops.com/blog/the-top-10-exceptions-types-in-production-java-applications-based-on-1b-events/ 97% 3% All Logged Errors 10 Unique Errors Other Errors 97% of Logged Errors by Frequency 1. NullPointerException 2. NumberFormatException 3. IllegalArgumentException 4. 


Slide 8

Slide 8 text

Slides by richargh.de and NullPointer The most popular exception 11

Slide 9

Slide 9 text

Slides by richargh.de and 12 I call it my billion-dollar mistake. It was the invention of the null reference in 1965 [ALGOL]. [
] This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. Tony Hoare Null References: The Billion Dollar Mistake 2009 InnoQ Did not design C#, Java, or Python though. J

Slide 10

Slide 10 text

Slides by richargh.de and 14 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in Java

Slide 11

Slide 11 text

Slides by richargh.de and Designing a Map 1. public class Addresses { 2. private final Map all = new HashMap<>(); 3. 4. public Address getById(PersonId personId) { 5. return all.get(personId); 6. } 7. /* 
 */ 15 Applying the Principle of Least Surprise: What should this return when person is unknown?

Slide 12

Slide 12 text

Slides by richargh.de and Modelling “Nothing” 16 1. public class Addresses { 2. private final Map all = new HashMap<>(); 3. 4. public Address getById(PersonId personId) { 5. return all.get(personId); 6. } 7. /* 
 */ Implicit Throw Exception C#, Python throw KeyNotFoundException Implicit Return null Java return null; Implicit Return “default” return Address.NULL; Explicit Require a default return GetOrDefault( personId, defaultAddress); Explicit Model absence Scala, Kotlin, F#, 
 return ???;

Slide 13

Slide 13 text

Slides by richargh.de and Explicit abscence 1. const address = addresses.find(personId); 17 2. // cannot be ignored 3. log(`Street is ${address.street}`); 4. // has to be checked before use 5. if(address != null) 6. log(
) Compile error Cannot access without explicit check

Slide 14

Slide 14 text

Slides by richargh.de and Wouldn‘t it be nice if Java had explicit absence? 18

Slide 15

Slide 15 text

Slides by richargh.de and 19 More secure languages by taking away options „Goto considered harmful“ * If, else for While return Manual memory considered harmful Garbage Collection, Reference Counting Ownership (Rust J) Implicit null considered harmful Optional Maybe T Nullable? *Edsger Dijkstra „A Case Against the Goto Statement“ aka Goto Statement Considered Harmful.

Slide 16

Slide 16 text

Slides by richargh.de and F# 2005 F# empowers everyone to write succinct, robust and performant code Has null but you cannot assign it to anything. 20 Rust 2010 A language empowering everyone to build reliable and efficient software. Doesn‘t even have null. Kotlin 2011 Modern, concise and safe programming language /* Get rid of those pesky NullPointerExceptions, you know, The Billion Dollar Mistake*/ Swift 2014 write software that is incredibly fast and safe by design. Has nil for Objective-C interop. There is a trend here https://en.wikipedia.org/wiki/Void_safety

Slide 17

Slide 17 text

Slides by richargh.de and Luckily, older languages evolve as well 21

Slide 18

Slide 18 text

Slides by richargh.de and Everyone tries to deal with null 22 Python Java C# https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history JSR-305 https://jcp.org/en/jsr/detail?id=305 FindBugs is dead: https://github.com/spotbugs/spotbugs.github.io/issues/24 Nullaway Paper https://www.uber.com/blog/research/nullaway-practical-type-based-null-safety-for-java/ ‘94 ‘96 ‘02 ‘05 ‘06 ‘14 ‘15 ‘19 C# 02 â€ș Nullable Value Types Ă  int? â€ș Null-coalescing operator ?? C# 06 â€ș Null-propagator ?. and ?[] C# 08 .NET Core 3+ â€ș Nullable reference types Ă  User? Java 8 â€ș Optional instead of return null; Python 3.5 â€ș Type hints â€ș Optional[T] JSR 305 â€ș @Nullable â€ș FindBugs initiative â€ș Dormant since ‘12 Nullaway â€ș Compile-time @Nullable check

Slide 19

Slide 19 text

Slides by richargh.de and 23 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in Java

Slide 20

Slide 20 text

Slides by richargh.de and Where’s the error? 24 1. public String findNotebookMaker(EmployeeId id) { 2. var employee = company.getById(id); 3. if (employee == null) 4. return "Employee does not exist"; 5. /* ... */ 6. 7. return employee.notebook().maker(); 8. }

Slide 21

Slide 21 text

Slides by richargh.de and Why is this an error? 1. public String findNotebookMaker(EmployeeId id) { 2. var employee = company.getById(id); 3. if (employee == null) 4. return "Employee does not exist"; 5. /* ... */ 6. 7. return employee.notebook().maker(); 8. } 25 But, but, but, an Employee always has a notebook!!1! I designed it that way! java.lang.NullPointerException: Cannot invoke "Notebook.maker()" because the return value of "Employee.notebook()" is null

Slide 22

Slide 22 text

Slides by richargh.de and The error is somewhere else 26 Under certain conditions (f.ex. notebook is being repaired), an employee does not have a notebook. This feature was added later, or maybe you just forgot. The type in employee does not reflect this. 1. public void startNotebookRepair(EmployeeId id) { 2. var employee = company.getById(id); 3. if (employee == null) 4. return; 5. /* ... */ 6. 7. company.put(id, employee.withNotebook(null)); 8. } 1. public record Employee( 2. EmployeeId id, 3. String name, 4. Notebook notebook) { 5. }

Slide 23

Slide 23 text

Slides by richargh.de and Stack traces are useless for NullPointerExceptions 27

Slide 24

Slide 24 text

Slides by richargh.de and Add compile-time null checks with Nullaway 1. // add processor to pom.xml (or .gradle): 2. 3. 4. maven-compiler-plugin> 5. 6. 7. 8. -Xplugin:ErrorProne 9. -Xep:NullAway:ERROR 10. -XepOpt:NullAway 11. :AnnotatedPackages=de.richargh>> 12. 13. 14. 15. com.google.errorprone>> 16. 17. com.uber.nullaway>> 28 Nullaway has instructions for Maven, Gradle, 
 https://github.com/uber/NullAway 1. // add annotation to *.java file: 2. public class { 3. // can now signal null 4. private @Nullable String text1; 5. { /* 
 */} 6. private @Nullable data; 7. { /* 
 */} 8. // this is not nullable 9. private String text2; 10.}

Slide 25

Slide 25 text

Slides by richargh.de and After Enable (1) 29 Compile error 1. public void startNotebookRepair(EmployeeId id) { 2. var employee = company.getById(id); 3. if (employee == null) 4. return; 5. /* ... */ 6. 7. company.put(id, employee.withNotebook(null)); 8. } [NullAway] passing @Nullable parameter 'null' where @NonNull is required

Slide 26

Slide 26 text

Slides by richargh.de and (2) Mandatory nullability modelling 30 Mark it as nullable 1. public record Employee( 2. EmployeeId id, 3. String name, 4. @Nullable Notebook notebook) { 5. }

Slide 27

Slide 27 text

Slides by richargh.de and (3) Cascading compile error(s) 31 1. public String findNotebookMaker(EmployeeId id) { 2. var employee = company.getById(id); 3. if (employee == null) 4. return "Employee does not exist"; 5. /* ... */ 6. 7. return employee.notebook().maker(); 8. } Compile error [NullAway] dereferenced expression employee.notebook() is @Nullable

Slide 28

Slide 28 text

Slides by richargh.de and The compiler guides you through the code base 32

Slide 29

Slide 29 text

Slides by richargh.de and We fixed the root cause, not a symptom 33 1. public void startNotebookRepair(EmployeeId id) { 2. var employee = company.getById(id); 3. if (employee == null) 4. return; 5. /* ... */ 6. 7. company.put(id, employee.withoutNotebook()); 8. } 1. public record Employee( 2. EmployeeId id, 3. String name, 4. @Nullable Notebook notebook) { 5. /* withoutNotebook() 
 */ 6. } 1. public String findNotebookMaker(EmployeeId id) { 2. var employee = company.getById(id); 3. if (employee == null) 4. return "Employee does not exist"; 5. /* ... */ 6. 7. return employee.notebook() != null 8. ? employee.notebook().maker() 9. : EMPLOYEE_DOES_NOT_HAVE_A_NOTEBOOK; 10.}

Slide 30

Slide 30 text

Slides by richargh.de and Nullaway helps us find&fix the root cause of bugs 34

Slide 31

Slide 31 text

Slides by richargh.de and Migration is worth it. You‘ll catch so many bugs 37

Slide 32

Slide 32 text

Slides by richargh.de and Suppose your system looks like this 38 
 UI Logic Data Adapter commons.lang jar Edge Elements commons.error commons
. UI Logic Data Adapter UI Logic Data Adapter UI Logic Data Adapter More, Focused steps: migrate one at a time Common jar(s) next Don’t trust the edge Migrate a simple jar first AnnotatedPackages + UnannotatedSubPackages

Slide 33

Slide 33 text

Slides by richargh.de and 39 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in Java What about Optional?

Slide 34

Slide 34 text

Slides by richargh.de and Optional Awesome but often misunderstood 40

Slide 35

Slide 35 text

Slides by richargh.de and It’s actually OptionalReturn and useful for for fluent apis 41 Optional is not for optional fields Optional is not for optional method parameters Optional does not eliminate NullPointerExceptions

Slide 36

Slide 36 text

Slides by richargh.de and While drafting the fluent Java 8 Streams API they realized a problem2: 1. String employeeNameById(EmployeeId id){ 2. return employees.stream() 3. .search(it -> it.id() == id) 4. .name(); 5. } It’s really OptionalReturn1 as in method return. 42 1 https://mail.openjdk.org/pipermail/lambda-dev/2012-September/005952.html 2 Optional - The Mother of All Bikesheds https://www.youtube.com/watch?v=Ej0sss6cq14 In the actual Stream Apithey added a limited mechanism to represent “no result” and keep the Api fluent2: 1. String employeeNameById(EmployeeId id){ 2. return employees.stream() 3. .filter(it -> it.id() == id) 4. .findFirst() 5. // now we have an Optional 6. .map(Employee::name) 7. .orElse(“UNKNOWN”); 8. } Non-obvious NullPointerException Will only call .name() when non-null

Slide 37

Slide 37 text

Slides by richargh.de and Optional does not eliminate NullPointerExceptions 43 1. // this is valid without Nullaway 2. Optional employeeName = null; 7. // and improper use still results in Exceptions 8. employees.stream() 9. .filter(it -> it.id() == id) 10. .findFirst() 11. .get(); // potential NoSuchElementException 3. // so is this 4. var person = new Person(null); 5. record Person( 6. Optional employer) {}

Slide 38

Slide 38 text

Slides by richargh.de and Optional for optional fields: error-prone and cluttered 44 Optional, rather inconvenient if you don’t want fluidity 1. if(sth.a().isEmpty() 2. && sth.b().isPresent() 3. && sth.b().get().bb().isPresent()){ 4. 5. aMethod(sth.b().get(), sth.c().get()); 6. } With @Nullable, less clutter, actual compile-safety 1. if(sth.a() == null 2. && sth.b() != null 3. && sth.b().bb() != null){ 4. 5. aMethod(sth.b(), sth.c()); 6. } NoSuchElementException: Forgot to check .c().isPresent() [NullAway] dereferenced expression something.c() is @Nullable

Slide 39

Slide 39 text

Slides by richargh.de and Optional for optional parameters: quite noisy and cluttered 45 With Optional 1. myMethod(a, Optional.of(b), c, Optional.of(d)) With @Nullable, less clutter, compile-safety 1. myMethod(a, b, c, d) NullPointerException: d is null, so this should’ve been Optional.ofNullable(d)

Slide 40

Slide 40 text

Slides by richargh.de and Modelling Absence in Java with Nullaway and sometimes Optional 48 Explicit Model absence Scala, Kotlin, F#, Java 1. public class Addresses { 2. private final Map all = new HashMap<>(); 3. 4. public Optional
getById(PersonId personId) { 5. return Optional.ofNullable(all.get(personId)); 6. } 7. /* 
 */ 1. record Person( 2. PersonId id, 3. @Nullable 123){ 4. 5. Address withoutNotebook(){ /* 
 */ } 6. } Compile-time safety Optional where Fluent Api useful

Slide 41

Slide 41 text

Slides by richargh.de and 49 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in Java

Slide 42

Slide 42 text

Slides by richargh.de and #4 Make non-null your default ‱ NullAway assumes this ‱ JSR-305 has @ParametersAreNonnullByDefault for classes and packages ‱ JSpecify has @NullMarked 50 1

Slide 43

Slide 43 text

Slides by richargh.de and 51 #4 Model Optional Fields with @Nullable 2 1. public class { 2. private @Nullable Address address; 3. } 4. 5. public record ( 6. @Nullable Address address){ 7. }

Slide 44

Slide 44 text

Slides by richargh.de and 52 #6 Never pass null, consider explicit methods instead 1. var p1 = notebookPriceFor( 2. employeeId, notebookType); 3. 4. // what does passing null mean? 5. // what is this supposed to return? 6. var p2 = notebookPriceFor( 7. null, notebookType); Clean Code Chapter 7 3 1. var p1 = notebookPriceFor( 2. employeeId, notebookType); 3. 4. // create an explicit method that indicates a query without an employee is just an estimate 5. var p2 = estimateNotebookPrice( 6. notebookType);

Slide 45

Slide 45 text

Slides by richargh.de and #4 Mark nullable method returns 53 4a 1. @Nullable myMethod(A a, B b) { 2. /*
*/ 3. }

Slide 46

Slide 46 text

Slides by richargh.de and 6. // Choice A: caller says null is not allowed 7. var account = accountsfindAccountById(id).orElseThrow(); #4 Occasionally consider Optional for method returns to give choices to caller 54 4b 1. // Optional is quite useful for repositories 2. Optional
getAddressById(employeeId id) { 3. return Optional.ofNullable(addresses.get(id)); 4. } 8. // Choice B: caller says default is possible 9. var account = accounts.findAccountById(id).orElse(Account.none()); 10.// Choice C: callers uses fluent chain 11.var budget = accounts.findAccountById(id) 12. .map(Account::budgetId) 13. .flatMap(budgets::getById)

Slide 47

Slide 47 text

Slides by richargh.de and 56 #5 Return empty collections or arrays, not nulls 1. // not like this 2. @Nullable List
recentAddresses(){ 3. return employee.isTrackingAllowed() 4. ? recentAddresses 5. : null; 6. } 1. // like this 2. List
recentAddresses(){ 3. return employee.isTrackingAllowed() 4. ? recentAddresses 5. : Collections.emptyList(); 6. } Effective Java 3rd Edition Item 54 Makes Api more difficult to use for all callers. 5

Slide 48

Slide 48 text

Slides by richargh.de and 57 #5 Use JDK 16 records for domain types 6 1. public class Address { 2. private final String city; 3. private final String street; 4. 5. Address( 6. String city, String street){ 7. /*
*/ 8. } 9. 10. @Override boolean equals /*
*/ 11. 12. @Override int hashCode /*
*/ 13. 14. @Override String toString /*
*/ 15.} 1. public record Address( 2. String city, 3. @Nullable String street 4. ) {}

Slide 49

Slide 49 text

Slides by richargh.de and Records provide value-equality and immutability 1. public record Address( 2. String city, 3. @Nullable String street 4. ); 58 5. var address1 = new Address( 6. “BrĂŒhl”, "Berggeiststraße 31-41"); 7. var address2 = new Address( 8. “BrĂŒhl”, "Berggeiststraße 31-41"); 9. 10. address1.equals(address2); // true

Slide 50

Slide 50 text

Slides by richargh.de and 59 #5 Don’t trust types from the edge of your system 7 1. public record RenterDto( 2. String id, 3. String name) 4. var json = """{ ”name": ”Alex" }"""; 5. var result = objectMapper.readValue(json, RenterDto.class); 6. // result = RenterDto[id=null, name=Alex] 7. result.id() == null; // true
 Mapping-Frameworks like Jackson see null-checks as validation. And they don’t do validation. We can write our own ConstraintValidator however

Slide 51

Slide 51 text

Slides by richargh.de and The Future
? 61 Java Checker Frameworkhttps://checkerframework.org/ Nullaway Paper https://www.uber.com/blog/research/nullaway-practical-type-based-null-safety-for-java/ JSpecify https://jspecify.dev/ ‘08 ‘19 ‘23 ‘25 Checker Framework â€ș Compile-time checks â€ș NullChecker is alternative to Nullaway Nullaway â€ș Compile-time @Nullable check Primitive Optional<>? â€ș Optional becomes Primitive â€ș Can then only be present or empty, never null JSpecify Ref. Impl. â€ș Unifying the JSR305 annotation-jungle â€ș @Nullable, @NullMarked JDK Null-Safety? â€ș Maybe here â€ș Might happen if C# Null-Addition works out well

Slide 52

Slide 52 text

Slides by @arghrich Thanks 62 IT-Tailor Richard Gross (he/him) Archaeologist Auditor richargh.de/ speakerdeck.com/richargh @arghrich Works for maibornwolff.de/ People. Code. Commitment. DE TN ES Code at https://github.com/Richargh/fixing-the-billion-dollar-mistake

Slide 53

Slide 53 text

Slides by richargh.de and Backup

Slide 54

Slide 54 text

Slides by richargh.de and 2022 CWE Top 20 Most Dangerous Software Weaknesses 1. Out-of-bounds Write 2. ‘Cross-site Scripting’ 3. ‘SQL Injection’ 4. Improper Input Validation 5. Out-of-bounds Read 6. ‘OS Command Injection’ 7. Use After Free 8. ‘Path Traversal’ 9. Cross-Site Request Forgery 10. Unrestricted File Upload with ‘Evil’ Type 11. NULL Pointer Dereference 12. Deserialization of Untrusted Data 13. Integer Overflow or Wraparound 14. Improper Authentication 15. Use of Hard-Coded Credentials 16. Missing Authorizations 17. ‘Command Injection’ 18. Missing Auth. for Critical Function 19. Improper Restriction 
 of Memory Bounds 20. Incorrect Default Permissions 64 https://cwe.mitre.org/top25/archive/2022/2022_cwe_top25.html & Comment by Jeff Atwood Rough categories Manual Memory Input Validation Auth

Slide 55

Slide 55 text

Slides by richargh.de and 2021 CWE Top 20 Most Dangerous Software Weaknesses 1. Out-of-bounds Write 2. ‘Cross-site Scripting’ 3. Out-of-bounds Read 4. Improper Input Validation 5. ‘OS Command Injection’ 6. ‘SQL Injection’ 7. Use After Free 8. ‘Path Traversal’ 9. Cross-Site Request Forgery 10. Unrestricted File Upload with ‘Evil’ Type 11. Missing Auth. for Critical Function 12. Integer Overflow or Wraparound 13. Deserialization of Untrusted Data 14. Improper Authentication 15. NULL Pointer Dereference 16. Use of Hard-Coded Credentials 17. Improper Restriction 
 of Memory Bounds 18. Missing Authorizations 19. Incorrect Default Permissions 20. Exposure of Sensitive Information 
 65 https://cwe.mitre.org/top25/archive/2021/2021_cwe_top25.html & Comment by Jeff Atwood Rough categories Manual Memory Input Validation Auth

Slide 56

Slide 56 text

Slides by richargh.de and 66 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in C# One more thing 


Slide 57

Slide 57 text

Slides by richargh.de and 67 I‘ve enabled nullable reference types but now I‘ve got null-checks everywhere!!11!eleven

Slide 58

Slide 58 text

Slides by richargh.de and Great, you‘ve fixed the root cause of many bugs at compile time 68

Slide 59

Slide 59 text

Slides by richargh.de and 1. public IResponse CreateEmployee(Request request) 2. { 3. var employee = EmployeeFromBody(request.Path); 4. CheckEmailIsUnique(employee); 5. StoreEmployee(employee); 6. EmailEmployee(employee); 7. return new OkResponse(200); 8. } 69

Slide 60

Slide 60 text

Slides by richargh.de and 1. public IResponse CreateEmployee(Request request) 2. { 3. var employee = EmployeeFromBody(request.Path); 4. if(employee is null) 5. { 6. return new BadResponse(400, “Employee invalid”); 7. } 8. if(EmailIsNotUnique(employee)) 9. { 10. return new BadResponse(400, “Email must be unique”); 11. } 12. StoreEmployee(employee); 13. try { 14. EmailEmployee(employee); 15. } 16. catch(ServerUnreachableException) 17. { 18. return new BadResponse(500, “could not send email”); 19. } 20. return new OkResponse(200); 21.} 70 Where did my happy path go? Only one null- check to keep example short

Slide 61

Slide 61 text

Slides by richargh.de and Can we clarify the business rules again? 71

Slide 62

Slide 62 text

Slides by richargh.de and A shallow dive into Railway-Oriented Programming* 72 *By Scott Wlaschin https://fsharpforfunandprofit.com/rop/

Slide 63

Slide 63 text

Slides by richargh.de and Happy Path without error handling 73 1. public IResponse CreateEmployee(Request request) 2. { 3. var employee = EmployeeFromBody(request.Path); 4. CheckEmailIsUnique(employee); 5. StoreEmployee(employee); 6. EmailEmployee(employee); 7. return new OkResponse(200); 8. }

Slide 64

Slide 64 text

Slides by richargh.de and ROP Path with error handling 74 1. public IResponse CreateEmployee(Request request) 2. { 3. EmployeeFromBody(request.Path).AsResult(ifnull: “invalid employee passed”) 4. .ThenTry(CheckEmailIsUnique) 5. .Then(StoreEmployee) 6. .ThenTry(EmailEmployee) 7. .Then(_ => new OkResponse(200)); 8. } I can see my business logic again

Slide 65

Slide 65 text

Slides by richargh.de and Focus on Happy Path, Result does the rest 76 J L thenTry then thenTryFix thenFix 1. Result Then( 2. Func onOk){ /*
*/ } 3. Result ThenTry( 4. Func> onOk){ /*
*/ }

Slide 66

Slide 66 text

Slides by richargh.de and How to get on the rail 77 J L Ok(sth) Fail(“why”) OfResult(sth?)

Slide 67

Slide 67 text

Slides by richargh.de and Against Railway-Oriented Programming* 78 *Also Scott Wlaschin https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/ https://eiriktsarpalis.wordpress.com/2017/02/19/youre-better-off-using-exceptions/

Slide 68

Slide 68 text

Slides by richargh.de and Don‘t wrap all exceptions with Results 79 *Also Scott Wlaschin https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/ https://eiriktsarpalis.wordpress.com/2017/02/19/youre-better-off-using-exceptions/

Slide 69

Slide 69 text

Slides by richargh.de and Use ROP only for Domain Errors 80 *Also Scott Wlaschin https://fsharpforfunandprofit.com/posts/against-railway-oriented-programming/ https://eiriktsarpalis.wordpress.com/2017/02/19/youre-better-off-using-exceptions/

Slide 70

Slide 70 text

Slides by richargh.de and How to get started? 81 Railway Oriented Programming [Original, Scott Wlaschin 2014] Against Railway-Oriented Programming [Scott Wlaschin 2019] Railway Oriented Programming: C# Edition [Tama Waddell 2019] Switch to F# J Or Copy/Code your Java Result class Watch a deep- dive talk J

Slide 71

Slide 71 text

Slides by richargh.de and Now we‘re done J 82

Slide 72

Slide 72 text

Slides by richargh.de and I‘m not the first to talk about ROP :) ‱ Railway Oriented Programming [Original, Scott Wlaschin 2014] ‱ Against Railway-Oriented Programming [Scott Wlaschin 2019] ‱ Railway Oriented Programming: C# Edition [Tama Waddell 2019] ‱ Railway-Oriented Programming in C# [Marcus Denny 2017] ‱ Railway-oriented programming in Java [Tom Johnson 2021] 83

Slide 73

Slide 73 text

Slides by richargh.de and UseCase: Change Employee Address 84 EmployeeId From Request Path Find Employee Address from Path Change Address Store changed Employee Greet Employee with new Email

Slide 74

Slide 74 text

Slides by richargh.de and 1. public IResponse ChangeAddress(Request request) 2. { 3. var employeeId = EmployeeIdFromPath(request.Path); 4. var employee = _employees.FindById(employeeId); 5. var address = AddressFromBody(request.Body); 6. employee = employee.ChangeAddress(address); 7. _employees.Store(employee); 8. GreetEmployee(employee.Email); 9. return new OkResponse(200); 10.} 85 What about DbExceptions? Does not compile. All nullable.

Slide 75

Slide 75 text

Slides by richargh.de and 1. public IResponse ChangeAddress(Request request) 2. { 3. var employeeId = EmployeeIdFromPath(request.Path); 4. if(employeeId is null) 5. { 6. return new BadResponse(400, “Employee Id invalid”); 7. } 8. var employee = _employees.FindById(employeeId); 9. if(employee is null) 10. { 11. return new BadResponse(400, “Employee not found”); 12. } 13. var address = AddressFromBody(request.Body); 14. if(address is null) 15. { 16. return new BadResponse(400, “address invalid”); 17. } 18. employee = employee.ChangeAddress(address); 19. try { 20. _employees.Store(employee); 21. } 22. catch(MyDbException ex) 23. { 24. return new BadResponse(500, “could not change email”); 86 Where did my happy path go?

Slide 76

Slide 76 text

Slides by richargh.de and ROP Path with error handling 87 1. public IResponse ChangeAddress(Request request) 2. { 3. EmployeeIdFromPath(request.Path) 4. .ThenTry(_employees.FindById) 5. .ThenTryToPair(_ => AddressFromBody(request.Body)) 6. .ThenTry((employee, address) => employee.ChangeAddress(address)) 7. .ThenTry(_employees.Store) 8. .ThenTry(employee => GreetEmployee(employee.Email)) 9. .Then(_ => new OkResponse(200)); 10.} I can see my business logic again

Slide 77

Slide 77 text

Slides by richargh.de and UseCase: Rent NotebookType 88 Employee doesn’t have a notebook Employee has enough budget? Notebook with desired type is available? Rent notebook for employee

Slide 78

Slide 78 text

Slides by richargh.de and 1. public IResponse Rent(NotebookType type, EmployeeId eId) 2. { 3. var employee = _employees.FindById(eId); 4. if (AlreadyHasANotebook(employee)) 5. { 6. return Bad(”Already has a notebook); 7. } 8. var notebook = _inventory 9. .FindNotebooksByType(type) 10. .FirstOrDefault(IsAvailable); 11. 12. var budget = _budget.FindById(eId); 13. if (HasNotEnoughBudget(budget, notebook)) 14. { 15. return Bad(”Not enough budget”); 16. } 17. var remainingBudget = RentNotebook( employee, budget, notebook); 18. NotifyOfRent( employee, notebook, remainingBudget); 19. return Ok(notebook); 20.} 89 Employee doesn’t have a notebook Employee has enough budget? Notebook with desired type is available? Rent notebook for employee Notify Employee Does not compile What about DbExceptions? Negative framing

Slide 79

Slide 79 text

Slides by richargh.de and 1. public IResponse Rent(NotebookType type, EmployeeId eId) 2. { 3. var employee = _employees.FindById(eId); 4. if (employee is null) 5. { 6. return Bad("Employee does not exist); 7. } 8. if (AlreadyHasANotebook(employee)) 9. { 10. return Bad(”Already has a notebook); 11. } 12. var notebook = _inventory 13. .FindNotebooksByType(type) 14. .FirstOrDefault(IsAvailable); 15. if (notebook is null) 16. { 17. return Bad("Notebook does not exist); 18. } 19. var budget = _budget.FindById(eId); 20. if (budget is null) 21. { 22. return Bad("Employee has no budget); 23. } 24. 25. if (HasNotEnoughBudget(budget, notebook)) 26. { 27. return Bad(”Not enough budget”); 28. } 29. 30. try 31. { 32. var remainingBudget = RentNotebook( employee, budget, notebook); 33. NotifyOfRent( employee, notebook, remainingBudget); 34. return Ok(notebook); 35. } 36. catch (MyDbException e) 37. { 38. Console.WriteLine(e); 39. return Bad("Could not notebook"); 40. } 41.} 90 Where did all my business rules go?