Entwicklung & Code
Die wichtigsten Neuerungen von Java 25: Schneller Startup mit Stable Values
Die Veröffentlichung für das OpenJDK 25 ist für den 16. September 2025 vorgesehen. Diesmal sind 18 JEPs (JDK Enhancement Proposals) eingeplant. Das ist zwar etwas weniger als noch bei Java 24, aber dafür werden dieses Mal einige Features nach vorangehenden Previews finalisiert. Außerdem bieten die meisten Distributoren für Java 25 wieder einen verlängerten Support (LTS) an. Viele Java-Projekte wechseln in den nächsten Monaten vom letzten LTS Release 21 zu 25.
Wir werfen hier einen ersten Blick auf die aus Developer-Sicht interessantesten Punkte. Weitere Details lassen sich über die jeweiligen JEPs nachvollziehen. In einem zweiten Post betrachten wir dann die weiteren Änderungen, die mehr unter der Haube versteckt oder nicht so relevant für die Java-Entwicklerinnen und -Entwickler sind. Die meisten in diesem Beitrag beschriebenen JEPs sind übrigens Wiedervorlagen, die bereits in früheren Java-Versionen als Inkubator oder Preview veröffentlicht wurden. Ganz neu sind die Stable Values, die eine größere Flexibilität bei der Initialisierung von final-Feldern bieten.
JEP 502: Stable Values
Stable Values sind Objekte, die genau einmal zum Einsatz kommen und danach unveränderlich sind. Die JVM (Java Virtual Machine) behandelt ihren Inhalt wie eine echte Konstante und kann dieselben Optimierungen (zum Beispiel Constant Folding) anwenden, wie bei final‑Feldern – nur, dass die Initialisierung beliebig spät erfolgen darf. Damit bekommt Java eine „Deferred Immutability“, die Start‑ und Aufwärmzeiten verkürzt, ohne Sicherheitsrisiken mit Thread einzugehen. Die Ziele sind:
- Schnelleres Start‑Up: Keine monolithische Initialisierung aller Komponenten mehr.
- Entkopplung von Erzeugung & Initialisierung eines unveränderlichen Werts – ohne Performance‑Kosten.
- Garantierte At‑Most‑Once‑Initialisierung auch in hoch parallelen Szenarien.
- Konstanten‑Optimierungen für Anwendungscode zugänglich machen, nicht nur für JDK‑internen Code.
Stable Values sind eine reine Library‑API. Es wird kein neues Schlüsselwort geben. Außerdem gibt es keine Änderung an der Semantik von final, bestehender Code bleibt unberührt. Bisher müssen final‑Felder eager (sofort) initialisiert werden – die Initialisierung eines Loggers oder eine Datenbank‑Connection bremst so den Programmstart aus. Workarounds wie Lazy Holder, Double‑Checked Locking oder ConcurrentHashMap.computeIfAbsent
sind entweder eingeschränkt, fehleranfällig oder verhindern JIT‑Optimierungen (Just in Time). Stable Values schließen die Lücke zwischen strenger Immutability und flexibler Lazy‑Initialisierung.
Der Lambda-Ausdruck in orElseSet
wird garantiert genau einmal ausgeführt, selbst wenn mehrere Virtual Threads gleichzeitig logger()
aufrufen. Danach kann der JIT (Just in Time Compiler der Hotspot VM) alle Zugriffe optimieren, wie durch Constant Folding.
class OrderController {
private final StableValue logger = StableValue.of();
private Logger logger() {
return logger.orElseSet(() -> Logger.create(OrderController.class));
}
}
Stable Values sind besonders für parallele Programmierung interessant. Im Umfeld von Virtual Threads arbeitet das Entwicklerteam schon seit Längerem an weiteren Bausteinen. Während Structured Concurrency bereits zum sechsten Mal als Preview dabei ist, gelten die Scoped Values nach vier Previews jetzt als final.
JEP 506 – Scoped Values (Final in JDK 25)
Scoped Values sind eine Alternative zu ThreadLocal-Variablen, die Threads unterschiedliche Zugriffe auf einen globalen Zustand ermöglichen. ThreadLocal lässt sich beliebig ändern, hat eine unbegrenzte Lebensdauer (Leak-Gefahr, vor allem in Pools) und ist teurer durch Vererbung an Kind-Threads. Scoped Values begrenzen die Lebensdauer klar, sind read-only und lassen sich über viele Threads hinweg platz- und zeitsparend nutzen. Gegenüber den Previews wird die API finalisiert. Einzige Änderung: ScopedValue.orElse
akzeptiert kein null
mehr. Ein Web-Framework bindet beispielsweise einmal pro Request den FrameworkContext (zum Beispiel Spring ApplicationContext oder ein AuthenticationContext). Die Handler und interne Framework-Aufrufe (etwa DB-Zugriff) können auf diesen FrameworkContext zugreifen, ohne dass er immer als zusätzlicher Parameter durchgereicht werden muss. Und auch in Kind-(Virtual) Threads ist er automatisch verfügbar.
JEP 505 – Structured Concurrency (Preview)
Mit dem StructuredTaskScope erhält eine API Einzug, die zusammengehörige Nebenläufigkeit als eine Einheit behandelt: Subtasks werden im Block geforkt und über ein join() wieder zusammengeführt. Fehler sorgen gegebenenfalls für einen direkten Abbruch anderer Subtasks. Beim Debugging lässt sich in den Thread-Dumps die Task-Hierarchie leicht nachvollziehen. Damit erlaubt Structured Concurrency die Implementierung auf eine besonders les- und wartbare Art und Weise. Alternativ konnten Entwicklerinnen und Entwickler für diesen Zweck bisher die Parallel Streams, den ExecutorService oder reaktive Programmierung einsetzen. Alles sehr mächtige Ansätze, die aber gerade einfache Umsetzungen unnötig kompliziert und fehleranfällig machen.
Neu gegenüber der letzten Preview: Scopes lassen sich über statische Fabrikmethoden öffnen (open()
für den Standardfall, d. h. entweder sind alle erfolgreich oder bei einem Fehler bricht alles ab). Alternative Abschluss-Policies und Rückgabewerte werden über Joiner-basierte open(...)
-Varianten gesteuert. Ziele sind unter anderem das Vermeiden von Thread-Leaks und Cancellation-Delays sowie bessere Observability. Nicht beabsichtigt ist der Ersatz von ExecutorService/Future, neue Channel-Primitiven oder eine neue Cancel-Mechanik. Gerade bei I/O-Fan-out mit Virtual Threads wird die Koordination einfacher, robuster und nachvollziehbarer.
// --enable-preview beim Kompilieren und Starten
record Response(String user, int order) {}
Response handle() throws InterruptedException {
try (var scope = StructuredTaskScope.
Trotz seiner 30 Jahre zieht Java auch weiter viele Programmierneulinge an. Die JEPs 511 (Module Import Declarations) und 512 (Compact Source Files and Instance Main Methods) helfen aber nicht nur Anfängern, sie machen auch erfahrenen Developern das Leben einfacher. Beide JEPs erreichen nun nach mehreren vorangegangenen Previews den finalen Status.
JEP 511 – Module Import Declarations (Final in JDK 25)
Dieses JEP ergänzt die Sprache um import module
, sodass alle exportierten Pakete eines Moduls mit einer Deklaration verfügbar sind. Das reduziert Import-Boilerplate, erleichtert die Wiederverwendung modularer Bibliotheken (ohne dass der eigene Code modular sein muss) und funktioniert reibungslos neben den bekannten import-Deklarationen. Für Einsteiger oder auch für Anwender des Prototypings in JShell sinkt die Hürde, weil sie nicht mehr wissen müssen, in welchem Paket sich einzelne Typen befinden. In Szenarien mit transitiven Modulabhängigkeiten (zum Beispiel java.sql, java.xml) vereinfacht sich die Nutzung zusammengehöriger APIs spürbar.
import module java.base;
List xs = List.of("a", "b");
Path p = Path.of("data.txt");
// List/Map/Stream/Path sind ohne einzelne Paket-Imports nutzbar
JEP 512 – Compact Source Files & Instance-main (Final in JDK 25)
Wir können jetzt in einer einzelnen Java-Datei viel schlanker kleine Programme abspeichern und ausführen. Ziel ist eine sanfte Einstiegskurve: Grundkonzepte zuerst, Programmieren-im-Kleinen ohne überflüssige Zeremonie. Bei wachsender Komplexität lässt sich der Code nahtlos zu Klassen/Packages/Modulen ausbauen. Gegenüber den Previews wird das Feature finalisiert und umbenannt (aus simple wird compact). Die neue IO-Klasse liegt nun in java.lang und wird damit implizit importiert, ihre statischen Methoden aber nicht. Außerdem basiert IO jetzt auf System.out
/System.in
statt java.io.Console
.
// > java Main.java
void main() {
IO.println("Hello, World!");
}
JEP 513 – Flexible Constructor Bodies (Final in JDK 25)
Konstruktoren dürfen nun Anweisungen vor dem Aufruf von super(...)
oder this(...)
enthalten. Diese Prolog-Phase dient beispielsweise zur Validierung von Argumenten, zum Initialisieren eigener Felder, ohne das entstehende Objekt anderweitig zu referenzieren, oder zum Transformieren von Inputwerten. Danach folgt der gewohnte Epilog. Das macht viele Konstruktoren natürlicher (z. B. fail-fast
) und erhöht die Sicherheit, weil Unterklassen-Felder vor einem möglichen Zugriff durch Superklassen-Konstruktoren oder dort aufgerufene Methoden bereits gesetzt sind. Ziel ist es, überflüssige Beschränkungen zu entfernen und zusätzliche Garantien für einen vollständig initialisierten Objektzustand zu geben. Das umgeht die praktischen Probleme der bisherigen Regel „super() muss zuerst kommen“, durch die es zu unnötiger Arbeit, zu tückischen Initialisierungsfehlern bei mehreren Threads und zu Integritätsverletzungen kommen konnte.
JEP 507 – Primitive Typen in Patterns, instanceof und switch (3. Preview)
Dieser JEP macht Pattern Matching einheitlich für alle Typen, inklusive Primitive. Er erlaubt primitive Typmuster in allen Pattern-Kontexten (top-level und verschachtelt), erweitert instanceof auf Primitive (inklusive sichere, verlustfreie Umwandlungen) und lässt switch über alle primitiven Typen laufen (auch boolean, long, float, double). Ziel ist weniger Reibung und mehr Ausdrucksstärke: unsichere Casts und Boxing entfallen, da ein Pattern nur matcht, wenn die Konversion verlustfrei ist. Außerdem lassen sich Typmuster, instanceof
und „safe casting“ semantics sauber aufeinander ausrichten.
// switch mit primitive type pattern
switch (x.getStatus()) {
case 0 -> "okay";
case 1 -> "warning";
case 2 -> "error";
case int i -> "unknown: " + i; // zeigt unbekannten int-Status
}
// instanceof mit sicherer Konversion (matcht nur verlustfrei)
if (i instanceof byte b) { /* b nutzen */ }
// Record-Pattern mit sicherem Narrowing
if (json instanceof JsonObject(var map)
&& map.get("age") instanceof JsonNumber(int age)) {
// nur wenn double → int verlustfrei passt
}
JEP 508 – Vector API (erneut Inkubator)
Die Vector-API erlaubt, Vektorberechnungen in Java auszudrücken, die der HotSpot-Compiler zu nativen SIMD-Instruktionen (x64: SSE/AVX, AArch64: NEON/SVE) kompiliert – mit stabilerer Performance als rein skalare Implementierungen. In JDK 25 ist diese API erneut im Inkubatorstatus, da man noch auf die Implementierung von Value Classes als Grundlage für die Vector API wartet. Neu sind:
- VectorShuffle ↔ MemorySegment-Zugriff
- Anbindung nativer Mathe-Bibliotheken über die FFM-API statt HotSpot-C++-Code (wartungsärmer)
- Auto-Vektorisierung von Float16-Operationen (Add/Sub/Mul/Div/Sqrt/FMA) auf unterstützten x64-CPUs.
Ziel ist eine klare, plattform-agnostische API mit verlässlicher Abbildung auf Vektorbefehle und „graceful degradation“.
Das waren 8 der insgesamt 18 neuen JEPs. Die anderen, die weniger für Entwicklerinnen und Entwickler relevant, aber trotzdem spannende Themen sind, schauen wir uns in einem zweiten Post an. Zusätzlich sind die vielen kleineren Neuerungen, für die es keine JEPs gibt, in den Release-Notes zum Nachlesen. Änderungen am JDK (Java Klassenbibliothek) wie die Stable Values oder Structured Concurrency kann man sich zudem über den Java Almanac anzeigen lassen.
(mdo)
Entwicklung & Code
Die Produktwerker: Mehr Wirkung durch verbesserte Metriken und KPIs
Tim Klein spricht in dieser Podcastfolge mit Marc Roulet von sevdesk darüber, warum bessere Metriken und Key Performance Indicators (KPIs) den Unterschied machen, wenn es um gute Produktentscheidungen geht. Beide erleben in ihrer Arbeit immer wieder, dass Organisationen Zahlen erheben, die zwar schnell verfügbar sind, aber wenig darüber aussagen, ob ein Produkt wirklich Nutzen stiftet. Marc Roulet ist ein erfahrener Experte im Bereich Analytics und Metriken und bei sevdesk verantwortlich für Data und Analytics. Er hat in ähnlichen Rollen unter anderem auch schon bei eBay, mobile.de und Xing Erfahrungen gesammelt.
(Bild: deagreez/123rf.com)
So geht Produktmanagement: Auf der Online-Konferenz Product Owner Day von dpunkt.verlag und iX am 13. November 2025 können Product Owner, Produktmanagerinnen und Service Request Manager ihren Methodenkoffer erweitern, sich vernetzen und von den Good Practices anderer Unternehmen inspirieren lassen.
Klarheit und Orientierung durch sinnvolle Metriken
Viele Teams orientieren sich an einfachen Kennzahlen wie Story Points oder der Anzahl ausgelieferter Features. Das zeigt den „Fleiß“, im Sinne von Output, sagt aber kaum etwas über die Wirkung aus. Bessere Metriken schauen auf Outcomes und helfen zu erkennen, ob Kundinnen und Kunden tatsächlich profitieren. Wer das ernst nimmt, stellt fest, dass nicht jede neue Funktion Wert für die Nutzer schafft – und dass auch das Weglassen eine wichtige Entscheidung sein kann.
Metriken sind kein starres System, das man einmal definiert und dann abarbeitet. Sie entfalten ihren Wert erst, wenn Teams regelmäßig hinschauen, sie diskutieren und gegebenenfalls anpassen – also aktiv mit ihnen arbeiten. So entsteht ein gemeinsames Verständnis, worauf es wirklich ankommt. Oft geht es darum, Hypothesen zu prüfen: Führt eine bestimmte Änderung tatsächlich zu mehr Nutzung? Verbessert sie ein relevantes Kundenerlebnis? Oder verpufft der Effekt?
Gerade für Product Owner liegt hier eine Chance. Sie sind nah an den Entscheidungen und können dafür sorgen, dass Gespräche über bessere Metriken nicht an der Oberfläche bleiben. Es geht nicht darum, Zahlen zu liefern, die gut aussehen, sondern um eine Grundlage, die schwierige Fragen und Entscheidungen ermöglicht. Was bedeutet Erfolg für unser Produkt? Wie messen wir Fortschritt, der über Auslastung und Geschwindigkeit hinausgeht und in Richtung unserer Produktvision führt?
Wer sich auf diesen Weg einlässt, wird merken, dass bessere Metriken Orientierung geben. Sie bringen Klarheit in Diskussionen mit Stakeholdern, machen Annahmen transparent und helfen Teams, bewusster zu entscheiden. So wird die Produktentwicklung weniger zu einer Abfolge von Aktivitäten und mehr zu einem Prozess von Wertgenerierung, der echten Unterschied macht.
Wer mit Marc Roulet direkt in Kontakt treten und weitere Fragen klären möchte, kontaktiert ihn am besten über sein LinkedIn-Profil.
Weiterführende Links
Die aktuelle Ausgabe des Podcasts steht auch im Blog der Produktwerker bereit: „Die Produktwerker: Bessere Metriken & KPIs für richtige Entscheidungen und mehr Wirkung„.
(mai)
Entwicklung & Code
KI-Überblick 3: Was sind neuronale Netze – und wie funktionieren sie?
Neuronale Netze gelten als Herzstück des modernen maschinellen Lernens. Sie sind die Grundlage zahlreicher Anwendungen – von der Spracherkennung über die Bildverarbeitung bis hin zu generativen Sprachmodellen wie GPT-5.
Oft klingen sie nach einem hochkomplexen, schwer durchschaubaren Konstrukt. Dabei basieren sie auf einem vergleichsweise einfachen Prinzip: der Verknüpfung vieler kleiner, gleichförmiger Recheneinheiten zu einem Netz, das in der Lage ist, selbst hochdimensionale Zusammenhänge zu modellieren.
Golo Roden ist Gründer und CTO von the native web GmbH. Er beschäftigt sich mit der Konzeption und Entwicklung von Web- und Cloud-Anwendungen sowie -APIs, mit einem Schwerpunkt auf Event-getriebenen und Service-basierten verteilten Architekturen. Sein Leitsatz lautet, dass Softwareentwicklung kein Selbstzweck ist, sondern immer einer zugrundeliegenden Fachlichkeit folgen muss.
Dieser Beitrag erläutert die Grundstruktur neuronaler Netze, erklärt die wichtigsten Begriffe und zeigt, warum bereits einfache Varianten erstaunlich leistungsfähig sein können.
Biologisch inspiriert, aber nicht biologisch
Neuronale Netze wurden ursprünglich von der Struktur des menschlichen Gehirns inspiriert. In der Biologie bestehen Gehirne aus Neuronen, die über Synapsen miteinander verbunden sind und elektrische Signale verarbeiten. Diese Analogie diente als Vorbild – allerdings ist sie oberflächlich zu verstehen. Die „Neuronen“ in einem künstlichen neuronalen Netz sind einfache mathematische Funktionen, die Eingabewerte gewichten, aufsummieren und das Ergebnis durch eine sogenannte Aktivierungsfunktion schicken. Es handelt sich also nicht um echte Nachbildungen biologischer Vorgänge, sondern um abstrahierte Rechenelemente.
Aufbau eines neuronalen Netzes
Ein künstliches neuronales Netz besteht aus mehreren Schichten:
- Eingabeschicht (Input Layer): Die Eingabeschicht nimmt die Rohdaten auf. Jede Eingabevariable entspricht einem Knoten in dieser Schicht.
- Verborgene Schichten (Hidden Layers): Verborgene Schichten bestehen aus Neuronen, die die Daten transformieren. Je nach Anzahl und Aufbau dieser Schichten spricht man von flachen oder tiefen Netzen.
- Ausgabeschicht (Output Layer): Die Ausgabeschicht gibt das Ergebnis des Netzes zurück – zum Beispiel eine Klassifikation oder einen numerischen Wert.
Jedes Neuron einer Schicht ist mit den Neuronen der nächsten Schicht verbunden. Diese Verbindungen tragen sogenannte Gewichte, die während des Trainings angepasst werden. Zusätzlich besitzt jedes Neuron einen Bias, also eine Verschiebung, die unabhängig von den Eingaben wirkt.
Ein typisches Neuron berechnet eine gewichtete Summe seiner Eingaben, addiert den Bias und wendet dann eine Aktivierungsfunktion an. Diese Funktion entscheidet, ob und wie stark das Neuron „feuert“. Gängige Aktivierungsfunktionen sind die ReLU-Funktion (Rectified Linear Unit) oder die Sigmoid-Funktion.
Warum mehrere Schichten?
Ein einzelnes Neuron kann nur sehr einfache Abbildungen realisieren – etwa eine lineare Trennung zwischen zwei Klassen. Erst durch die Kombination vieler Neuronen in mehreren Schichten entsteht ein Netz, das auch komplexe, nicht lineare Zusammenhänge modellieren kann. Jede Schicht lernt dabei gewissermaßen eine andere Abstraktionsebene: In einem Netz zur Bilderkennung erkennen die ersten Schichten möglicherweise einfache Kanten, die mittleren geometrische Formen und die letzten komplexe Objekte wie Gesichter oder Schriftzeichen.
Diese Hierarchiebildung ist ein entscheidender Erfolgsfaktor neuronaler Netze. Sie macht es möglich, dass Systeme mit vergleichsweise wenig explizitem Wissen aus Beispieldaten lernen, was relevante Merkmale sind.
Wie ein Netz lernt
Das Training eines neuronalen Netzes erfolgt in zwei Schritten: dem Vorwärtsdurchlauf (Forward Propagation) und der Rückpropagierung (Backpropagation).
- Vorwärtsdurchlauf: Die Eingabedaten werden Schicht für Schicht durch das Netz geleitet, bis eine Ausgabe entsteht.
- Fehlerberechnung: Die Ausgabe wird mit dem erwarteten Ergebnis verglichen. Daraus ergibt sich ein Fehlerwert (zum Beispiel durch eine Verlustfunktion wie die mittlere quadratische Abweichung).
- Rückpropagierung: Der Fehler wird von der Ausgabeschicht rückwärts durch das Netz propagiert. Dabei werden die Gewichte schrittweise so angepasst, dass der Fehler beim nächsten Durchlauf kleiner wird. Dieser Prozess basiert auf Gradientenverfahren und wiederholt sich über viele Iterationen.
Dieser Ablauf ist rein rechnerisch. Das Netz „versteht“ dabei nichts im menschlichen Sinn – es passt lediglich Zahlenwerte an, um eine mathematische Funktion zu approximieren, die möglichst gut zu den Trainingsdaten passt.
Grenzen und Herausforderungen
Neuronale Netze sind leistungsfähig, aber nicht universell einsetzbar. Sie benötigen typischerweise große Mengen an Trainingsdaten, um verlässlich zu funktionieren. Zudem sind sie anfällig für Overfitting, also die Überanpassung an Trainingsdaten, wodurch sie bei neuen Eingaben schlechter generalisieren.
Ein weiterer Kritikpunkt ist die eingeschränkte Erklärbarkeit: Gerade tiefe Netze sind oft schwer zu analysieren, weil nicht klar ist, welche internen Repräsentationen sie gelernt haben. Deshalb wird an Explainable-AI-Verfahren geforscht, die mehr Transparenz ermöglichen sollen.
Ausblick
In der nächsten Folge wenden wir uns dem Deep Learning zu – also der Frage, was neuronale Netze „tief“ macht, warum Tiefe oft hilfreich ist und wie typische Architekturen wie Convolutional Neural Networks (CNNs) und Recurrent Neural Networks (RNNs) funktionieren. Damit rücken wir ein Stück näher an die Methoden heran, die viele moderne KI-Anwendungen ermöglichen.
(rme)
Entwicklung & Code
software-architektur.tv: Was hält eine Softwarearchitektur flexibel?
Die Zukunft ist schwer vorhersehbar – umso wichtiger ist es, dass die Softwarearchitektur auf neue Anforderungen und Veränderungen reagieren kann. Doch wie erreicht man diese Flexibilität?
In dieser Episode seines Videocasts spricht Eberhard Wolff mit den Teilnehmenden über ihre Ideen und Ansätze, und im Anschluss teilt er seine eigenen Konzepte. Im Vorfeld hat er über Social Media (Bluesky, Mastodon, LinkedIn) nach Ideen gefragt.
Lisa Maria Schäfer malt dieses Mal keine Sketchnotes.
Livestream heute, am 1. September
Die Ausstrahlung findet heute, am 1. September 2025, live von 18 bis 19 Uhr statt. Die Folge steht im Anschluss als Aufzeichnung bereit. Während des Livestreams können Interessierte Fragen via Twitch-Chat, YouTube-Chat, Bluesky, Mastodon, Slack-Workspace oder anonym über das Formular auf der Videocast-Seite einbringen.
software-architektur.tv ist ein Videocast von Eberhard Wolff, Blogger sowie Podcaster auf iX und bekannter Softwarearchitekt, der als Head of Architecture bei SWAGLab arbeitet. Seit Juni 2020 sind über 250 Folgen entstanden, die unterschiedliche Bereiche der Softwarearchitektur beleuchten – mal mit Gästen, mal Wolff solo. Seit mittlerweile mehr als zwei Jahren bindet iX (heise Developer) die über YouTube gestreamten Episoden im Online-Channel ein, sodass Zuschauer dem Videocast aus den Heise Medien heraus folgen können.
Weitere Informationen zur Folge finden sich auf der Videocast-Seite.
(mdo)
-
Datenschutz & Sicherheitvor 3 Monaten
Geschichten aus dem DSC-Beirat: Einreisebeschränkungen und Zugriffsschranken
-
UX/UI & Webdesignvor 2 Wochen
Der ultimative Guide für eine unvergessliche Customer Experience
-
Apps & Mobile Entwicklungvor 3 Monaten
Metal Gear Solid Δ: Snake Eater: Ein Multiplayer-Modus für Fans von Versteckenspielen
-
Online Marketing & SEOvor 3 Monaten
TikTok trackt CO₂ von Ads – und Mitarbeitende intern mit Ratings
-
UX/UI & Webdesignvor 2 Tagen
Adobe Firefly Boards › PAGE online
-
Social Mediavor 2 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 2 Wochen
Posit stellt Positron vor: Neue IDE für Data Science mit Python und R
-
Digital Business & Startupsvor 2 Monaten
10.000 Euro Tickets? Kann man machen – aber nur mit diesem Trick