|
Entwickler-Story zum Astronomieprogramm PlanetensucheWie aus einem QuickBasic-Programm zur Suche nach der zweiten Erde über 23 Jahre ein 172k LoC Java-Projekt wurde - und was ich dabei über Architektur, KI und das Refaktorisieren gelernt habe. Als ich im Mai 2000 einen Artikel über "Auf der Suche nach der zweiten Erde" im Magazin "bild der wissenschaft" las, wurde ich vom Thema Exoplaneten infiziert. Es hat mich seit dem nicht mehr losgelassen. Die Idee, dass es in unserer Milchstraße noch einen Planeten wie die Erde geben könnte (womöglich sogar mit Lebewesen), hat mich einfach fasziniert. Damals war das noch reine Sci-Fi. Denn es waren gerade etwas über 30 Exoplaneten bekannt (im Mai 2026 knapp 6300) und die meisten davon waren große Gasriesen, sehr nah an ihrem Stern. Also alles andere als eine zweite Erde. Heute sieht die Datenlage schon deutlich vielversprechender aus. Im Sommer 2002 habe ich dann angefangen meine erste Programmiersprache QuickBasic zu lernen. Dabei kam mir die Idee irgend etwas langlebiges zu entwickeln, um auch wirklich programmieren lernen zu können. Und so entstand Planetensuche. Ich wollte ein Programm haben welches ich mit den Rohdaten der entdeckten Exoplaneten "füttern" kann. Auf dieser Basis sollte dann eine Wahrscheinlichkeitsberechnung mittels Checkliste abschätzen, ob Leben auf einem der Planeten möglich wäre. Die damals verfügbaren Daten waren nicht ausreichend, um eine Aussage treffen zu können. Trotzdem war das die erste Funktion in Planetensuche. Bis heute kenne ich kein vergleichbares Programm zum Thema Exoplaneten und Exobiologie. Ende 2002 wechselte ich dann auf VisualBasic und habe Planetensuche darin komplett neu entwickelt (Version 2.0). Damit waren nun klickbare GUIs möglich, was deutlich attraktiver wirkt als DOS Fenster. Die Version 3.0 enthielt ein größeres Refactoring, blieb aber ansonsten auf der gleichen Technologie. Während meines Informatik Studiums (2004-2008) lernte ich zahlreiche neue Programmiersprachen kennen und entschloss mich bis Ende 2007 Planetensuche in Java 5 neu zu schreiben (Version 4.0). Mit dieser Neuentwicklung wurde Planetensuche modularisiert (Maven Multimodul Projekt), was bis heute Bestand hat (mehr dazu später). Sehr hilfreich war dabei der GUI Editor in Netbeans, mit dem man schnell den Frontend Teil in Swing (und teilweise AWT) fertig stellen konnte. Mit Version 4.1 war es dann auch möglich in der Sternkarte alle Sterne mit bekannten Exoplaneten markieren zu lassen. Dieses Feature kam nach meinem Vorbild auch später in Stellarium hinzu. Die Version 5.0 Mitte 2013 kam zustande wegen der Einführung von Querydsl. Einerseits ersetzt es die "magic strings" im Quellcode, hilft beim Schreiben von SQL und gewährleistet auch eine Typsicherheit. Querydsl ist auch heute noch in Verwendung und sehr hilfreich als schlanke Persistenzschicht (Planetensuche benutzt kein Hibernate o.ä.). Leider scheint es so als ob das Querydsl Projekt eingeschlafen ist. Hier steht mir wohl ein zukünftiges großes Refactoring auf jOOQ u.ä. bevor... Außerdem wurde eine weitere Neuerung eingeführt. Die Versionierung erfolgt nun nicht mehr nach semantic versioning "Major.Minor.Bugfix" sondern nach two-part versioning. Das vereinfachte die ganze Release- und Versionierungsthematik erheblich. Mit Version 6.0 Anfang 2020 kam dann ein größerer Sprung von Java 8 auf 11. Wegen der neuen Releasepolitik des JDK entschloss ich mich auf den LTS Versionen zu bleiben. Künftige Planetensuche Versionen enthielten dann keinen Major-Sprung mehr, nur wegen der Java Version. So folgte Mitte 2023 auf die 6.22 die 6.23, trotz des Umstiegs von Java 11 auf 17. Und auch in 2025 kam mit dem Umstieg auf Java 21 kein großer Versionswechsel (Version 6.29). Auch kleinere Softwareprojekte können sich dem KI Hype nicht mehr entziehen. Die Version 6.31 (veröffentlicht 15.03.2026) war dann die aller erste Version die mit Hilfe von KI (Gemini 3.1) entwickelt wurde. Da ich der einzige Entwickler bin, leidet manchmal die Umsetzung an einem fehlenden Review. Dank Gemini habe ich nun die Möglichkeit entweder meinen Code reviewn zu lassen oder umgekehrt den erzeugten Code zu reviewn. Enorm hilfreich war Gemini beim refaktorisieren von altem Code (z.B. sehr alte Klassen auf Java 21 aktualisieren oder die Umstellung von Junit 4 auf 5). Dank der riesigen Wissensbasis die Gemini erlernt hat lassen sich auch schnell Lösungen für Probleme finden, die ich sonst hätte stundenlang recherchieren müssen. Auch eher langweilige Aufgaben wie das übersetzen des Changelogs lässt sich super schnell mit KI erledigen. Die Geschwindigkeit die man zusammen mit KI erreicht ist enorm. Ich würde sagen ich bin um das 4-fache schneller als in der vor-KI Ära. Das funktioniert hier natürlich gut da ich mich mit niemand sonst abstimmen muss. Auf Arbeit sieht die Sache deutlich ernüchternder aus, wo der Programmier-Anteil an meiner Gesamtarbeitszeit eher überschaubar ist und damit der Vorteil von KI viel geringer ausfällt. Was können wir daraus lernen? 1. Neu bauen vs kontinuierliches refactoring Planetensuche gibt es nun seit mehr als 23 Jahren. Wie anfangs beschrieben gab es einige Zeitpunkte wo Planetensuche komplett neu programmiert wurde (und jedes mal in einer neuen Programmiersprace). Ich denke anfangs ist es für ein neues Softwareprojekt wichtig sich auf den Aufbau von Domänenwissen und Features zu konzentrieren. Egal ob privates Projekt, OpenSource oder kommerziell, niemand weis gleich am ersten Tag welche Features am Ende den Durchbruch bringen, dass Benutzer die Software unbedingt wollen. Gleich am Anfang eine Software zu entwickeln die den ersten Preis bei Clean Code und Testabdeckung gewinnen könnte hilft nicht weiter. Wenn niemand die Software braucht dann ist das Projekt früher oder später tot. Zumal sich anfangs so viel ändern kann, dass eine 100% Testabdeckung und Dokumentation eher hinderlich wäre. All die vielen Tests und Dokumente müssen jedes mal mit angepasst werden, was sehr zeitraubend ist und einem vom Entwickeln eines neuen Features abhält. Die eigentliche Herausforderung besteht darin nach dem man den USP entwickelt hat die Entwicklung der Software hin zu einem langfristigen Fokus umzustellen. Das heißt statt ständig neu bauen findet ein kontinuierliches refactoring statt. Eine hohe Testabdeckung und Dokumentation bringt dann plötzlich einen Mehrwert. Auch die Architektur muss sich in Richtung Wartbarkeit und Zuverlässigkeit verschieben. Maximale Flexibilität ist dann nicht mehr so wichtig. Bei Planetensuche sind in jedem Release kleinere oder größere refactorings mit dabei. 2. Clean Code ist wichtig Am Anfang eines neuen Softwareprojektes könnte man versucht sein zu denken "clean code ist nicht so wichtig, ich habe schließlich jede Klasse selbst geschrieben und kenne mich hier ganz genau aus". Hat ein Projekt jedoch Erfolg und die Jahre vergehen, dann setzt auch das Vergessen ein. Planetensuche hat inzwischen über 172k LoC (in über 1900 Java Klassen) und auch wenn ich jede einzelne davon geschrieben habe, erinnere ich mich nicht mehr an alle davon. Design pattern sind äußerst wichtig, um sich auch nach Jahren des vergessens gut durch den Code hangeln zu können. Und damit es auch nach Jahren noch Spaß macht, den Code zu lesen und zu erweitern, ist Clean Code einfach Gold wert. Das erfordert natürlich auch Mut regelmäßig alten Code zu refaktorisieren. Leider ist dieser Mut keine Selbstverständlichkeit unter Softwareentwicklern. So manche OpenSource Projekte sind über die Jahre gewollt verkeimt und schrecken potentielle Mitstreiter gehörig ab. In Planetensuche verwende ich Checkstyle und PMD, um schon während des Maven Builds Rückmeldung zu potentiellen Problemen zu bekommen. Auch die aktivierten Compiler Warnings sind recht hilfreich. Früher hatte ich auch mal SonarQube im Einsatz. Aber leider war die Konfiguration nach jedem Upgrade nicht mehr lesbar und ein komplett neues Setup sehr zeitraubend. Am Ende habe ich das SonarQube Thema dann beerdigt. Das ist leider ein negativ-Beispiel für eine schlechte Software. Der Aufwand zur Administration sollte gering sein, andernfalls hemmt das die Verbreitung. 3. Modularisierung hilft Wie bereits erwähnt ist Planetensuche modularisiert. Aktuell gibt es 21 Maven Module. Da ich Planetensuche modularisiert hatte, bevor das mit Java 9 offiziell eingeführt wurde, habe ich quasi ein individuelles Modulsystem. Grob eingeteilt gibt es Hilfsmodule, welche Utils und andere Basisklassen enthalten und GUI-Module welche Frontend und Backend Klassen enthalten (MVC). Beispielsweise die über die Oberfläche zu öffnenden Programmteile "Datenbank-Modul" und "Sternkarte" sind 2 GUI-Module. Jedes Modul hat eine klare fachliche oder technische Aufgabe. Während die Klassen in den Hilfs-Modulen von überall her aufgerufen werden können (und sollen), gibt es bei den GUI-Modulen klare Schnittstellen, mit denen diese gestartet werden können. Die Modularisierung ist sehr nützlich, da es beim Entwickeln nicht nötig ist jedes mal alles neu zu bauen (was sehr lange dauern würde, bei den vielen Tests). Planetensuche ist damit quasi ein Deployment-Monolith (intern viele kleine Funktions-Einheiten die am Ende zu einer großen fetten jar Datei zusammen gebaut werden). Für eine Desktop-Anwendung macht es auch viel mehr Sinn ein einzelnes Artefakt am Ende zu haben, das die Nutzer ausführen können. Ich kann nur empfehlen heutzutage keine echten Monolithen mehr zu bauen. Wird ein Projekt erfolgreich dann ist es enorm zeitaufwändig den Monolithen zu entflechten. Also lieber gleich von Anfang an kleine sauber getrennte Funktionseinheiten bauen (also maximal modulare Deployment-Monolithen bauen). 4. Internationalisierung bringt Reichweite Um eine höhere Verbreitung zu erreichen ist es eine gute Idee eine Software auch auf Englisch anzubieten (GUI, Changelog, Dokumentation, Homepage, ...). Auf Grund der Verteilung der Weltbevölkerung bzw. Sprachverbreitung würde man heute wohl auch noch Indisch und Chinesisch hinzu fügen. Dank des Internets ist man potentiell mit allen Menschen verbunden. Mir schrieb einmal ein Nutzer aus Asunción in Paraguay. Als ich Planetensuche entwickelt hatte kam mir nie in den Sinn dass ich einmal so weit entfernte Nutzer haben könnte. Wie aufregend! Hinzu kommt das dank KI es nicht mehr nötig ist dass man Englisch, Mandarin oder was auch immer können muss. Solange die Texte für die Beschriftung der GUIs vom Code sauber getrennt sind, kann die Software auch leicht übersetzt werden. Das ist in Planetensuche der Fall. 5. die KI Ära Da ich gerade KI erwähnt habe... Noch ist unklar wohin uns KI am Ende bringen wird. Im Moment ist KI ein großartiges Werkzeug zur Produktivitätssteigerung. Gerade Softwareprojekte die als "one-man-show" wie Planetensuche laufen profitieren enorm davon. Es fühlt sich an als hätte ich 4 Mitarbeiter eingestellt, die neue Ideen generieren, alten Code refaktorisieren, und neue Features bauen. Ich hatte sogar den einen extrem-Fall wo ich vor 20 Jahren ein Feature bauen wollte, aber irgend wo der Wurm drin war. Erst mit Gemini konnte ich den Rechenfehler finden und das Feature in Version 6.31 veröffentlichen. Auch unliebsame Aufgaben wie das Umstellen von Java 6 auf Java 21 Code sind mit KI schnell erledigt. Und zumindest im nicht-komerziellen Bereich hat man die Wahl seinen Code trotzdem noch selbst zu schreiben (und KI macht das Review). Da nun die KI die Programmierarbeit erledigt, wird es für Softwareentwickler immer wichtiger sich auf Domänenwissen und die Softwarearchitektur zu konzentrieren. Um eine gute und passende Architektur zu finden erfordert es einen gewissen Durchhaltewillen, um die Auswirkungen von Designentscheidungen überhaupt mitbekommen zu können. Im beruflichen Umfeld lerne ich immer wieder Entwickler kennen, die (aus unterschiedlichen Gründen) nur 1-2 Jahre in einem Unternehmen zubringen. Bei solchen kurzen "besuchen" lernt man weder die Projekte richtig kennen (d.h. Domänenwissen ist nur oberflächlich vorhanden), noch erfährt man die Auswirkungen seiner getroffenen Designentscheidungen. Damit fehlt aber ein wichtiges Instrument beim Lernen: Feedback. Hier zeigt sich dann wer am Ende echte Berufserfahrung sammelt oder nur Dienstjahre anhäuft. Planetensuche hat eine lange Historie von Designentscheidungen, die mal gut und mal schlecht waren. Erfahrung ist unbezahlbar. 6. der Wert eines Backlogs Ich weis nicht mehr in welchem Buch ich den Tipp gelesen hatte Ideen sofort aufzuschreiben. Der Tipp war jedenfalls Gold Wert. Für Planetensuche benutze ich MantisBT. Das ist zwar primär ein bug tracker, aber der eignet sich auch gut um neue Features und Verbesserungen festzuhalten. Ich habe jede Idee die ich zum Programm hatte schriftlich festgehalten. Am Ende war ich selbst erstaunt wie viele Ideen mir so über die Jahre gekommen sind. Selbst nach 23 Jahren Entwicklungszeit sind immer noch rund 30% der Features offen (weil stetig neues hinzu kommt). 7. Testen aber sinnvoll Das ist der umstrittenste Punkt unter Softwareentwicklern. Ich persönlich halte nichts von einer 100% Testabdeckung durch Unit-Tests. Tests sollten sinnhaftig sein und einen Mehrwert bringen, egal was mal früher in den Lehrbüchern zur Testpyramide stand. In Planetensuche gibt es sehr viele Klassen und Methoden die irgend etwas berechnen oder Daten von einem DTO in ein anderes mappen. Diese Klassen eignen sich sehr gut für Unit Tests und hier machen diese auch Sinn. Ab und an hat man jedoch Klassen und Methoden die dafür schlicht ungeeignet sind, z.B. weil diese nur andere Klassen aufrufen und sonst nichts weiter tun. Beruflich habe ich schon sehr viele schlechte Unit Tests gesehen, die im Prinzip nur Testen dass Mockito richtig funktioniert. Refaktorisiert man dann etwas am Code sind diese Tests entweder alle samt Grün, obwohl die Methode ein falsches Ergebnis zurück gibt oder alle Tests sind Rot, weil die ein bestimmtes Verhalten der Methode erwarten, unabhängig davon dass das Ergebnis der Methode immer noch das gleiche ist. In Planetensuche habe ich auf solche nutzlosen Tests verzichtet. Die Testabdeckung ist bei den Hilfs-Modulen am höchsten und nimmt dann in den GUI Modulen ab (da keine Klick-Tests existieren). Für Methoden die nur andere Klassen aufrufen gibt es nur Integrationstests, so dass ich direkt sehe ob der Code noch funktioniert. Insbesondere bei externen Abhängigkeiten hat man keine Kontrolle über das Verhalten der Schnittstelle, so dass hier Integrations-/Systemtests die nötige Sichtbarkeit erzeugen. An einigen Stellen gibt es auch Performancetests. Diese Tests werden von Maven nach allen anderen Tests pro Modul ausgeführt, da diese am längsten dauern. 8. Java ist alt und Java Anwendungen schwerfällig Einige Menschen denken beim Thema Java an eine alte, umständliche Programmiersprache (wegen viel boilerplate code), mit der man schwergewichtige Webanwendungen baut. Meiner Erfahrung nach ist dem nicht so, bzw. muss nicht so sein. Inzwischen gab es einige Projekte um in Java neue Sprach-Funktionen einzubauen. Man muss heute deutlich weniger Code schreiben (z.B. record statt POJO mit getter & setter) und Java wird ständig verbessert. Inzwischen gibt es zahlreiche Funktionen im JDK, wofür man früher noch externe Libs wie commons-io, commons-lang3, Guava, Joda-Time etc. brauchte. Und jetzt wo KI den Code generiert ist es vielleicht auch nicht mehr so wichtig ob eine Sprache möglichst minimalistisch ist (bei Minimalismus leidet auch die Lesbarkeit). Der andere Punkt mit den schwerfälligen Anwendungen hängt vom Kontext ab. Mit Java kann man auch kleine schlanke Programme bauen. Planetensuche setzt auf schlanke Libs bei Basisthemen wie Dependency Injection (DI) mit Guice, statt Spring und Querydsl statt Hibernate, für ORM. Das Planetensuche trotzdem inzwischen sehr Speicher-hungrig geworden ist liegt einzig daran, dass die in-memory Datenbank HSQLDB benutzt wird und die Datenbank über die Jahre gewachsen ist. Es ist also sehr wohl möglich eine Ressourcen-schonende Anwendung zu bauen und zu betreiben (die JVM bietet zahlreiche Parameter zur Optimierung, gerade bei Server Anwendungen lässt sich da viel heraus holen). 9. Domänenmodell und Validierung Planetensuche importiert Daten aus unterschiedlichsten Quellen. All diese Daten müssen validiert werden, damit die zahlreichen Diagramme und andere Programmfunktionen korrekt arbeiten können. Hier kommt eine zweistufige Strategie zum Einsatz: Einerseits passieren quellenspezifische Validierungen direkt beim Import (z.B. Prüfungen, ob Objektbezeichnungen und Katalognummern korrekt sind). Ganz am Schluss, wenn die Daten gespeichert werden sollen, erfolgt eine grundsätzliche Validierung auf sinnvolle Wertebereiche pro Objekt-Eigenschaft (z.B. darf die Objektmasse nicht negativ sein). Die Speicherung der Daten erfolgt hier nicht direkt mit den Entity-Klassen (also in meinem Fall den generierten Querydsl-Klassen), sondern über ein Domänenobjekt. Das Domänenobjekt übernimmt dann die Validierung aller Daten. Beim Auslesen der Daten können hingegen direkt die Entity-Klassen oder eine POJO-Variante des Domänenobjektes benutzt werden. Damit ist sichergestellt, dass keine unsinnigen Daten in der Datenbank landen und gleichzeitig das Lesen von Daten (was viel häufiger vorkommt) schnell genug ist. Damit bleibt auch die Fachlogik von der Datenbankschicht getrennt, was ein späteres Refactoring von Querydsl zu jOOQ o.ä. erleichtern sollte. 10. Update-Funktion Mit dem Neubau in Java (Dez. 2007) hat Planetensuche auch eine Update-Funktion bekommen. Ich finde es extrem wichtig für eine Desktop-Anwendung, dass sich diese bei Bedarf selbst aktualisieren kann. Schließlich sollen Nutzer von neuesten Funktionen und Fehlerbehebungen profitieren. Selbst heute noch gibt es einige Programme auf meinen Computern, die das nicht hinbekommen. Für einen Nutzer ist es sehr umständlich, wenn man jedes Mal deinstallieren, manuell herunterladen und neu installieren muss. Dabei ist eine selbst gebaute Update-Funktion gar nicht so kompliziert (im Fall von Planetensuche ist es etwas kniffliger, da ggf. auch die interne JRE aktualisiert werden muss, falls das Programm im Bundle heruntergeladen wurde). „Die meisten guten Programmierer programmieren nicht, weil sie erwarten, dafür bezahlt oder von der Öffentlichkeit gefeiert zu werden, sondern weil das Programmieren einfach Spaß macht.“ – Linus Torvalds zuletzt aktualisiert am 25.05.2026 |
| |
| Gerd Gühne Kohlweg 10 04347 Leipzig |