Entwicklung & Code
Angular Signals: Elegante Reaktivität als Architekturfalle
Mit Angular 17 hielten Signals 2023 offiziell Einzug in das Framework. Sie versprechen eine modernere, klarere Reaktivität: weniger Boilerplate-Code, bessere Performance. Gerade im Template- und Komponentenbereich lösen sie viele Probleme eleganter als klassische Observable-basierte Ansätze.
Nicolai Wolko ist Softwarearchitekt, Consultant und Mitgründer der WBK Consulting AG. Er unterstützt Unternehmen bei komplexen Web- und Cloudprojekten und wirkt als Sparringspartner sowie Gutachter für CTOs. Fachbeiträge zur modernen Softwarearchitektur veröffentlicht er regelmäßig in Fachmedien und auf seinem Blog.
Statt Subscriptions, pipe()
und komplexen Streams genügen nun wenige Zeilen mit signal()
, computed()
und effect()
. Der Code wirkt schlanker, intuitiver und näher am User Interface (UI).
Da liegt die Idee nahe: Wenn Signals im UI überzeugen, warum nicht auch in der Applikationslogik? Warum nicht RxJS vollständig ersetzen? Ein Application Store ohne Actions, Meta-Framework und Observable: direkt, deklarativ, minimalistisch.
Ein Ansatz, der im Folgenden anhand eines konkreten Fallbeispiels analysiert und kritisch hinterfragt wird. Anschließend wird behandelt, in welchen Kontexten sich Signals sinnvoll einsetzen lassen.
Aufbau des Fallbeispiels
Auf den ersten Blick besitzt dieses Beispiel einen klar strukturierten Architekturansatz. Doch der Wandel beginnt unauffällig. RxJS bleibt zunächst außen vor. Das UI reagiert flüssig, der Code bleibt übersichtlich. Komplexe Streams, verschachtelte Operatoren oder eigenes Subscription Handling entfallen. Stattdessen kommen Signals zum Einsatz. Es liegt nahe, diese unkomplizierte Herangehensweise auch für die Applikationslogik zu übernehmen. Im folgenden Beispiel übernimmt ein ProductStore
die Zustandslogik. Signals organisieren Kategorien, Filter und Produktdaten – reaktiv und direkt.
@Injectable({ providedIn: 'root' })
export class ProductStore {
private allProducts = signal([]);
readonly selectedCategory = signal('Bücher');
readonly onlyAvailable = signal(false);
readonly productList = computed(() => {
return this.allProducts().filter(p =>
this.onlyAvailable() ? p.available : true
);
});
selectCategory(category: string) {
this.selectedCategory.set(category);
}
toggleAvailabilityFilter() {
this.onlyAvailable.set(!this.onlyAvailable());
}
constructor(private api: ProductApiService) {
effect(() => {
const category = this.selectedCategory();
const onlyAvailable = this.onlyAvailable();
this.api.getProducts(category, onlyAvailable).then(products => {
this.allProducts.set(products);
});
});
}
}
Die Struktur überzeugt zunächst durch Klarheit. Die Komponente konsumiert productList
direkt, ohne eigene Logik. Der Store verwaltet den Zustand, Signals sorgen für die Weitergabe von Änderungen.
Doch mit der nächsten Anforderung ändert sich das Bild: Bestimmte Produkte sollen zwar im Katalog verbleiben, aber im UI nicht mehr erscheinen. Da auch andere Systeme die bestehende API verwenden, ist eine Anpassung nicht möglich. Stattdessen liefert das Backend eine Liste freigegebener Produkt-IDs, anhand derer das UI filtert.
@Injectable({ providedIn: 'root' })
export class ProductStore {
// [...]
readonly backendEnabledProductIds = signal>(new Set());
readonly productList = computed(() => {
return this.allProducts().filter(p =>
this.onlyAvailable() ? p.available : true
).filter(p => this.backendEnabledProductIds().has(p.id));
});
constructor(private api: ProductApiService) {
effect(() => {
const category = this.selectedCategory();
const onlyAvailable = this.onlyAvailable();
this.api.getProducts(category, onlyAvailable).then(products => {
this.allProducts.set(products);
});
});
effect(() => {
this.api.getEnabledProductIds().then(ids => {
this.backendEnabledProductIds.set(new Set(ids));
});
});
}
// [...]
}
Nach außen bleibt die Architektur zunächst unverändert. Die Komponente enthält weiterhin keine eigene Logik, Subscriptions sind nicht notwendig, und die Reaktivität scheint erhalten zu bleiben. Im Service jedoch nimmt die Zahl der effect()
s zu, Abhängigkeiten werden vielfältiger, und die Übersichtlichkeit leidet.
Nach und nach wandert Logik in verteilte effect()
s, bis ihre Zuständigkeiten kaum noch greifbar sind. Aus einem überschaubaren ViewModel entsteht ein Gebilde mit immer mehr impliziten Reaktionen – eine Entwicklung, die ein waches Auge für Architektur erfordert.
Wenn reaktive Systeme entgleisen
Das Setup wirkt zunächst unspektakulär. Die Produktliste wird über ein computed()
erstellt, gefiltert nach Verfügbarkeit und den vom Backend freigegebenen IDs. Zwei effect()
s laden die Daten.
Der Code wirkt aufgeräumt und lässt sich modular erweitern. Doch der nächste Feature-Wunsch stellt das System auf die Probe: Die Stakeholder möchten wissen, wie oft bestimmte Kategorien angesehen werden. Die Entwicklerinnen und Entwickler entscheiden sich für einen naheliegenden Ansatz. Eine Änderung der Kategorie löst ein Tracking-Event aus. Ein effect()
scheint dafür perfekt geeignet – unkompliziert und ohne erkennbare Nebenwirkungen:
effect(() => {
const category = this.selectedCategory();
this.analytics.trackCategoryView(category);
});
Schnell eingebaut, kein zusätzlicher State, keine neue Subscription. Eine Reaktion auf das bestehende Signal, unkompliziert und ohne erkennbare Nebenwirkungen. Doch damit verlässt der Code den Bereich kontrollierter Reaktivität.
Der Kipppunkt
Die Annahme ist klar: Ändert sich die Kategorie, wird ein Tracking ausgelöst. Was dabei leicht zu übersehen ist: Signals reagieren nicht auf Bedeutung, sondern auf jede Mutation. Auch wenn set()
denselben Wert schreibt oder zwei Komponenten nacheinander dieselbe Auswahl treffen, passiert zwar technisch etwas, semantisch aber nicht. Das Ergebnis sind doppelte Events und verzerrte Metriken, ohne dass der Code einen Hinweis darauf gibt. Alles sieht korrekt aus.
Das Tracking erfolgt unmittelbar im selben Ausführungstakt (Tick), ohne Möglichkeit zur Entkopplung. Wenn parallel ein weiterer effect()
ausgelöst wird – etwa durch ein zweites Signal –, fehlt jegliche Koordination.
Die Reihenfolge ist nicht vorhersehbar, und das UI kann in einen inkonsistenten Zustand geraten: Daten werden mehrfach geladen, Reaktionen überschneiden sich, Seiteneffekte sind nicht mehr eindeutig zuzuordnen. Mit jedem zusätzlichen effect()
steigt die Zahl impliziter Wechselwirkungen. Was wie ein reagierendes System wirkt, ist längst nicht mehr entscheidungsfähig.
In einem Kundenprojekt führte genau dieser Zustand dazu, dass ein effect()
mehrfach pro Sekunde auslöste. Nicht wegen einer echten Änderung, sondern weil derselbe Wert mehrfach gesetzt wurde. Das UI zeigte korrekte Daten, aber das Backend war mit redundanten Anfragen überlastet.
Das Missverständnis
effect()
wirkt wie ein deklarativer Controller: „Wenn sich X ändert, tue Y.“ Doch in Wirklichkeit ist es ein reaktiver Spion. Er beobachtet jedes Signal, das gelesen wird, unabhängig von der semantischen Bedeutung. Er feuert sogar dann, wenn niemand es erwartet. Und er ist nicht koordiniert. Jeder effect()
lebt in seiner eigenen Welt, ohne zentrale Regie.
Was als architektonische Vereinfachung begann, endet in einer Blackbox aus Zuständen, Reaktionen und Nebenwirkungen. Mit jedem weiteren Feature wächst diese Komplexität. Es gibt keinen großen Knall, aber eine zuvor elegant erscheinende Struktur driftet leise auseinander.
Entwicklung & Code
Projektmanagement: Wieso Diversität im Team wichtig ist
Moin.
(Bild: Stefan Mintert )
Stefan Mintert arbeitet mit seinen Kunden daran, die Unternehmenskultur in der Softwareentwicklung zu verbessern. Das derzeit größte Potenzial sieht er in der Leadership; unabhängig von einer Hierarchieebene.
Die Aufgabe, dieses Potenzial zu heben, hat er sich nach einem beruflichen Weg mit einigen Kurswechseln gegeben. Ursprünglich aus der Informatik kommend, mit mehreren Jahren Consulting-Erfahrung, hatte er zunächst eine eigene Softwareentwicklungsfirma gegründet. Dabei stellte er fest, dass Führung gelernt sein will und gute Vorbilder selten sind.
Es zeichnete sich ab, dass der größte Unterstützungsbedarf bei seinen Kunden in der Softwareentwicklung nicht im Produzieren von Code liegt, sondern in der Führung. So war es für ihn klar, wohin die Reise mit seiner Firma Kutura geht: Führung verbessern, damit die Menschen, die die Produkte entwickeln, sich selbst entwickeln und wachsen können.
Für Heise schreibt Stefan als langjähriger, freier Mitarbeiter der iX seit 1994.
Im gesellschaftlichen Diskurs geht es bei der Diversität um eine Vielfalt bestimmter Gruppenmerkmale. Als Beispiel für die Vielfalt der Arbeitswelt sei die „Charta der Vielfalt“ genannt: „Ziel der Charta der Vielfalt ist es, ein Arbeitsumfeld zu schaffen, in dem alle Beschäftigten die gleiche Wertschätzung und Förderung erfahren, unabhängig von Nationalität, ethnischer Herkunft, Religion oder Weltanschauung, sozialer Herkunft, Behinderung, Alter sowie sexueller Orientierung und Identität.“ (Wikipedia)
Nun gilt es als akzeptierte Wahrheit, dass diverse Teams besser geeignet sind, um komplexe Probleme zu lösen, die etwa in der Softwareentwicklung auftreten. Bedeutet das, ich sollte in meinem Entwicklerteam alle Weltreligionen und ein paar Nationalitäten vertreten haben?
Nach meiner Erfahrung sind das nicht die Diversitätsfaktoren, die den Unterschied machen. Es kommt auf etwas anderes an. Ein Artikel in Harvard Business Review unterstützt diese Meinung und spricht von „kognitiver Diversität“.
Worauf kommt es an, wenn man ein Team divers aufstellen will? Ohne Anspruch auf Allgemeingültigkeit kann ich folgende Faktoren nennen, bei denen Vielfalt innerhalb eines Teams nach meiner Beobachtung einen positiven Einfluss hat.
Alter und Geschlecht spielen eine Rolle. Einerseits glaube ich, dass Männer und Frauen Probleme tendenziell unterschiedlich angehen. Das ist gerade bei komplexen Aufgaben hilfreich. Genauso sorgt eine größere Altersbandbreite im Team dafür, dass die Teammitglieder sehr verschiedene fachliche Erfahrungen mitbringen.
Die Dauer der Firmenzugehörigkeit ist ebenfalls ein wichtiger Faktor. Es ist hilfreich, wenn der „alte Hase“, der sehr gut vernetzt ist, weiß, wen das Team bei einem Problem ansprechen kann. Auf der anderen Seite laufen die jungen Teammitglieder nicht so leicht Gefahr, ein Problem so anzugehen, „wie wir das immer schon gemacht haben“. Sie bringen neue Ansätze und Sichtweisen ins Team.
Eine große Vielfalt der Perspektiven kommt ins Team, wenn nicht alle Teammitglieder die gleichen Ausbildungen durchlaufen haben. Eine Gruppe von Informatikern wird ein Problem sehr wahrscheinlich mit den Mitteln lösen, die man im Informatikstudium gelernt hat. Kommen weitere Disziplinen hinzu, etwa Sozialwissenschaften, Physik, Philosophie oder Ingenieurwesen, steigt die Zahl der Lösungsansätze. Mein Lehrer im Mathe-Leistungskurs hat gerne eine Geschichte aus der Eigentümerversammlung in seinem Haus erzählt. Bis auf eine Person handelte es sich um Akademiker, ihn selbst eingeschlossen. Wenn im Haus ein Defekt auftrat, haben die Studierten in der Versammlung über ausgefeilte Lösungsansätze diskutiert. In der gleichen Zeit hat der einzige Nicht-Akademiker den Schaden einfach repariert. Diese Anekdote zeigt, dass Monokulturen in der Ausbildung von Teammitgliedern oft nicht zu schlagkräftigen Teams führen.
Auch unterschiedliche Muttersprachen erlebe ich im Team als Bereicherung. Das mag an der damit verbundenen Vielfalt der Kulturen liegen, in denen die Teammitglieder aufgewachsen sind, oder daran, dass Sprache das Denken formt.
Einen weiteren Faktor nenne ich in Anlehnung an ein bekanntes Buch: schnelles Denken, langsames Denken. Manchmal ist es wichtig, schnell voranzuschreiten, um beispielsweise mit einem frühen Prototypen einer Software schnell Nutzer-Feedback zu bekommen. Andererseits ist es hin und wieder gut, ein Problem von allen Seiten zu betrachten, um mit einer fundierten Lösung an die Öffentlichkeit zu gehen. Teams, in denen beide Ansätze durch unterschiedliche Personen vertreten sind, haben oft Konflikte. Wenn sie es aber gelernt haben, die Konflikte in der Sache auszutragen und nicht auf der persönlichen Ebene, ist es eine Stärke, mal einen Schnellschuss machen zu können und ein anderes Mal langsam und gewissenhaft zu arbeiten. Wichtig ist, dass sich die Vertreter der jeweiligen Fraktion im richtigen Moment zurücknehmen können.
Und, last, but not least, schätze ich Softwareteams, die in verschiedenen Programmierparadigmen (imperativ, OO, funktional, Logikprogrammierung) denken können. Zwar ist es oft keine Option, während der Produktentwicklung die Sprache zu wechseln oder eine weitere hinzuzufügen, jedoch erlauben einige Sprachen, mehrere Paradigmen zu verwenden. Als Beispiel sei die funktionale Programmierung genannt, die ehemals ausschließlich in funktionalen Sprachen zu finden war und in den vergangenen Jahren auch in objektorientierte Sprachen eingezogen ist.
Welche weiteren Faktoren von Diversität sind in Softwareteams hilfreich? Ich würde mich freuen, in den Kommentaren Eure Meinungen zu lesen.
Abschließend möchte ich auf eine Frage eingehen: Wie kann man die Diversität bei Bedarf erhöhen? Das ist eine Frage der Teamentwicklung. Doch ich habe noch nicht erlebt, dass eine HR-Abteilung die genannten Aspekte beim Recruiting berücksichtigt. Auch ein Teamleiter sucht bei einer Neueinstellung eher nach jemandem, „bei dem die Chemie stimmt“. Das ist nicht pauschal schlecht, doch sollte man sich im Klaren darüber sein, dass eine höhere Diversität damit vielleicht nicht erreicht wird.
Das Team kann selbst die Führung übernehmen und an der Vielfalt arbeiten. Während sich am Alter, Geschlecht oder der Muttersprache der Teammitglieder nichts ändern lässt, ist eine bewusste Weiterbildung in Richtung neuer Skills und Kompetenzen möglich. Den Ausgangspunkt kann ein Team zum Beispiel damit machen, die Diversität der Gruppe zu bestimmen und sichtbar zu machen. Das ist der erste wichtige Schritt, um abzuleiten, in welchen Bereichen man nachbessern sollte. Und wenn eine Neubesetzung einer Stelle ansteht, kann das Team Einfluss nehmen. Damit übernehmen die Teammitglieder einen Teil der Führungsverantwortung, die sonst häufig von niemandem ausgeübt wird.
Erst Lesen, dann Hören
Im Podcast Escape the Feature Factory greife ich ausgewählte Themen des Blogs auf und diskutiere sie mit einem Gast. Durch den Austausch lerne ich eine zweite Perspektive kennen. Wenn Du auch daran interessiert bist, findest Du den Podcast bei Spotify, Deezer, Amazon Music, und Apple Podcasts. Wenn Du die Themen, die ich im Blog anspreche, verbessern möchtest, komm’ in unsere Leadership-Community für Softwareentwicklung.
(rme)
Entwicklung & Code
programmier.bar: Text-to-Speech mit Thorsten Müller
Immer mehr Alltagsgeräte geben Sprache aus. Was mit Screenreadern und Navigationssystemen begann, findet heute in Wohnzimmern und Hosentaschen mit Alexa und Siri statt. Doch wie lernen Computer eigentlich zu sprechen? Und lässt sich sogar die eigene Stimme klonen? Darüber sprechen Jan Gregor Emge-Triebel und Garrelt Mock von der programmier.bar mit Thorsten Müller, dem Macher und der Stimme hinter Thorsten-Voice. Die Besonderheit an dem Open-Source-Projekt: Sprachausgabe wird hier lokal erzeugt, komplett ohne Cloud-Dienste. Es ist auf Grundlage von Thorsten Müllers eigener Stimme trainiert und damit frei von rechtlich problematischen Trainingsdaten.
Thorsten Müller erzählt, was er beim Aufbau des Projekts gelernt hat, und gibt Tipps für alle, die selbst mit Sprachsynthese experimentieren wollen. Zusätzlich wirft er einen Blick auf die rasante Entwicklung der künstlichen Sprachausgabe. Neben Podcasting gibt es spannende Anwendungsfälle quer durch den Alltag.
Empfohlener redaktioneller Inhalt
Mit Ihrer Zustimmung wird hier ein externer Inhalt geladen.
Die aktuelle Ausgabe des Podcasts steht auch im Blog der programmier.bar bereit: „Text-to-Speech mit Thorsten Müller„. Fragen und Anregungen gerne per Mail oder via Mastodon, Bluesky, LinkedIn oder Instagram.
(mai)
Entwicklung & Code
Cleverer Werbebetrug mit Android-Apps doch aufgeflogen
224 betrügerische Android-Apps hat Google aus seinem Play Store entfernt. Sie waren insgesamt 38 Millionen Mal installiert, von Android-Nutzern in 228 Ländern, und lösten täglich 2,3 Milliarden betrügerische Werbeanzeigen aus, die nie jemand zu Gesicht bekam. Klassischer Werbebetrug, aber besonders gut versteckt. Dennoch aufgedeckt haben ihn Sicherheitsforscher der Firma Human. Sie nennen den Fall „SlopAds“, in Anspielung an „AI Slop“, was KI-generierte Medieninhalte geringer Qualität bezeichnet.
Die betrügerischen Anwendungen hatten meist KI-Bezug und enthielten, so wie sie im Play Store eingereicht und zum Download angeboten wurden, keine Malware-Funktion im engeren Sinne. Erst nach erfolgter Installation wurde eine verschlüsselte Konfiguration mittels Firebase Remote Config nachgeladen. Darin enthalten waren Hyperlinks: eine Liste über 300 betrügerischer Webseiten, die der Bereitstellung der fremden Reklame dienten; ein Link zum Download eines Javascripts, das den heimlichen Abruf der Reklame über Webview steuerte; und ein Link zu vier PNG-Bilddateien.
In diesen Bildern war, steganografisch, weiterer Code versteckt. Die Apps bauten daraus die eigentliche Schadroutine, die Human „FatModule“ nennt. Diese Software prüfte zunächst, auf welchem Wege der Nutzer an die App gelangt war. Wurde sie mittels Suche im Play Store gefunden und installiert, arbeitete sie nur wie angepriesen, die Schadroutine wurde dann nie scharfgeschaltet.
Wer auf Werbung hereinfiel, wurde für Werbedownloads missbraucht
Allerdings hatten die Werbebetrüger auch selbst Werbung geschaltet, nämlich für ihre Apps. Klickte ein Nutzer auf solche Reklame, landete auf diesem Weg in Googles Play Store und installierte die App, wurde deren Betrugsmodus aktiviert. Das sollte Sicherheitsforscher ausschließen, die sich eher im Play Store direkt bedienen, anstatt irgendwelche Reklame zu klicken.
Zusätzlich suchten die Apps nach Hinweisen auf mögliche Ausführung durch Sicherheitsforscher, etwa ein gerootetes Betriebssystem, einen Emulator oder Debugging-Werkzeuge. Nur wenn nichts dergleichen gefunden wurde, begannen die heimlichen Downloads von Werbung in einem versteckten Webview-Prozess. Selbst dann wurden die Abrufe über mehrere Weiterleitungen geschickt, um dem Werbeserver keine verdächtigen Referrer zu liefern.
Workflow der nicht-immer-betrügerischen Apps
(Bild: Human)
Human hat Google informiert, dass die 224 bekannten Apps aus dem Play Store gelöscht wurden. Google Play wird jene Anwender, die solche Apps bereits installiert haben, zu deren Löschung von ihren Geräten auffordern.
Neuer Anlauf erwartet
Wie lange die Täter schon am Werk waren, ist nicht bekannt. Sie hatten es immerhin auf 38 Millionen Downloads gebracht. Und noch während der laufenden Untersuchung Humans sind weitere Apps hinzugekommen.
Die Forscher erwarten nicht, dass die Täter sich fortan redlichem Broterwerb widmen werden; wahrscheinlicher sei, dass sie bald neuen Anlauf nehmen, mit einer noch ausgefeilteren Werbebetrugsmasche. Opfer sind einerseits Werbetreibende, die für Werbung bezahlen, die nie ein Mensch würdigt, und andererseits die App-Nutzer, deren Bandbreite, Prozessorleistung und Akkuladung für systematischen Betrug vergeudet wird.
(ds)
-
UX/UI & Webdesignvor 4 Wochen
Der ultimative Guide für eine unvergessliche Customer Experience
-
UX/UI & Webdesignvor 3 Wochen
Adobe Firefly Boards › PAGE online
-
Social Mediavor 4 Wochen
Relatable, relevant, viral? Wer heute auf Social Media zum Vorbild wird – und warum das für Marken (k)eine gute Nachricht ist
-
Entwicklung & Codevor 4 Wochen
Posit stellt Positron vor: Neue IDE für Data Science mit Python und R
-
Entwicklung & Codevor 2 Wochen
EventSourcingDB 1.1 bietet flexiblere Konsistenzsteuerung und signierte Events
-
Digital Business & Startupsvor 3 Monaten
10.000 Euro Tickets? Kann man machen – aber nur mit diesem Trick
-
UX/UI & Webdesignvor 2 Tagen
Fake It Untlil You Make It? Trifft diese Kampagne den Nerv der Zeit? › PAGE online
-
Digital Business & Startupsvor 3 Monaten
80 % günstiger dank KI – Startup vereinfacht Klinikstudien: Pitchdeck hier