Slide 1

Slide 1 text

Slides by @arghrich Fixing the Billion Dollar Mistake 19.11.21 richargh.de/ speakerdeck.com/richargh Richard Gross pronoun.is/he Works at maibornwolff.de/en

Slide 2

Slide 2 text

Slides by @arghrich 5 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in C#

Slide 3

Slide 3 text

Slides by @arghrich What is the most dangerous error programmers make? 6 Slides by @arghrich

Slide 4

Slide 4 text

Slides by @arghrich 2021 CWE Top 20 Most Dangerous* Software Weaknesses 7 *„most common and impactful issues experienced over the previous two calendar years.” https://cwe.mitre.org/top25/archive/2021/2021_cwe_top25.html & Comment by Jeff Atwood #15 Null Pointer Dereference Input Validation â€ș ‚Cross-Site Scripting‘ â€ș ‚OS Command Injection‘ Auth â€ș Missing Authentication / Authorization Manual Memory â€ș Out-of-bounds read/write #12 Int Overflow / Wraparound 1. // this morning in the Keynote 2. binarySearch(int[] a, int key){ 3. int low = 0; 4. int high = a.length - 1; 5. 6. while (low <= high) { 7. // Bug in JDK for 20 years 8. int mid = (low + high) / 2; 9. int midVal = a[mid]; 10. // 


Slide 5

Slide 5 text

Slides by @arghrich Handling null is important but not the most dangerous mistake 8 Slides by @arghrich

Slide 6

Slide 6 text

Slides by @arghrich Still 
 9 Slides by @arghrich

Slide 7

Slide 7 text

Slides by @arghrich 97% of Logged Errors are Caused by 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% 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 @arghrich 11 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 9

Slide 9 text

Slides by @arghrich 13 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in C#

Slide 10

Slide 10 text

Slides by @arghrich Designing a Dictionary 1. public class Addresses 2. { 3. private readonly Dictionary _addresses = new(); 4. 5. public Address Find(PersonId personId) 6. { 7. return _addresses[personId]; 8. } 9. // 
 14 Slides by @arghrich Applying the Principle of Least Surprise: What should this return when personId is unknown?

Slide 11

Slide 11 text

Slides by @arghrich Modelling “Nothing” 15 1. public class Addresses 2. { 3. private readonly Dictionary _addresses = new(); 4. 5. public Address Find(PersonId personId) 6. { 7. return _addresses[personId]; 8. } 9. // 
 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 12

Slide 12 text

Slides by @arghrich Explicit abscence 1. const address = addresses.Find(personId); 16 Slides by @arghrich 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 13

Slide 13 text

Slides by @arghrich Wouldn‘t it be nice if C# had explicit absence? 17 Slides by @arghrich

Slide 14

Slide 14 text

Slides by @arghrich 20 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 15

Slide 15 text

Slides by @arghrich F# 2005 F# empowers everyone to write succinct, robust and performant code Has null but you cannot assign it to anything. 21 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 16

Slide 16 text

Slides by @arghrich Luckily, languages evolve 22 Slides by @arghrich

Slide 17

Slide 17 text

Slides by @arghrich Everyone tries to deal with null 23 Python Java C# https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history ‘94 ‘96 ‘02 ‘05 ‘14 ‘15 ‘19 ‘20 ‘22 C# 02 â€ș Generics â€ș Nullable Value Types â€ș Null-coalescing operator ?? C# 06 â€ș Null-propagator ?. and ?[] C# 08 .NET Core 3+ â€ș Nullable reference types C# 09 .NET 5+ â€ș Generic nullable improvements â€ș Records â€ș Init-only setters Java 8 â€ș Optional instead of nullable Python 3.5 â€ș Type hints â€ș Optional[T]

Slide 18

Slide 18 text

Slides by @arghrich 24 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in C#

Slide 19

Slide 19 text

Slides by @arghrich Where’s the error? 25 Slides by @arghrich public string FindNotebookMaker(EmployeeId id) { var employee = _company.FindById(id); if (employee is null) return "Employee does not exist"; /* ... */ return employee.Notebook.Maker; }

Slide 20

Slide 20 text

Slides by @arghrich Why is this an error? public string FindNotebookMaker(EmployeeId id) { var employee = _company.FindById(id); if (employee is null) return "Employee does not exist"; /* ... */ return employee.Notebook.Maker; } 26 Slides by @arghrich But, but, but, an Employee always has a notebook!!1! I designed it that way! System.NullReferenceException at FindNotebookMaker(EmployeeId id) in UsesLegacyCode.cs:line 22

Slide 21

Slide 21 text

Slides by @arghrich The error is somewhere else public void StartRepairNotebook(EmployeeId id) { var employee = _company.FindById(id); if (employee is null) return; employee.Notebook = null; _company.Put(employee); } 27 Slides by @arghrich 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. public class Employee { public EmployeeId Id {get;} public string Name {get;} public Notebook Notebook {get; set;} /*
*/ }

Slide 22

Slide 22 text

Slides by @arghrich Stack traces are useless for NullReferenceExceptions 28 Slides by @arghrich

Slide 23

Slide 23 text

Slides by @arghrich Enable nullable reference types 1. // per *.csproj file: 2. 3. 4. 5. enable 6. 7. 8. 8.0 9. 10. 11. nullable 12. 13. 14. 29 Slides by @arghrich https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-reference-types 1. // per *.cs file: 2. #nullable enable 3. namespace 4. { 5. public class 6. { 7. // can now signal null 8. static string? Name() 9. { /* 
 */} 10. static ? Data() 11. { /* 
 */} 12.// disable wherever you want 13.#nullable disable OR

Slide 24

Slide 24 text

Slides by @arghrich After Enable (1) public void StartRepairNotebook(EmployeeId id) { var employee = _company.FindById(id); if (employee is null) return; employee.Notebook = null; _company.Put(employee); } 30 Slides by @arghrich Compile error

Slide 25

Slide 25 text

Slides by @arghrich (2) Mandatory nullability modelling 31 Slides by @arghrich public class Employee { public EmployeeId Id {get;} public string Name {get;} public Notebook? Notebook {get; set;} /*
*/ } Mark it as nullable

Slide 26

Slide 26 text

Slides by @arghrich (3) Cascading compile error(s) 32 Slides by @arghrich Compile error public string FindNotebookMaker(EmployeeId id) { var employee = _company.FindById(id); if (employee is null) return "Employee does not exist"; /* ... */ return employee.Notebook.Maker; }

Slide 27

Slide 27 text

Slides by @arghrich The compiler guides you through the code base 33 Slides by @arghrich

Slide 28

Slide 28 text

Slides by @arghrich We fixed the root cause, not a symptom 34 Slides by @arghrich public string FindNotebookMaker(EmployeeId id) { var employee = _company.FindById(id); if (employee is null) return "Employee does not exist"; /* ... */ return employee.Notebook?.Maker; } public class Employee { public EmployeeId Id {get;} public string Name {get;} public Notebook? Notebook {get; set;} /*
*/ } public void StartRepairNotebook(EmployeeId id) { var employee = _company.FindById(id); if (employee is null) return; employee.Notebook = null; _company.Put(employee); }

Slide 29

Slide 29 text

Slides by @arghrich Nullable Reference Types help us find&fix the root cause of bugs 35 Slides by @arghrich

Slide 30

Slide 30 text

Slides by @arghrich 37 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in C#

Slide 31

Slide 31 text

Slides by @arghrich Recap: Modelling “Nothing” 38 1. public class Addresses 2. { 3. private readonly Dictionary _addresses = new(); 4. 5. public Address Find(PersonId personId) 6. { 7. return _addresses[personId]; 8. } 9. // 
 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 32

Slide 32 text

Slides by @arghrich Modelling Absence in C#8 39 1. public class Addresses 2. { 3. private readonly Dictionary _addresses = new(); 4. 5. public Address? FindAddress(PersonId personId) 6. { 7. return _addresses.GetValueOrDefault(personId); 8. } 9. // 
 Explicit Model absence Scala, Kotlin, F#, C#

Slide 33

Slide 33 text

Slides by @arghrich Dealing with Absence in C#8 40 1. // bad, does not compile 2. string? address = FindAddress(personId).Street; 3. // good, uses null-propagating operator 4. string? address = FindAddress(personId)?.Street; Explicit Model absence Scala, Kotlin, F#, C# https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/null-checking-preferences 5. // maybe better, uses null-coalescing operator for default 6. string address = FindAddress(personId)?.Street ?? "Konstitucijos Av. 20”; 7. // maybe better, uses null-coalescing assignment operator for default 8. string? address = FindAddress(personId)?.Street; 9. address ??= "Konstitucijos Av. 20”; 10.// good, use pattern matching is null check 11.if(address is null) {/*
*/} 12.if(address is not null) {/*
*/}

Slide 34

Slide 34 text

Slides by @arghrich Nullable Reference Types more important than introduction of Generics 41 Slides by @arghrich

Slide 35

Slide 35 text

Slides by @arghrich Migration is worth it. You‘ll catch so many bugs 42 Slides by @arghrich

Slide 36

Slide 36 text

Slides by @arghrich Suppose your system looks like this 43 
 UI Logic Data Adapter Common.Lang csproj Edge Elements Common.Error Common
. UI Logic Data Adapter UI Logic Data Adapter UI Logic Data Adapter Migrate a simple proj first More, Focused steps: migrate one at a time If on C#9+, common proj(s) next Don’t trust the edge

Slide 37

Slide 37 text

Slides by @arghrich Use nullable reference types effectively 44 Slides by @arghrich

Slide 38

Slide 38 text

Slides by @arghrich 45 #4 Always specify when returning null 1. Address? FindAddress(PersonId pId) 2. { 3. return _addresses.GetValueOrDefault(pId); 4. } 1

Slide 39

Slide 39 text

Slides by @arghrich 46 #5 Return empty collections or arrays, not nulls 1. // not like this 2. IList
? RecentAddresses() 3. { 4. return _employee.TrackingAllowed 5. ? _recentAddresses 6. : null; 7. } 1. // like this 2. IList
RecentAddresses() 3. { 4. return _employee.TrackingAllowed 5. ? _recentAddresses 6. : new List
(); 7. } Effective Java 3rd Edition Item 54 Makes Api more difficult to use for all callers. 2

Slide 40

Slide 40 text

Slides by @arghrich 48 Slides by @arghrich #5 Use init-setters for configuration with known defaults 3 1. class MyConfiguration 2. { 3. // known defaults 4. int Port {get; init;} = 7070; 5. string? AppName {get; init;} 6. } 1. // instantiate like this 2. var address = new MyConfiguration { 3. Port = “Trakai”, 4. AppName = “Billion” 5. } 6. // not settable, does not compile 7. address.Port = 9999;

Slide 41

Slide 41 text

Slides by @arghrich Init-setters are useless for domain types, where default values are unknown 1. public class Address 2. { 3. public string City {get; init;} 4. public string? Street {get; init;} 5. // does not compile, Compiler can’t know that City is non-null 6. } 49 Slides by @arghrich 1. public class Address 2. { 3. public string City { get; } 4. public string? Street { get; } 5. 6. // tedious but compile-safe 7. public Address( 8. string city, string? street) 9. { 10. City = city; 11. Street = street; 12. } 13.}

Slide 42

Slide 42 text

Slides by @arghrich 50 Slides by @arghrich #5 Use C#9 records for domain types 4 1. public class Address 2. { 3. public string City {get; init;} 4. public string? Street {get; init;} 5. // does not compile, Compiler can’t know that City of Address is actually set 6. } 1. public record Address( 2. string City, 3. string? Street 4. ); 1. // we can now init like this: 2. var address = new Address( 3. City: “Trakai”, 4. Street: null 5. )

Slide 43

Slide 43 text

Slides by @arghrich C#9 records provide value-equality and non-destructive mutation 1. public record Address( 2. string City, 3. string? Street 4. ); 51 Slides by @arghrich 5. // value equality 6. var address1 = new Address( 7. City: “Vilnius”, 8. Street: "Konstitucijos Av. 20" 9. ); 10. var address2 = new Address( 11. City: “Vilnius”, 12. Street: "Konstitucijos Av. 20" 13. ); 14. 15. address1 == address2 // true https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record 16.// non-destructive mutation 17. var address3 = address2 with { 18. Street = “Ozo g. 18” 19. } 20. address2 == address3 // false

Slide 44

Slide 44 text

Slides by @arghrich 52 Slides by @arghrich #5 Don’t trust types from the edge of your system 5 1. public record EmployeeDto( 2. string Id, 3. string Name, 4. string? Email) 1. var json = @"{ ""foo"": ""bar"” }"; 2. var result = JsonConvert.DeserializeObject(json); 3. // result = EmployeeDto { Id=, Name=, Email= } 4. result.Id is null // true which should not be possible

Slide 45

Slide 45 text

Slides by @arghrich 54 € Dealing with “nothing” Effective “nothing” Fixing the Mistake in C# One more thing 


Slide 46

Slide 46 text

Slides by @arghrich 55 Slides by @arghrich I‘ve enabled nullable reference types but now I‘ve got null-checks everywhere!!11!eleven

Slide 47

Slide 47 text

Slides by @arghrich Great, you‘ve fixed the root cause of many bugs at compile time 56 Slides by @arghrich

Slide 48

Slide 48 text

Slides by @arghrich 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. } 57 Slides by @arghrich

Slide 49

Slide 49 text

Slides by @arghrich 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.} 58 Slides by @arghrich Where did my happy path go? Only one null- check to keep example short

Slide 50

Slide 50 text

Slides by @arghrich Can we clarify the business rules again? 59 Slides by @arghrich

Slide 51

Slide 51 text

Slides by @arghrich A shallow dive into Railway-Oriented Programming* 60 Slides by @arghrich *By Scott Wlaschin https://fsharpforfunandprofit.com/rop/

Slide 52

Slide 52 text

Slides by @arghrich Happy Path without error handling 61 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 53

Slide 53 text

Slides by @arghrich ROP Path with error handling 62 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 54

Slide 54 text

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

Slide 55

Slide 55 text

Slides by @arghrich How to get on the rail 65 J L Ok(sth) Fail(“why”) OfResult(sth?)

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

Slides by @arghrich How to get started? 69 Slides by @arghrich 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 C# Result class Watch a deep- dive talk J

Slide 60

Slide 60 text

Slides by @arghrich Now we‘re done J 70 Slides by @arghrich

Slide 61

Slide 61 text

Slides by @arghrich Examples available on Github ‱ https://github.com/Richargh/fixing-the-billion-dollar-mistake ‱ Includes Snippets for C#, F#, Java, Python, Scala ‱ Includes Railway-Oriented Programming Examples for C# 71 Slides by @arghrich

Slide 62

Slide 62 text

Slides by @arghrich I‘m not the first to talk about this :) ‱ Null References: The Billion Dollar Mistake [Tony Hoare 2009] ‱ Nullable Reference Types in C# 8 ‱ [Jon Skeet GOTO 2019] 72 Slides by @arghrich

Slide 63

Slide 63 text

Slides by @arghrich 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] 73 Slides by @arghrich

Slide 64

Slide 64 text

Slides by @arghrich Done richargh.de/ speakerdeck.com/richargh Code at https://github.com/Richargh/fixing-the-billion-dollar-mistake Small Icons by Fontawesome Richard Gross pronoun.is/he Works at maibornwolff.de/en Slides by @arghrich Null: the billion dollar mistake Try to migrate one csproj at a time (1) Always specify when returning null (5) Don’t trust types from the edge of your system Many languages are trying to fix it Fix the root cause of bugs (2) Return empty collections or arrays, not nulls You can fix it in C# 8+.NET Core 3+ Nullable ref. types more important than Generics (3) Only use init- setters where defaults are known Try out ROP to clarify business rules Enable nullable (4) Use records for domain types Use ROP only for Domain Errors

Slide 65

Slide 65 text

Slides by @arghrich Backup

Slide 66

Slide 66 text

Slides by @arghrich 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 
 76 https://cwe.mitre.org/top25/archive/2021/2021_cwe_top25.html & Comment by Jeff Atwood Rough categories Manual Memory Input Validation Auth

Slide 67

Slide 67 text

Slides by @arghrich UseCase: Change Employee Address 77 EmployeeId From Request Path Find Employee Address from Path Change Address Store changed Employee Greet Employee with new Email

Slide 68

Slide 68 text

Slides by @arghrich 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.} 78 Slides by @arghrich What about DbExceptions? Does not compile. All nullable.

Slide 69

Slide 69 text

Slides by @arghrich 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”); 79 Slides by @arghrich Where did my happy path go?

Slide 70

Slide 70 text

Slides by @arghrich ROP Path with error handling 80 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 71

Slide 71 text

Slides by @arghrich UseCase: Rent NotebookType 81 Employee doesn’t have a notebook Employee has enough budget? Notebook with desired type is available? Rent notebook for employee

Slide 72

Slide 72 text

Slides by @arghrich 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.} 82 Slides by @arghrich 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 73

Slide 73 text

Slides by @arghrich 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.} 83 Slides by @arghrich Where did all my business rules go?

Slide 74

Slide 74 text

Slides by @arghrich 84 Slides by @arghrich Goto considered very harmful, so please never use this in C# 1. [Fact(DisplayName = "We should never ever use the goto statement, it is very harmful, but note that in C# you can")] 2. void ComplexGotoExample() 3. { 4. _output.WriteLine("I'd wager this is hard to understand"); 5. var isDone = false; 6. var repeatCount = 0; 7. 8. start: 9. if (isDone) 10. goto end; 11. _output.WriteLine("First"); 12. 13. 14. 15. 16. repeat: 17. if(repeatCount > 3) 18. { 19. isDone = true; 20. goto start; 21. } 22. _output.WriteLine("Repeat"); 23. repeatCount++; 24. goto repeat; 25. 26. end: 27. _output.WriteLine("End"); 28.}