building a product

Sven Wiegand – orgavision GmbH

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:
  • 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
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
  • Arrow: „Functional companion to Kotlin's Standard Library“
  • jOOQ: „Build type save SQL queries through a fluent API“
  • valiktor: „Type-safe, powerful and extensible fluent DSL to validate objects in Kotlin“

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
00:00:43
Benjamin.
Sven
00:00:45
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.
Benjamin
00:01:20
Es waren Wochen. Es hat sich irgendwie gefühlt ewig hingezogen.
Sven
00:01:24
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?
Benjamin
00:01:40
Ja, hatten wir.
Sven
00:01:41
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.
Benjamin
00:01:53
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?
Sven
00:02:11
Warum waren das so?
Benjamin
00:02:12
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.
Sven
00:03:40
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.
Benjamin
00:04:04
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.
Sven
00:04:46
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.
Benjamin
00:05:04
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.
Sven
00:05:37
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.
Benjamin
00:06:11
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.
Sven
00:06:55
Eine eigene Datenbank mit oder.
Benjamin
00:06:57
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.
Sven
00:07:10
Aber es bringt keine eigene Datenbank mit, sondern es wird in unserer PostgreSQL gespeichert. Okay.
Benjamin
00:07:11
Nein, es wird in unserer Datenbank abgelegt. Ihr habt keine eigene Datenbank, das wäre nicht sinnvoll. Es sind halt eigene Tabellen. Ja.
Sven
00:07:19
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?
Benjamin
00:07:30
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.
Sven
00:08:06
Nein, du musst. Du musst ihnen dann noch mal beschreiben, was du eigentlich erwartest, was die Software tut.
Benjamin
00:08:09
Ja, genau.
Sven
00:08:11
Und ich glaube, das nennt man Testing. Ja, gut.
Benjamin
00:08:15
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.
Sven
00:08:46
Ein bestimmtes Pattern erfüllt.
Benjamin
00:08:51
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.
Sven
00:09:04
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.
Benjamin
00:09:15
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?
Sven
00:09:39
Den kann es geben, aber es ist sehr anwendungs, oder? Das ist tatsächlich eine.
Benjamin
00:09:41
Ja, es ist.
Sven
00:09:44
Fachliche Anforderung, die, um die es da geht.
Benjamin
00:09:46
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.
Sven
00:09:55
Genommen.
Benjamin
00:09:57
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.
Sven
00:10:19
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.
Benjamin
00:10:26
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.
Sven
00:11:50
Wie heißt dieses Validierungsframework?
Benjamin
00:11:52
Ich müsste selber noch mal nachgucken, weil Ktor heiß ist, glaube ich. Das sollte man noch in den Schulen verlinken.
Sven
00:11:57
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.
Benjamin
00:12:36
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.
Sven
00:13:19
Vielleicht führst du mal kurz aus für Leute, die jetzt bisher hauptsächlich.
Benjamin
00:13:23
Hm, ja, also da ist ein Rückgabewert, der heißt einer von zwei Fällen sein kann, also entweder oder.
Sven
00:13:24
Mit Exceptions gearbeitet haben, wie das funktioniert? Entweder oder.
Benjamin
00:13:35
Eingebürgert hat sich da als der als Bezeichnung für die beiden Typen left und right.
Sven
00:13:41
Ich total bescheuert finde, im Sinne von Clean code semantischer Benennung.
Benjamin
00:13:44
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.
Sven
00:13:58
Und dieser Typ oder Ida, ich weiß gar nicht, wie man es ausspricht, der kommt aus der Kotlin Standardlib, oder?
Benjamin
00:14:04
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.
Sven
00:15:07
Es ist Reflektion.
Benjamin
00:15:10
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.
Sven
00:15:58
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.
Benjamin
00:16:11
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.
Sven
00:16:42
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?
Benjamin
00:16:53
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.
Sven
00:17:11
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.
Benjamin
00:17:18
Genau. Genau. Aber das war halt auch so ein Weg, bis wir dahin gekommen sind.
Sven
00:17:19
Ihren Vor und Nachteilen sind.
Benjamin
00:17:24
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.
Sven
00:18:25
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.
Benjamin
00:19:37
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.
Sven
00:19:45
Nicht.
Benjamin
00:19:45
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.
Sven
00:19:54
Hast du erst mal gemacht.
Benjamin
00:19:55
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.
Sven
00:20:07
Stimmt. So war das damals.
Benjamin
00:20:08
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.
Sven
00:20:40
Da kümmere ich mich erst mal nicht drum. Mach ich später. Was dann nie passiert.
Benjamin
00:20:41
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.
Sven
00:22:16
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.

Feedback

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.

Mit einem Klick auf "Nachricht absenden" erklärst Du Dich damit einverstanden, dass wir Deine Daten zum Zwecke der Beantwortung Deiner Anfrage verarbeiten dürfen. Die Verarbeitung und der Versand Deiner Anfrage an uns erfolgt über den Server unseres Podcast-Hosters LetsCast.fm. Eine Weitergabe an Dritte findet nicht statt. Hier kannst Du die Datenschutzerklärung & Widerrufshinweise einsehen.

★★★★★

Gefällt Dir die Show?
Bewerte sie jetzt auf Apple Podcasts