17 Punkte von GN⁺ 2024-08-04 | 3 Kommentare | Auf WhatsApp teilen

„Die Tabelle merchants2? Ja, wir haben merchants2 erstellt, weil merchants nicht genug Spalten hatte.“

  • Als ich mit dem Programmieren anfing, wusste ich nicht, dass Menschen damit Geld verdienen
  • In meinem ersten Softwarejob habe ich viel gelernt, und die Codebasis dort war zugleich die schlimmste und die beste

Datenbanken überleben für immer

  • In Legacy-Systemen ist die Datenbank mehr als nur ein Datenspeicher. Sie legt die Einschränkungen des gesamten Systems fest und ist der Treffpunkt für allen Code
  • SQL Server hat eine Begrenzung für die Anzahl der Spalten in einer Tabelle. Damals waren es 1024, heute sind es 4096. In der Tabelle Merchants reichten die Spalten nicht aus, also wurde Merchants2 erstellt (mit mehr als 500 Spalten)
  • Merchants und Merchants2 waren der Kern des Systems. Alles war mit Merchants verbunden. Es gab auch andere normalisierte Tabellen, aber sie hatten Fremdschlüsselbeziehungen zu Merchants

SequenceKey

  • SequenceKey ist eine einfache Tabelle mit nur einer Spalte und einem einzigen Wert
  • Sie wurde zur Erzeugung von IDs verwendet. Vermutlich wurde sie erstellt, weil SQL Server keine automatisch inkrementierenden IDs unterstützt habe
  • In jeder Stored Procedure wurde ein Schlüssel aus SequenceKey geholt und erhöht und dann als ID in mehreren Tabellen verwendet. Sie fungierte als impliziter Join

Calendar

  • Calendar ist eine manuell gepflegte Kalendertabelle. Wenn Calendar ablief, konnte man sich nicht im System anmelden
  • Das ist vor einigen Jahren tatsächlich passiert, und ein Praktikant hat weitere fünf Jahre eingetragen. Niemand weiß, welches System das verwendet

Employees

  • Jeden Morgen um 7:15 Uhr wurde die Tabelle employees gelöscht und mit einer von ADP erhaltenen CSV-Datei neu befüllt
  • Während dieses Vorgangs konnte man sich nicht im System anmelden. Manchmal schlug dieser Vorgang auch fehl
  • Die Daten mussten an die Zentrale repliziert werden, also wurden sie per E-Mail an eine Person geschickt, die jeden Tag einen Button drückte, um die Daten zu kopieren

Ersatzdatenbank

  • Man könnte denken, man könne die Datenbank aufräumen. Das dachte auch das Unternehmen
  • Es gab eine Kopie der Datenbank, deren Daten etwa 10 Minuten hinterherhinkten. Die Synchronisierung verlief nur in eine Richtung
  • Diese Datenbank war normalisiert, sodass man 7 Joins brauchte, um eine Telefonnummer aus merchants zu finden

Vertriebserfolge

  • Alle Vertriebsmitarbeiter hatten jeden Monat ein Soll an „wins“, das sie erreichen mussten
  • Die Tabelle zur Verwaltung davon war äußerst komplex. Täglich lief ein Job, der hinzugefügte oder geänderte Zeilen fand und sie mit dem System der Zentrale synchronisierte
  • Ein Problem entstand, als ein Vertriebsmitarbeiter darum bat, einen Datensatz manuell ändern zu lassen
  • Dieser Mitarbeiter hatte sein Soll bereits erfüllt und zusätzlich in diesem Monat noch einen großen Verkauf gemacht, wollte diesen aber in den nächsten Monat verschieben
  • Ein Praktikant übernahm diese Aufgabe, und als sich das herumsprach, stiegen die Anfragen über drei Jahre exponentiell an
  • Zeitweise bestand die gesamte Arbeit von drei Praktikanten darin, SQL-Statements dafür zu schreiben. Eine Anwendung dafür zu bauen galt als zu schwierig

Die Codebasis

  • Die erste Codebasis, mit der ich zu tun hatte, lag in Team Foundation Server, einem zentralisierten Versionsverwaltungssystem
  • Die Codebasis, an der ich hauptsächlich arbeitete, bestand zur Hälfte aus VB und zur Hälfte aus C#. Sie lief auf IIS und verwendete überall Session State
  • Das bedeutete praktisch, dass man auf derselben Seite völlig unterschiedliche Dinge sah, je nachdem, ob man über Pfad A oder Pfad B dorthin gelangte
  • Jedes JavaScript-Framework, das es damals gab, war in dieses Repository eingecheckt worden. Meist mit benutzerdefinierten Änderungen, die der jeweilige Autor für nötig hielt. Besonders auffällig waren knockout, backbone und marionette, dazu jquery und jquery-Plugins
  • Zusätzlich zu dieser Codebasis gab es etwa 12 SOAP-Services und mehrere native Windows-Anwendungen

Gilfoyles Festplatte

  • Gilfoyle galt als unglaublich schneller Programmierer. Ich habe ihn nie getroffen, aber ich kannte ihn durch seinen Code und den Code, der auf seiner Festplatte übrig geblieben war
  • Munch bewahrte Gilfoyles Festplatte noch Jahre nachdem er das Unternehmen verlassen hatte als RAID-Konfiguration auf seinem Schreibtisch auf
  • Denn Gilfoyle war dafür bekannt, Code nicht einzuchecken und stattdessen zufällige Wegwerf-Windows-Anwendungen für einzelne Benutzer zu bauen
  • Es war nicht ungewöhnlich, dass Benutzer mit Bugreports für Anwendungen ankamen, die nur auf Gilfoyles Festplatte existierten

Versand-Bug

  • Der Großteil meiner Arbeit bestand darin, Bugs nachzuverfolgen, die das Team keiner eigentlichen Arbeit zuweisen wollte
  • Es gab einen besonders lästigen Bug, der alle paar Monate auftrat: Es gab Bestellungen, die nach dem Versand in der Versandwarteschlange hängen blieben und als nicht versandt erschienen, obwohl sie bereits versandt waren
  • Für die Behebung wurden mehrere Ansätze ausprobiert (SQL-Skripte, Windows-Anwendungen usw.). Mir wurde geraten, der eigentlichen Ursache nicht nachzugehen, aber ich konnte es nicht lassen
  • Dabei lernte ich Gilfoyles Denkweise kennen. Die Versand-App lud die gesamte Datenbank und filterte dann nach Datum, sodass alle Bestellungen seit dem Startdatum der Anwendung gehalten wurden
  • Die App hing von einem SOAP-Service ab, aber nicht, um serviceartige Dinge zu tun, sondern als reine Funktion. Alle Nebenwirkungen verursachte der Client
  • In diesem Client entdeckte ich eine riesige Klassenhierarchie mit 120 Klassen, jeweils mit diversen Methoden, und Vererbung bis zu 10 Ebenen tief
  • Das einzige Problem war, dass alle Methoden leer waren
  • Das diente dazu, eine Struktur aufzubauen, die Reflection verwenden konnte. Diese Reflection erzeugte einen durch Pipes getrennten String (datenbankgestützt, aber völlig statisch aufgebaut) und schickte ihn über einen Socket
  • Am Ende wurde das an Kewill geschickt, einen Service zur Kommunikation mit Frachtführern. Der Bug trat auf, weil Kewill jeden Monat 9-stellige Zahlen wiederverwendete und jemand den Cron-Job deaktiviert hatte, der alte Bestellungen löschte

Wunderschönes Chaos

  • Über diese Codebasis ließe sich noch viel mehr sagen: über das Team von Senior-Entwicklern, das fünf Jahre lang keine Software auslieferte und alles neu schrieb, oder über die Red-Hat-Berater, die eine einzige Datenbank bauen wollten, die alles kontrolliert
  • Diese Codebasis hatte viele verrückte Ecken und genug Gründe, warum man ein eigenes Team gebraucht hätte, nur um eine einzelne Funktion von Grund auf neu zu beginnen
  • Die wichtigste Geschichte ist aber die, wie Justin die Seite Merchants Search verbessert hat. Diese Seite war der Einstiegspunkt in die gesamte Anwendung
  • Alle Customer-Service-Mitarbeiter suchten während eines Telefonats mit einem Händler nach Informationen, indem sie eine ID oder einen Namen eingaben. Danach landeten sie auf einer riesigen Seite mit allen Informationen
  • Diese Seite war mit allen nötigen Informationen und allen relevanten Links vollgepackt und auf die bestmögliche Weise informationsdicht. Aber sie war unglaublich langsam
  • Justin war der einzige Senior-Entwickler in meiner Gruppe. Er war klug, sarkastisch und am Business kaum interessiert
  • Er sagte die Dinge, wie sie waren, nahm kein Blatt vor den Mund und konnte Probleme allein immer schneller lösen als die Teams um ihn herum
  • Eines Tages hatte Justin genug davon, ständig zu hören, wie langsam die Händler-Suchseite war, also ging er hin und reparierte sie
  • Jede Box auf dem Bildschirm bekam ihren eigenen Endpoint. Beim Laden wurde alles oberhalb des Folds angefragt, und sobald eines geladen war, folgten weitere Requests
  • Die Ladezeit der Seite sank von mehreren Minuten auf unter eine Sekunde

Zwei Wege zur Entkopplung

  • Justin konnte das tun, weil es in dieser Codebasis keinen Masterplan gab
  • Es gab keinen groben Bauplan, an den sich das System halten musste, kein erwartetes Format für APIs, kein dokumentiertes Design-System und kein Architektur-Review-Board, das Konsistenz sicherstellte
  • Die App war ein komplettes Chaos. Niemand konnte sie reparieren, also hat es auch niemand versucht. Stattdessen erschufen wir unsere eigenen kleinen Inseln der Vernunft
  • Diese monolithische App wuchs aus reiner Notwendigkeit an ihren Rändern zu einem Mikrokosmos guter kleiner Apps heran
  • Jede Person, die den Auftrag bekam, einen Teil der App zu verbessern, gab zwangsläufig den Versuch auf, das Spinnennetz zu entwirren, suchte sich stattdessen eine gute kleine Ecke, um etwas Neues zu bauen, aktualisierte dann nach und nach die Links auf das neue gute Ding und machte das alte damit zum Waisenkind
  • Das mag chaotisch klingen. Aber es war überraschend angenehm, darin zu arbeiten. Die Sorge um Code-Duplizierung verschwand, ebenso die Sorge um Konsistenz und um Skalierbarkeit
  • Code wurde für die Nutzung geschrieben, so dass möglichst wenig von den umliegenden Bereichen berührt wurde und er leicht ersetzbar blieb. Unser Code war entkoppelt, weil Koppeln einfach schwieriger war

Danach

  • In meiner weiteren Laufbahn hatte ich nie wieder das Privileg, in einer so erstaunlich hässlichen Codebasis zu arbeiten
  • Jede hässliche Codebasis, die ich danach gesehen habe, konnte das Bedürfnis nach Konsistenz nicht überwinden
  • Vielleicht lag es daran, dass die „ernsthaften“ Entwickler die Codebasis schon lange zuvor aufgegeben hatten. Übrig geblieben waren nur chaotische Praktikanten und Junior-Entwickler
  • Oder vielleicht lag es daran, dass es keine Zwischenschicht zwischen Entwicklern und Benutzern gab. Keine Übersetzung, keine Anforderungserhebung, keine Tickets. Man stand einfach am Schreibtisch eines Customer-Service-Mitarbeiters und fragte, wie man sein Leben verbessern könnte
  • Mir fehlt diese direkte Verbindung. Das schnelle Feedback, die Tatsache, dass man keine großen Pläne machen musste, die direkte Verbindung zwischen einem einfachen Problem und dem Code
  • Vielleicht ist es einfach naive Nostalgie. Aber wie bei dem Wunsch, in die schlimmsten Jahre meiner Kindheit zurückzukehren, kehrt mein Herz jedes Mal, wenn ich mit „Enterprise Design Patterns“ konfrontiert werde, zu dieser wunderschönen, schrecklichen Codebasis zurück

Meinung von GN⁺

  • Dieser Text kann Entwicklern, die mit Legacy-Systemen arbeiten, Verständnis und Trost geben. Er zeigt, dass auch unvollkommene Codebasen wertvoll sein und viele Lernchancen bieten können
  • Die Probleme dieser Codebasis sollten jedoch nicht romantisiert werden. Wenn sich technische Schulden anhäufen, verlangsamt das die Entwicklung erheblich und erschwert die Wartung. Langfristig kostet es mehr
  • Die richtige Antwort ist nicht, Versuche zur Verbesserung der Codebasis aufzugeben und weiter Provisorien anzuhäufen. Es braucht eine Strategie, um Legacy-Systeme schrittweise zu verbessern oder zu migrieren
  • Auch Entwicklungskultur und Prozesse sind wichtig. Gute Engineering-Praktiken wie Code-Reviews, Architekturdesign und Dokumentation helfen dabei, bessere Codebasen zu schaffen
  • Die enge Kommunikation mit Benutzern und schnelles Feedback sind positiv. Ideal ist es, das etwa durch Methoden wie Agile zu fördern und gleichzeitig die Codequalität zu steuern
  • Letztlich ist alles eine Frage des Gleichgewichts. Wichtiger als Perfektion ist es, die Bedürfnisse der Benutzer nachhaltig zu erfüllen und technische Schulden zu managen

3 Kommentare

 
bus710 2024-08-07

In meinem ersten Job als Firmware-Entwickler bestand meine Aufgabe darin, den aus der Hex-Datei eines 8051-MCUs extrahierten Assemblercode eines Produkts ohne Entwickler und ohne Quellcode zu analysieren und ihn in C neu zu implementieren …
Zum Glück gab es immerhin ein funktionierendes Produkt, also habe ich mir irgendwie damit geholfen, den Code anzuschauen, das Produkt zu testen und es so am Ende doch hinzubekommen …
Ich wurde sogar schon bedroht nach dem Motto, ich solle es entweder auf einer Dienstreise in der Provinz reparieren oder mir den Finger abschneiden und dann gehen.
Und ein mysteriöser Bug stellte sich einmal tatsächlich als der Aufzug hinter der Wand heraus, an der das Gerät installiert war.
Außerdem erinnere ich mich noch daran, wie ich vor der offiziellen Eröffnung des APEC-Tagungsorts auf der Dongbaekseom-Insel in Busan hineinging und dies und das installiert habe, haha

 
GN⁺ 2024-08-04
Hacker-News-Kommentare
  • Im ersten Unternehmen eine komplexe VB-Anwendung betreut

    • Es gab viele globale Variablen, zugeschnitten auf die Anforderungen jedes Kunden
    • Bugs traten nur nicht im Debug-Modus auf, daher wurde dem Kunden beigebracht, Visual Studio zu installieren und die Anwendung im Debug-Modus auszuführen
    • Es gab keine Versionsverwaltung, und der Code wurde in mehrere Ordner kopiert, was zu Verwirrung führte
    • Bei Kundenproblemen wurde der Code direkt vor Ort geändert
    • Es gab keine Einigung auf eine finale Version, daher nutzte jeder Kunde eine andere Version
  • Im ersten Job ein Legacy-Produkt gewartet, das in COBOL und Java geschrieben war

    • Im Source-Control-System wurden Dateien einzeln ausgecheckt und bearbeitet
    • Für jeden Kunden gab es eine "Master"-jar-Datei, die das zuletzt kompilierte Produkt darstellte
    • Nach Codeänderungen wurden Skripte ausgeführt, um die Master-jar-Datei zu aktualisieren
    • Die Codebasis wurde nicht als Ganzes kompiliert, sondern manuell gepatcht
    • Dadurch entstanden viele Inkonsistenzen in der Codebasis
    • Die Migration zu git dauerte 2 Jahre
  • Ein Perl-Skript mit mehr als 12.000 Zeilen refaktoriert

    • Der Autor kannte keine Arrays und hatte Arrays stattdessen mit Strings implementiert
    • Nach dem Refactoring schrumpfte der Code auf 200 Zeilen
  • Den Unterschied zwischen Theorie und Praxis gespürt

    • Viele Unternehmen und Projekte durchlaufen ähnliche Prozesse
    • Man diskutiert ideale Vorgehensweisen, in der Praxis arbeitet man jedoch mit dem, was funktioniert
  • Die Codebasis des Telegram-Android-Clients war extrem komplex

    • GitHub gab wegen der großen Dateien das Rendern auf
    • Das komplette Rendering und die gesamte Interaktion von Nachrichten wurden in einer einzigen Klasse verarbeitet
    • Es wurde die Erlaubnis zum Refactoring gegeben, aber es wurde nicht umgesetzt
  • Im ersten Job Speicherprobleme bei Reporting-Aufgaben gelöst

    • Teamleiter und Vorgesetzter waren überrascht
    • Andere Entwickler mochten Linux nicht und wollten zu .NET wechseln
  • Die Erfahrung, Probleme im direkten Austausch mit Kunden zu lösen, war positiv

    • Prototypen wurden schnell erstellt und vom Kunden getestet
    • Die Codebasis war chaotisch, funktionierte aber gut
  • Ein System entwickelt, das mehrere Märkte unterstützte

    • Das ursprüngliche System wurde kopiert, um eine internationale Version zu erstellen
    • Nach 5 Jahren waren die Systeme nicht getrennt, sondern miteinander verflochten
    • Das neue und das alte System waren eng miteinander gekoppelt
  • Im ersten Job gab es viele unerfahrene Leute

    • Nach dem Sammeln von Erfahrung wechselte man zu einem besseren Arbeitsplatz
  • Erklärt, wie sich Probleme in modernem SQL Server lösen lassen

    • Kundenspezifische Anpassungen, gemeinsame IDs, manuelle Kalendertabellen, Tabellenpartitionierung, verzögerte Reporting-Replikate usw.
    • Gemischte Codebasen aus VB und C# sind häufig
    • Es können automatische Konvertierungstools verwendet werden
 
reddiana 2024-08-05

Sequenznummern-Tabelle (SequenceKey) und Geschäftstags-Tabelle (Calendar)
wecken Erinnerungen. Ich weiß nicht, wie man das heute macht, aber früher waren das weit verbreitete Tabellen. Wenn man SI-Projekte gemacht hat, wurde die zugehörige Funktionalität im geschäftsübergreifenden Teil implementiert.