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

[DE] Fixing the Billion Dollar Mistake (C# Brille)

Richard
February 08, 2022

[DE] Fixing the Billion Dollar Mistake (C# Brille)

Sir Anthony Hoare bezeichnet die Null-Referenz als seinen Milliarden-Dollar Fehler, weil sie zu "unzähligen Fehlern, Schwachstellen und Systemabstürzen geführt hat, die in den letzten vierzig Jahren wahrscheinlich eine Milliarde Dollar an Schmerz und Schaden verursacht hat."

Ob die Größenordnung stimmt, muss die Wissenschaft erst noch beweisen, aber ich denke, man kann mit Fug und Recht behaupten, dass Null- Referenzen eine der Hauptursachen für Schlafentzug bei Entwicklern sind, die kurz vor dem Release stehen. Glücklicherweise wurden die meisten Programmiersprachen um alternative Möglichkeiten erweitert, mit der "absence of value" umzugehen, was zu aussagekräftigerem Code, weniger Raum für Fehler und besserem Schlaf führt.

In diesem Vortrag werden wir die Nullability-Möglichkeiten erkunden, die C# 8 und 9 bieten. Dies vergleichen wir mit den Wege, die andere Sprachen wie Python, Kotlin oder F# gewählt haben. Und werfen auch einen Blick auf Techniken wie Railway-Oriented Programming oder das Null-Object-Pattern, die helfen können, wenn die Sprache der Wahl keine Option bietet.

Gehalten bei der JUG-Hamburg: https://www.meetup.com/de-DE/jug-hamburg/events/283401850/

Richard

February 08, 2022
Tweet

More Decks by Richard

Other Decks in Programming

Transcript

  1. Folien von @arghrich
    Fixing the
    Billion Dollar Mistake
    (C# Brille)
    08.02.22
    richargh.de/
    speakerdeck.com/richargh
    Richard Gross (er/ihm)
    Arbeitet bei maibornwolff.de/

    View Slide

  2. Folien von @arghrich
    5
    € Arbeiten
    mit “nix”
    Effektives
    “nix”
    Fixing the
    Mistake in
    C#

    View Slide

  3. Folien von @arghrich
    Was ist der
    gefährlichste Fehler,
    den Programmierer
    machen?
    6

    View Slide

  4. Folien von @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. // Java binary Search bug
    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. // …

    View Slide

  5. Folien von @arghrich
    Der Umgang mit Null
    ist wichtig, aber nicht
    der gefährlichste
    Fehler
    8

    View Slide

  6. Folien von @arghrich
    Trotzdem …
    9

    View Slide

  7. Folien von @arghrich
    Nur 10 Exceptions sind verantwortlich für
    97% aller geloggten Fehler
    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% der geloggten Exceptions nach Häufigkeit
    1. NullPointerException
    2. NumberFormatException
    3. IllegalArgumentException
    4. …

    View Slide

  8. Folien von @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
    Hat aber nicht C#, Java,
    oder Python designt. J

    View Slide

  9. Folien von @arghrich
    13
    € Arbeiten
    mit “nix”
    Effektives
    “nix”
    Fixing the
    Mistake in
    C#

    View Slide

  10. Folien von @arghrich
    Ein Dictionary designen
    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
    Wenden wir das Principle of Least Surprise an:
    Was sollte zurückgegeben werden, wenn die
    personId unbekannt ist?

    View Slide

  11. Folien von @arghrich
    “Nix” modellieren
    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. // …
    Implizit
    Exception
    C#, Python
    throw KeyNotFoundException
    Implizit
    return null;
    Java
    return null;
    Implizit
    return “default”
    C# (bei structs)
    return Address.NULL;
    return default;
    Explizit
    Verpf. Standardwert
    return GetOrDefault(
    personId,
    defaultAddress);
    Explizit
    “Nix” modellieren
    Scala, Kotlin, F#, …
    return ???;

    View Slide

  12. Folien von @arghrich
    Explizites nichts
    1. const address = addresses.Find(personId);
    16
    2. // kann nicht ignoriert werden
    3. log(`Street is ${address.Street}`);
    4. // Nur zugreifbar nach Check
    5. if(address != null)
    6. log(`${address.Street}`)
    Compile error
    Expliziter check notwendig

    View Slide

  13. Folien von @arghrich
    Wäre es nicht toll,
    wenn C# explizites
    „nix“ hätte?
    17

    View Slide

  14. Folien von @arghrich
    19
    Sicherere
    Sprachen
    durch
    Wegfall von
    Optionen
    „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.

    View Slide

  15. Folien von @arghrich
    F# 2005
    F# empowers everyone
    to write succinct, robust
    and performant code
    Hat null aber man kann es
    nicht mehr zuweisen.
    20
    Rust 2010
    A language empowering
    everyone to build reliable
    and efficient software.
    Besitzt kein 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.
    Besitzt nil für Objective-C
    interop.
    Man erkennt einen gewissen Trend
    https://en.wikipedia.org/wiki/Void_safety

    View Slide

  16. Folien von @arghrich
    Auch bestehende
    Sprachen entwickeln
    sich weiter
    21

    View Slide

  17. Folien von @arghrich
    Jeder versucht, mit Null umzugehen
    22
    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]

    View Slide

  18. Folien von @arghrich
    23
    € Arbeiten
    mit “nix”
    Effektives
    “nix”
    Fixing the
    Mistake in
    C#

    View Slide

  19. Folien von @arghrich
    Finde den Fehler
    24
    public string FindNotebookMaker(EmployeeId id)
    {
    var employee = _company.FindById(id);
    if (employee is null)
    return "Employee does not exist";
    /* ... */
    return employee.Notebook.Maker;
    }

    View Slide

  20. Folien von @arghrich
    Warum ist das ein Fehler?
    public string FindNotebookMaker(EmployeeId id)
    {
    var employee = _company.FindById(id);
    if (employee is null)
    return "Employee does not exist";
    /* ... */
    return employee.Notebook.Maker;
    }
    25
    Aber, aber, aber, ein Employee hat immer ein
    notebook!!1! Ich hab das selbst so programmiert.
    System.NullReferenceException
    at FindNotebookMaker(EmployeeId
    id) in UsesLegacyCode.cs:line 22

    View Slide

  21. Folien von @arghrich
    Der eigentliche Fehler ist wo anders
    public void StartRepairNotebook(EmployeeId id)
    {
    var employee = _company.FindById(id);
    if (employee is null)
    return;
    employee.Notebook = null;
    _company.Put(employee);
    }
    26
    Unter bestimmten Umständen (z. B. wenn das Notebook
    repariert wird), hat ein Mitarbeiter kein Notebook. Dieses
    feature wurde erst viel später hinzugefügt oder vielleicht
    hast du das auch nur vergessen. Der Typ von Employee hat
    das nicht reflektiert.
    public class Employee
    {
    public EmployeeId Id {get;}
    public string Name {get;}
    public Notebook Notebook {get; set;}
    /*…*/
    }

    View Slide

  22. Folien von @arghrich
    Stack traces sind nicht
    hilfreich bei
    NullReferenceExceptions
    27

    View Slide

  23. Folien von @arghrich
    Nullable reference types aktivieren
    1. // per *.csproj file:
    2.
    3.
    4.
    5. enable
    6.
    7.
    8. 8.0
    9.
    10.
    11. nullable
    12.
    13. start without WarningsAsErrors -->
    14.
    28
    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

    View Slide

  24. Folien von @arghrich
    Nach Enable (1)
    public void StartRepairNotebook(EmployeeId id)
    {
    var employee = _company.FindById(id);
    if (employee is null)
    return;
    employee.Notebook = null;
    _company.Put(employee);
    }
    29
    Compile error

    View Slide

  25. Folien von @arghrich
    (2) Explizite nullability Modellierung
    30
    public class Employee
    {
    public EmployeeId Id {get;}
    public string Name {get;}
    public Notebook? Notebook {get; set;}
    /*…*/
    }
    Markieren als nullable

    View Slide

  26. Folien von @arghrich
    (3) Kaskadierende Compile Fehler
    31
    Compile Fehler
    public string FindNotebookMaker(EmployeeId id)
    {
    var employee = _company.FindById(id);
    if (employee is null)
    return "Employee does not exist";
    /* ... */
    return employee.Notebook.Maker;
    }

    View Slide

  27. Folien von @arghrich
    Der Compiler führt uns
    durch den Code
    32

    View Slide

  28. Folien von @arghrich
    Wir haben die Ursache behoben, nicht nur
    ein Symptom
    33
    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);
    }

    View Slide

  29. Folien von @arghrich
    Nullable Reference Types
    helfen uns Bugs an der
    Wurzel zu beheben
    34 Slides by @arghrich

    View Slide

  30. Folien von @arghrich
    36
    € Arbeiten
    mit “nix”
    Effektives
    “nix”
    Fixing the
    Mistake in
    C#

    View Slide

  31. Folien von @arghrich
    Recap: “Nix” modellieren
    37
    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. // …
    Implizit
    Exception
    C#, Python
    throw KeyNotFoundException
    Implizit
    return null;
    Java
    return null;
    Implizit
    return “default”
    C# (bei structs)
    return Address.NULL;
    return default;
    Explizit
    Verpf. Standardwert
    return GetOrDefault(
    personId,
    defaultAddress);
    Explizit
    “Nix” modellieren
    Scala, Kotlin, F#, …
    return ???;

    View Slide

  32. Folien von @arghrich
    Modellieren von “nix” in C#8
    38
    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. // …
    Explizit
    “Nix” modellieren
    Scala, Kotlin, F#, C#

    View Slide

  33. Folien von @arghrich
    Umgang mit “nix” in C#8
    39
    1. // schlecht, kompiliert nicht
    2. string? address = FindAddress(personId).Street;
    3. // gut, benutzt null-propagating operator
    4. string? address = FindAddress(personId)?.Street;
    Explizit
    “Nix” modellieren
    Scala, Kotlin, F#, C#
    https://docs.microsoft.com/en-us/dotnet/fundamentals/code-analysis/style-rules/null-checking-preferences
    5. // vielleicht besser, benutzt null-coalescing operator
    6. string address = FindAddress(personId)?.Street ?? "Konstitucijos Av. 20”;
    7. // vielleicht noch besser, nutzt null-coalescing assignment operator
    8. string? address = FindAddress(personId)?.Street;
    9. address ??= "Konstitucijos Av. 20”;
    10.// gut, benutzt pattern matching is null check
    11.if(address is null) {/*…*/}
    12.if(address is not null) {/*…*/}

    View Slide

  34. Folien von @arghrich
    Die Einführung der Nullable
    Reference Types sind
    wichtiger als Generics
    40

    View Slide

  35. Folien von @arghrich
    Effective
    Nullable reference types
    42

    View Slide

  36. Folien von @arghrich
    43
    #4 Immer angeben, wenn null
    zurückgegeben wird
    1. Address? FindAddress(PersonId pId)
    2. {
    3. return _addresses.GetValueOrDefault(pId);
    4. }
    1

    View Slide

  37. Folien von @arghrich
    44
    #5 Leere collections oder arrays
    zurückgeben, niemals null
    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
    Erschwert die Nutzung
    der Api für alle
    Aufrufer.
    2

    View Slide

  38. Folien von @arghrich
    45
    #5 C#9 records für domain types nutzen
    3
    1. public class Address
    2. {
    3. public string City {get; init;}
    4. public string? Street {get; init;}
    5. // kompiliert nicht; der Compiler
    kann nicht wissen, dass City
    tatsächlich gesetzt wurde
    6. }
    1. public record Address(
    2. string City,
    3. string? Street
    4. );
    1. // so wird ein Record initialisiert:
    2. var address = new Address(
    3. City: “Trakai”,
    4. Street: null
    5. )

    View Slide

  39. Folien von @arghrich
    C#9 records bieten value-equality und
    non-destruktive Mutation
    1. public record Address(
    2. string City,
    3. string? Street
    4. );
    46
    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-destruktive Mutation
    17. var address3 = address2 with {
    18. Street = “Ozo g. 18”
    19. }
    20. address2 == address3 // false

    View Slide

  40. Folien von @arghrich
    47
    #5 Vertraue nicht auf die Non-Null-
    Signatur am Rande deines Systems
    4
    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 was unmöglich sein sollte

    View Slide

  41. Folien von @arghrich
    49
    € Arbeiten
    mit “nix”
    Effektives
    “nix”
    Fixing the
    Mistake in
    C#
    One more thing …

    View Slide

  42. Folien von @arghrich
    50
    Ich habe nullable reference types aktiviert
    aber jetzt habe ich null-checks überall!!11!elf

    View Slide

  43. Folien von @arghrich
    Großartig, Du hast die
    Fehler bereits während der
    Compile-Time behoben
    51

    View Slide

  44. Folien von @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. }
    52

    View Slide

  45. Folien von @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.}
    53
    Wo ist mein
    happy path hin?
    Kurzes Beispiel
    mit nur einem
    null-check

    View Slide

  46. Folien von @arghrich
    Wie können wir die
    business rules wieder klar
    modellieren?
    54

    View Slide

  47. Folien von @arghrich
    Ein seichter Einstieg in das
    Thema Railway-Oriented
    Programming*
    55
    *By Scott Wlaschin https://fsharpforfunandprofit.com/rop/

    View Slide

  48. Folien von @arghrich
    Happy Path ohne error handling
    56
    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. }

    View Slide

  49. Folien von @arghrich
    ROP Path mit error handling
    57
    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. }
    Ich kann die
    business logic
    wieder sehen

    View Slide

  50. Folien von @arghrich
    Fokus auf den Happy Path,
    Result macht den Rest
    58
    J L
    thenTry
    then
    thenTryFix
    thenFix
    1. Result Then(
    2. Func onOk){ /*…*/ }
    3. Result ThenTry(
    4. Func> onOk){ /*…*/ }

    View Slide

  51. Folien von @arghrich
    How to get on the rail
    59
    J L
    Ok(sth) Fail(“why”)
    OfResult(sth?)

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  55. Folien von @arghrich
    How to get started?
    63
    Railway Oriented Programming [Original, Scott Wlaschin 2014]
    Against Railway-Oriented Programming [Scott Wlaschin 2019]
    Railway Oriented Programming: C# Edition [Tama Waddell 2019]
    F# nutzen J Oder Copy/Code eine
    eigene Result class
    Deep-dive talk
    anschauen J

    View Slide

  56. Folien von @arghrich
    Jetzt simma done J
    64

    View Slide

  57. Folien von @arghrich
    Beispiele liegen auf Github
    • https://github.com/Richargh/fixing-the-billion-dollar-mistake
    • Beinhalten Beispiele in C#, F#, Java, Python, Scala
    • Beinhalten Railway-Oriented Programming Beispiele in C#
    • Beinhalten eine Optional-Variation von ROP in Java
    65

    View Slide

  58. Folien von @arghrich
    Ich bin nicht der erste, der über nullable
    reference types spricht :)
    • Null References: The Billion Dollar Mistake [Tony Hoare 2009]
    • Nullable Reference Types in C# 8 • [Jon Skeet GOTO 2019]
    66

    View Slide

  59. Folien von @arghrich
    Ich bin nicht der erste, der über ROP
    spricht :)
    • 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]
    67

    View Slide

  60. Folien von @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
    Arbeitet bei maibornwolff.de/en
    Folien von @arghrich

    View Slide

  61. Folien von @arghrich
    Backup

    View Slide

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

    View Slide

  63. Folien von @arghrich
    UseCase: Change Employee Address
    71
    EmployeeId From
    Request Path
    Find Employee
    Address from Path
    Change Address
    Store changed
    Employee
    Greet Employee with
    new Email

    View Slide

  64. Folien von @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.}
    72
    What about DbExceptions?
    Does not compile. All nullable.

    View Slide

  65. Folien von @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”);
    73
    Where did my
    happy path go?

    View Slide

  66. Folien von @arghrich
    ROP Path with error handling
    74
    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

    View Slide

  67. Folien von @arghrich
    UseCase: Rent NotebookType
    75
    Employee doesn’t have
    a notebook
    Employee has enough
    budget?
    Notebook with desired
    type is available?
    Rent notebook for
    employee

    View Slide

  68. Folien von @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.}
    76
    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

    View Slide

  69. Folien von @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.}
    77
    Where did all my business
    rules go?

    View Slide

  70. Folien von @arghrich
    78
    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.}

    View Slide