Entwicklung & Code
Asynchrone Programmierung – Teil 2: Koroutinen in C++ mit Boost.Asio
Mit dem Boost.Asio-Framework steht C++-Developern eine altbewährte Werkzeugsammlung zur Verfügung, die auch im modernen C++ noch ihre Berechtigung hat. Mit Kontexten, Exekutoren und Completion Tokens erlaubt sie es, asynchrone Programme nach unterschiedlichen Prinzipien sauber und effizient zu entwickeln. Callbacks Futures, spawn und yield_context gestatten auch einen an Koroutinen angelehnten Stil. Das hat der vorangegangene Artikel bereits gezeigt, der die Grundlagen von Boost.Asio vorgestellt hat.
Weiterlesen nach der Anzeige

Martin Meeser ist selbständiger Diplominformatiker (Uni) und bietet Dienstleistungen zum Thema Softwareentwicklung an: Individual-Software-Entwicklung, Beratung zu Prozessen und Schulungen. In zahlreichen Projekten betreute er bisher Kunden unter anderem aus den Bereichen Automotive, Finance, Raumfahrt, Radioastronomie und Medizintechnik.
Ab C++20 stehen nun aber Compiler-basierte stackless Koroutinen zur Verfügung. Damit kombiniert, spielt Boost.Asio seine vollen Stärken aus. Entwickler können jede Funktion oder Methode zu einer Koroutine ändern, indem sie eines der Schlüsselwörter co_yield, co_await odere co_return einsetzen. Der Compiler transformiert diese Funktion in eine Zustandsmaschine (Coroutine Frame), deren Ausführung an den durch co_await oder co_yield markierten Suspensionspunkten (Suspension Points) unterbrochen und später über einen std::coroutine_handle fortgesetzt wird. Der Coroutine Frame, der den Zustand speichert, liegt standardmäßig auf dem Heap, nicht auf dem Stack.
In Boost.Asio muss jede Koroutine eine Instanz vom Typ boost::asio::awaitable zurückgeben. awaitable kapselt den Rückgabe-Typen: awaitable bei einer Funktion ohne Rückgabewert, awaitable bei int, awaitable<:string/> bei String-Typen usw. (Listing 1).
boost::asio::awaitable async_void_sample()
{
co_return;
}
boost::asio::awaitable async_int_sample()
{
co_return 42;
}
boost::asio::awaitable<:string> async_string_sample()
{
co_return "Hello async";
Listing 1: Kapselung der Rückgabe-Typen mit awaitable
Für den Wechsel aus einem normalen Programmteil in einen Koroutinenteil dient die Funktion boost::asio::co_spawn. Sie erwartet drei Parameter:
- den Executor (oder
execution_contextausconvenience) auf dem die verzahnte oder parallele Ausführung der Koroutinen erfolgt - eine Instanz von
awaitable - ein CompletionToken vom Typ
detached, Funktions-Objekt oderuse_future
Weiterlesen nach der Anzeige
Mit detached läuft awaitable in einem eigenen Ablauf-Ast, ohne dass man das Ergebnis verarbeiten kann. Dieses wird oft in der main-Methode verwendet, um initial eine Koroutine aufzurufen (Listing 2).
int main()
{
boost::asio::io_context io_context;
// co_spawn mit CompletionToken Function-Object
boost::asio::co_spawn(io_context, async_int_sample(), [](std::exception_ptr,
int result)
{
std::cout << "async_int_sample() = " << result << std::endl;
});
// co_spawn mit CompletionToken use_future
std::future future = boost::asio::co_spawn(io_context,
async_string_sample(), boost::asio::use_future);
// co_spawn mit CompletionToken detached
boost::asio::co_spawn(io_context, async_caller(), boost::asio::detached);
io_context.run();
std::string s = future.get();
std::cout << "async_string_sample() = " << s << std::endl;
Listing 2: Beispiele von co_spawn mit awaitable zum Aufruf einer Koroutine.
Verwenden von co_await in Boost.Asio
Listing 3 zeigt die Verwendung von co_await. Bei dem Aufruf von awaitable mit co_await passiert Folgendes:
- Der Executor kann an dieser Stelle die Ausführung der Koroutine unterbrechen. Ob die Unterbrechung tatsächlich stattfindet, ist unbekannt und unerheblich.
- Unter Umständen führt der Executor andere Aktionen aus – verzahnt ( concurrent) auf einem Kontext mit einem Thread oder parallel auf einem Kontext mit mehreren Threads.
- An einem dem Entwickler unbekannten Zeitpunkt bringt der Exekutor
awaitableauf einem der Threads desexecution_contextzur Ausführung – auf welchem genau, ist unbekannt und unerheblich.
Sobald das Ergebnis von awaitable vorliegt, setzt der Exekutor die Koroutine an der Stelle fort, an der er sie verlassen hat.
boost::asio::awaitable async_callee(int i)
{
std::cout << "hello from awaitable, i=" << i << std::endl;
co_return i + 1;
}
boost::asio::awaitable async_caller()
{
int i = co_await async_callee(1);
// erzeugt ein awaitable, es wird aber nicht ausgeführt
boost::asio::awaitable aw = async_callee(2);
// awaitable kann nicht kopiert werden, move erforderlich
// co_await std::move(aw);
std::cout << "i=" << i << std::endl;
co_return;
}
// Ausgabe:
hello from awaitable, i=1
i=2
Listing 3: Beispiel für die Verwendung von co_await mit boost::asio::awaitable.
Der Ablauf aus Sicht der einzelnen Funktion ist also synchron – in dem Sinn, dass die Funktion stoppt, bis das Ergebnis von awaitable vorliegt. Es kommt aber nicht zu einer Blockierung des Threads, auf dem Entwickler ihre Funktion aufgerufen haben. Der Thread stand für andere Aktionen kooperativ zur Verfügung.
Entwickler können awaitable nur einmal verwenden. Wenn sie ein awaitable erzeugen, aber nicht mit co_await aufrufen, dann führt das Programm die Funktion nicht aus.
Listing 3 zeigt auch, wie der mit co_return zurückgegebene Wert durch den co_await-Ausdruck aus dem awaitable-Objekt extrahiert und einer Variablen in der aufrufenden Funktion zugewiesen wird. Dies ist ein sehr komfortabler Mechanismus, schließlich ist gar nicht bekannt, auf welches Thread async_callee zur Ausführung kommt.
Thread-übergreifendes Exception-Handling bei Koroutinen
Unbehandelte Execeptions, die innerhalb einer Koroutine auftreten, nimmt die aufrufende Koroutine co_await entgegen. Koroutinen fangen Exceptions also analog zur synchronen Funktionsweise ab, unabhängig davon, in welchem konkreten Thread die Exception auftrat. Listing 4 zeigt ein kurzes Beispiel.
#include
#include
boost::asio::awaitable async_ex_sample()
{
throw std::runtime_error{ "some exception" };
co_return;
}
int main()
{
boost::asio::thread_pool pool(4);
boost::asio::co_spawn(pool, []()->boost::asio::awaitable
{
try
{
co_await async_ex_sample();
}
catch (const std::exception& ex)
{
std::cout << ex.what() << std::endl;
}
co_return;
}, boost::asio::detached);
pool.join();
}
Listing 4: Thread-übergreifendes Exception-Handling.
Verwendung der Boost-Asio Funktionen mit CompletionToken use_awaitable
Listing 5 zeigt, wie Entwickler die asynchronen Funktionen der Boost.Asio-Bibliothek mit co_await verwenden: Dazu übergeben sie das CompletionToken use_awaitable als Parameter an die Methode async_wait des timer – Objektes, dementsprechend ist der Rückgabetyp der Funktion nun awaitable.
boost::asio::awaitable async_sample(
boost::asio::steady_timer timer,
boost::asio::ip::tcp::socket socket)
{
co_await timer.async_wait(boost::asio::use_awaitable);
char buf[4096];
std::size_t n = co_await socket.async_read_some(boost::asio::buffer(buf),
boost::asio::use_awaitable);
}
Listing 5: Beispiel für die Verwendung der Boost.Asio-Funktionen mit co_await und dem CompletionToken use_awaitable.
Asynchrone Schleifen
Ein weiterer Vorteil der Koroutinen ist, dass sich damit Schleifen im gewohnten for– oder while-Stil auch für asynchrone Abläufe formulieren lassen. Ohne Koroutinen müsste man die Iterationen in Callbacks auflösen oder komplexere Zustandsautomaten schreiben.
Der Code in Listing 6 liest einen Socket in einer Endlosschleife und schreibt die empfangenen Daten direkt wieder zurück – ein einfacher Echo-Server. Obwohl der Code wie eine normale Schleife aussieht, blockiert er keinen Thread. Jeder co_await-Ausdruck gibt die Kontrolle an den Exekutor zurück. Sobald Daten verfügbar sind oder ein Schreibvorgang abgeschlossen ist, läuft die Schleife an der unterbrochenen Stelle weiter – auf welchem Thread die Lese- und Schreibvorgänge ablaufen, ist unbekannt – dies wird durch den Exekutor bestimmt, den der Entwickler im co_spawn angegeben hat.
boost::asio::awaitable async_sample(boost::asio::ip::tcp::socket socket)
{
char buf[4096];
for (;;)
{
std::size_t n = co_await socket.async_read_some(
boost::asio::buffer(buf),
boost::asio::use_awaitable);
co_await boost::asio::async_write(
socket,
boost::asio::buffer(buf, n),
boost::asio::use_awaitable);
}
}
Listing 6: Beispiel für asynchrone for-Iteration.
Fehlerbehandlungen
Wie in vorangegangenen Beispielen bereits gezeigt, gibt es bei Koroutinen mehrere Möglichkeiten der Fehlerbehandlung:
- keine Fehlerbehandlung
- mit Try-Catch
- lokale Fehlervariable mit
boost::asio::redirect_errorals CompletionToken - als Teil eines Tupel-Rückgabewertes mit dem CompletionToken
boost::asio::as_tuple.
Das folgende Listing 7 zeigt entsprechende Beispiele:
boost::asio::awaitable errors_sample(boost::asio::ip::tcp::socket socket)
{
boost::asio::any_io_executor executor = co_await
boost::asio::this_coro::executor;
std::array buffer;
// 1. ohne Fehler-Behandlung
std::size_t n = co_await socket.async_read_some(boost::asio::buffer(buffer),
boost::asio::use_awaitable);
// 2. mit try-catch
try
{
std::size_t bytes_read = co_await
socket.async_read_some(boost::asio::buffer(buffer),
boost::asio::use_awaitable);
}
catch (const boost::system::system_error& e)
{
std::cerr << "Boost error during async_read_some: " << e.what() <<
std::endl;
}
// 3. mit redirect_error CompletionToken
boost::system::error_code ec;
std::size_t bytes_read2 = co_await
socket.async_read_some(boost::asio::buffer(buffer),
boost::asio::redirect_error(boost::asio::use_awaitable, ec));
if (ec)
{
std::cerr << "Error using error_code: " << ec.message() << std::endl;
}
// 4. mit as_tuple CompletionToken
auto [ec2, bytes_read3] = co_await
socket.async_read_some(boost::asio::buffer(buffer),
boost::asio::as_tuple(boost::asio::use_awaitable));
if (ec2)
{
std::cerr << "Error using as_tuple: " << ec2.message() << std::endl;
}
}
Listing 7: Beispiele für verschiedene Arten der Fehlerbehandlung in Boost.Asio.
Entwicklung & Code
Aus Softwarefehlern lernen – Teil 9: Bugs entscheiden über Leben und Tod
In den bisherigen Kategorien ging es um viele Fehler, die wirtschaftliche Verluste oder Projektkatastrophen ausgelöst haben. Doch es gibt eine Klasse von Softwarefehlern, die weit schwerwiegendere Konsequenzen haben: sicherheitskritische Fehler, die Menschenleben kosten können.
Weiterlesen nach der Anzeige

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.
Die Teile der Serie „Aus Softwarefehlern lernen“:
Muster 9: Sicherheitskritische Systeme – Wenn Software über Leben und Tod entscheidet
Sicherheitskritische Systeme finden sich überall dort, wo Software unmittelbar mit physischen Prozessen interagiert – in der Luftfahrt, der Medizintechnik, der Automobilbranche, der Bahntechnik und in der Industrieautomation. Wenn hier ein Bug auftritt, sind die Folgen oft irreversibel.
Der Therac-25 war ein medizinischer Linearbeschleuniger, entwickelt zur Strahlentherapie von Krebspatientinnen und -patienten. Die Maschine war eine Weiterentwicklung der Vorgängermodelle Therac-6 und Therac-20, die noch stark auf Hardwaresicherungen und physische Interlocks setzten. Beim Therac-25 verlagerte der Hersteller viele dieser Sicherheitsmechanismen in die Software – eine Entscheidung, die sich als fatal herausstellen sollte.
Zwischen 1985 und 1987 kam es nämlich zu mindestens sechs dokumentierten Strahlenunfällen. Mehrere Patientinnen und Patienten erhielten extrem hohe Dosen, einige starben an den Folgen. Die Ursachenanalyse deckte damals eine Kombination aus Design- und Prozessfehlern auf:
- Race Conditions in der Steuerungssoftware: Wenn Anwenderinnen oder Anwender sehr schnell Eingaben tätigten, konnte die Maschine einen internen Statuswechsel doppelt interpretieren. Sie glaubte, der Schutzmechanismus sei aktiv, obwohl er es nicht war.
- Verlass auf Software-Interlocks: Früher sorgten physische Sicherungen dafür, dass Hochleistungsstrahlung nur bei korrekter Konfiguration ausgelöst wurde. Beim Therac-25 vertraute man darauf, dass die Software dies fehlerfrei prüft.
- Fehlende formale Spezifikationen und unabhängige Reviews: Weder gab es einen formalen Sicherheitsnachweis, noch waren die Testprozesse robust genug, um die Race Conditions unter realistischen Bedingungen zu entdecken. Die Software wurde von einem einzelnen Entwickler geschrieben, der auch gleichzeitig der einzige Tester war.
Das Therac-25-Desaster zeigt ein Muster, das sich in vielen sicherheitskritischen Vorfällen wiederholt:
Weiterlesen nach der Anzeige
- Technische Schulden durch Verlagerung auf Software: Hersteller ersetzen Hardware-Interlocks aus Kosten- oder Komfortgründen durch Softwarelogik – oft ohne gleichwertige Absicherung.
- Mangelndes Verständnis von Nebenläufigkeit und Timing: Sicherheitskritische Systeme müssen deterministisch arbeiten. Unerwartete Race Conditions sind hier besonders gefährlich.
- Fehlende unabhängige Prüfung: Wenn die Entwicklerinnen und Entwickler auch die Tester sind, bleiben viele Annahmen unentdeckt. Safety Engineering verlangt Redundanz in der Prüfung, nicht nur in der Hardware.
Gegenmaßnahmen sind auch in diesem Fall wieder verhältnismäßig einfach zu ergreifen:
- Defensive Architektur: Safety-kritische Zustände dürfen nicht von einem einzigen Signal oder Prozess abhängen. Hardware- und Software-Interlocks sollten sich ergänzen.
- Formale Spezifikationen und Verifikation: Wichtige Abläufe sollten mathematisch modelliert und verifiziert werden.
- Kompromisslose Post Mortems: Jede Abweichung ist zu dokumentieren und zu analysieren, mit konkreten Änderungen im Design.
- Lückenlose Telemetrie und Logging: Safety-Systeme müssen jederzeit nachvollziehbar machen, warum sie eine Aktion ausgeführt oder blockiert haben.
- Unabhängige Audits und Zertifizierungen: Branchen wie Medizintechnik (IEC 62304), Luftfahrt (DO-178C) und Automobil (ISO 26262) schreiben genau das vor – und das aus gutem Grund.
Safety-kritische Fehler entstehen selten durch schlampige Entwicklerinnen oder Entwickler. Häufiger liegt die Ursache in organisatorischem Druck: Termine, Kosten oder das Vertrauen darauf, dass „die Software es schon regelt“. Das Problem ist, dass Softwarefehler leise eskalieren, bis sie plötzlich sichtbar und irreversibel werden.
Der Therac-25 ist heute ein Lehrbuchbeispiel dafür, dass man bei sicherheitskritischen Systemen niemals auf formale Sicherheitsmechanismen verzichten darf. Jede vermeintliche Abkürzung in Richtung „rein softwarebasiert“ kann hier Menschenleben kosten.
(who)
Entwicklung & Code
PHP 8.5 bringt neue Sprachfeatures und mehr Lesbarkeit für Developer
Nach mehreren Release Candidates hat das PHP-Team die stabile Version 8.5 veröffentlicht. Die neue Hauptversion der Programmiersprache führt mehrere Sprachverbesserungen ein, die Lesbarkeit und Sicherheit erhöhen sollen. Neben Feinschliff an bestehenden Funktionen kommen einige neue Features hinzu.
Weiterlesen nach der Anzeige
URI-Erweiterung für saubere Adressverarbeitung
Mit der neuen URI-Extension hält erstmals eine native Unterstützung für strukturierte Arbeit mit URLs und URIs Einzug in PHP. Sie soll eine einfache und sichere Zerlegung, Manipulation und den Wiederaufbau von Adressen ermöglichen – ohne auf externe Libraries zurückgreifen zu müssen. Ziel ist, typische Fehlerquellen beim Parsen und Zusammenfügen von URLs zu vermeiden und den Code zugleich klarer und konsistenter zu machen.
(Bild: nuevoimg / 123rf.com)

Am 25. November findet die betterCode() PHP statt, eine Online-Konferenz von iX und dpunkt.verlag in Kooperation mit thePHP.cc. Interessierte können sich in Vorträgen unter anderem über PHP 8.5, den Caddy Server und FrankenPHP informieren. Tickets sind über die Konferenz-Website erhältlich.
Pipe Operator: Datenflüsse lesbarer
Eine weitere zentrale Neuerung ist der Pipe Operator (|>), der funktionale Aufrufketten deutlich lesbarer macht. Statt verschachtelter Funktionsaufrufe lassen sich Ergebnisse nun Schritt für Schritt weiterreichen. Das sorgt für eine bessere Lesbarkeit von komplexen Datenflüssen.
Das Beispiel aus dem RFC soll dies verdeutlichen:
function getUsers(): array {
return [
new User('root', isAdmin: true),
new User('john.doe', isAdmin: false),
];
}
function isAdmin(User $user): bool {
return $user->isAdmin;
}
// This is the new syntax.
$numberOfAdmins = getUsers()
|> (fn ($list) => array_filter($list, isAdmin(...)))
|> count(...);
var_dump($numberOfAdmins); // int(1);
Clone With: Klonen mit gezielten Anpassungen
Weiterlesen nach der Anzeige
PHP 8.5 hat das neue Sprachfeature clone with zum Erzeugen leicht veränderter Objektkopien im Gepäck. Während bisher beim Klonen alle Eigenschaften übernommen und Entwicklerinnen und Entwickler Änderungen manuell vornehmen mussten, lassen sich nun gezielt einzelne Werte direkt beim Klonvorgang anpassen.
Folgendes Beispiel soll das Prinzip verdeutlichen:
readonly class Color
{
public function __construct(
public int $red,
public int $green,
public int $blue,
public int $alpha = 255,
) {}
public function withAlpha(int $alpha): self
{
return clone($this, [
'alpha' => $alpha,
]);
}
}
$blue = new Color(79, 91, 147);
$transparentBlue = $blue->withAlpha(128);
So ermöglicht clone with vor allem bei unveränderlichen Datenstrukturen eine elegantere und besser lesbare Syntax.
Feinere Kontrolle mit dem #[NoDiscard]-Attribut
Das neue Attribut #[\NoDiscard] unterstützt Entwicklerinnen und Entwickler beim Aufspüren potenzieller Logikfehler. Wird das Ergebnis einer mit #[\NoDiscard] markierten Funktion verworfen, gibt PHP künftig eine Warnung aus. So lassen sich versehentlich ignorierte Rückgabewerte schneller aufdecken, was die Codequalität erhöhen und unnötige Fehlersuche reduzieren soll.
Konstante Ausdrücke mit mehr Macht
Schließlich stärkt die Version auch PHPs Möglichkeiten zur Metaprogrammierung: Ab sofort lassen sich Closures, Casts und First-Class Callables in konstanten Ausdrücken verwenden. Diese Erweiterung öffnet die Tür für noch flexiblere Definitionen im Compile-Time-Kontext – beispielsweise beim Aufbau von Konfigurationsobjekten oder dynamischen Standardwerten.
Weitere Informationen zum Release finden sich bei php.net sowie im Changelog.
(mdo)
Entwicklung & Code
Angular 21 vollzieht den Abschied von zone.js
Angular 21 ist erschienen. Die neue Hauptversion des Webframeworks bietet experimentelle Signal Forms, eine Developer-Preview für Angular Aria für barrierefreie Headless-Komponenten und weitere Neuerungen. Die Bibliothek zone.js ist zugunsten der Zoneless Change Detection in neuen Angular-Anwendungen nicht mehr enthalten.
Weiterlesen nach der Anzeige
(Bild: jaboy/123rf.com)

Die enterJS 2026 wird am 16. und 17. Juni in Mannheim stattfinden. Das Programm wird sich rund um JavaScript und TypeScript, Frameworks, Tools und Bibliotheken, Security, UX und mehr drehen. Vergünstigte Blind-Bird-Tickets sind bis zum Programmstart erhältlich.
Barrierefreie Komponenten dank Angular Aria
Als Developer-Preview ist Angular Aria enthalten. Diese Bibliothek für gängige UI-Pattern legt den Fokus auf Accessibility – für barrierefreie Headless-Komponenten gemäß der Webstandard-Suite WAI-ARIA (Web Accessibility Initiative – Accessible Rich Internet Applications) des World Wide Web Consortium (W3C).
Derzeit umfasst Angular Aria ein Set aus acht UI-Pattern – Accordion, Combobox, Grid, Listbox, Menu, Tabs, Toolbar und Tree – mit 13 Komponenten. Diese sind nicht gestylt und lassen sich nach Belieben mit eigenen Styles versehen.
Angular Aria lässt sich mit npm i @angular/aria installieren. Für dieses neue Feature steht ein Guide bereit, der Codebeispiele für alle Komponenten, Skins zum Ausprobieren und weitere Informationen enthält.
Experimentelle Signal Forms
Weiterlesen nach der Anzeige
Angular-Entwickler können nun Signal Forms ausprobieren, eine experimentelle Bibliothek für das Verwalten von Form-State auf Basis der reaktiven Signals. Sie soll sowohl eine gute Developer Experience als auch vollständige Typsicherheit für den Zugriff auf Formularfelder ermöglichen und zentraler, Schema-basierter Validationslogik folgen.
Für den Einsatz der experimentellen Signal Forms halten der entsprechende Guide und die Dokumentation alle Details bereit.
Abschied von zone.js und Begrüßung von Vitest
Vitest ist in Angular 21 zum Standard-Test-Runner gewählt worden. Das gilt für neue Projekte, während die bisher genutzten Technologien Karma und Jasmine weiterhin vollständigen Support durch das Angular-Team genießen, sodass Entwicklerinnen und Entwickler noch nicht zu Vitest migrieren müssen. Wer es allerdings testen möchte, kann die experimentelle Migration durchführen.
Und schließlich verabschiedet sich Angular 21 endgültig von zone.js – ein Prozess, der sich schon seit Angular 18 abzeichnete. Die eigenständige Bibliothek zone.js, die von Beginn an in Angular zum Einsatz kam, ist nun nicht mehr standardmäßig enthalten. Neue Angular-Anwendungen nutzen demnach das Zoneless-Verfahren, während für bestehende Anwendungen eine Migrationsanleitung zur Verfügung steht.
Die Zoneless Change Detection bietet laut dem Angular-Team zahlreiche Vorteile: verbesserte Core Web Vitals, natives async/await, Ökosystem-Kompatibilität, verringerte Bundle-Größe, vereinfachtes Debugging und eine verbesserte Kontrolle. Dennoch habe zone.js eine wichtige Rolle bei der Gestaltung von Angular gespielt, hebt das Entwicklungsteam hervor.
Weitere Informationen zu den Updates in Angular 21 hält der Angular-Blog bereit.
(mai)
-
UX/UI & Webdesignvor 3 MonatenAdobe Firefly Boards › PAGE online
-
UX/UI & Webdesignvor 1 MonatIllustrierte Reise nach New York City › PAGE online
-
Apps & Mobile Entwicklungvor 3 MonatenGalaxy Tab S10 Lite: Günstiger Einstieg in Samsungs Premium-Tablets
-
Datenschutz & Sicherheitvor 3 MonatenHarte Zeiten für den demokratischen Rechtsstaat
-
Datenschutz & Sicherheitvor 2 MonatenJetzt patchen! Erneut Attacken auf SonicWall-Firewalls beobachtet
-
Online Marketing & SEOvor 3 Monaten„Buongiorno Brad“: Warum Brad Pitt für seinen Werbejob bei De’Longhi Italienisch büffeln muss
-
Online Marketing & SEOvor 3 MonatenCreator und Communities: Das plant der neue Threads-Chef
-
Entwicklung & Codevor 3 MonatenEventSourcingDB 1.1 bietet flexiblere Konsistenzsteuerung und signierte Events
