004: Henne oder Ei: Was brauchen wir zuerst?
Über die Herausforderungen eines Green-Field-Projects …
21.03.2023 54 min
Zusammenfassung & Show Notes
Benjamin und Sven unterhalten sich über die alltäglichen Herausforderungen während der Entwicklung eines Produkts, das von Grund auf entsteht.
Die Herausforderungen dabei:
Die Herausforderungen dabei:
- Wo sollen wir starten?
- Fehlende Frameworks und Grundlagen für Datenbankzugriffe, Validierung und Fehlerbehandlung
- Aufbaue einer Code- und Package-Struktur
- Anbindung an externe Dienste wie Datenbanken, Message-Broker und Mail-Server
Wie gewohnt schweifen wir dabei ab und reden über
- Type-driven Development
- Funktionale Fehlerbehandlung
- ktor-Module
- DAO-Schnitte
Unter Berücksichtigung all dieser Herausforderungen fragen wir uns, ob die Umsetzung der fachlichen Funktionalität als Self-contained-System wirklich eine gute Idee war.
Bei der Erkenntnis der Woche gibt's (mal wieder) ein Loblied von Benjamin auf jOOQ (und nein: Wir werden nicht dafür bezahlt).
Code-Beispiel zum funktionalen Fehlerhandling mit either und bind
Code-Beispiel zum funktionalen Fehlerhandling mit either und bind
fun readTeams(): Either<Throwable, List<Team>> { ... } fun readUsers(): Either<Throwable, List<User>> { ... } fun assignUserToTeam(user: User, team: Team): Either<Throwable, Unit> { ... } suspend fun addAllUsersToAllTeams(): Either<Throwable, Unit> = either { val teams = readTeams().bind() val users = readUsers().bind() teams.forEach { team -> users.forEach { user -> assignUserToTeam(user, team).bind() } } }
Links
Transkript
Henne oder Ei? Herausforderungen eines Greenfield Projekts.
Herzlich willkommen zu unserer vierten Episode mit einem super poetischen Titel.
Wie schon angekündigt, geht es heute um die Herausforderungen,
die uns so im Alltag begleiten,
bei der Entstehung eines Projekts, wo einfach am Anfang nichts da ist.
Ich bin Sven und ich bin hier zusammen mit …
Benjamin.
Der Anfang eines solchen Projekts. Der ist ja gar nicht so leicht.
Also wir hatten schon irgendwie über Architektur geredet, was so eine Sache
war. Aber irgendwann muss man ja auch hingehen und muss die ersten Zeilen Code schreiben.
Und ich erinnere mich noch, in der Vergangenheit gab es da mal so ein Projekt,
da man zu dem Zeitpunkt, als die ersten Zeilen Code geschrieben wurden
– Es war nicht bei Orgavision,
da wird uns so was nicht passieren – gab es noch gar keine Anforderung,
sondern die waren auch noch in der Mache gewesen.
Und da erinnerte ich mich auch noch, dass das darauf hinauslief,
dass dann erst mal gefühlt Wochen. Ich glaube, es waren aber wirklich Wochen.
Eine Loginmaske entwickelt wurde, weil den braucht man ja immer.
Es waren Wochen. Es hat sich irgendwie gefühlt ewig hingezogen.
Ja, genau. Und am Ende hat man natürlich immer die Herausforderung:
Mit irgendwas muss man anfangen. Man braucht einen Aufhänger.
Und deswegen war es bei uns einfach so, dass wir von Anfang an User-Story-orientiert gearbeitet haben.
Ich glaube, wir hatten ab dem ersten Tag die erste User Story, Benjamin?
Ja, hatten wir.
Und das ist dann natürlich spannend, weil das eigentlich eine Story in einem
bestehenden Produkt wäre, die relativ überschaubar wären.
Und in dem Szenario war es dann halt eine Story, die glaube ich,
anderthalb Monate gedauert hat.
Ja, ich glaube nicht die erste Story alleine, aber es war am Anfang im ganzen
so, jede Story am Anfang hat sich gefühlt ewig hingezogen, bis wir da mal Ergebnisse,
hatten und es waren halt durch die Bank, welche in einem bestehenden Produkt
hätte man die wahrscheinlich alle innerhalb von wenigen Tagen umsetzen können?
Warum waren das so?
Ja, also wenn man an einem bestehenden Produkt arbeitet, was es vielleicht schon
seit vielen Jahren gibt, dann hat man regelmäßig die Situation,
man macht irgendwas und dann verwendet man ein Haufen Frameworks,
die man sich intern schon geschaffen hat.
In der Regel läuft es darauf hinaus, dass man jedes Mal denkt das ist hier nicht
gut, das hat man damals so gemacht, aber heute würde man das eher so und so
machen. Das müsste man mal ändern.
Es geht halt nicht, weil das überall verwendet wird und man wahnsinnig viel
anpassen müsste, um es zu ändern.
Und im Greenfield Project denkt man sich dann erst mal Super,
ich kann jetzt alles richtig machen.
Und wenn er dann anfängt zu entwickeln, stellt man fest, dass man nichts hat,
worauf man aufbauen kann. Also am Anfang wirklich einfach mal nichts.
Es ist keine einzige Klasse da, kein Stück Code, gar nichts.
Und man muss halt diese ganzen Sachen, über die man sich in alten Projekten
dann oft aufregt, muss man sich erst mal erschaffen.
Und dann, also am Anfang war es wirklich so, dass gefühlt für jede Zeile Code,
den wir schreiben mussten, mussten wir erst mal ein paar Zeilen Produktiv,
Framework Code produzieren, um das dann auch immer wieder gleich machen zu können,
wiederverwendbar Sachen zu haben, um dann erst mal auf irgendwas aufsetzen zu können.
Ja, das fängt so ganz primitiv schon bei so Sachen an. Wir brauchen ein Datenmodell,
da hatten wir ja gesagt, wir machen das über Liqubase.
Ja, es ist halt nicht damit getan, Changelog irgendwo anzulegen und da jenseits
reinzupacken. Es muss auch durch irgendwas ausgeführt werden und schon allein
das muss man dann erst mal ins Code schreiben.
Und vielleicht noch wichtig zu erwähnen, dass du hier mit Framework natürlich
nicht meinst, dass wir uns jetzt unsere eigenen Frameworks für STANDARD Aufgaben
schreiben, sondern du meinst im Prinzip den Anwendungsrahmen.
Deswegen passt ja Framework da ganz gut, weil irgendwie muss am Anfang halt
irgendwie ein bisschen was da sein.
Ja, genau das kann ich mir gut vorstellen, auch wenn man den ersten Datenbankzugriff machen will.
Dann fällt einem plötzlich auffallen auch eine Connection dafür.
Idealerweise hat man einen Connection Pool dafür und abhängig davon,
was man macht, muss man sich vielleicht auch um Transaktionshandling kümmern in irgendeiner Form.
Und da fehlt dann einfach eine ganze Menge. In der bestehenden Anwendung ist das was.
Das macht man einfach. Das ist so nebenbei, da gibt es ja schon alles.
Im Zweifelsfall packt man irgendein Annotation rein, damit eine Transaktion
ausgeführt wird. Oder man hat halt irgendwie anderen Code, aber man hat es.
Ist bei uns inzwischen auch so, aber als wir angefangen haben,
hatten wir nichts davon und mussten halt wirklich alles erst mal uns überlegen,
wie wollen wir es überhaupt machen und es dann auch so entsprechend durchziehen
und den Code dafür schreiben.
Hat da aus deiner Sicht die Wahl des Frameworks auch was mit zu tun?
Weil wenn ich jetzt spring jünger wäre, was ich nicht bin, dann könnte ich ja
sagen na ja, aber gut, Datenbankconnection, dann mach ich da halt meine Klasse,
schmeiß eine Notation ran an den Konstruktor und dann bekomme ich die reingegeben.
Natürlich hat das auch was damit zu tun. Ich meine, wir hatten auch ein bisschen
später ja die Situation, dass wir dann sagten okay, jetzt haben wir hier Jobs,
die wir einfach mal gesgat und regelmäßig ausführen müssen, gehabt.
Von Spring gab es da auch was fertig bei.
Darum müssen wir uns dann auch selber kümmern.
Da hätte uns Spring in der Anfangsphase durchaus dann auch ein bisschen Zeit
gespart, weil wir es dann halt einfach da vorhandenes Framework hätten nutzen können.
Es hätte uns dann allerdings andererseits auch Dinge aufgezwungen,
die wir gar nicht haben wollen, weil wir es dann halt wirklich komplett hätten nutzen müssen.
Genau das ist ja das Problem. Man muss es auch an Stellen nutzen,
wo man sagt, man möchte ja eigentlich eine alternative Lösung.
Das ist so diese, diese leidige Wahl zwischen dem Jetzt zum Beispiel bei der
Hamburg Auswahl, die leidige Wahl zwischen dem da gibt es eins, was alles kann.
Oder ich suche mir für jeden Problemfall den Besten. Und um bei dem Beispiel
zu bleiben die Jobausführung.
Da habt ihr dann ja glaube ich auch das Framework genommen, was bei Spring im
Hintergrund dahinterhängt.
Genau. Also das heißt auch da dann nicht selber entwickelt. Und viele Sachen
hat ja auch Spring gar nicht selbst gebaut, sondern da werden dann halt Frameworks
zusammengesammelt und in die Spring logik eingegliedert.
Ja, ja, ja, genau. Also, ich weiß es nicht hundertProzentig,
aber ich meine Spring intern auch Quarz verwendet, was eigentlich so das STANDARD
Job Framework für Java ist.
Ich glaube, es gibt noch andere, aber ich glaube nicht, dass irgendein anderes
wirklich eine Rolle spielt im Sinne von es wird in vielen Projekten verwendet.
Ja, und letztendlich ist es auch kein Hexenwerk, das einzubinden und zu verwenden.
Und jetzt haben wir es. Wenn wir jetzt Jobs starten wollen, ist es auch ganz einfach.
Am Anfang hatten wir es nicht und auch da mussten wir es erstmal einbinden und
uns damit auseinandersetzen, wie wir die benötigte Datenbank dafür ein.
Also die hätten wir nicht unbedingt nutzen müssen, aber wir wollten das so machen,
also haben wir sie, muss dann halt auch erstellt werden usw. und so fort.
Eine eigene Datenbank mit oder.
Ja, letztendlich sind da auch Jobs gesteuert, weil wir ja auch so Sachen machen
wollen, dass wir Jobs anlegen, die dann halt wirklich regelmäßig ausgeführt
werden. Und auch wenn die Anwendung neu gestartet wird und dafür ist es dann
halt ganz gut, wenn die Jobs in der Datenbank abgelegt sind.
Aber es bringt keine eigene Datenbank mit, sondern es wird in unserer PostgreSQL gespeichert. Okay.
Nein, es wird in unserer Datenbank abgelegt.
Ihr habt keine eigene Datenbank, das wäre nicht sinnvoll. Es sind halt eigene Tabellen. Ja.
Ja, okay, wir haben was auch mal erwähnt. Es war nur so Thema Validierung von
Daten wäre auch eine auch so eine Sache gewesen, wo man dann am Anfang merkt
ups, da habe ich ja noch gar nichts. Was meinst du damit?
Ja, also das ist auch so ein bisschen so ein Thema, das hätten wir so nicht
unbedingt machen müssen. Es bringt uns aber einiges an Vorteil.
Also da vielleicht mal ein kurzer Schritt zurück.
Ich persönlich habe eigentlich immer so die Wunschvorstellung,
dass wenn ich den Code kompiliert kriege, dass das bedeutet, er funktioniert,
dass ich keine Zeile Text schreiben muss und auch keine Code brauchen,
weil der Compiler praktisch alles so checkt, dass ich danach weiß, es funktioniert.
Das ist unrealistisch. Wird wahrscheinlich nie funktionieren,
es sei denn, man hat dann irgendwie extrem gute künstliche Intelligenz im Compiler
drin. Glaube ich eher nicht dran.
Nein, du musst. Du musst ihnen dann noch mal beschreiben, was du eigentlich
erwartest, was die Software tut.
Ja, genau.
Und ich glaube, das nennt man Testing. Ja, gut.
Ja, vielleicht, aber man kann an einigen Stellen schon sehr gut arbeiten,
indem man nämlich so Sachen macht, wie zu sagen okay, ganz primitiv angefangen.
Wenn ich Werte habe, die optional sind, dann sind die eben auch in meinen Klassen
als Build definiert. Wenn ich Werte habe, die nicht optional sind,
dann sind sie auch nicht in meinen Klassen.
Da kann man aber noch einige Schritte weiter gehen, indem man nämlich zum Beispiel
auch validiert, dass ein String eine bestimmte Maximallänge nicht überschreitet,
oder dass er einen bestimmten Muster erfüllt, ein bestimmtes Pattern erfüllt usw. und so fort.
Ein bestimmtes Pattern erfüllt.
Und wenn er dann spezialisierte Typen dafür hat, dann wird es eben letztendlich
auch wieder durch den Compiler sichergestellt, dass ich nicht an irgendeiner
Stelle, zum Beispiel auf unterster Ebene im DAO Code plötzlich Text reinkriege, der ungültig ist.
Das heißt, das ist so ein bisschen das, was aus dem Typ Driven Development gab
es, glaube ich, mal eine Zeit lang als Stichwort.
Das heißt, du nutzt nicht einfach überall einen String, sondern du sagst stattdessen
Das hier ist ein Vorteil. Ja.
Ja, zum Beispiel. Also in dem Fall, wir haben jetzt auf der Ebene keine nicht
unbedingt sprechende Titel, sondern dann halt so Sachen wie None,
mdstring 80 für einen String, der darf nicht leer sein.
Das ist halt auch wieder so ein Punkt String nicht.
Das ist schön und gut, aber wenn ich dann am Ende den leeren String reinreichen
kann, dann habe ich im Grunde nichts gewonnen.
Denn was ist der Unterschied zwischen gar nicht gesetzt und ich habe einen leeren String?
Den kann es geben, aber es ist sehr anwendungs, oder? Das ist tatsächlich eine.
Ja, es ist.
Fachliche Anforderung, die, um die es da geht.
Es ist in den meisten Stellen fragwürdig und überall wo wir es bisher haben,
ist es halt so, dass man ganz klar sagen muss, wenn da was steht,
dann sollte auch was drinstehen und halt nicht nur Leerzeichen oder so was.
Genommen.
Und von daher haben wir praktisch die Stelle, wo wir sagen können,
überall da, wo wir Daten von der Oberfläche kriegen, also in dem Fall über die
REST API, validieren wir die, dass die unseren Anforderungen genügen.
Wenn Sie es nicht tun, gibt es sofort einen Fehler. Und bei allem,
was weitergeht bei uns sind die Typen so gestrickt, dass keine ungültigen Daten drin stehen können.
Okay. Das heißt, ab der Validierung sind sie in unserer Typisierung drinnen,
und damit ist es dann durch den Rest der Anwendung sichergestellt. Okay.
Genau. Und da ist es jetzt auch nicht so, dass wir uns komplett selber ein Framework,
geschrieben haben, sondern wir, wir, wir verwenden da auch was Fertiges,
was auch ganz, ganz viele Sachen dann noch mit abtesten kann.
Da könnte man theoretisch sogar auch so Sachen testen wie irgendwas,
was einem übergeben wurde, ist eine E Mail Adresse.
Das kann natürlich nicht prüfen, ob die gültig ist oder nicht und ob die auch
dem Anwender gehört, der sie eingetragen hat. Aber das kann halt anhand von
regulären Ausdrücken prüfen.
Also zumindest vermute ich, dass das über reguläre Ausdrücke läuft.
Ob das, ob die E Mail Adresse vom Muss daher gültig ist.
Ja, und da geht eine ganze Menge. Man könnte auch so Sachen machen,
wenn, dann irgendwo, weil sie nicht in einem passen.
Objekt ein Datumsobjekt hat, dass man sagt okay, das Datum darf nicht vor irgendwas
liegen, wo wir halt sagen, es ist unrealistisch, um eingrenzen zu können,
dass sinnvolle Daten eingegeben werden.
Da nutzen wir noch gar nicht alles, was an Features da ist. Aber wir haben eine
ganze Menge, was wir dann einfach verwenden können.
Und inzwischen haben wir halt auch bei uns intern das Framework und auch eine
gewisse Vorgehensweise festgelegt. Wie.
Wie sorgen wir dafür, dass die Validierung stattfindet, so dass wir dann halt
im Hintergrund sagen können Ja, ich bin jetzt hier auf der sicheren Seite,
ich muss jetzt nicht mehr viel machen, weder im Code noch in den Tests,
um dafür sorgen zu können, dass die Daten halt gültig sind.
Wie heißt dieses Validierungsframework?
Ich müsste selber noch mal nachgucken, weil Ktor heiß ist, glaube ich.
Das sollte man noch in den Schulen verlinken.
Ja, genau. Das klingt ja nach einem. Klingt ja nach den üblichen Problemen.
Ja, du sagst es dir so schön an der Stelle. Dann haben wir da schon bestimmte
Vorgehensweisen, wie wir das mit der Validierung machen.
Das würde wahrscheinlich auch so ein grundlegendes Problem sein,
erstmal allgemeine Muster Patterns,
zu etablieren, die man in einer schon existierenden Anwendung schon hat,
die aber hier ja auf einem weißen Blatt Papier bietet es ja auch neue Möglichkeiten,
Sachen besser zu machen, die man an Erfahrungen schlecht gemacht hat und somit
auch viele Gefahren und viele neue Sachen falsch zu machen.
Ja, garantiert. Ich bin mir auch jetzt schon sicher, so viel Mühe wir uns auch
gerade geben, wir werden garantiert in zwei Jahren spätestens die Situation
haben, dass wir bei einigen Sachen denken Ach verdammt, hätten wir das mal so und so gemacht.
Aber ja, so Vorgehensweisen haben wir an vielen Stellen auch. Das ist das Problem.
Also jetzt mal ein Beispiel dafür Fehlerbehandlung.
Wir versuchen ja hier möglichst funktional zu arbeiten, was für mich dann auch
bedeutet, wir werfen keine Exception, sondern die Funktionen haben dann irgendwie
Rückgabewerte, die dann.
Also an den meisten Stellen ist es eigentlich nie da und im Fehlerfall war es
am Anfang so, dass dann Strobel drin stand. Also halt doch irgendwie eine Exception und.
Vielleicht führst du mal kurz aus für Leute, die jetzt bisher hauptsächlich.
Hm, ja, also da ist ein Rückgabewert, der heißt einer von zwei Fällen sein kann, also entweder oder.
Mit Exceptions gearbeitet haben, wie das funktioniert?
Entweder oder.
Eingebürgert hat sich da als der als Bezeichnung für die beiden Typen left und right.
Ich total bescheuert finde, im Sinne von Clean code semantischer Benennung.
Er ist halt in englischsprachigen Raum ist das right dann durchaus klar.
Das ist halt das korrekte Ergebnis und dann ist das left als Alternative dazugekommen.
Ich finde es auch nicht so übermäßig gut, man gewöhnt sich aber dran.
Und dieser Typ oder Ida, ich weiß gar nicht, wie man es ausspricht,
der kommt aus der Kotlin Standardlib, oder?
Nein, kommt er tatsächlich nicht. Es gibt bei Kotlin auch was drinnen.
Das ist so ein Result, dass es im Grunde genommen. Also auf dem ersten Blick ist es das Gleiche.
Du hast entweder das Ergebnis, was du haben wolltest, oder eine Exception.
Genau letzteres ist für mich so ein bisschen das Problem dabei.
Ich kann mich da nicht hinstellen und sagen Nee, fehlerfrei möchte ich was anderes
als eine Exception zurückgeben.
Das ist nämlich der Punkt. Wir hatten halt für uns intern einfach auch gesagt okay,
wenn wir Bibliotheken aufrufen, die aus der Java Welt kommen,
wo wir halt wissen, da werden Exceptions geschmissen, dann rufen wir die immer
so auf, dass wir die Exceptions fangen und dann halt,
in unser gewünschtes Ergebnis transformieren.
Was wir aber nach Möglichkeit nicht machen wollten, selber Exceptions zu werfen.
Denn wenn man damit dann mal irgendwo anfängt, dann wird es auch so ein bisschen inkonsistent.
Und was man bei Exceptions immer auch bedenken muss Exceptions sind von der
Performance nicht unbedingt so gut.
Denn das Ermitteln des Traces kostet einiges an Zeit.
Es ist Reflektion.
Und ich erinnere mich noch, dass ich in irgendwie nicht der letzten,
sondern der vorletzten Firma.
Da habe ich dann irgendwann mal auf irgendeinem Weg dafür gesorgt,
dass bei den von uns selbst erzeugten Exceptions im Test für die Tests keins
der Trace generiert wurde und die Tests liefen plötzlich deutlich schneller.
Ja, also die Exceptions können einiges an Performance kosten und deswegen fanden wir es.
Also unter anderem deswegen fanden wir es halt auch nicht sinnvoll,
selber welche zu werfen.
Und was wir inzwischen gemacht haben, ist, dass wir so eine eigene Basisklasse haben.
Error, die wir halt verwenden, um um irgendwie zu signalisieren hier ist was
falsch gelaufen und unsere Rückgabewerte sehen eigentlich immer so aus, dass wir haben ein einer,
Links, also der der ungültige Typ ist dann halt ein Error und der gültige Typ
ist dann halt irgendwas anderes, was an der Stelle einfach sinnvoll ist.
Und der Error keine Exception. Oder halt auch was anderes sein.
An einer Stelle, wo ich halt keine Exception habe, sondern wo es um Code von
uns geht, der schon gar keine Exception wirft, sondern einen anders gearteten Fehler zurückgibt.
Zum Teil ist es halt auch so, dass wir dann weiß ich nicht in einem DAO ein
paar Exceptions, die wir von Joku erwarten fangen und daraus spezielle Errortypen
generieren, die halt dann eher so fachlicher, fachlicher Natur sind.
Von daher wir übersetzen dann zum Teil auch Exceptions, die kommen.
Aber im Zweifelsfall haben wir dann zum Beispiel auch einen Typ Annex Error.
Da steckt dann halt die Exception drin, die geflogen ist. Und das ist das,
was notfalls zurückgegeben wird, wenn irgendeine Exception auftritt,
mit der wir halt nicht gerechnet haben.
Und dieser Error ist es einfach nur. Also die Autos, die von uns kommen.
Klang jetzt nicht so, als wenn es einfach nur ein String wäre,
sondern wenn man da tatsächlich auch wieder eine Basisklasse haben oder wie?
Wie kann ich mir das vorstellen?
Ja, wir haben letztendlich eine Basisklasse, einfach um, um irgendwie einen
gemeinsamen Typ zu haben, dass wir halt immer sagen können, hier kommt eben
in irgendeiner Form Error zurück.
Allerdings haben wir da jetzt nicht unbedingt eine komplexe Hierarchie aufgebaut,
sondern die meisten Sachen sind halt direkt davon abgeleitet.
Ja, genau. Das heißt, wenn wir uns noch mal so ein bisschen eine einfache Exception
Hierarchie nachgebaut, nur dass es halt nicht die nativen Java Exceptions mit.
Genau. Genau. Aber das war halt auch so ein Weg, bis wir dahin gekommen sind.
Ihren Vor und Nachteilen sind.
Dass wir halt von vornherein gesagt hatten Nee, wir wollen nicht mit Katz und
Frau arbeiten, sondern wir wollen halt mit da arbeiten.
Erst waren Exceptions drin, dann haben wir angefangen, eigene Exceptions zu
generieren, um den Rückgabewert als einer Probe oder irgendetwas anderes zu behalten.
Fanden dann selber, dass das irgendwie nicht gut ist. Und ja,
bis wir dann halt da hingekommen sind, das war halt auch ein Weg,
der funktioniert bisher sehr gut.
Also ich bin momentan total zufrieden damit und wüsste nicht,
warum wir da was ändern sollten. Wer weiß, wie es in einem Jahr aussieht oder
schon in einem halben Jahr.
Aber das sind halt so die ganzen Punkte, die wir an vielen Stellen haben.
Zum Teil im Kleinen, zum Teil im Großen, wo wir mit Sachen anfangen,
erst mal etwas auf eine bestimmte Art und Weise machen, dann mit der Zeit feststellen,
es ist irgendwie nicht das was, was wir wollen.
Es fühlt sich komisch an oder es erzeugt Aufwände, die wir nicht haben wollen.
Und dann kommen wir halt so schrittweise bei dem an, was wir später wirklich haben wollen.
Ich muss noch immer den Fehler finden. Nach vorne. Das ist ja mal so Exceptions.
Gott waren ja schon episch diskutiert und haben alle ihre bekannten Vor und Nachteile.
Und dann gibt es ja noch bei Java spezifisch die Check, die Check,
das Konzept der checkt Exceptions, wo schon in der Methodensignatur deklariert ist.
Das Ding hier wird eine Exception und dann muss ich die im Code danach auch
behandeln oder halt wieder deklarieren, dass ich die weiter schmeiße.
Okay, da haben wir alle, glaube ich auch über Jahre gemerkt.
In manchen Situationen ganz nett, aber irgendwie nie so richtig.
Der Weisheit letzter Schluss ist es auch nicht.
Aber der Weg, den du ja jetzt beschreibst, der klingt ja eher so ich sage mal,
wie man früher programmiert hat. Ich erinnere mich noch an die Windows 32 API.
Die gibt dann immer hat dann glaube ich immer ein Harry sei zurückgegeben.
Das war so ein 32 Bit wert und da war dann irgendwie, wenn der null war,
war alles gut und wenn nicht, dann war das ein Fehler.
Das wirkt jetzt auch wieder so ein bisschen danach und nur danach,
als wenn es anders scheiße ist.
Ganz ehrlich. Fehler ist glaube ich immer scheiße. Weil du es eigentlich ja
nicht haben will. Eigentlich will sie ja keine Fehler drin haben.
Nicht.
Ich sehe da aber verschiedene Unterschiede bei dem bei dem Thema,
so wie es bei C war. Da war dann ja auch der Klassiker.
Wenn du eine Funktion hattest, die irgendwas zurückgegeben hat.
Hast du erst mal gemacht.
Dann hast du den Rückgabewert.
Ja, also das wo der Rückgabewert wird den Speicherbereich, wo der Rückgabewert
sein sollte, hast du als Pointer an die Funktion übergeben. Und die Funktion
hat eben in irgendeiner Form der Zeit zurückgegeben.
Stimmt. So war das damals.
Und das ist halt schon vom Gesamtkonzept her ist das halt schon,
ja, sagen wir mal unschön.
Die die Situation haben wir an der Stelle nicht, weil wir mit dem Viewer schon
sehr klar definierten Rückgabewert haben und du bist dann halt auch in der Situation, also,
was ja im Vergleich zu diesem alten C Weg beim Exception Handling ganz gut ist,
dass wir halt auch einfach sagen kannst, ich ruf eine Funktion an,
die gibt mir was zurück und mit dem Rückgabewert mach ich was.
Und wenn eine Exception fliegt, die interessiert mich an der Stelle vielleicht.
Da kümmere ich mich erst mal nicht drum. Mach ich später. Was dann nie passiert.
Gar nicht. Ich kümmer mich erst mal nicht drum.
Ja genau, es passiert dann hoffentlich an irgendeiner Stelle später.
Aber wenn ich jetzt irgendwo so in der Zwischenschicht bin, wo ich sage,
mich interessiert der Fehler hier nicht, der muss, dann muss ich weiter oben
jemand drum kümmern, dann ist das völlig okay.
Das kann ich mit dem Eimer auch machen. Das ist eine Monade.
Ich habe eine Funktion oder auch eine Funktion, je nachdem, wie ich damit weiter vorgehen will.
Das heißt, ich kann mich halt auch einfach hinstellen. Ich nehme jetzt diesen
Rückgabewert und ich mache damit nur was für den Fall, dass das erfolgreich
gelaufen ist, wenn kein Fehler aufgetreten ist.
Da muss ich schon ein bisschen mehr Code schreiben, als wenn da eine Exception
geworfen wird, wo ich dann ja einfach mich nicht darum kümmere,
sondern ich muss halt schon über Map Aufruf explizit sagen, okay,
ich mache jetzt was mit dem Wert, der im Erfolgsfall kommt.
Und ganz ehrlich im Fall eines Fehlers, da macht das alles keinen Unterschied,
ob da nun irgendwo ein Catch Block heißt, wo du prüfst, was für eine Art von
Exception das ist und darauf basierend, was machst
oder ob du irgendwo in einem Block eine Zahl, die zurückgekommen bist auf verschiedene
Konstanten, die sich hoffentlich nicht ändern, mit irgendeiner Version prüfst,
um zu sehen, was ist falsch gelaufen, was muss ich jetzt machen,
oder ob du halt wie bei uns jetzt diesen Error Typ hast im Notfall und dann halt über.
Halt in einem mit einem Faktor Handel oder was auch immer man dann sinnvollerweise
macht wenn ein Fehler aufgetreten ist, dann mache ich jetzt das und das.
Das ist dann im Grunde egal.
Noch mehr Fragen dazu. Wahrscheinlich hätten wir die Episode lieber umbenennen sollen.
Ihr habt euch aber trotzdem entschlossen, den AIDA Typ zu nehmen und nicht zu
sagen, ihr definiert noch mal euren eigenen Reiz als Teil, der dann auf der
einen Seite ein Errorhead also, der im Prinzip AIDA nur mit einem anderen Namen ist.
Aber ich finde immer noch, dieses AIDA ist so unspezifisch, Du kehrst hier irgendwas zurück.
Klingt und klingt so ein bisschen arg groß.
Wie ist das bei Kotlin nativen Libraries? Wie melden die denn Fehler? Nutzen die also?
Gibt es da irgendeine Einheitlichkeit oder macht das jeder anders und nutzen
die auch so was wie eiser oder nutzen die dann eher diesen?
Ich glaube da hieß es da am Anfang oder schmeißen die dann doch wieder Exceptions?
Bei Cato zum Beispiel.
Sammlung von funktionalen.
Und dann meine hoffentlich letzte Frage zum Error Handling. Bevor wir mal weitergehen,
um wie es jetzt deine Erfahrung so nach der Zeit, die ihr das jetzt nutzt.
Weil ich stelle mir vor, dass das Boiler Plate produziert, weil ich kann ja
im Prinzip keine Java Funktion oder Library Funktion direkt aufrufen,
sondern ich muss die ja immer noch mal rappen, damit ich dann quasi in einer zurück kriege.
Fühlt sich das irgendwie als Overhead an oder ist das Gefühl,
der Code wird da dogmatisch unübersichtlich?
Um einen bestimmten Use Case abzudecken?
Klingt auch wieder so ein bisschen wie try catch. Aber ja, ja.
Es ist ganz spannend. Ich habe gerade doch wieder die Analogie der Hektik,
weil ich habe natürlich schon den Fall nebenan.
Ich rufe jetzt irgendwo eine Funktion auf. Ich wollte eigentlich vom Fehler wegkommen, aber egal.
Nehmen wir mal einen Fall. Ich habe eine.
Ich rufe irgendeine Funktion auf, die ich auch selber implementiert habe.
Die gibt es ja. Ein einfaches Spiel sei zurück und später merke ich halt Verdammt,
da muss ich jetzt noch ein bisschen mehr machen. Ich muss dann auch auf die Datenbank zugreifen.
Dann habe ich schon wieder so ein Stück weit den Fall wie bei Exceptions,
dass ich im Prinzip die gesamte Aufrufhierarchie hoch den Rückgabewert jetzt
ersetzen muss durch einen Eimer.
Also da sind dann so die die Nachteile, die man auf.
Und schneiden kann man diese Module dann wie man will, aber sinnvoll wäre irgendwie
was fachlich orientiertes, was weiß ich, wie ein Kontext im Domain driven Design,
mit dem ich eine bestimmte Funktionalität könnte.
Benjamin
00:00:43
Sven
00:00:45
Benjamin
00:01:20
Sven
00:01:24
Benjamin
00:01:40
Sven
00:01:41
Benjamin
00:01:53
Sven
00:02:11
Benjamin
00:02:12
Sven
00:03:40
Benjamin
00:04:04
Sven
00:04:46
Benjamin
00:05:04
Sven
00:05:37
Benjamin
00:06:11
Sven
00:06:55
Benjamin
00:06:57
Sven
00:07:10
Benjamin
00:07:11
Sven
00:07:19
Benjamin
00:07:30
Sven
00:08:06
Benjamin
00:08:09
Sven
00:08:11
Benjamin
00:08:15
Sven
00:08:46
Benjamin
00:08:51
Sven
00:09:04
Benjamin
00:09:15
Sven
00:09:39
Benjamin
00:09:41
Sven
00:09:44
Benjamin
00:09:46
Sven
00:09:55
Benjamin
00:09:57
Sven
00:10:19
Benjamin
00:10:26
Sven
00:11:50
Benjamin
00:11:52
Sven
00:11:57
Benjamin
00:12:36
Sven
00:13:19
Benjamin
00:13:23
Sven
00:13:24
Benjamin
00:13:35
Sven
00:13:41
Benjamin
00:13:44
Sven
00:13:58
Benjamin
00:14:04
Sven
00:15:07
Benjamin
00:15:10
Sven
00:15:58
Benjamin
00:16:11
Sven
00:16:42
Benjamin
00:16:53
Sven
00:17:11
Benjamin
00:17:18
Sven
00:17:19
Benjamin
00:17:24
Sven
00:18:25
Benjamin
00:19:37
Sven
00:19:45
Benjamin
00:19:45
Sven
00:19:54
Benjamin
00:19:55
Sven
00:20:07
Benjamin
00:20:08
Sven
00:20:40
Benjamin
00:20:41
Sven
00:22:16
Feedback geben
Wir freuen uns über Dein Feedback – sowohl zu unseren technischen Erlebnissen und Entscheidungen als auch zu unserem Podcast. Gibt es vielleicht ein Thema, dass Du gerne behandelt haben möchtest? Dann kontaktiere uns hier gerne.