66 Punkte von GN⁺ 2025-08-18 | 1 Kommentare | Auf WhatsApp teilen
  • Eine Zusammenstellung nicht intuitiver Fallstricke, in die Entwickler häufig tappen, mit einer Einführung in die Ursachen leicht auftretender Bugs
  • Behandelt häufige Probleme in verschiedenen Technologien wie HTML, CSS, Unicode/Textkodierung, Fließkommazahlen und Zeit
  • Betont, dass durch subtile Unterschiede in Syntax und Verhalten zwischen einzelnen Sprachen und Frameworks Missverständnisse oder Fehler entstehen können
  • Erläutert anhand von Beispielen Fallstricke in zentralen Backend-Bereichen wie Nebenläufigkeit, Netzwerken und Datenbanken, die in realen Betriebsumgebungen auftreten können
  • Zeigt mithilfe verschiedener Beispiele und Referenzlinks Problemsituationen, Lösungsansätze und Verbesserungen bei unerwartetem Verhalten auf

HTML und CSS

  • Standardwert von min-width in Flexbox/Grid

    • min-width ist standardmäßig auto
    • min-width: auto wird durch die Größe des Inhalts bestimmt und hat Vorrang vor flex-shrink, overflow: hidden, width: 0 und max-width: 100%
    • Empfehlung: min-width: 0 explizit angeben
  • Unterschied zwischen horizontal und vertikal in CSS

    • width: auto versucht, den Platz des Elternelements auszufüllen, height: auto richtet sich nach dem Inhalt
    • width: auto bei inline-, inline-block- und float-Elementen erweitert sich nicht
    • margin: 0 auto zentriert horizontal, margin: auto 0 zentriert nicht vertikal (außer bei flex-direction: column, wo vertikale Zentrierung möglich ist)
    • Margin-Collapsing tritt nur vertikal auf
    • Wenn sich mit writing-mode: vertical-rl usw. die Layoutrichtung ändert, kehrt sich auch das Verhalten um
  • Block Formatting Context (BFC)

    • Ein BFC kann mit display: flow-root erzeugt werden (auch overflow: hidden/auto/scroll, display: table usw. sind möglich, haben aber Nebenwirkungen)
    • Sich überlappende vertikale Margins benachbarter Geschwister oder nach außen austretende Kind-Margins lassen sich mit einem BFC verhindern
    • Wenn ein Elternelement nur floatende Kinder enthält, kollabiert seine Höhe auf 0 → mit BFC behebbar
    • Bei vorhandenem border oder padding tritt kein Margin-Collapsing auf
  • Stacking Context

    • Bedingungen, die einen neuen Stacking Context erzeugen
      • Rendering-Eigenschaften wie transform, filter, perspective, mask, opacity
      • position: fixed oder sticky
      • gesetztes z-index + Positionierung mit absolute/relative
      • gesetztes z-index + Elemente innerhalb von Flexbox/Grid
      • isolation: isolate
    • Eigenschaften
      • z-index gilt nur innerhalb des jeweiligen Stacking Context
      • Die Koordinaten von position: absolute/fixed beziehen sich auf den nächstgelegenen positionierten Vorfahren
      • sticky funktioniert nicht über einen Stacking Context hinweg
      • Selbst overflow: visible wird durch den Stacking Context abgeschnitten
      • background-attachment: fixed wird relativ zum Stacking Context positioniert
  • Viewport-Einheiten

    • In mobilen Browsern ändert sich 100vh, wenn Adressleiste/Navigationsleiste beim Scrollen vom Bildschirm verschwinden
    • Aktuelle Lösung: 100dvh verwenden
  • Bezugsrahmen für Absolute Positioning

    • position: absolute bezieht sich nicht auf das Elternelement, sondern auf den nächstgelegenen relative/absolute- oder Stacking-Context-Vorfahren
  • Blur-Verhalten

    • backdrop-filter: blur berücksichtigt keine umliegenden Elemente
  • Aufhebung von Float

    • Wenn das Elternelement flex oder grid ist, hat float bei Kindelementen keine Wirkung
  • Prozentangaben bei width/height

    • Funktionieren nicht, wenn die Größe des Elternelements nicht vorab festgelegt ist (um zirkuläre Referenzen zu vermeiden)
  • Eigenschaften von Inline-Elementen

    • display: inline ignoriert width, height, margin-top und margin-bottom
  • Whitespace-Verarbeitung

    • Standardmäßig werden Zeilenumbrüche in HTML als Leerzeichen behandelt, aufeinanderfolgende Leerzeichen werden auf eines reduziert
    • `` verhindert die Reduzierung von Leerzeichen, hat aber besonderes Verhalten am Anfang und Ende
    • Leerzeichen am Anfang/Ende von Inhalten werden meist ignoriert, `` ist jedoch eine Ausnahme
    • Leerzeichen/Zeilenumbrüche zwischen inline-block-Elementen werden als tatsächlicher Abstand dargestellt (bei flex/grid tritt das nicht auf)
  • text-align

    • Wirkt auf Text und Inline-Elemente, aber nicht auf die Ausrichtung von Block-Elementen
  • box-sizing

    • Standardwert ist content-boxpadding/border sind nicht enthalten
    • Bei width: 100% + gesetztem padding kann das Elternelement überschritten werden
    • Lösung: box-sizing: border-box
  • Cumulative Layout Shift

    • Werden bei `` keine Attribute width und height gesetzt, kann verzögertes Laden von Bildern zu Layout-Verschiebungen führen
    • Empfehlung: Attribute setzen, um CLS zu vermeiden
  • Netzwerkanfragen bei Dateidownloads in Chrome

    • Werden im Netzwerk-Panel der DevTools nicht angezeigt (sie werden in einem anderen Tab verarbeitet)
    • Falls Analyse nötig ist: chrome://net-export/ verwenden
  • JavaScript-Parsing-Probleme in HTML

Unicode und Textkodierung

  • Code Points und Grapheme Cluster

    • Grapheme Cluster sind die „Zeicheneinheit“ in einer GUI
    • Bei sichtbaren ASCII-Zeichen gilt: 1 Code Point = 1 Grapheme Cluster
    • Ein Emoji kann aus mehreren Code Points bestehen und dennoch ein einzelnes Grapheme Cluster bilden
    • In UTF-8 belegt ein Code Point 1 bis 4 Byte; Anzahl der Bytes und Anzahl der Code Points stimmen nicht überein
    • In UTF-16 belegt ein Code Point 2 oder 4 Byte (Surrogatpaar)
    • Der Standard setzt keine Begrenzung für die Anzahl der Code Points in einem Cluster, Implementierungen begrenzen dies jedoch oft aus Performancegründen
  • Unterschiede im String-Verhalten je nach Sprache

    • Rust: Verwendet intern UTF-8 für Strings, len() gibt die Byte-Anzahl zurück, direktes Indexing ist nicht möglich, chars().count() liefert die Anzahl der Code Points, UTF-8-Gültigkeit wird strikt geprüft
    • Golang: Strings sind faktisch Byte-Arrays, Länge und Indexing erfolgen byteweise, meist wird UTF-8 verwendet
    • Java, C#, JS: Basieren auf UTF-16, messen Länge in 2-Byte-Einheiten, auch Indexing erfolgt in 2-Byte-Einheiten, Surrogatpaare kommen vor
    • Python: len() gibt die Anzahl der Code Points zurück, Indexing liefert einen String, der genau einen Code Point enthält
    • C++: std::string hat keine Vorgaben zur Kodierung, verhält sich wie ein Byte-Vektor, Länge/Indexing erfolgen byteweise
    • Unter den genannten Sprachen gibt es keine, die Länge/Indexing auf Ebene von Grapheme Clustern bereitstellt
  • BOM (Byte Order Mark)

    • Manche Textdateien enthalten ein BOM, z. B. EF BB BF als Kennzeichnung für UTF-8-Kodierung
    • Wird vor allem unter Windows verwendet; Software außerhalb von Windows kann ein BOM unter Umständen nicht korrekt verarbeiten
  • Sonstige Hinweise

    • Beim Umwandeln von Binärdaten in Zeichenketten werden fehlerhafte Teile durch � (U+FFFD) ersetzt
    • Es gibt confusable characters (Zeichen, die sich gegenseitig sehr ähnlich sehen)
    • Normalisierung (Normalization): z. B. kann é als U+00E9 (ein einzelner Code Point) oder als U+0065+U+0301 (zwei Code Points) dargestellt werden
    • Es gibt Zero-width characters und Invisible characters
    • Unterschiede bei Zeilenumbrüchen: Windows verwendet CRLF \r\n, Linux/MacOS LF \n
    • Han unification: Je nach Sprache verwenden Zeichen mit leicht unterschiedlicher Form denselben Code Point
      • Schriftarten rendern dies passend, indem sie sprachspezifische Varianten enthalten
      • Bei der Internationalisierung muss die richtige Font-Variante gewählt werden

Gleitkommazahlen (Floating point)

  • NaN-Eigenschaften

    • NaN ist mit keinem Wert gleich, auch nicht mit sich selbst (NaN == NaN ist immer false)
    • NaN != NaN ist immer true
    • Ergebnisse von Operationen mit NaN propagieren in den meisten Fällen ebenfalls zu NaN
  • Spezielle Werte

    • Es gibt +Inf und -Inf, sie unterscheiden sich von NaN
    • -0.0 ist ein von +0.0 unterscheidbarer Wert
      • Bei Vergleichsoperationen sind sie gleich, in einigen Berechnungen verhalten sie sich jedoch unterschiedlich
      • Beispiel: 1.0 / +0.0 == +Inf, 1.0 / -0.0 == -Inf
  • Kompatibilität mit JSON

    • Der JSON-Standard erlaubt NaN und Inf nicht
      • JS JSON.stringify wandelt NaN und Inf in null um
      • Python json.dumps(...) gibt NaN und Infinity unverändert aus (Verstoß gegen den Standard)
        • Mit der Option allow_nan=False tritt bei NaN/Inf ein ValueError auf
      • Golang json.Marshal gibt bei vorhandenem NaN/Inf einen Fehler zurück
  • Präzisionsprobleme

    • Direkte Vergleiche von Gleitkommazahlen können fehlschlagen → empfohlen ist die Form abs(a - b) < ε
    • JS behandelt alle Zahlen als Gleitkommazahlen
      • Der sichere Integer-Bereich ist -(2^53 - 1) bis 2^53 - 1
      • Außerhalb dieses Bereichs ist die Integer-Darstellung ungenau
      • Für große Integer wird BigInt empfohlen
      • Wenn JSON Integer außerhalb des sicheren Bereichs enthält, kann das Ergebnis von JSON.parse ungenau sein
      • Zeitstempel in Millisekunden sind bis zum Jahr 287.396 sicher, bei Nanosekunden treten Probleme auf
  • Nichtanwendbarkeit von Rechengesetzen

    • Je nach Reihenfolge der Operationen gelten Assoziativgesetz und Distributivgesetz aufgrund von Präzisionsverlusten nicht streng
    • Parallele Berechnungen (Matrixmultiplikation, Summen usw.) können nichtdeterministische Ergebnisse erzeugen
  • Leistung

    • Division ist deutlich langsamer als Multiplikation
    • Wenn mehrfach durch denselben Wert geteilt wird, kann man optimieren, indem man zuerst den Kehrwert berechnet und dann multipliziert
  • Unterschiede je nach Hardware

    • Unterstützung für FMA (Fused Multiply-Add): Manche Hardware führt Zwischenberechnungen mit höherer Präzision aus
    • Behandlung des Subnormal range: Moderne Hardware unterstützt ihn, manche ältere behandelt ihn als 0
    • Unterschiede bei Rundungsmodi
      • Es gibt u. a. RNTE (auf die nächste gerade Zahl runden) und RTZ (auf 0 abschneiden)
      • x86/ARM kann dies als thread-lokalen veränderbaren Zustand setzen
      • Bei GPUs unterscheidet sich der Rundungsmodus je nach Instruktion
    • Unterschiede im Verhalten mathematischer Funktionen wie trigonometrische Funktionen oder Logarithmen
    • x86 hat eine Legacy-80-Bit-FPU und einen per-core rounding mode → Verwendung nicht empfohlen
    • Auch darüber hinaus können verschiedene Faktoren dazu führen, dass Gleitkommaergebnisse je nach Hardware unterschiedlich ausfallen
  • Methoden zur Verbesserung der Präzision

    • Den Berechnungsgraphen flach halten (weniger aufeinanderfolgende Multiplikationsstrukturen)
    • Vermeiden, dass Zwischenwerte extrem groß oder extrem klein werden
    • Hardware-Operationen wie FMA nutzen

Zeit (Time)

  • Schaltsekunde (Leap second)

    • Unix-Zeitstempel ignorieren Schaltsekunden
    • Beim Auftreten einer Schaltsekunde wird die Zeit in der Umgebung gestreckt oder verkürzt (Leap smear)
  • Zeitzone (Time zone)

    • UTC und Unix-Zeitstempel sind weltweit einheitlich
    • Menschenlesbare Zeit hängt von der jeweiligen lokalen Zeitzone ab
    • Es wird empfohlen, Zeitstempel in der DB zu speichern und erst in der UI umzuwandeln
  • Sommerzeit (DST)

    • In manchen Regionen wird die Uhr im Sommer um 1 Stunde angepasst
  • NTP-Synchronisierung

    • Während der Synchronisierung kann es vorkommen, dass die Zeit „rückwärts läuft“
  • Einstellung der Server-Zeitzone

    • Für Server wird UTC empfohlen
    • In verteilten Systemen entstehen Probleme, wenn Knoten unterschiedliche Zeitzonen verwenden
    • Nach dem Ändern der Systemzeitzone ist eine Neukonfiguration oder ein Neustart der DB erforderlich
  • Hardware-Uhr vs. Systemuhr

    • Die Hardware-Uhr kennt kein Zeitzonenkonzept
    • Linux: behandelt die Hardware-Uhr als UTC
    • Windows: behandelt die Hardware-Uhr als lokale Zeit

Java

  • == vergleicht Objektreferenzen, für den Vergleich des Objektinhalts muss .equals verwendet werden
  • Wenn equals und hashcode nicht überschrieben werden, beurteilen map/set die Gleichheit von Objekten referenzbasiert
  • Wenn der Inhalt eines key-Objekts in einer map oder eines Elementobjekts in einem set geändert wird, funktioniert der Container nicht mehr korrekt
  • Methoden, die List zurückgeben, liefern je nach Fall ein veränderbares ArrayList oder ein unveränderliches Collections.emptyList(); beim Ändern des Letzteren tritt UnsupportedOperationException auf
  • Es gibt Fälle, in denen eine Methode, die Optional zurückgeben soll, null zurückgibt (nicht empfohlen)
  • Wenn im finally-Block ein return ausgeführt wird, werden Ausnahmen aus try oder catch ignoriert und der Rückgabewert aus finally verwendet
  • Es gibt Bibliotheken, die interrupts ignorieren; außerdem kann die Initialisierung von Klassen einschließlich IO durch interrupts gestört werden
  • Bei Tasks, die in einem thread pool mit .submit() übergeben werden, werden Ausnahmen standardmäßig nicht ins Log geschrieben und können nur über das future geprüft werden; wird das future ignoriert, bleiben die Ausnahmen unbemerkt
    • scheduleAtFixedRate-Jobs werden bei einer Ausnahme stillschweigend beendet
  • Wenn ein Zahlenliteral mit 0 beginnt, wird es als Oktalzahl behandelt (0123 → 83)
  • Der Debugger ruft .toString() lokaler Variablen auf; da toString() bei einigen Klassen Nebenwirkungen hat, kann sich das Verhalten des Codes beim Debugging ändern (in der IDE deaktivierbar)

Golang

  • append() verwendet bei freier capacity denselben Speicher weiter; ein append auf eine subslice kann daher auch den Speicher des übergeordneten Bereichs überschreiben
  • defer wird beim Return der Funktion ausgeführt, nicht beim Ende eines Block-Scopes
  • defer erfasst veränderbare Variablen
  • Zu nil
    • Eine nil slice und eine leere slice sind verschieden
    • string kann nicht nil sein, es gibt nur den leeren String
    • Eine nil map kann gelesen, aber nicht beschrieben werden
    • Eigenartiges Verhalten bei interface nil: Wenn der data pointer null ist, aber die type info nicht null ist, ist es nicht gleich nil
  • Dead wait: Es gibt in Go reale Beispiele für derartige Nebenläufigkeits-Bugs
  • Es gibt verschiedene Timeout-Arten, ausführlich behandelt in net/http

C/C++

  • Wenn nach dem Speichern eines Zeigers auf ein std::vector-Element der Vektor wächst, kommt es zu einer Reallokation und der Zeiger wird ungültig
  • Ein std::string, der aus einem String-Literal erzeugt wurde, kann ein temporäres Objekt sein; ein Aufruf von c_str() ist dann riskant
  • Wird ein Container während der Iteration verändert, werden Iteratoren ungültig
  • std::remove löscht nicht wirklich, sondern ordnet Elemente nur um; zum Löschen ist erase nötig
  • Beginnt ein Zahlenliteral mit 0, wird es als Oktalzahl behandelt (0123 → 83)
  • Undefined behavior (UB): Bei der Optimierung kann UB beliebig verändert werden, daher ist jede Abhängigkeit davon riskant
    • Zugriff auf nicht initialisierten Speicher ist UB
    • Wird char* in einen Struct-Zeiger umgewandelt, ist der Zugriff vor Beginn der Objektlebensdauer UB; Initialisierung per memcpy wird empfohlen
    • Ungültiger Speicherzugriff (z. B. Nullzeiger) ist UB
    • Integer-Overflow/-Underflow ist UB (bei unsigned ist Underflow unter 0 möglich)
    • Aliasing: Wenn Zeiger verschiedener Typen auf denselben Speicher verweisen, kann durch die Strict-Aliasing-Regel UB entstehen
      • Ausnahmen: 1) Typen in einer Vererbungsbeziehung 2) Umwandlung in char*, unsigned char*, std::byte* (gilt nicht für die Rückumwandlung)
      • Für erzwungene Umwandlungen werden memcpy oder std::bit_cast empfohlen
    • Zugriff auf unaligned memory ist UB
  • Memory Alignment
    • Bei 64-Bit-Integern muss die Adresse durch 8 teilbar sein
    • Auf ARM kann unaligned Zugriff zu einem Crash führen
    • Wird ein Byte-Puffer direkt als Struct interpretiert, können Alignment-Probleme auftreten
    • Alignment kann durch Struct-Padding zu Speicherverlust führen
    • Einige SIMD-Befehle (z. B. AVX) können nur ausgerichtete Daten verarbeiten; meist ist ein Alignment von 32 Byte nötig

Python

  • Standardargumente von Funktionen werden nicht bei jedem Aufruf neu erzeugt, sondern der initiale Wert wird beibehalten

SQL Databases

  • Umgang mit Null

    • x = null funktioniert nicht; stattdessen muss x is null verwendet werden
    • Null ist nicht einmal sich selbst gleich (ähnlich wie NaN)
    • Ein Unique-Index erlaubt doppelte Null-Werte (mit Ausnahme von Microsoft SQL Server)
    • Wie Null bei select distinct behandelt wird, unterscheidet sich je nach DB
    • count(x) und count(distinct x) ignorieren Zeilen mit Null-Werten
  • Allgemeines Verhalten

    • Implizite Datumsumwandlungen können von der Zeitzone abhängen
    • Komplexe Join- + Distinct-Abfragen können langsamer sein als verschachtelte Queries
    • In MySQL (InnoDB) führt ein String-Feld, das nicht utf8mb4 verwendet, beim Einfügen von 4-Byte-UTF-8-Zeichen zu Fehlern
    • MySQL (InnoDB) ist standardmäßig nicht case-sensitive
    • MySQL (InnoDB) erlaubt implizite Umwandlungen: select '123abc' + 1; → 124
    • Gap Locks in MySQL (InnoDB) können Deadlocks verursachen
    • In MySQL (InnoDB) liefert eine Abweichung zwischen group by und select-Spalten nichtdeterministische Ergebnisse
    • In SQLite ist der Feldtyp ohne strict weitgehend bedeutungslos
    • Foreign Keys können implizite Locks auslösen und dadurch Deadlocks verursachen
    • Locking kann je nach DB die Repeatable-Read-Isolation verletzen
    • Verteilte SQL-Datenbanken unterstützen Locking möglicherweise nicht oder verhalten sich ungewöhnlich (je nach DB unterschiedlich)
  • Performance/Betrieb

    • Das N+1-Query-Problem erscheint nicht im Slow-Query-Log, weil jede einzelne Query schnell ist
    • Lang laufende Transaktionen können Lock-Probleme usw. verursachen → daher sollten Transaktionen schnell beendet werden
    • Fälle mit vollständigem Tabellen-Lock
      • In MySQL 8.0+ können das Hinzufügen eines Unique-Index oder eines Foreign Keys meist parallel verarbeitet werden
      • Bei älteren MySQL-Versionen kann ein vollständiger Tabellen-Lock auftreten
      • Fehlt bei mysqldump die Option --single-transaction, wird ein vollständiger Read Lock auf die Tabelle gesetzt
      • In PostgreSQL verursachen create unique index oder alter table ... add foreign key einen vollständigen Read Lock auf die Tabelle
        • Vermeidung: create unique index concurrently verwenden
        • Bei Foreign Keys die Methode ... not valid und anschließend validate constraint verwenden
  • Range-Abfragen

    • Nicht überlappende Bereiche:
      • Die einfache Bedingung p >= start and p file 2>&1 → stdout+stderr beide in die Datei, cmd 2>&1 > file → nur stdout in die Datei, stderr bleibt unverändert
  • Dateinamen sind groß-/kleinschreibungssensitiv (anders als unter Windows)

  • Für ausführbare Dateien gibt es ein Capability-System (prüfbar mit getcap)

  • Gefahr durch nicht gesetzte Variablen: Wenn DIR nicht gesetzt ist, kann rm -rf $DIR/ zu rm -rf / werden → vermeidbar mit set -u

  • Umgebung anwenden: Um ein Skript in der aktuellen Shell anzuwenden, source script.sh verwenden → für dauerhafte Anwendung in ~/.bashrc eintragen

  • Bash nutzt Command-Caching: Wird eine Datei innerhalb von $PATH verschoben, kann ENOENT auftreten → Cache mit hash -r aktualisieren

  • Werden Variablen ohne Anführungszeichen verwendet, werden Zeilenumbrüche als Leerzeichen behandelt

  • set -e: Beendet das Skript bei Fehlern sofort, funktioniert aber nicht innerhalb von Bedingungen (||, &&, if)

  • Konflikt zwischen K8s livenessProbe und Debugger: Ein Breakpoint-Debugger kann die gesamte App anhalten, wodurch Health Checks fehlschlagen → der Pod kann beendet werden

React

  • State direkt verändern im Rendering-Code
  • Hooks innerhalb von if/loop verwenden → Regelverstoß
  • Im Dependency-Array von useEffect notwendige Werte weglassen
  • In useEffect Clean-up-Code vergessen
  • Closure-Falle: Durch Capturing eines veralteten State entstehen Bugs
  • Daten an der falschen Stelle verändern → unreine Komponente
  • useCallback nicht verwenden → unnötige Re-Renders
  • Bei memoisierten Komponenten nicht memoisierten Werte übergeben → memo-Optimierung wird wirkungslos

Git

  • Rebase schreibt die Historie um

    • Nach einem Rebase führt ein normales Push zu Konflikten → es ist ein Force Push erforderlich
    • Wenn sich die Historie eines Remote-Branches geändert hat, sollte auch beim Pull --rebase verwendet werden
    • --force-with-lease kann in manchen Fällen verhindern, dass Commits anderer Entwickler überschrieben werden; wer aber nur fetch und nicht pull macht, ist dadurch nicht geschützt
  • Probleme mit dem Revert eines Merge

    • Ein Merge-Revert macht die Wirkung nicht vollständig rückgängig → wird derselbe Branch erneut gemergt, gibt es keine Änderung
    • Lösung: den Revert revertieren oder einen sauberen Weg wählen (backup → reset → cherry-pick → force push)
  • Hinweise zu GitHub

    • Selbst wenn ein Secret wie ein API-Key nach dem Commit per Force Push überschrieben wird, bleibt die Spur in GitHub erhalten
    • Wenn ein privates Repo B ein Fork des privaten Repo A ist und A später public wird, wird auch der Inhalt von B öffentlich (und kann selbst nach dem Löschen zugänglich bleiben)
  • git stash pop: Bei Konflikten wird der Stash nicht gedroppt

  • .DS_Store wird von macOS automatisch erzeugt → empfohlen wird **/.DS_Store in .gitignore

Networking

  • Manche Router und Firewalls trennen inaktive TCP-Verbindungen stillschweigend → dadurch können Connection Pools von HTTP-Clients und DB-Clients ungültig werden → Lösung: TCP keepalive konfigurieren
  • traceroute-Ergebnisse sind nur begrenzt zuverlässig → je nach Fall ist tcptraceroute nützlicher
  • TCP slow start kann zusätzliche Latenz verursachen → lässt sich durch Deaktivieren von tcp_slow_start_after_idle beheben
  • TCP-Sticky-Packet-Problem: Der Nagle-Algorithmus verzögert die Paketübertragung → lässt sich durch Aktivieren von TCP_NODELAY beheben
  • Bei Backends hinter Nginx muss Connection-Reuse konfiguriert werden → andernfalls können in Hochlastumgebungen Verbindungen wegen interner Portknappheit fehlschlagen
  • Nginx betreibt standardmäßig Packet Buffering → dadurch kann es bei SSE (EventSource) zu Verzögerungen kommen
  • Der HTTP-Standard verbietet keinen Body bei GET- und DELETE-Requests → manche nutzen einen Body, aber viele Libraries und Server unterstützen das nicht
  • Auf einer einzelnen IP lassen sich mehrere Websites hosten → unterschieden wird über den HTTP-Host-Header und SNI bei TLS → deshalb gibt es Websites, die nicht per bloßem IP-Zugriff erreichbar sind
  • CORS: Bei Requests von einer anderen Origin blockiert der Browser den Zugriff auf die Response → der Server muss den Header Access-Control-Allow-Origin setzen
    • Wenn auch Cookies übertragen werden sollen, sind zusätzliche Einstellungen nötig
    • Wenn Frontend und Backend dieselbe Domain und denselben Port verwenden, gibt es kein CORS-Problem

Other

  • YAML-Hinweise

    • YAML ist leerzeichenempfindlichkey:value ist fehlerhaft, key: value ist korrekt
    • Der Ländercode NO kann ohne Anführungszeichen als false interpretiert werden
    • Ein Git-Commit-Hash kann ohne Anführungszeichen in eine Zahl umgewandelt werden
  • Excel-CSV-Probleme

    • Excel führt beim Öffnen von CSV-Dateien automatische Umwandlungen durch
      • Datumsumwandlung: 1/2, 1-22-Jan
      • Ungenaue Umwandlung großer Zahlen: 1234567890123456789012345678901234500000
    • Die Ursache ist, dass Excel Zahlen intern als Floating Point verarbeitet
    • Durch dieses Problem wurde z. B. der Genname SEPT1 schon falsch verändert

1 Kommentare

 
GN⁺ 2025-08-18
Hacker-News-Kommentar
  • Einige Router und Firewalls trennen gelegentlich stillschweigend inaktive TCP-Verbindungen, ohne der Anwendung irgendein Signal zu geben. Code, der TCP-Connection-Pools aufrechterhält, wie HTTP-Client-Bibliotheken oder Datenbank-Clients, erlebt dadurch das Problem, dass Verbindungen ohne jede Warnung ungültig werden. Um das zu beheben, kann man systemweites TCP Keep-Alive konfigurieren oder bei HTTP Header wie Connection: keep-alive, Keep-Alive: timeout=30, max=1000 verwenden. Sobald eine TCP-Verbindung einmal aufgebaut ist, bleibt in zwischengeschalteten Routern kein Zustand erhalten. Das Problem sind Firewalls oder NAT-Session-Timeouts. Dabei kommt auch kein RST-Paket an. In einer K8s-Umgebung hatte ich einmal das Problem, dass die conntrack-Modulkonfiguration zu niedrig war. Selbst wenn man HTTP Keep-Alive verwendet, hilft das nur bei der Wiederverwendung von Verbindungen und hält nicht die Netzwerkverbindung selbst aufrecht (Erklärungslink). HTTP Keep-Alive erzeugt tatsächlich keine Pakete, sondern verzögert nur das Schließen. TCP Keep-Alive hingegen erzeugt regelmäßig Pakete und setzt damit den Timer zurück.

    • TCP Keep-Alive funktioniert möglicherweise nicht gut mit Mobilgeräten. Mobile Betriebssysteme können Keep-Alive-Pakete nur dann einzeln verfolgen oder verwalten, wenn sie auf Anwendungsebene erzeugt werden. TCP Keep-Alive arbeitet jedoch unterhalb der Anwendungsebene und kann daher unter Umständen deaktiviert werden, während die App trotzdem weiterhin erreichbar bleibt.
  • Methoden, die Optional<T> zurückgeben, können null zurückgeben. Diese Praxis ist viel zu verwirrend. Wenn ich emotional etwas gelassener wäre, hätte ich wohl ein JEP eingereicht, das eine Annotation wie @java.lang.NonNullReference einführt. Wenn man mit dieser Annotation einen Typ deklariert, sollte der Compiler Zuweisungen von null als Fehler behandeln. Zum Beispiel könnte Alpha null zugewiesen bekommen, während Beta einen Fehler wirft. Ich habe das Gefühl, man müsste sich auch in die Spezifikation ansehen, wie Dead-Code-Elimination in javac tatsächlich funktioniert. Zum Beispiel könnte in if (true) der Teil b = null in der Praxis entfernt werden und rechtlich gesehen dennoch zulässiger Code sein.

    • In Kotlin ist so etwas bereits ein Compilerfehler; eine Annotation braucht man dort gar nicht erst.

    • Ich frage mich, ob man in einer Sprache mit null überhaupt Optional<T> braucht. Wenn man wie in Python beim Rückgabewert einer Funktion ohnehin prüfen muss und statt eines Optional-Objekts einfach T | None verwendet, ist der Unterschied eines Frameworks nicht mehr besonders klar. Wenn man keinen speziellen monadischen Stil nutzt, bleibt die Prüfung letztlich dieselbe.

  • Es hieß, Java, C# und JS würden In-Memory-Strings in einer Art UTF-16 kodieren, aber für Java stimmt das nicht. Für C# und JS könnte es ebenfalls nicht stimmen. Wenn der String-Typ einer Sprache hinreichend undurchsichtig ist, dann ist die In-Memory-Darstellung ein Implementierungsdetail. In Java ist das seit Version 9 ausdrücklich klar (zugehöriger JEP-Link). Das ist auch ein Grund, warum Änderungen an Implementierungsdetails bei vorhandener FFI schwierig sein können. Außerdem wird über Zahlen in JS gesagt, die größte exakt darstellbare Ganzzahl sei 2^53−1, aber tatsächlich lassen sich auch viel größere ganze Zahlen wie 2^100 exakt darstellen. 2^53−1 bedeutet, dass n-1, n und n+1 in IEEE Double alle exakt darstellbar sind. Daher ergeben sowohl n == n-1 als auch n == n+1 den Wert false.

    • Bei C# ist die In-Memory-Darstellung sehr stark festgelegt. Es gibt viele Fälle, in denen man mit ReadOnlySpan<char> oder rohen char* direkt auf den Puffer zugreift, daher ist char ein UTF-16-Codepoint-Typ. Bei JS lässt sich das vielleicht irgendwie umgehen.

    • Statt von einer „max accurate integer“ würde ich lieber von einer „max safe integer“ sprechen.

    • (Zum Thema Base64-Kodierung:) Dass In-Memory-Strings in Java, C# und JS keine Art UTF-16 sind, mag technisch korrekt sein, aber wenn man in einer UTF-8-basierten Sprache einen String Base64-kodiert und ihn dann in Java dekodiert, gibt es Fälle, in denen durch Javas UTF-16-Darstellung Probleme entstehen.

  • Tipps oder Informationen im Handbuchstil scheinen vor allem das schneller verständlich zu machen, was man ohnehin schon weiß oder fast schon wusste. Die meisten Handbücher sind weniger fürs eigentliche Lernen geeignet als für Ordnung und Wiederholung; um völlig Unerfahrene etwas zu lehren, sind sie ineffizient.

    • Ein Handbuch existiert im Kern dafür, Aufzeichnungen zu hinterlassen, statt sich nur auf das Gedächtnis zu verlassen. Die meisten Unix-Handbücher haben genau diese Form. Man weiß bereits, was eine Software tut, aber wenn einem die konkrete Verwendung entfallen ist, schlägt man im Handbuch nach. Wenn absolute Anfänger dagegen erst Konzepte lernen müssen, brauchen sie eher ein Tutorial oder einen Guide. Das Handbuch ist eher eine Art Vorbereitung, um bessere Fragen stellen zu können.
  • Ich habe den Artikel „Traceroute Isn’t Real“ wirklich mit großem Interesse gelesen. Ich hatte schon lange den Eindruck, dass Traceroute-Daten oft sehr ungenau oder bedeutungslos wirken, und es war hilfreich zu verstehen, warum das so ist (Originallink). Falls jemand neuere Informationen dazu hat, würde ich mich freuen.

  • Dieser Text ist weniger eine Sammlung echter Fallstricke oder Fallen als vielmehr eine Liste kleiner Tipps, die der Autor aus Erfahrung gelernt hat. Vieles gilt nur in sehr engen, spezifischen Kontexten, aber dieser Kontext wird nicht klar benannt, und manches wirkt sogar schlicht falsch. Deshalb sollte man den gesamten Text nicht zu wörtlich nehmen, sondern eher als eine Art Gedankenstrom oder Notizsammlung lesen.

  • Die Sache damit, dass Standardargumentwerte in Python nicht bei jedem Aufruf neu erzeugt werden, sondern gespeicherte Werte sind, ist etwas, das man besonders bei datetime-Variablen unbedingt wissen sollte.

    • Ich war nie ein Entwickler, der ständig Python benutzt hat, aber diese Woche habe ich sehr unter genau dieser Eigenheit gelitten, dass Standardargumentwerte gespeichert werden. Ich wollte einem Parameter, wenn nichts übergeben wird, ein leeres set zuweisen, aber das set wurde wiederverwendet, was zu einem Bug führte. Es dauerte ziemlich lange, bis ich die Ursache verstanden hatte.
  • Bei der ersten „Falle“ auf der Seite hieß es, min-width: auto bestimme die Mindestbreite anhand des Inhalts, aber außerhalb von Flex/Grid stimmt das eigentlich nicht. Laut MDN wird auto bei block, inline, table usw. auf 0 korrigiert (offizieller Dokumentationslink).

    • Die eigentliche erste Falle ist, dass man „keine CSS-Eigenschaft isoliert lesen kann“. Wie der Name cascading schon sagt, werden Standardwerte und die Ergebnisse mehrerer Regeln irgendwo miteinander kombiniert, daher ist der Kontext des gesamten Dokuments wichtig.

    • Die Cascade bei CSS-Texteigenschaften kann ich noch einigermaßen nachvollziehen. Aber CSS-Layout ist aus Sicht des Seitendesigners, des Implementierers und auch des Nutzers viel zu schwer zu verstehen. Ich kann mich nicht damit anfreunden, für wen dieses Design eigentlich gedacht sein soll.

  • Insgesamt ist das ein brauchbarer Listenartikel, ich habe aber ein paar Anmerkungen.

    • Bei der Unicode-Han-Unification: Dass Zeichen mit derselben Bedeutung in mehreren Sprachen denselben Codepoint verwenden und je nach Schriftart unterschiedlich angezeigt werden, ist keine Falle. Solche Han-Zeichen wie im gegebenen Beispiel werden in China und Japan fast identisch verwendet, und Nutzer beider Sprachen erkennen auch andere Varianten als dasselbe begriffliche Zeichen. Der Autor spricht so, als müsse man den Buchstaben „A“ im Englischen und Französischen jeweils unterschiedlich definieren. In Wirklichkeit ist das nicht so. Siehe den Artikel Han unification.
    • -0.0 und +0.0 (negatives und positives Null) werden bei Gleitkomma-Vergleichen als gleich behandelt, aber man kann sie unterscheiden, etwa über das Bitmuster oder anhand der Ergebnisse 1.0/-0.0 = -Unendlichkeit und 1.0/0.0 = +Unendlichkeit.
    • Der Empfehlung, die Server-Zeitzone auf UTC zu setzen, stimme ich sehr zu. Für Server, Logs, das Speichern von Fotos und alles andere, das aufbewahrt werden soll oder präzise Zeitstempel braucht, sollte man UTC verwenden. Lokale Zeit würde ich nur für Gespräche verwenden.
    • Es wurde empfohlen, bei Ganzzahlen statt (low + high) / 2 wegen Overflow-Risiko low + (high - low) / 2 zu verwenden, aber wenn low oder high negativ sein können, verlagert man den Overflow-Bereich möglicherweise nur. Das ist ein wichtiges Thema bei allgemeiner binärer Suche.
    • Die korrekte Verwendung von Integer-Typen und -Operationen in C/C++ ist ebenfalls eine große Falle, siehe zugehöriger Guide-Link.
    • Es wurde gesagt, rebase könne die History verändern, aber rebase ist per Definition ein Command, das History umschreibt.
    • Auch wenn Nutzer beider Sprachen begrifflich dasselbe Zeichen erkennen, sollte man das nicht einfach als bloße „Schriftartvariante“ abtun. Nur weil der Unicode-Codepoint gleich ist, heißt das nicht, dass eine Zeichenersetzung sicher ist. Aus Sicht japanischer Nutzer kann so eine Ersetzung ein Grund sein, ein Produkt nicht zu verwenden.

    • Zwar unterscheidet man im Englischen und Französischen kein anderes A, aber es gibt Fälle wie А (kyrillisch) und A (lateinisch), die gleich aussehen und dennoch unterschiedliche Codepoints haben. Auch bei der Han-Unification werden oft Zeichen mit recht unterschiedlichen Formen zusammengefasst, sodass Lernende des Japanischen oder Chinesischen tatsächlich Verwirrung erleben können. Zum Beispiel sieht das Zeichen '喝' (drink) je nach Darstellung, wie in diesem Link, ziemlich unterschiedlich aus. Wenn man es kopiert, ändert sich die Form teilweise sogar unmittelbar, was zeigt, wie komplex die Verarbeitung ist. Han-Unification ist in der Praxis ein ziemlich kniffliges Thema.

  • Es wurde ein feiner Unterschied zwischen numpy und pytorch erwähnt, aber ohne konkrete Beispiele ist das kaum nützlich und liefert zu wenig Information, um als realistische Falle zu wirken.