Lass mich den Stack beschreiben, in den ich bei Talentera im Juni 2018 eingetaucht bin: eine Multi-Tenant-B2B-Recruitment-SaaS, die Regierungsministerien und Enterprise-Clients im MENA-Raum bediente, mit einem TCL/TK-Core auf AOL Server 4.x, PHP 7 daneben für neuere Features, Node.js für einige API-Schichten und einer Camunda-BPMN-Engine für Workflow-Orchestrierung. Das war kein Prototyp. Das war Produktion. Das verarbeitete echte Einstellungsentscheidungen für echte Regierungen. Und ja, TCL 8.4 und 8.6, im Jahr unseres Herrn 2018.

Ich weiß, was du denkst. Lass mich dich stoppen.

TCL/TK ist kein Witz

Die erste Reaktion der meisten Engineers, wenn sie TCL in einem Produktions-Stack sehen, ist: “Ah, die haben noch nicht Zeit gehabt zu migrieren.” Diese Rahmung ist falsch und führt zu schlechten Entscheidungen.

TCL — Tool Command Language — entstand 1988 und wurde für genau eine Sache designed: Scripting und das Zusammenkleben heterogener Systeme. AOL Server, Talentera’s Web-Runtime, nutzt TCL als native Extension-Sprache. AOL Server selbst ist echte beeindruckende Infrastruktur: event-driven, multi-threaded (mit einem Thread-Pool, nicht einem Thread pro Request), effizienter Connection-Pooling eingebaut. Der ursprüngliche Naviserver-Fork ist noch immer in aktiver Entwicklung, weil das Modell tatsächlich funktioniert. Als Googles Web-Infrastruktur-Team in den frühen 2000ern noch Grundlagenarbeit machte, wurde die AOL-Server-Architektur lobend erwähnt. Das hier ist kein Legacy durch Verfall — es ist Legacy durch Langlebigkeit, was eine andere Sache ist.

TCL 8.4 und 8.6 sind keine Versionsnummern, die man 2018 gewählt hätte, wenn man von null anfinge — aber sie bedeuten, dass deine Runtime stabil ist. Keine Breaking Changes. Keine überraschenden Deprecations. Jede Zeile TCL-Code, die vor deiner Ankunft geschrieben wurde, läuft noch so, wie sie geschrieben wurde. Für eine Multi-Tenant-SaaS, bei der Uptime direkt Regierungen beim Durchführen von Einstellungsrunden für tausende von Kandidaten beeinflusst, ist langweilige Stabilität ein Wettbewerbsvorteil.

Die Sprache selbst — Tcl hat eine eigentümliche Schönheit. Alles ist ein String. Befehle sind nur Listen. Das Ausführungsmodell ist bemerkenswert composable. Für die Art der Request-Verarbeitung, die Talentera machte — Queries formen, Responses zusammenstellen, strukturierte Daten in engen Server-seitigen Schleifen manipulieren — ist es ehrlich gesagt okay. Nicht großartig, aber okay. Und “okay, stabil und bereits geschrieben” schlägt “besser, aber erfordert einen Rewrite” ungefähr jedes Mal, wenn du Enterprise-SLAs einhalten musst.

Was ein “Legacy-Stack” wirklich kostet

Hier ist die ehrliche Zahl: die echten Kosten eines Legacy-Stacks sind nicht die Runtime. Sie sind die operative Oberfläche.

AOL Server + TCL hat keine reiche moderne Observability-Story. Distributed Tracing hinzuzufügen, wenn dein Core ein TCL-Namespace ist, der in einem Thread-Pool auf einem Server-Modell von 2003 läuft, erfordert, Probleme zu lösen, die das Open-Source-Ökosystem bereits für Go, Java und Node.js gelöst hat — aber nicht dafür. Das ist Engineering-Zeit, die nicht für Features genutzt wird. Als ich anfing, verbrachte das Team einen unverhältnismäßig großen Teil des kognitiven Overheads mit “wie bekommen wir Sichtbarkeit in das, was TCL in Produktion tut” im Vergleich zu “was sollte TCL als nächstes tun.”

Das ist das eigentliche Problem, das zu lösen ist. Nicht “TCL neu schreiben” — das ist ein mehrjähriges Projekt, das das Unternehmen umbringen könnte — sondern “den kognitiven Overhead des Betreibens von TCL reduzieren, damit das Team sich auf das Bauen von Produkt konzentrieren kann.”

Die Antwort, zu der wir kamen: den TCL-Core nicht migrieren. Stattdessen isolieren.

DDD + Microservices als Hülle, nicht als Ersatz

Der Architekturansatz, den ich leitete, war Domain-Driven Design, das um den TCL-Core herum geschichtet wurde, nicht statt ihm. Camunda BPMN wurde zum Workflow-Orchestrator für die Prozesse, die zu komplex wurden, um sie in reinem TCL zu verwalten — mehrstufige Approval-Chains, bedingte Verzweigungen für verschiedene Regierungskunden-Konfigurationen, Dokument-Handling-Pipelines. Camunda spricht Java, was bedeutete: Java-8-Services, die die Workflow-Definition- Schicht besaßen, mit TCL als einem der Worker-Typen, die spezifische Task- Implementierungen ausführten.

Keycloak kam für Identity and Access Management — OAuth2 und OpenID Connect über die Multi-Tenant-Landschaft. Das war 2018 die richtige Entscheidung. Vor Keycloak wurde Tenant-Identity auf eine Art gehandhabt, die… handwerklich war. Custom Session-Tokens, per-Tenant-Auth-Logik verstreut über Request-Handler, begrenzte Federation-Unterstützung. Keycloak zentralisierte das, gab uns Standards-konformes Auth und ließ die neueren Services (Kotlin-Microservices, Node.js-API-Schichten) am selben Identitätsmodell teilnehmen, ohne die TCL- Session-Interna verstehen zu müssen.

RabbitMQ übernahm das Async-Messaging. Redis das Caching. Die neuen Mobile- Features — Ionic und schließlich Flutter — sprachen mit Node.js-API-Gateways, die zwischen den Erwartungen der Mobile-Clients und der TCL-basierten Backend- Realität übersetzten. Nichts davon erforderte, den TCL-Core anzufassen. Der TCL-Core lief weiter. Die neuen Services wuchsen um ihn herum wie eine Stadt, die um einen Fluss herum wächst.

Die Woche, in der ich Kotlin und TCL im selben Sprint shippte

Es gibt eine spezifische Art von kognitivem Schleudertrauma, das entsteht, wenn man innerhalb derselben Arbeitswoche zwischen TCL 8.4 und Kotlin wechselt. In TCL denkt man über String-Manipulation, Namespace-Management und sicherstellen, dass die proc-Argumentlisten korrekt sind. In Kotlin denkt man über Coroutines, Data Classes und Null Safety. Die Paradigmen sind so verschieden, dass sie fast orthogonal sind.

Was ich feststellte, was mich überraschte: das ist tatsächlich okay. Die strenge Trennung der Concerns, die die Architektur erzwang — TCL besitzt die Request- Handling-Schicht, Kotlin besitzt die neuen Domain-Services, Camunda besitzt die Process-Definitionen — bedeutete, dass der Context-Switch sauber war. Wenn ich in TCL war, dachte ich über TCL-Probleme nach. Wenn ich in Kotlin war, dachte ich über Kotlin-Probleme nach. Die Kontexte bluteten nicht ineinander, weil die Grenzen real waren.

Das ist eines der unterschätzten Argumente für explizit begrenzte Architektur: Sie macht Multi-Technologie-Shops operativ handhabbar. Wenn du einen Monolithen baust und versuchst, eine neue Sprache einzuführen, bekommst du überall Grenzverwirrung — wo hört PHP auf und wo fängt Node.js an? Wenn du DDD richtig gemacht hast, ist die Grenze eine Deployment-Grenze, nicht nur ein Namespace. Diese Klarheit zählt, wenn dein Team innerhalb desselben Sprints zwischen einer 30 Jahre alten Skriptsprache und einer modernen JVM-Sprache wechselt.

Wie Migration wirklich aussieht

Gegen Ende meiner Zeit bei Talentera hatte ich einen klaren Blick darauf, was der Migrationspfad war, auch wenn wir ihn nicht abgeschlossen hatten. Die Antwort lautet nicht “TCL neu schreiben.” Die Antwort lautet:

  1. Identifiziere die Domains, bei denen die TCL-Implementierung tatsächlich Probleme verursacht. Nicht “das ist alt”, sondern “das begrenzt uns aktiv” — Stellen, an denen das Fehlen eines reicheren Typsystems oder die begrenzte Observability-Story echte Engineering-Zeit kostet.
  2. Definiere eine neue Service-Grenze für diese Domain. Besitze die Daten für diese Domain im neuen Service.
  3. Lass Camunda den Übergang vermitteln — die Workflow-Engine kann Tasks an den neuen Service routen, während der neue Service online kommt, während die TCL-Implementierung dieselben Tasks parallel handhabt, bis du zuversichtlich bist.
  4. Wenn du den Traffic für diese Domain aus der TCL-Implementierung abgelassen hast, retire sie.

Das ist ein Migrations-Timeline, der in Quartalen gemessen wird, nicht in Jahren. Und es erfordert nicht, alles auf einmal neu zu schreiben. Die entscheidende Erkenntnis ist, dass “Migration” nicht “Ersatz” ist — sondern “systematische Umverteilung von Ownership.” TCL muss nicht verschwinden. Es muss nur aufhören, Domains zu besitzen, für die es nicht gut geeignet ist.

Was dir niemand über das Erben von Legacy-Stacks sagt

Wenn man einem Unternehmen beitritt, das einen Stack wie diesen betreibt, gibt es die Versuchung, seinen Stempel zu hinterlassen, indem man einen vollständigen Rewrite vorschlägt. Dieser Versuchung zu widerstehen ist eine der wichtigsten Architekturentscheidungen, die du treffen wirst. Ein vollständiger Rewrite einer Produktions-Multi-Tenant-SaaS ist ein Wett-das-Unternehmen-Projekt. Es wird fast nie termingerecht fertig. Das Neue repliziert fast nie alle Edge Cases, die das Alte handhabte. Und während des Rewrites baust du kein neues Produkt — du baust einen Ersatz für bestehendes Produkt. Deine Konkurrenten stehen nicht still.

Die wertvollere Skill ist, einen Stack mit Wohlwollen lesen zu lernen. TCL auf AOL Server ist kein Technical Debt, weil es alt ist. Es ist Technical Debt an spezifischen Stellen, wo sein operatives Modell echte Reibung erzeugt. Verstehe, wo diese Stellen sind. Fix diese Stellen gezielt. Lass den Rest in Ruhe.

Ich shippte Kotlin-Microservices in dieser Umgebung. Ich shippte BPMN-Workflow- Orchestrierung. Ich shippte OAuth2-federated Identity über Tenants. Der TCL-Core lief die ganze Zeit weiter.

Das ist der Job.