<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Doogal Simpson</title>
    <description>The latest articles on DEV Community by Doogal Simpson (@doogal).</description>
    <link>https://dev.to/doogal</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3657111%2Fac69c96a-33e1-4023-99ef-ee3059b3ccb6.jpeg</url>
      <title>DEV Community: Doogal Simpson</title>
      <link>https://dev.to/doogal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/doogal"/>
    <language>en</language>
    <item>
      <title>TypeScript private vs Native JS Private Fields (#)</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sun, 01 Nov 2026 13:35:49 +0000</pubDate>
      <link>https://dev.to/doogal/typescript-private-vs-native-js-private-fields--kll</link>
      <guid>https://dev.to/doogal/typescript-private-vs-native-js-private-fields--kll</guid>
      <description>&lt;p&gt;&lt;strong&gt;TypeScript's &lt;code&gt;private&lt;/code&gt; keyword only enforces access restrictions during compilation, leaving properties visible at runtime and during serialization like &lt;code&gt;JSON.stringify()&lt;/code&gt;. To achieve true runtime privacy, you must use JavaScript's native &lt;code&gt;#&lt;/code&gt; private fields introduced in ES2022, which natively protect data at both compile-time and runtime.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine you are building a payment integration and store an API key inside a client class. You add TypeScript's &lt;code&gt;private&lt;/code&gt; modifier to the property, feeling confident that your key is safe from unintended access. But the second you serialize that class instance or inspect it in your browser's developer console, your secret key is displayed in plain text.&lt;/p&gt;

&lt;p&gt;This happens because TypeScript's type system disappears the moment your code compiles down to JavaScript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is TypeScript &lt;code&gt;private&lt;/code&gt; not private at runtime?
&lt;/h2&gt;

&lt;p&gt;TypeScript's &lt;code&gt;private&lt;/code&gt; modifier is an abstraction that exists purely to provide feedback inside your IDE and during the build step. Once your code is transpiled into plain JavaScript, the &lt;code&gt;private&lt;/code&gt; keyword is completely removed, converting the property into a standard public property on the object.&lt;/p&gt;

&lt;p&gt;Think of TypeScript as a security guard standing outside a building checking badges. Once you pass the guard and step inside the building (the JavaScript runtime), the guard is gone. If another piece of code inspects your object at runtime using &lt;code&gt;JSON.stringify()&lt;/code&gt; or &lt;code&gt;Object.keys()&lt;/code&gt;, it bypasses all TypeScript checks.&lt;/p&gt;

&lt;p&gt;Here is what happens when you serialize an object using TypeScript's &lt;code&gt;private&lt;/code&gt; modifier:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthToken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;secretKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shh-secret-value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AuthToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// Output: {"secretKey":"shh-secret-value"}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because the transpiler strips the &lt;code&gt;private&lt;/code&gt; keyword away, the property remains fully enumerable and visible at runtime.&lt;/p&gt;

&lt;h2&gt;
  
  
  How do JavaScript &lt;code&gt;#&lt;/code&gt; private fields guarantee runtime privacy?
&lt;/h2&gt;

&lt;p&gt;ECMAScript 2022 introduced native private class fields using the &lt;code&gt;#&lt;/code&gt; prefix, which are enforced directly by the JavaScript engine at runtime. When you prefix a property with &lt;code&gt;#&lt;/code&gt;, the runtime hides it from external access, reflection, and enumeration.&lt;/p&gt;

&lt;p&gt;Instead of relying on build-step type checking, the JavaScript runtime allocates private fields using a hard-coded internal metadata structure. If external code attempts to access a &lt;code&gt;#&lt;/code&gt; property, the engine throws a runtime &lt;code&gt;TypeError&lt;/code&gt; immediately.&lt;/p&gt;

&lt;p&gt;Let's update our example to use native private fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AuthToken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;#secretKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shh-secret-value&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AuthToken&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="c1"&gt;// Output: {}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;#secretKey&lt;/code&gt; is enforced natively at runtime, &lt;code&gt;JSON.stringify()&lt;/code&gt; completely ignores the property, preserving your data boundaries.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the difference between &lt;code&gt;private&lt;/code&gt; and &lt;code&gt;#&lt;/code&gt; private fields?
&lt;/h2&gt;

&lt;p&gt;Choosing between &lt;code&gt;private&lt;/code&gt; and &lt;code&gt;#&lt;/code&gt; comes down to whether you need design-time type safety or true runtime isolation. &lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;TypeScript &lt;code&gt;private&lt;/code&gt;
&lt;/th&gt;
&lt;th&gt;JS Private Fields (&lt;code&gt;#&lt;/code&gt;)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Enforcement Phase&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compile-time only&lt;/td&gt;
&lt;td&gt;Compile-time and Runtime&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;JSON.stringify()&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Included in output&lt;/td&gt;
&lt;td&gt;Excluded from output&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;&lt;code&gt;Object.keys()&lt;/code&gt; Visibility&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Enumerable / Visible&lt;/td&gt;
&lt;td&gt;Completely hidden&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bypass via Type Casting&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Possible (&lt;code&gt;(obj as any)&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;Impossible&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Minimum Target&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Any ES version&lt;/td&gt;
&lt;td&gt;ES2022 (or polyfilled)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you simply want to signal to teammates that a property shouldn't be touched directly, the &lt;code&gt;private&lt;/code&gt; keyword is often enough. But if you are handling sensitive credentials, internal state machines, or library internals that must never be exposed or serialized, native &lt;code&gt;#&lt;/code&gt; private fields are essential.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can you bypass JavaScript &lt;code&gt;#&lt;/code&gt; private fields using &lt;code&gt;any&lt;/code&gt; in TypeScript?
&lt;/h3&gt;

&lt;p&gt;No, you cannot bypass &lt;code&gt;#&lt;/code&gt; private fields using type casting like &lt;code&gt;(instance as any).#field&lt;/code&gt;. Because runtime privacy is enforced directly by the V8 engine, attempting to access a private field outside its declaring class will throw an uncatchable syntax or type error.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do &lt;code&gt;#&lt;/code&gt; private fields show up in &lt;code&gt;Object.keys()&lt;/code&gt; or &lt;code&gt;for...in&lt;/code&gt; loops?
&lt;/h3&gt;

&lt;p&gt;No, native &lt;code&gt;#&lt;/code&gt; private fields do not appear in &lt;code&gt;Object.keys()&lt;/code&gt;, &lt;code&gt;Object.getOwnPropertyNames()&lt;/code&gt;, or &lt;code&gt;for...in&lt;/code&gt; loops. They are omitted from object key lists entirely.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does using native &lt;code&gt;#&lt;/code&gt; private fields affect performance?
&lt;/h3&gt;

&lt;p&gt;Modern JavaScript engines like V8 optimize &lt;code&gt;#&lt;/code&gt; private fields aggressively. While historical polyfills added runtime overhead, modern native implementations run with virtually identical performance to standard class properties.&lt;/p&gt;

</description>
      <category>typescript</category>
      <category>javascript</category>
      <category>webdev</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Transactional Outbox Pattern: Fix Dual Writes</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sat, 31 Oct 2026 15:08:04 +0000</pubDate>
      <link>https://dev.to/doogal/transactional-outbox-pattern-fix-dual-writes-3fl9</link>
      <guid>https://dev.to/doogal/transactional-outbox-pattern-fix-dual-writes-3fl9</guid>
      <description>&lt;p&gt;&lt;strong&gt;Writing to a database and immediately publishing an event to a message broker in separate steps causes distributed state inconsistencies whenever network drops or crashes occur. The Transactional Outbox Pattern solves this by saving the event to an outbox table within the same database transaction, ensuring resilient, at-least-once event delivery paired with idempotent consumers.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine your application handles a fresh order request. The database record saves cleanly, but right before your code dispatches the &lt;code&gt;OrderCreated&lt;/code&gt; event to RabbitMQ, the container dies from an Out-Of-Memory (OOM) error. Your database insists the order exists, but your downstream billing and shipping services never hear about it. &lt;/p&gt;

&lt;p&gt;I see this scenario play out far too often in microservices architectures. Let's look at why this happens and how to fix it cleanly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is saving to a database and publishing an event an anti-pattern?
&lt;/h2&gt;

&lt;p&gt;Saving to a database and publishing an event in two sequential steps creates a distributed dual-write problem without transactional guarantees. If the application crashes, network drops, or the broker goes down between the database commit and the event dispatch, your system enters an unrecoverable inconsistent state.&lt;/p&gt;

&lt;p&gt;When building event-driven systems, developers frequently write code that executes a local database transaction and then immediately makes an API or TCP call to a message broker like Kafka or RabbitMQ. Because these are two distinct network systems, you cannot wrap them in a single local ACID transaction. &lt;/p&gt;

&lt;p&gt;If your app drops connection right after the database commit, that event is lost forever. Conversely, if you publish the event first and the subsequent database write fails, downstream services will process actions for data that technically doesn't exist in your database.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the Outbox Pattern solve distributed dual writes?
&lt;/h2&gt;

&lt;p&gt;The Outbox Pattern guarantees event persistence by saving the message payload directly into an outbox table inside the exact same database transaction as your domain entity. A dedicated background process then reads pending messages from this outbox table, publishes them to the message broker, and marks them as processed.&lt;/p&gt;

&lt;p&gt;By keeping the outbox write inside the local database transaction, you rely on the database's atomic guarantees. Either both the order and the outbox event payload are written, or neither is.&lt;/p&gt;

&lt;p&gt;Here is how the transactional outbox pipeline processes events:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Step 1: Atomic Local Transaction&lt;/strong&gt;: Insert your business record (e.g., &lt;code&gt;orders&lt;/code&gt;) and an event record into an &lt;code&gt;outbox&lt;/code&gt; table within a single transaction boundary.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 2: Background Processing&lt;/strong&gt;: A separate worker process or CDC tool periodically reads pending records from the &lt;code&gt;outbox&lt;/code&gt; table.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 3: Event Dispatch&lt;/strong&gt;: The worker publishes the event payload to your message broker (e.g., Kafka, RabbitMQ, SNS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Step 4: Acknowledgment &amp;amp; Update&lt;/strong&gt;: Once the broker confirms receipt, the worker marks the outbox row as sent or deletes it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why does the Outbox Pattern require idempotent event consumers?
&lt;/h2&gt;

&lt;p&gt;The Outbox Pattern guarantees at-least-once event delivery, which means network blips or worker restarts during status updates can cause duplicate events to be published. Downstream consumers must be idempotent so that processing the same event multiple times yields the exact same outcome.&lt;/p&gt;

&lt;p&gt;Consider a scenario where your outbox background worker successfully dispatches an event to the message broker, but the application crashes before the worker can mark the outbox row as "sent." When the worker recovers, it sees the outbox row still marked as pending and publishes the message again.&lt;/p&gt;

&lt;p&gt;Trying to achieve strictly "exactly-once" delivery across separate network boundaries is functionally impossible without massive performance penalties. By accepting at-least-once delivery, you push the responsibility of duplicate filtering to the consumer using unique event IDs or idempotency keys.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What happens if the outbox background worker crashes mid-execution?
&lt;/h3&gt;

&lt;p&gt;If the background worker crashes before dispatching the event, the outbox record remains pending and will be picked up when the worker restarts or another instance takes over. If it crashes after dispatching but before updating the database row, a duplicate message is sent, which your idempotent consumers safely handle.&lt;/p&gt;

&lt;h3&gt;
  
  
  How does the Outbox Pattern differ from Change Data Capture (CDC)?
&lt;/h3&gt;

&lt;p&gt;CDC is often an implementation strategy for the Outbox Pattern. Instead of writing a custom worker that repeatedly polls the outbox table, a CDC engine (like Debezium) tails the database transaction log directly, streaming new outbox entries straight to your message broker with zero polling overhead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Does using an outbox table introduce event publishing latency?
&lt;/h3&gt;

&lt;p&gt;Yes, event delivery becomes asynchronous, introducing a minor delay (typically milliseconds) while the polling worker or CDC log miner processes the new outbox entries. However, this slight latency trade-off eliminates system-wide data corruption and lost events.&lt;/p&gt;

</description>
      <category>microservices</category>
      <category>systemdesign</category>
      <category>softwareengineering</category>
      <category>eventdrivenarchitecture</category>
    </item>
    <item>
      <title>Java Date Formatting Bug: YYYY vs yyyy Explained</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Thu, 29 Oct 2026 14:48:27 +0000</pubDate>
      <link>https://dev.to/doogal/java-date-formatting-bug-yyyy-vs-yyyy-explained-23dm</link>
      <guid>https://dev.to/doogal/java-date-formatting-bug-yyyy-vs-yyyy-explained-23dm</guid>
      <description>&lt;p&gt;&lt;strong&gt;In Java date formatting, uppercase &lt;code&gt;YYYY&lt;/code&gt; represents the week-based year, while lowercase &lt;code&gt;yyyy&lt;/code&gt; represents the calendar year. Using &lt;code&gt;YYYY&lt;/code&gt; can cause dates in late December to print as the following calendar year depending on the system locale, leading to silent end-of-year production bugs. I always recommend auditing date patterns for lowercase &lt;code&gt;yyyy&lt;/code&gt;.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I see this exact bug hit production every single year around late December: an application suddenly starts printing dates from the future. You haven't accidentally built a time machine; you've just fallen into one of Java's most notorious date-formatting traps—the subtle difference between uppercase &lt;code&gt;YYYY&lt;/code&gt; and lowercase &lt;code&gt;yyyy&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the difference between YYYY and yyyy in Java date formatting?
&lt;/h2&gt;

&lt;p&gt;In Java, lowercase &lt;code&gt;yyyy&lt;/code&gt; formats the standard calendar year, whereas uppercase &lt;code&gt;YYYY&lt;/code&gt; formats the week-based year (or week year). I strictly reserve &lt;code&gt;YYYY&lt;/code&gt; for ISO week-numbering formats and use &lt;code&gt;yyyy&lt;/code&gt; for virtually every standard application date pattern.&lt;/p&gt;

&lt;p&gt;The calendar year increments predictably at midnight on January 1st. In contrast, the week-based year ties dates to full seven-day week cycles dictated by locale rules. Because a single week can span two calendar years, ISO 8601 and regional calendar definitions assign the entire week to whichever year contains the majority (or specific boundary days) of that week.&lt;/p&gt;

&lt;h2&gt;
  
  
  How does the YYYY week year bug break dates in production?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;YYYY&lt;/code&gt; pattern calculates the year based on the week to which a day belongs, where week one starts in whichever week contains January 1st. If January 1st lands mid-week, days in late December get dragged into the upcoming year.&lt;/p&gt;

&lt;p&gt;Let's look at a concrete hypothetical scenario using the US locale, where calendar weeks begin on Sunday:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;January 1, 2027 falls on a Friday.&lt;/li&gt;
&lt;li&gt;The week containing January 1, 2027 starts on Sunday, December 27, 2026.&lt;/li&gt;
&lt;li&gt;Because January 1st lands within that week, the US locale assigns the whole week to week-year 2027.&lt;/li&gt;
&lt;li&gt;If you format December 27, 2026 using &lt;code&gt;YYYY&lt;/code&gt;, Java outputs &lt;code&gt;2027&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I've reproduced this exact behavior in a few lines of code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;LocalDate&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LocalDate&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;of&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2026&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;27&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// WRONG: Week year prints 2027 in US locale&lt;/span&gt;
&lt;span class="nc"&gt;DateTimeFormatter&lt;/span&gt; &lt;span class="n"&gt;wrongFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DateTimeFormatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofPattern&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"YYYY-MM-dd"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;wrongFormat&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 2027-12-27&lt;/span&gt;

&lt;span class="c1"&gt;// RIGHT: Calendar year prints 2026&lt;/span&gt;
&lt;span class="nc"&gt;DateTimeFormatter&lt;/span&gt; &lt;span class="n"&gt;rightFormat&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DateTimeFormatter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ofPattern&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"yyyy-MM-dd"&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rightFormat&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// 2026-12-27&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How do locale rules affect Java date format specifiers?
&lt;/h2&gt;

&lt;p&gt;System locale dictates both the start day of the week and the minimum threshold of days required to establish week one of a year. Because these rules vary by country, code using &lt;code&gt;YYYY&lt;/code&gt; can produce completely different output depending on where your application runs.&lt;/p&gt;

&lt;p&gt;I find that localized date shifts are particularly dangerous in microservices deployed across multiple regions. What renders correctly on a local dev machine running under one locale might flip to the wrong year on a production server configured with another.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Format Specifier&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Example Pattern&lt;/th&gt;
&lt;th&gt;Output for Dec 27, 2026 (US Locale)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;yyyy&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Calendar Year&lt;/td&gt;
&lt;td&gt;&lt;code&gt;yyyy-MM-dd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2026-12-27&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YYYY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Week-Based Year&lt;/td&gt;
&lt;td&gt;&lt;code&gt;YYYY-MM-dd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2027-12-27&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ww&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Week of Week-Based Year&lt;/td&gt;
&lt;td&gt;&lt;code&gt;YYYY-ww&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;2027-01&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Why does Java provide YYYY if it causes errors?
&lt;/h3&gt;

&lt;p&gt;Uppercase &lt;code&gt;YYYY&lt;/code&gt; isn't a defect; it exists specifically to support ISO 8601 week-date formatting (like &lt;code&gt;2027-W01-5&lt;/code&gt;). I only ever use &lt;code&gt;YYYY&lt;/code&gt; alongside week-of-year specifiers like &lt;code&gt;ww&lt;/code&gt; when building reports grouped strictly by calendar weeks.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do other programming languages have the YYYY vs yyyy issue?
&lt;/h3&gt;

&lt;p&gt;Yes, many date handling libraries across JavaScript, Swift, and C# follow Unicode Technical Standard #35. I've spotted this same format mismatch in libraries like Moment.js and date-fns when developers accidentally type uppercase format strings.&lt;/p&gt;

&lt;h3&gt;
  
  
  How can I catch YYYY formatting bugs before going to production?
&lt;/h3&gt;

&lt;p&gt;I recommend configuring static analysis tools like SpotBugs, ArchUnit, or SonarQube in your build pipeline. These tools include dedicated linter rules that flag uppercase &lt;code&gt;YYYY&lt;/code&gt; inside &lt;code&gt;SimpleDateFormat&lt;/code&gt; or &lt;code&gt;DateTimeFormatter&lt;/code&gt; patterns immediately.&lt;/p&gt;

&lt;p&gt;Cheers,&lt;br&gt;
Doogal&lt;/p&gt;

</description>
      <category>java</category>
      <category>softwareengineering</category>
      <category>backend</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>How GPS Works: Why Positioning Requires 4 Satellites</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Wed, 28 Oct 2026 11:22:22 +0000</pubDate>
      <link>https://dev.to/doogal/how-gps-works-why-positioning-requires-4-satellites-2023</link>
      <guid>https://dev.to/doogal/how-gps-works-why-positioning-requires-4-satellites-2023</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; GPS is fundamentally a distributed network of atomic clocks broadcasting timestamps from space. Your phone calculates its location by measuring signal propagation delays. Because smartphone quartz clocks drift, receiving signals from three satellites yields massive location errors. A fourth satellite solves for local time drift, enabling meter-level positional accuracy and hyper-precise time synchronization.&lt;/p&gt;

&lt;p&gt;We tend to think of GPS as a giant map in the sky tracking our every step. But if you strip away the map UI on your phone, a GPS satellite has a surprisingly simple job description: it just broadcasts its current location and the exact time the signal left the orbit.&lt;/p&gt;

&lt;p&gt;Your phone acts as the receiver, listening to these timestamps to figure out where you are on Earth. The magic—and the engineering headache—comes down to how we measure time.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does GPS Determine Your Location?
&lt;/h2&gt;

&lt;p&gt;GPS calculates your position through trilateration by measuring the exact time it takes for radio signals to travel from satellites to your receiver. Because radio waves travel at the speed of light, knowing the transmission timestamp and the reception timestamp reveals your exact distance from each satellite. Combining distances from multiple satellites pinpoints your location in space.&lt;/p&gt;

&lt;p&gt;Imagine you are standing in an open field. Radio signals travel at the speed of light, which is roughly 300 meters per microsecond. If a satellite broadcasts a message at 12:00:00.000000 and your phone receives it at 12:00:00.000100, that 100-microsecond delay means you are exactly 30,000 meters away from that satellite.&lt;/p&gt;

&lt;p&gt;In pure geometry, intersecting three spheres drawn around three known satellite points gives you a single point in 3D space: your latitude, longitude, and altitude. However, this math assumes your receiver's clock is perfectly synchronized with the satellites. In the real world, it isn't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does GPS Require a Fourth Satellite?
&lt;/h2&gt;

&lt;p&gt;GPS requires a fourth satellite to calculate and cancel out the time offset of your phone's cheap internal quartz clock. Satellites use hyper-accurate atomic clocks, but phone clocks drift significantly relative to light-speed calculations. The fourth satellite provides an extra equation to solve for local clock error alongside your three spatial coordinates.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Clock Type&lt;/th&gt;
&lt;th&gt;Accuracy / Drift Rate&lt;/th&gt;
&lt;th&gt;Positional Error Potential&lt;/th&gt;
&lt;th&gt;Primary System Role&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Onboard Atomic Clock&lt;/td&gt;
&lt;td&gt;~1 nanosecond per day&lt;/td&gt;
&lt;td&gt;Less than 0.3 meters&lt;/td&gt;
&lt;td&gt;Satellite signal generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Receiver Quartz Oscillator&lt;/td&gt;
&lt;td&gt;~1 microsecond per second&lt;/td&gt;
&lt;td&gt;~300 meters per microsecond&lt;/td&gt;
&lt;td&gt;Smartphone signal reception&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;GPS-Corrected Local Time&lt;/td&gt;
&lt;td&gt;Synchronized to atomic time&lt;/td&gt;
&lt;td&gt;1 to 5 meters (after correction)&lt;/td&gt;
&lt;td&gt;Real-time map navigation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Because light travels 300 meters every microsecond, a clock error of just one microsecond puts your location calculation off by 300 meters—roughly three football fields. If your phone relied on only three satellites, your local quartz clock's drift would ruin the math entirely.&lt;/p&gt;

&lt;p&gt;To resolve this, we treat time offset as a fourth unknown variable. You need four equations to solve for four unknowns:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;X coordinate (Latitude)&lt;/li&gt;
&lt;li&gt;Y coordinate (Longitude)&lt;/li&gt;
&lt;li&gt;Z coordinate (Altitude)&lt;/li&gt;
&lt;li&gt;Clock offset error (t_error)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By adding a fourth satellite, your phone receives four independent signals. It solves the system of equations, determines the local clock bias, and corrects its own time reading. Once the clock error is zeroed out, your positional accuracy drops from several hundred meters down to a tiny radius.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Are GPS Receivers Used on Electrical Power Grids?
&lt;/h2&gt;

&lt;p&gt;Power grids and telecommunications networks use GPS receivers to achieve absolute microsecond-level time synchronization across massive geographic areas. Because stationary infrastructure already knows its exact spatial coordinates, it uses satellite signals strictly to synchronize local clocks rather than compute location.&lt;/p&gt;

&lt;p&gt;If you place a stationary GPS receiver on a substation roof, the positional variables (X, Y, Z) are fixed. The receiver only needs to solve for time. &lt;/p&gt;

&lt;p&gt;This makes GPS the cheapest, most accurate clock distribution network on the planet. Alternating current (AC) grids spanning whole continents must maintain perfect phase synchronization to avoid catastrophic tripping and outages. Cellular towers rely on the exact same microsecond timing to hand off data packets without overlapping frequencies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can GPS work with only three satellites?
&lt;/h3&gt;

&lt;p&gt;GPS can work with three satellites only if the receiver already knows its exact altitude or contains an internal atomic clock. Without those assumptions, a three-satellite setup cannot resolve local quartz clock drift, leading to location errors of several hundred meters.&lt;/p&gt;

&lt;h3&gt;
  
  
  How fast do GPS signals travel to Earth?
&lt;/h3&gt;

&lt;p&gt;GPS radio signals travel at the speed of light, which is approximately 299,792 kilometers per second (about 300 meters per microsecond). This extreme speed is why microsecond-level timing accuracy is mandatory for accurate positioning.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens to GPS accuracy if a satellite clock drifts?
&lt;/h3&gt;

&lt;p&gt;Ground control stations continuously monitor onboard satellite atomic clocks from Earth. If a satellite clock drifts, controllers upload correction data to the satellite, which then broadcasts those offset parameters to ground receivers inside the navigation message.&lt;/p&gt;

</description>
      <category>gps</category>
      <category>softwareengineering</category>
      <category>computerscience</category>
      <category>math</category>
    </item>
    <item>
      <title>SQL NOT IN vs NOT EXISTS: Handling NULL Values Safely</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Tue, 27 Oct 2026 11:44:24 +0000</pubDate>
      <link>https://dev.to/doogal/sql-not-in-vs-not-exists-handling-null-values-safely-2cl0</link>
      <guid>https://dev.to/doogal/sql-not-in-vs-not-exists-handling-null-values-safely-2cl0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; In SQL, using &lt;code&gt;WHERE column NOT IN (SELECT ...)&lt;/code&gt; returns zero rows if the subquery contains a single &lt;code&gt;NULL&lt;/code&gt;. This occurs because &lt;code&gt;NOT IN&lt;/code&gt; expands to chained &lt;code&gt;!=&lt;/code&gt; operators joined by &lt;code&gt;AND&lt;/code&gt;, and any comparison against &lt;code&gt;NULL&lt;/code&gt; yields &lt;code&gt;UNKNOWN&lt;/code&gt;. To avoid silent query failures, always use &lt;code&gt;NOT EXISTS&lt;/code&gt; or explicitly filter out &lt;code&gt;NULL&lt;/code&gt; values in the subquery.&lt;/p&gt;

&lt;p&gt;Imagine running a routine query to find customers who have never placed an order, only for the database to return an empty result set—even though you know inactive users exist in your table. You double-check your dataset, confirm that inactive customer records exist, and run the query again. Still, nothing comes back.&lt;/p&gt;

&lt;p&gt;This exact behavior is one of SQL's most notorious traps, and it almost always traces back to a single &lt;code&gt;NULL&lt;/code&gt; hiding inside a subquery.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Does SQL NOT IN Return Zero Rows When NULL Is Present?
&lt;/h2&gt;

&lt;p&gt;When you run &lt;code&gt;WHERE column NOT IN (subquery)&lt;/code&gt;, SQL converts the operation into a series of &lt;code&gt;!=&lt;/code&gt; (NOT EQUAL TO) conditions joined by &lt;code&gt;AND&lt;/code&gt;. If any value in the subquery is &lt;code&gt;NULL&lt;/code&gt;, evaluating &lt;code&gt;column != NULL&lt;/code&gt; produces an &lt;code&gt;UNKNOWN&lt;/code&gt; result. Because SQL requires a &lt;code&gt;WHERE&lt;/code&gt; clause to evaluate strictly to &lt;code&gt;TRUE&lt;/code&gt;, a single &lt;code&gt;UNKNOWN&lt;/code&gt; causes the entire expression to fail and return zero rows.&lt;/p&gt;

&lt;p&gt;Let's trace how this happens with a simple example. Suppose we want to find customers without orders using this subquery:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt; 
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; 
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;IN&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, SQL transforms that &lt;code&gt;WHERE&lt;/code&gt; clause into an expanded logical chain for every customer evaluation:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;WHERE (customer_id != 1) AND (customer_id != 2) AND (customer_id != NULL)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;NULL&lt;/code&gt; represents an absent or unassigned value, evaluating &lt;code&gt;customer_id != NULL&lt;/code&gt; yields &lt;code&gt;UNKNOWN&lt;/code&gt;. In three-valued SQL logic, &lt;code&gt;TRUE AND UNKNOWN&lt;/code&gt; resolves to &lt;code&gt;UNKNOWN&lt;/code&gt;. Since a &lt;code&gt;WHERE&lt;/code&gt; clause only returns rows where the condition evaluates strictly to &lt;code&gt;TRUE&lt;/code&gt;, the database filters out every single row.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does Three-Valued Logic Affect SQL Subqueries?
&lt;/h2&gt;

&lt;p&gt;SQL operates on three-valued logic, where boolean expressions evaluate to &lt;code&gt;TRUE&lt;/code&gt;, &lt;code&gt;FALSE&lt;/code&gt;, or &lt;code&gt;UNKNOWN&lt;/code&gt;. Comparing any column against &lt;code&gt;NULL&lt;/code&gt; using standard operators never returns &lt;code&gt;TRUE&lt;/code&gt; or &lt;code&gt;FALSE&lt;/code&gt; because an unassigned value cannot be conclusively proven equal or unequal to another value.&lt;/p&gt;

&lt;p&gt;Understanding how different SQL subquery constructs handle &lt;code&gt;NULL&lt;/code&gt; values helps prevent silent logic failures in production:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Subquery Pattern&lt;/th&gt;
&lt;th&gt;Behavior with NULL Values&lt;/th&gt;
&lt;th&gt;Boolean Result&lt;/th&gt;
&lt;th&gt;Safety Level&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOT IN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fails silently if any subquery row contains &lt;code&gt;NULL&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;UNKNOWN&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Risky (Requires non-null data)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;NOT IN&lt;/code&gt; + &lt;code&gt;IS NOT NULL&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Safe when &lt;code&gt;NULL&lt;/code&gt; values are filtered in the subquery&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;TRUE&lt;/code&gt; / &lt;code&gt;FALSE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Safe (If non-null filter is maintained)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;NOT EXISTS&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fully safe; evaluates row matching without equality chains&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;TRUE&lt;/code&gt; / &lt;code&gt;FALSE&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Recommended (Ignores nullability issues)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How Do You Fix the SQL NOT IN Null Trap?
&lt;/h2&gt;

&lt;p&gt;To fix this issue, replace &lt;code&gt;NOT IN&lt;/code&gt; with a correlated &lt;code&gt;NOT EXISTS&lt;/code&gt; subquery, which checks for row existence rather than value equality. Alternatively, you can add an explicit &lt;code&gt;WHERE column IS NOT NULL&lt;/code&gt; condition to your &lt;code&gt;NOT IN&lt;/code&gt; subquery to prevent &lt;code&gt;NULL&lt;/code&gt; values from evaluating.&lt;/p&gt;

&lt;p&gt;Here is how you rewrite the problematic query safely using &lt;code&gt;NOT EXISTS&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;customers&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="k"&gt;NOT&lt;/span&gt; &lt;span class="k"&gt;EXISTS&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; 
    &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt; 
    &lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;o&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;customer_id&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If an order row contains a &lt;code&gt;NULL&lt;/code&gt; customer ID—for example, from an anonymous guest checkout—the inner condition &lt;code&gt;o.customer_id = c.customer_id&lt;/code&gt; simply evaluates to false for that specific order row. The overall &lt;code&gt;NOT EXISTS&lt;/code&gt; check continues to work as expected, correctly returning customer records that have no order history.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is NOT EXISTS faster than NOT IN in modern SQL databases?
&lt;/h3&gt;

&lt;p&gt;In modern relational databases like PostgreSQL, MySQL, and SQL Server, the query optimizer produces identical execution plans for &lt;code&gt;NOT EXISTS&lt;/code&gt; and &lt;code&gt;NOT IN&lt;/code&gt; when columns are non-nullable. However, when columns allow &lt;code&gt;NULL&lt;/code&gt; values, &lt;code&gt;NOT EXISTS&lt;/code&gt; usually performs better because the query engine does not need to evaluate complex fallback paths for three-valued logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does IN work with NULL values while NOT IN fails?
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;IN&lt;/code&gt; operator expands using &lt;code&gt;OR&lt;/code&gt; conditions rather than &lt;code&gt;AND&lt;/code&gt; conditions (for example, &lt;code&gt;id = 1 OR id = NULL&lt;/code&gt;). If any equality test in an &lt;code&gt;OR&lt;/code&gt; chain evaluates to &lt;code&gt;TRUE&lt;/code&gt;, the whole expression becomes &lt;code&gt;TRUE&lt;/code&gt;, allowing &lt;code&gt;IN&lt;/code&gt; to return matching records even when &lt;code&gt;NULL&lt;/code&gt; values exist in the list.&lt;/p&gt;

&lt;h3&gt;
  
  
  Can I safely use NOT IN if my subquery column is constrained to NOT NULL?
&lt;/h3&gt;

&lt;p&gt;Yes. If your database schema enforces a strict &lt;code&gt;NOT NULL&lt;/code&gt; constraint on the subquery's target column, &lt;code&gt;NOT IN&lt;/code&gt; cannot encounter a &lt;code&gt;NULL&lt;/code&gt; value during evaluation. In that scenario, &lt;code&gt;NOT IN&lt;/code&gt; is logically safe, though using &lt;code&gt;NOT EXISTS&lt;/code&gt; remains a defensive coding habit.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>database</category>
      <category>backend</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Measuring Execution Time Safely with Monotonic Clocks</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sun, 25 Oct 2026 21:33:12 +0000</pubDate>
      <link>https://dev.to/doogal/measuring-execution-time-safely-with-monotonic-clocks-1e8k</link>
      <guid>https://dev.to/doogal/measuring-execution-time-safely-with-monotonic-clocks-1e8k</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; Measuring execution time using wall-clock functions like &lt;code&gt;Date.now()&lt;/code&gt; or &lt;code&gt;System.currentTimeMillis()&lt;/code&gt; can yield negative durations if your server synchronizes its clock mid-operation. To measure elapsed code performance accurately, always use monotonic clock APIs like &lt;code&gt;performance.now()&lt;/code&gt; in JavaScript or &lt;code&gt;System.nanoTime()&lt;/code&gt; in Java, which strictly count forward from an arbitrary baseline.&lt;/p&gt;

&lt;p&gt;Imagine profiling an asynchronous task in your API that typically takes 200 milliseconds to complete, only for your logging framework to report a duration of &lt;code&gt;-800ms&lt;/code&gt;. While a negative duration sounds like a physics violation, I often see this exact issue trip up engineers who rely on wall-clock time for benchmarking.&lt;/p&gt;

&lt;p&gt;When I time execution intervals, relying on time-of-day calls exposes metrics to background operating system updates. In my experience, understanding how system time works—and why monotonic clocks exist—will prevent your benchmarks from reporting impossible numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does Date.now() produce negative durations?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Date.now()&lt;/code&gt; in JavaScript and &lt;code&gt;System.currentTimeMillis()&lt;/code&gt; in Java read the system's wall-clock time, which continuously synchronizes with external network servers. If Network Time Protocol (NTP) adjusts your system clock backward mid-execution, subtracting your start timestamp from your end timestamp produces a negative duration.&lt;/p&gt;

&lt;p&gt;Wall-clock APIs return the server's current opinion of the global time of day. This time isn't static or linear; background processes continuously adjust it to prevent clock drift across distributed infrastructure. A manual system administrator edit or an automatic NTP sync can shift your server's clock backward by a full second at any given moment.&lt;/p&gt;

&lt;p&gt;Let's walk through a hypothetical scenario. Say you log a start time of &lt;code&gt;1,000ms&lt;/code&gt; using a wall-clock API right before executing a task that takes &lt;code&gt;200ms&lt;/code&gt;. If an NTP server sync fires mid-task and sets your machine's clock back by &lt;code&gt;1,000ms&lt;/code&gt;, your ending timestamp becomes &lt;code&gt;200ms&lt;/code&gt;. Subtracting start from finish yields a result of &lt;code&gt;-800ms&lt;/code&gt; for a task that ran normally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Unsafe: Wall-clock time can shift mid-execution&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;executeTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Can be negative!&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What is a monotonic clock and how does it work?
&lt;/h2&gt;

&lt;p&gt;A monotonic clock is an internal CPU timer that steadily counts forward from an arbitrary starting point, such as system boot time, without ever jumping backward or skipping. Because it ignores global time-of-day adjustments, it provides a safe baseline for measuring interval durations between two events.&lt;/p&gt;

&lt;p&gt;Unlike wall-clock timers, the actual number returned by a monotonic clock has no real-world calendar meaning. You cannot convert a raw monotonic reading into a human-readable date or time string like "10:15 AM." Its sole purpose is delta computation: taking two readings and subtracting the first from the second to yield an exact elapsed time.&lt;/p&gt;

&lt;p&gt;Because monotonic sources are bound to hardware ticks rather than atomic clock synchronization, they guarantee strict forward progression. They remain completely unaffected by leap seconds, daylight saving shifts, or NTP corrections.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;Wall-Clock Timer&lt;/th&gt;
&lt;th&gt;Monotonic Clock&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;JavaScript API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Date.now()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;performance.now()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Java API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;System.currentTimeMillis()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;System.nanoTime()&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Purpose&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Displaying calendar time of day&lt;/td&gt;
&lt;td&gt;Measuring elapsed code execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;NTP Adjustment Risk&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Subject to clock skew and backward jumps&lt;/td&gt;
&lt;td&gt;Completely immune to time adjustments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Return Value&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Milliseconds since Unix Epoch&lt;/td&gt;
&lt;td&gt;Ticks since an arbitrary starting baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How do you measure execution time correctly in JavaScript and Java?
&lt;/h2&gt;

&lt;p&gt;In JavaScript, use &lt;code&gt;performance.now()&lt;/code&gt; instead of &lt;code&gt;Date.now()&lt;/code&gt; to get sub-millisecond, monotonic timing data. In Java, replace &lt;code&gt;System.currentTimeMillis()&lt;/code&gt; with &lt;code&gt;System.nanoTime()&lt;/code&gt; to measure elapsed execution durations safely.&lt;/p&gt;

&lt;p&gt;When I write performance measurements, I rely on these APIs to guarantee a continuous forward-moving timeline. Here is how you write safe interval calculations in both environments.&lt;/p&gt;

&lt;p&gt;In JavaScript:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nf"&gt;doWork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;duration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;performance&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Always positive and precise&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Java:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nanoTime&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;doWork&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="kt"&gt;long&lt;/span&gt; &lt;span class="n"&gt;durationNanos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nanoTime&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Immune to NTP clock skew&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By ensuring your timing logic relies on monotonic clocks, your metrics, traces, and performance profiling will remain accurate regardless of what your underlying operating system does with its wall clock.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Can I convert a monotonic timestamp into a human-readable date?
&lt;/h3&gt;

&lt;p&gt;No. Monotonic clock values are arbitrary numbers based on points like system startup. To log human-readable timestamps, use wall-clock functions like &lt;code&gt;Date.now()&lt;/code&gt;, but save monotonic APIs strictly for elapsed time calculations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Is performance.now() supported in Node.js?
&lt;/h3&gt;

&lt;p&gt;Yes. Modern Node.js environments support &lt;code&gt;performance.now()&lt;/code&gt; globally, matching the browser Web API implementation for high-resolution timing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Do monotonic clocks handle operating system sleep mode?
&lt;/h3&gt;

&lt;p&gt;Behavior varies across operating systems and runtimes. While a monotonic clock will never move backward when a system wakes from sleep, the time spent sleeping may or may not be counted in the total elapsed interval.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>java</category>
      <category>performance</category>
      <category>softwareengineering</category>
    </item>
    <item>
      <title>Why a Single Emoji Quadruples Python String Memory</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sun, 25 Oct 2026 11:35:42 +0000</pubDate>
      <link>https://dev.to/doogal/why-a-single-emoji-quadruples-python-string-memory-15jp</link>
      <guid>https://dev.to/doogal/why-a-single-emoji-quadruples-python-string-memory-15jp</guid>
      <description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; CPython stores strings using PEP 393 (Flexible String Representation) rather than UTF-8. It selects a fixed width of 1, 2, or 4 bytes per character based on the widest character present. If an ASCII string contains just one 4-byte emoji, Python instantly upgrades every single character in that string to 4 bytes, quadrupling its memory footprint.&lt;/p&gt;

&lt;p&gt;Imagine loading a massive log file into memory, doing basic parsing, and watching your server's RAM jump from 100 MB to 400 MB. You haven't added millions of extra records—you just processed a line containing a single thumbs-up emoji. When I profile Python memory footprints, I often see engineers get tripped up by this exact behavior. This isn't a memory leak; it's a deliberate architecture trade-off built into CPython.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does Python Store Strings in Memory?
&lt;/h2&gt;

&lt;p&gt;CPython stores strings using PEP 393, known as the Flexible String Representation, rather than variable-width UTF-8 encoding. It selects a uniform byte width—1, 2, or 4 bytes per character—for the entire string based on its widest single character.&lt;/p&gt;

&lt;p&gt;When I inspect string allocations in CPython, the interpreter evaluates the highest Unicode code point in the sequence and assigns one of three internal array storage kinds:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;PyUnicode_1BYTE_KIND (1 byte):&lt;/strong&gt; Used when all characters fit into Latin-1 / ASCII range (&lt;code&gt;uint8_t&lt;/code&gt; layout).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PyUnicode_2BYTE_KIND (2 bytes):&lt;/strong&gt; Used when the widest character fits in the Basic Multilingual Plane, like European symbols (€) or most CJK characters (&lt;code&gt;uint16_t&lt;/code&gt; layout).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PyUnicode_4BYTE_KIND (4 bytes):&lt;/strong&gt; Used when any character requires supplementary planes, such as emojis or ancient scripts (&lt;code&gt;uint32_t&lt;/code&gt; / &lt;code&gt;Py_UCS4&lt;/code&gt; layout).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;CPython chooses this fixed-width strategy over variable-length UTF-8 to guarantee O(1) character indexing. When I evaluate &lt;code&gt;my_string[500]&lt;/code&gt;, Python doesn't iterate through preceding bytes counting multibyte sequences; it computes &lt;code&gt;pointer_address + (500 * byte_width)&lt;/code&gt; instantly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;

&lt;span class="c1"&gt;# 1,000,000 ASCII characters = ~1 MB
&lt;/span&gt;&lt;span class="n"&gt;ascii_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;1_000_000&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ascii_str&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# ~1000048 bytes
&lt;/span&gt;
&lt;span class="c1"&gt;# Add a single emoji, and the entire string converts to 4 bytes per char
&lt;/span&gt;&lt;span class="n"&gt;emoji_str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ascii_str&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;👍&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getsizeof&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;emoji_str&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;  &lt;span class="c1"&gt;# ~4000076 bytes
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Why Does Adding One Emoji Multiply String Memory by Four?
&lt;/h2&gt;

&lt;p&gt;When CPython detects a character requiring 4 bytes, it must reallocate the string's internal character array to a 4-byte (&lt;code&gt;Py_UCS4&lt;/code&gt;) array structure. To preserve pointer offset calculations for constant-time indexing, every 1-byte ASCII character is expanded into a 32-bit memory slot padded with zeros.&lt;/p&gt;

&lt;p&gt;At the C level, a string object contains header metadata followed by a contiguous array of fixed-size integers. If an entire string contains 999,999 ASCII characters stored as 8-bit unsigned integers (&lt;code&gt;uint8_t&lt;/code&gt;), each character takes exactly one byte. The moment a 4-byte character (like &lt;code&gt;U+1F44D&lt;/code&gt; for a thumbs-up emoji) is appended, CPython cannot mix 8-bit and 32-bit values in the same contiguous array without losing O(1) offset arithmetic.&lt;/p&gt;

&lt;p&gt;To maintain &lt;code&gt;array_start + (index * 4)&lt;/code&gt;, CPython allocates a new buffer where every single character—including all 999,999 original ASCII letters—is promoted to a 32-bit &lt;code&gt;Py_UCS4&lt;/code&gt; integer. The ASCII character &lt;code&gt;'a'&lt;/code&gt; (hex &lt;code&gt;0x61&lt;/code&gt;) expands from &lt;code&gt;0x61&lt;/code&gt; to &lt;code&gt;0x00000061&lt;/code&gt; in memory, consuming four times the physical RAM purely for zero-padding.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Character Category&lt;/th&gt;
&lt;th&gt;Example Characters&lt;/th&gt;
&lt;th&gt;Byte Width per Character&lt;/th&gt;
&lt;th&gt;Memory for 1M Characters&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ASCII / Latin-1&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;a&lt;/code&gt;, &lt;code&gt;B&lt;/code&gt;, &lt;code&gt;9&lt;/code&gt;, &lt;code&gt;!&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;1 Byte (&lt;code&gt;uint8_t&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;~1 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BMP (UCS-2)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;€&lt;/code&gt;, &lt;code&gt;文&lt;/code&gt;, &lt;code&gt;α&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;2 Bytes (&lt;code&gt;uint16_t&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;~2 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Supplementary (UCS-4)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;👍&lt;/code&gt;, &lt;code&gt;🚀&lt;/code&gt;, &lt;code&gt;𓀀&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;4 Bytes (&lt;code&gt;uint32_t&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;~4 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How Can You Fix Python Memory Inflation from Emojis?
&lt;/h2&gt;

&lt;p&gt;To reclaim memory, slice away wide characters or isolate multibyte strings from large text bodies. Because PEP 393 evaluates width on a per-string-object basis, new strings derived without high-code-point characters instantly revert to 1 byte per character.&lt;/p&gt;

&lt;p&gt;When I need to optimize memory-heavy pipelines handling untrusted input, I recommend splitting large documents into smaller structural components rather than keeping raw, unified strings. If you slice off an emoji from a string, CPython constructs a fresh string object, re-scans the highest code point in the slice, sees only ASCII, and allocates a 1-byte array layout.&lt;/p&gt;

&lt;p&gt;If you process large streams (like log files or HTML dumps), isolating wide characters early or handling text in line-by-line chunks prevents a single emoji from inflating the memory of your entire dataset.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does Python use UTF-8 internally for strings?
&lt;/h3&gt;

&lt;p&gt;No, CPython does not store active string objects as UTF-8 in memory. UTF-8 is a variable-width encoding that breaks O(1) index lookups, so CPython uses PEP 393 fixed-width array representations (1, 2, or 4 bytes) in RAM and only converts to UTF-8 when serializing to disk or network sockets.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is PEP 393 in Python?
&lt;/h3&gt;

&lt;p&gt;PEP 393, introduced in Python 3.3, defined the "Flexible String Representation." It dynamically sets array element sizes to 1, 2, or 4 bytes based on the single highest Unicode code point in a string, optimizing memory for standard ASCII while preserving O(1) random access.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does CPython prioritize fixed-width strings over UTF-8 memory savings?
&lt;/h3&gt;

&lt;p&gt;CPython prioritizes fixed-width strings to guarantee O(1) character lookup performance. With fixed-width memory layout, indexing any character in a string requires a simple pointer offset calculation (&lt;code&gt;base + index * width&lt;/code&gt;), whereas variable-width UTF-8 requires scanning byte boundaries sequentially from the start of the string (an O(n) operation).&lt;/p&gt;

</description>
      <category>python</category>
      <category>softwareengineering</category>
      <category>performance</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>How Netflix Architecture Works: AWS &amp; Open Connect CDN</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sat, 24 Oct 2026 12:25:53 +0000</pubDate>
      <link>https://dev.to/doogal/how-netflix-architecture-works-aws-open-connect-cdn-477a</link>
      <guid>https://dev.to/doogal/how-netflix-architecture-works-aws-open-connect-cdn-477a</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; Netflix separates its control plane (AWS) from its data plane (Open Connect). Instead of streaming massive video files directly from AWS data centers, Netflix embeds custom hardware called Open Connect Appliances (OCAs) inside Internet Service Providers' networks. These local boxes pre-cache predicted content overnight, serving up to 70% of video traffic locally to reduce latency and ISP bandwidth costs.&lt;/p&gt;

&lt;p&gt;When you press play on Netflix, you aren't streaming gigabytes of video data from an AWS data center halfway across the country. In fact, if Netflix attempted to route all its stream traffic directly through AWS, public cloud egress costs and internet exchange bottlenecks would render the business model unsustainable. &lt;/p&gt;

&lt;p&gt;Here is a breakdown of how Netflix delivers petabytes of data seamlessly using a hybrid cloud and edge infrastructure.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does Netflix Architecture Separate Control Plane and Data Plane?
&lt;/h2&gt;

&lt;p&gt;Netflix splits its system architecture into two distinct operational layers: AWS manages the control plane, while an in-house Content Delivery Network (CDN) called Open Connect handles the data plane. AWS handles lightweight API requests like user authentication, catalog browsing, and recommendations, while local Open Connect Appliances (OCAs) store and deliver actual video streams.&lt;/p&gt;

&lt;p&gt;Think of AWS as a hotel's front desk and the Open Connect Appliance as the mini-fridge inside your guest room. You interact with the front desk to check in, get your room key, and request recommendations. However, when you want a bottle of water, you do not call the lobby to send someone up from the basement—you reach directly into the mini-fridge right next to you. &lt;/p&gt;

&lt;p&gt;By isolating heavy payload delivery from core application logic, Netflix offloads massive bandwidth demands from public cloud providers. When a user hits play, AWS simply returns a manifest file containing the direct network address of the optimal local OCA.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;System Component&lt;/th&gt;
&lt;th&gt;Architectural Layer&lt;/th&gt;
&lt;th&gt;Primary Responsibilities&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS Cloud&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Control Plane&lt;/td&gt;
&lt;td&gt;User authentication, billing, search, UI rendering, recommendation engine&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Open Connect Appliance (OCA)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Data Plane&lt;/td&gt;
&lt;td&gt;Video chunk pre-caching, localized byte streaming, adaptive bitrate delivery&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Why Do Internet Service Providers Allow Netflix Hardware in Their Data Centers?
&lt;/h2&gt;

&lt;p&gt;Internet Service Providers (ISPs) host Netflix's custom hardware for free because it dramatically reduces their upstream bandwidth costs. Without local caching, thousands of subscribers watching the same high-definition release simultaneously would flood the ISP's internet exchange links with redundant data transfers.&lt;/p&gt;

&lt;p&gt;Imagine a local coffee shop serving a crowd of 500 office workers every morning. Instead of sending 500 individual delivery drivers to a central bakery across town every few minutes, the bakery sends one bulk shipment to a storage rack inside the coffee shop before doors open. &lt;/p&gt;

&lt;p&gt;For ISPs, transferring data across third-party backbones is expensive. Installing an OCA directly inside an ISP's central office transforms costly transit traffic into local network traffic. According to Netflix's figures, approximately 69% of their traffic in Central and Eastern Europe was served directly from inside ISP networks via these embedded boxes.&lt;/p&gt;

&lt;h2&gt;
  
  
  How Does Open Connect Pre-Cache Content Before You Press Play?
&lt;/h2&gt;

&lt;p&gt;Open Connect Appliances pre-fill their local drives during off-peak hours using predictive machine learning models. Every night, typically around 2 AM local time, the appliances download the specific movies and show episodes that Netflix algorithms predict local subscribers will stream the next day.&lt;/p&gt;

&lt;p&gt;Rather than reactively fetching a video file when a subscriber clicks play, the system operates proactively. If a popular series releases a new season, nearby OCAs have already fetched and assembled the encrypted media chunks over quiet overnight connections. By the time peak viewing hours arrive, the network load is already smoothed out, ensuring instant playback with minimal buffering.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Does Netflix pay ISPs to host Open Connect Appliances?
&lt;/h3&gt;

&lt;p&gt;No, Netflix provides Open Connect Appliances to qualified ISPs completely free of charge. ISPs host and power them voluntarily because the local caching saves them money on transit bandwidth costs.&lt;/p&gt;

&lt;h3&gt;
  
  
  What happens if a local Open Connect Appliance fails or lacks a requested title?
&lt;/h3&gt;

&lt;p&gt;If a local appliance fails or does not contain the requested video file, the Netflix client automatically fails over to stream the content from a secondary regional Open Connect server located at a nearby Internet Exchange Point (IXP).&lt;/p&gt;

&lt;h3&gt;
  
  
  Why doesn't Netflix use commercial CDNs like Cloudflare or Fastly?
&lt;/h3&gt;

&lt;p&gt;At Netflix's extreme scale, off-the-shelf CDNs are cost-prohibitive. Building custom, single-purpose hardware optimized purely for sequential video streaming allows Netflix to achieve significantly higher storage density and cost efficiency than general-purpose CDNs.&lt;/p&gt;

</description>
      <category>systemdesign</category>
      <category>architecture</category>
      <category>aws</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>Why Git Stores File History Backwards</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Wed, 21 Oct 2026 16:07:43 +0000</pubDate>
      <link>https://dev.to/doogal/why-git-stores-file-history-backwards-37d1</link>
      <guid>https://dev.to/doogal/why-git-stores-file-history-backwards-37d1</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer / TL;DR:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Git optimizes repository performance by storing the newest version of your files whole while keeping older history as backward deltas. When running garbage collection, Git sorts files by size and builds older, smaller revisions out of larger, newer ones. This ensures modern checkouts are fast, requiring minimal delta reconstruction.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Checking out &lt;code&gt;main&lt;/code&gt; in a decade-old repository with 50,000 commits should logically crawl to a halt. The fact that it happens in milliseconds comes down to a clever bit of engineering inside Git's object store: Git stores your file history backwards.&lt;/p&gt;

&lt;p&gt;I find this design choice brilliant because it directly targets real developer behavior. We rarely care about building code from three years ago, but we check out today's branch dozens of times a day.&lt;/p&gt;
&lt;h2&gt;
  
  
  How does Git delta compression work?
&lt;/h2&gt;

&lt;p&gt;Git compresses repository history by pairing similar files inside packfiles, keeping one complete base object and storing the other as a delta. Instead of duplicating entire file contents across commits, Git writes tiny byte-level instructions detailing how to transform one revision into another.&lt;/p&gt;

&lt;p&gt;When I run &lt;code&gt;git commit&lt;/code&gt;, Git doesn't immediately worry about space efficiency. It writes each modified file out as a standalone, loose object in &lt;code&gt;.git/objects&lt;/code&gt;. This makes commits instant, but loose objects quickly bloat your disk.&lt;/p&gt;

&lt;p&gt;Later, when I run &lt;code&gt;git gc&lt;/code&gt; or execute a &lt;code&gt;git push&lt;/code&gt;, Git squashes those loose objects into a single compressed binary &lt;code&gt;.pack&lt;/code&gt; file. During this packing phase, Git searches for files with matching paths and similar content. It keeps one file completely whole as a base object and compresses the other into a delta—a lean instruction list that basically says, "copy these bytes from the base, then insert these new bytes here."&lt;/p&gt;
&lt;h2&gt;
  
  
  Why does Git store history backwards?
&lt;/h2&gt;

&lt;p&gt;Git stores history backwards because developers almost exclusively query and checkout recent code rather than historical commits. By keeping the latest version of a file whole and storing past commits as reverse deltas, Git eliminates the need to apply historical patches when checking out current branches.&lt;/p&gt;

&lt;p&gt;To understand why this works so well, consider how source code naturally evolves. Over time, our files mostly grow as we add features and handle new edge cases.&lt;/p&gt;

&lt;p&gt;Let's say I have an 80-kilobyte file in my repository. I add a single line of logging, save, and commit.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Repository Stage&lt;/th&gt;
&lt;th&gt;File Revision&lt;/th&gt;
&lt;th&gt;Storage Format&lt;/th&gt;
&lt;th&gt;Disk Representation&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Initial Commit&lt;/td&gt;
&lt;td&gt;Revision 1&lt;/td&gt;
&lt;td&gt;Loose Object&lt;/td&gt;
&lt;td&gt;Full ~80 KB file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Subsequent Commit&lt;/td&gt;
&lt;td&gt;Revision 2 (Added 1 line)&lt;/td&gt;
&lt;td&gt;Loose Object&lt;/td&gt;
&lt;td&gt;Full ~80.01 KB file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post-&lt;code&gt;git gc&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Revision 2 (Newest)&lt;/td&gt;
&lt;td&gt;Base Packfile Object&lt;/td&gt;
&lt;td&gt;Full ~80.01 KB file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Post-&lt;code&gt;git gc&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Revision 1 (Oldest)&lt;/td&gt;
&lt;td&gt;Reverse Delta Object&lt;/td&gt;
&lt;td&gt;~11 Byte patch&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;When Git garbage collects these two commits, it compares both versions. Because Revision 2 is slightly larger than Revision 1, Git sorts by size and keeps Revision 2 completely intact. It then turns Revision 1 into an 11-byte reverse delta that describes how to roll back Revision 2 to reproduce Revision 1.&lt;/p&gt;

&lt;p&gt;If I delete code instead, Revision 1 ends up larger than Revision 2. Git still builds the smaller file out of the larger one, choosing whichever base object minimizes total packfile size.&lt;/p&gt;
&lt;h2&gt;
  
  
  How does Git choose which file version to keep whole?
&lt;/h2&gt;

&lt;p&gt;Git sorts candidate files by size and type, placing the largest file first as the base object. Since files typically grow over time, the newest revision is usually the largest, making it the base while older, smaller commits become reverse deltas.&lt;/p&gt;

&lt;p&gt;Whenever I want to see this mechanism in action on a repository, I use Git's plumbing tools to inspect the packfile index directly:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git verify-pack &lt;span class="nt"&gt;-v&lt;/span&gt; .git/objects/pack/pack-&lt;span class="k"&gt;*&lt;/span&gt;.idx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command lists object SHA-1 hashes, uncompressed sizes, and delta chains. When I review this output, current &lt;code&gt;HEAD&lt;/code&gt; blobs appear as standalone objects, while older commits link back to base blobs through explicit delta offsets.&lt;/p&gt;

&lt;p&gt;Because checking out today's code only requires reading whole base files, my dev workflow stays fast. It is last year's code that gets rebuilt from reverse deltas—and I almost never need to build last year's code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  What happens to Git packfile storage when you delete code?
&lt;/h3&gt;

&lt;p&gt;When you delete code, the newer file version becomes smaller than the older version. Because Git prefers using larger files as base objects to construct smaller deltas, Git may store the older revision as the base object and the newer revision as a delta if it saves disk space.&lt;/p&gt;

&lt;h3&gt;
  
  
  When does Git convert loose objects into delta packfiles?
&lt;/h3&gt;

&lt;p&gt;Git automatically converts loose objects into packed deltas during routine maintenance routines like &lt;code&gt;git gc&lt;/code&gt;, during automatic auto-packing triggers, or when transferring objects over the network during &lt;code&gt;git push&lt;/code&gt; or &lt;code&gt;git fetch&lt;/code&gt; operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why is &lt;code&gt;git gc&lt;/code&gt; necessary for Git performance?
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;git gc&lt;/code&gt; cleans up unreachable commits, consolidates loose object files into packed archives, and calculates optimal delta compression chains. Without garbage collection, repositories grow bloated with redundant full-file objects, slowing down checkout and diff operations.&lt;/p&gt;

</description>
      <category>git</category>
      <category>softwareengineering</category>
      <category>computerscience</category>
      <category>programming</category>
    </item>
    <item>
      <title>Why setTimeout Isn't in ECMAScript &amp; How to Fix TS Types</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Wed, 21 Oct 2026 11:46:09 +0000</pubDate>
      <link>https://dev.to/doogal/why-settimeout-isnt-in-ecmascript-how-to-fix-ts-types-1d0</link>
      <guid>https://dev.to/doogal/why-settimeout-isnt-in-ecmascript-how-to-fix-ts-types-1d0</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; &lt;code&gt;setTimeout&lt;/code&gt; is not defined in the ECMAScript (ECMA-262) language specification. It is a host-provided Web API implemented independently by environments like web browsers (via the HTML spec) and Node.js. Because host environments build their own timers, return types differ—a number in browsers versus a &lt;code&gt;Timeout&lt;/code&gt; object in Node.js—causing common TypeScript type mismatch errors.&lt;/p&gt;

&lt;p&gt;If you open up ECMA-262—the official specification for the JavaScript language—and search for &lt;code&gt;setTimeout&lt;/code&gt;, you will find exactly zero results. It feels a bit like discovering the engine in your car was made by a completely different company than the rest of the chassis. &lt;/p&gt;

&lt;p&gt;Most developers assume &lt;code&gt;setTimeout&lt;/code&gt; is a core language feature simply because it is available everywhere JavaScript runs. However, the core language spec intentionally stops at engine operations like memory management, execution contexts, and data types. Anything requiring an external clock or scheduling is delegated entirely to the host environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is &lt;code&gt;setTimeout&lt;/code&gt; missing from the ECMAScript spec?
&lt;/h2&gt;

&lt;p&gt;ECMAScript defines the syntax, grammar, and standard built-in objects of JavaScript, but it deliberately omits hardware-dependent capabilities like system timers. External features are left to the host environment—such as a browser or server runtime—to define and implement.&lt;/p&gt;

&lt;p&gt;Think of ECMAScript as the official rules of chess, while the browser or Node.js is the physical board and chess clock sitting on the table. The rules dictate how the pieces move, but they do not dictate how to manufacture the physical clock. When JavaScript needs to pause or schedule execution, it cannot handle that natively within the engine; it delegates the task to the host platform.&lt;/p&gt;

&lt;p&gt;In browser environments, &lt;code&gt;setTimeout&lt;/code&gt; is formally specified inside the WHATWG HTML specification. In Node.js, the core team built a custom timer implementation tightly coupled to its event loop architecture. While newer specs like ECMA-429 exist to establish a standardized checklist of Web APIs for server-side runtimes, it remains a runtime feature guide rather than part of the core language itself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does &lt;code&gt;setTimeout&lt;/code&gt; return an object in Node.js but a number in the browser?
&lt;/h2&gt;

&lt;p&gt;Because &lt;code&gt;setTimeout&lt;/code&gt; is created by host platforms rather than a central language specification, host implementers chose different return types to track timers. Browsers return a simple numeric timer ID, whereas Node.js returns an instance of a &lt;code&gt;Timeout&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;Because these implementations serve different operational needs, their signatures diverge significantly:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Host Environment&lt;/th&gt;
&lt;th&gt;Spec Source&lt;/th&gt;
&lt;th&gt;Return Type&lt;/th&gt;
&lt;th&gt;Purpose of Return Value&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Web Browser&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;WHATWG HTML Spec&lt;/td&gt;
&lt;td&gt;&lt;code&gt;number&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A unique integer ID used exclusively to clear the timer with &lt;code&gt;clearTimeout()&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Node.js&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Node.js Core Runtime&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Timeout&lt;/code&gt; object&lt;/td&gt;
&lt;td&gt;A reference object containing methods like &lt;code&gt;.ref()&lt;/code&gt; and &lt;code&gt;.unref()&lt;/code&gt; to control event loop behavior.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Server Runtimes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;ECMA-429 (WinterCG)&lt;/td&gt;
&lt;td&gt;Standardized Web API&lt;/td&gt;
&lt;td&gt;An effort to align server runtime APIs with web standard signatures.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How do you resolve TypeScript errors with &lt;code&gt;setTimeout&lt;/code&gt; return types?
&lt;/h2&gt;

&lt;p&gt;TypeScript errors occur when ambient type definitions from both DOM and Node environments overlap, causing type mismatches between &lt;code&gt;number&lt;/code&gt; and &lt;code&gt;NodeJS.Timeout&lt;/code&gt;. You can resolve this by explicitly typing the variable using TypeScript's &lt;code&gt;ReturnType&amp;lt;typeof setTimeout&amp;gt;&lt;/code&gt; utility or by referencing the host target directly.&lt;/p&gt;

&lt;p&gt;Imagine you are building a utility function meant to run both in the browser and inside a serverless Node worker. If you hardcode the return type as &lt;code&gt;number&lt;/code&gt;, Node's type definitions will trigger a compilation error.&lt;/p&gt;

&lt;p&gt;Here is how to write environment-agnostic timer references safely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Safe for cross-environment code (Browser + Node)&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;timerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;ReturnType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;setTimeout&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;timerId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Execution complete.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Explicitly targeting specific hosts when necessary:&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browserTimerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nodeTimerId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NodeJS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Timeout&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;globalThis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTimeout&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By leveraging &lt;code&gt;ReturnType&amp;lt;typeof setTimeout&amp;gt;&lt;/code&gt;, your type definitions dynamically adjust depending on whether the file is being evaluated in a browser or Node environment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Is &lt;code&gt;setInterval&lt;/code&gt; part of the JavaScript spec?
&lt;/h3&gt;

&lt;p&gt;No. Similar to &lt;code&gt;setTimeout&lt;/code&gt;, &lt;code&gt;setInterval&lt;/code&gt; is an environment-provided API defined in specifications like the WHATWG HTML standard for browsers, rather than the core ECMA-262 language specification.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is ECMA-429?
&lt;/h3&gt;

&lt;p&gt;ECMA-429 is a specification focused on common Web APIs for server-side JavaScript runtimes. It acts as an interoperability guide for non-browser environments like Node.js, Deno, and Bun, ensuring consistent API signatures across platforms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why does Node.js return a &lt;code&gt;Timeout&lt;/code&gt; object instead of an integer?
&lt;/h3&gt;

&lt;p&gt;Node.js returns an object so developers can interact directly with the Node event loop. Calling &lt;code&gt;.unref()&lt;/code&gt; on the returned &lt;code&gt;Timeout&lt;/code&gt; object allows the Node process to exit naturally even if the scheduled timer is still pending.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>typescript</category>
      <category>node</category>
      <category>webdev</category>
    </item>
    <item>
      <title>How Hashcash Works: Proof of Work Before Bitcoin</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Tue, 20 Oct 2026 13:56:23 +0000</pubDate>
      <link>https://dev.to/doogal/how-hashcash-works-proof-of-work-before-bitcoin-1pi5</link>
      <guid>https://dev.to/doogal/how-hashcash-works-proof-of-work-before-bitcoin-1pi5</guid>
      <description>&lt;p&gt;&lt;strong&gt;Quick Answer:&lt;/strong&gt; Hashcash, created by Adam Back in 1997, introduced Proof of Work to combat email spam by requiring senders to solve a computationally asymmetric puzzle. By forcing senders to compute a hash with leading zero bits, it imposed a tiny delay on single emails while rendering mass spam computationally infeasible—a principle Satoshi Nakamoto later adapted for Bitcoin consensus.&lt;/p&gt;

&lt;p&gt;When I look back at the foundational building blocks of modern decentralized systems, Hashcash is easily one of my favorite mechanisms. Back in 1997, email was rapidly becoming unusable due to aggressive, automated spam campaigns. The fix wasn't complex machine learning or centralized firewalls; it was a brilliantly simple application of asymmetric computational effort invented by Adam Back.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Hashcash and how did it fight email spam?
&lt;/h2&gt;

&lt;p&gt;Hashcash is a cryptographic Proof-of-Work system designed by Adam Back in 1997 to stop bulk email spam. It forces a sender's computer to calculate a hash header containing a specific number of leading zero bits before an email is accepted by the recipient's server.&lt;/p&gt;

&lt;p&gt;I find the core logic behind Hashcash remarkably elegant. Imagine you want to send a single email to a colleague. Under Hashcash, your mail client takes the recipient's address, the current date, and an incrementing counter, then hashes them together. Your client keeps bumping that counter and re-hashing until it hits a digest that starts with a pre-set number of zero bits—for instance, 20 zero bits.&lt;/p&gt;

&lt;p&gt;On standard hardware in the late 1990s, finding a hash satisfying that 20-bit zero requirement took roughly one million attempts, which translated to about one second of laptop CPU time. If you are sending ten legitimate emails a day, losing ten seconds of background processing is completely unnoticeable. But if you are a spammer trying to blast out one million emails, that one-second delay scales up to over 11 days of continuous computation. It fundamentally shifts the economic math of bulk spam.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Proof of Work computationally asymmetric?
&lt;/h2&gt;

&lt;p&gt;Proof of Work relies on computational asymmetry because generating a valid hash requires brute-forcing millions of candidate inputs, whereas verifying the proof requires running the hash function just once. This imbalance makes forging proofs extremely expensive for senders while keeping verification virtually free for recipients.&lt;/p&gt;

&lt;p&gt;The genius of Hashcash lies entirely in this structural asymmetry. Because cryptographic hash functions are unpredictable, there is no mathematical shortcut to finding a hash with leading zero bits; your CPU has to guess repeatedly until it hits a valid output. The recipient's server, however, simply reads the counter from your email header, executes the hash function once, and verifies the zero bits in a fraction of a millisecond.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operational Metric&lt;/th&gt;
&lt;th&gt;Header Generation (Sender)&lt;/th&gt;
&lt;th&gt;Header Verification (Recipient)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Primary Mechanism&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Brute-force counter incrementing&lt;/td&gt;
&lt;td&gt;Single hash execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Average Hash Operations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1,000,000 attempts&lt;/td&gt;
&lt;td&gt;Exactly 1 attempt&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time Cost (1 Email)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~1 second&lt;/td&gt;
&lt;td&gt;&amp;lt;1 millisecond&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Time Cost (1,000,000 Emails)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;~11.5 days&lt;/td&gt;
&lt;td&gt;~1,000 seconds total across all receivers&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How did Satoshi Nakamoto use Hashcash in Bitcoin?
&lt;/h2&gt;

&lt;p&gt;Satoshi Nakamoto adapted Hashcash's Proof-of-Work algorithm as the core consensus mechanism for Bitcoin to secure transaction blocks without requiring a central authority. In Bitcoin, miners solve a Hashcash-style puzzle over block data to earn block rewards and prevent history rewriting.&lt;/p&gt;

&lt;p&gt;When I read the original Bitcoin whitepaper, Hashcash is one of the few explicitly named influences. Nakamoto realized that Adam Back's "slow to produce, instant to check" paradigm could solve the double-spending problem in peer-to-peer networks. Instead of stamping individual email headers to prove CPU effort, Bitcoin miners hash block headers alongside a nonce to find a digest lower than the network's dynamic difficulty target.&lt;/p&gt;

&lt;h2&gt;
  
  
  Frequently Asked Questions
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Who invented Hashcash?
&lt;/h3&gt;

&lt;p&gt;Adam Back invented Hashcash in 1997 as a mechanism to curb bulk email spam and denial-of-service attacks.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is the primary difference between Hashcash and Bitcoin mining?
&lt;/h3&gt;

&lt;p&gt;Hashcash applies Proof of Work on an individual email header to rate-limit senders, whereas Bitcoin uses a dynamic-difficulty Proof of Work across a decentralized network to order transaction blocks and achieve global consensus.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why didn't Hashcash solve email spam permanently?
&lt;/h3&gt;

&lt;p&gt;Hashcash wasn't universally adopted for email because spammers began leveraging compromised botnets with massive aggregate CPU power, while mail providers shifted toward server-side domain verification protocols like SPF, DKIM, and modern filtering algorithms.&lt;/p&gt;

</description>
      <category>cryptography</category>
      <category>proofofwork</category>
      <category>bitcoin</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>The $460M Feature Flag: Knight Capital Software Disaster</title>
      <dc:creator>Doogal Simpson</dc:creator>
      <pubDate>Sun, 18 Oct 2026 11:16:25 +0000</pubDate>
      <link>https://dev.to/doogal/the-460m-feature-flag-knight-capital-software-disaster-63g</link>
      <guid>https://dev.to/doogal/the-460m-feature-flag-knight-capital-software-disaster-63g</guid>
      <description>&lt;p&gt;&lt;strong&gt;In 2012, Knight Capital lost $460 million in just 45 minutes due to an incomplete software deployment and dead code execution. Reusing an old feature flag across eight servers allowed an unverified legacy routine to execute 4 million unauthorized trades. Strict deployment verification and aggressive dead code deletion are essential safeguards for modern production systems.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I study classic software engineering disasters, the 2012 Knight Capital collapse stands out as the ultimate cautionary tale. Imagine pushing an update to production, stepping away for coffee, and returning to discover your organization has lost half a billion dollars. That nightmare bankrupted Knight Capital in under an hour, not because of a zero-day exploit, but because of an incomplete deployment and a single repurposed configuration flag.&lt;/p&gt;

&lt;h2&gt;
  
  
  What caused the 2012 Knight Capital software disaster?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Knight Capital's disaster occurred because new code was deployed to only seven of their eight trading servers, leaving the eighth running legacy software. When the market opened, the un-updated server received traffic intended for the new feature and executed an untested 2003 routine called PowerPeg.&lt;/strong&gt; This dead code lacked order safety checks, triggering four million automated trades in under an hour.&lt;/p&gt;

&lt;p&gt;Whenever I break down this incident for fellow engineers, I emphasize the setup of the infrastructure. Knight Capital relied on an automated trade router called Autorouter, running across a cluster of eight servers. During a routine rollout, an engineer manually deployed updated code to seven servers but overlooked the eighth. Crucially, no automated system verified whether every node in the cluster was running identical builds.&lt;/p&gt;

&lt;p&gt;When trading opened that morning, the seven updated servers processed incoming orders correctly. However, the eighth server received the same flag signal and interpreted it using 2003 logic. Back in 2005, engineers had removed the order-tracking limit counter from PowerPeg. Because nobody expected that code to run again, it was never retested. The eighth server entered an unbounded execution loop, turning 212 order requests into four million uncontrolled trades.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is reusing feature flags dangerous in production systems?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reusing feature flags creates ambiguity in configuration management, enabling dormant or legacy code paths to execute unintentionally. If a recycled flag triggers legacy behavior on missing or un-refactored nodes, it exposes systems to unmonitored state changes.&lt;/strong&gt; In my view, feature flag keys must always be unique, short-lived, and paired with immediate code removal once retired.&lt;/p&gt;

&lt;p&gt;I like to think of a feature flag as a light switch in a house. If I decide to repurpose an old hallway switch to control a new kitchen light, but leave the original wire connected to a defunct attic heater, flipping that switch might light the kitchen while quietly overheating the roof.&lt;/p&gt;

&lt;p&gt;Knight Capital repurposed the flag name PowerPeg without deleting the underlying legacy execution logic from their codebase. Because the dead code remained in the binary—after all, nobody expects to execute retired features—the eighth server ran it with full authority.&lt;/p&gt;

&lt;h3&gt;
  
  
  Anatomy of the Knight Capital System Failure
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Failure Vector&lt;/th&gt;
&lt;th&gt;Engineering Flaw&lt;/th&gt;
&lt;th&gt;Operational Impact&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Flag Hygiene&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reused an inactive flag name from 2003 for a 2012 release&lt;/td&gt;
&lt;td&gt;Created conflicting behavior between software versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deployment Pipeline&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manual deployment across 8 nodes; server #8 was missed&lt;/td&gt;
&lt;td&gt;Production cluster ran divergent, mismatched code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Cleanup&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Legacy tracking safety checks were removed in 2005&lt;/td&gt;
&lt;td&gt;Server #8 executed trades without volume bounds&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;System Guardrails&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Lack of automated deployment checks and live kill switches&lt;/td&gt;
&lt;td&gt;$460M loss in 45 minutes; forced merger 11 months later&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How can engineering teams prevent partial deployments and dead code bugs?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Teams can prevent these failures by using automated immutable deployments, continuous dead code removal, and strict feature flag lifecycles. Automating post-deployment health checks ensures that all infrastructure nodes run synchronized software versions before handling user traffic.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When I design deployment strategies today, I treat manual rollout steps as immediate operational risks. Suppose a team manages microservices infrastructure using continuous deployment. If engineers rely on manual updates or mutable server configurations, drift is inevitable. Enforcing automated CI/CD pipelines—where infrastructure is replaced rather than updated in place—guarantees that partial deployments cannot silently serve production traffic.&lt;/p&gt;

&lt;p&gt;Furthermore, I advocate treating feature flags as temporary technical debt. Once a feature completes its rollout, I always recommend opening a cleanup task to delete the flag configuration and strip the dormant conditional logic entirely from the codebase.&lt;/p&gt;

&lt;h3&gt;
  
  
  Frequently Asked Questions
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;How many trades did Knight Capital execute during the deployment failure?&lt;/strong&gt;&lt;br&gt;
Knight Capital executed approximately 4 million unintended trades from 212 parent order requests over a 45-minute window, resulting in an average loss of over $10 million per minute.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was the PowerPeg feature in Knight Capital's trading system?&lt;/strong&gt;&lt;br&gt;
PowerPeg was a legacy feature module created in 2003 for Knight Capital's Autorouter system. Its order safety counter was removed in 2005, making the code extremely dangerous when accidentally triggered during the 2012 deployment.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why does dead code pose a risk in software engineering?&lt;/strong&gt;&lt;br&gt;
Dead code inflates the attack and failure surface of an application. If dormant code paths remain inside production builds, accidental configuration changes or unexpected input can trigger untested logic, causing operational outages or security vulnerabilities.&lt;/p&gt;

</description>
      <category>softwareengineering</category>
      <category>devops</category>
      <category>cicd</category>
      <category>systemarchitecture</category>
    </item>
  </channel>
</rss>
