<?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: Zubair Maqsood</title>
    <description>The latest articles on DEV Community by Zubair Maqsood (@zubairmaqsood866).</description>
    <link>https://dev.to/zubairmaqsood866</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2648859%2F11a5c318-3ee7-4a36-83c0-be70d06f4c0d.jpeg</url>
      <title>DEV Community: Zubair Maqsood</title>
      <link>https://dev.to/zubairmaqsood866</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/zubairmaqsood866"/>
    <language>en</language>
    <item>
      <title>The Cracked Engineer - A Journey to Low-Latency Principles</title>
      <dc:creator>Zubair Maqsood</dc:creator>
      <pubDate>Mon, 24 Feb 2025 20:18:22 +0000</pubDate>
      <link>https://dev.to/zubairmaqsood866/the-cracked-engineer-a-journey-to-low-latency-principles-1jnc</link>
      <guid>https://dev.to/zubairmaqsood866/the-cracked-engineer-a-journey-to-low-latency-principles-1jnc</guid>
      <description>&lt;p&gt;&lt;em&gt;For the past couple of months, I have been studying High-Frequency Trading (HFT) systems, how they work and how they are architected. For complete disclosure, I do not have any Quant/HFT experience [even though I'd love to!], I am merely studying its principles on how to scale with ultra-low latency and how these principles can be applied to the Cloud. I'm also using this as a learning opportunity to develop my own simplified version of a &lt;a href="https://github.com/GH05T-97/hft-engine" rel="noopener noreferrer"&gt;HFT Engine&lt;/a&gt;. You can click the link to follow my developments, or if you want fork and clone and make your own adjustments.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9d9cdj26lpl5gev5mn60.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9d9cdj26lpl5gev5mn60.png" alt="Market_chart" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Microsecond Mindset: Why Latency Matters Beyond HFT
&lt;/h2&gt;

&lt;p&gt;Latency matters because of one core reason. User's do not want to wait around for an indefinite amount of time for an application or service to run. In fact research has shown&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;For every second delay in mobile page load, conversions can fall by up to 20%. &lt;br&gt;
&lt;a href="https://www.thinkwithgoogle.com/marketing-strategies/app-and-mobile/mobile-page-speed-conversion-data/#:~:text=For%20every%20second%20delay%20in,fall%20by%20up%20to%2020%25." rel="noopener noreferrer"&gt;Source&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now that's for your regular mobile/SaaS application that delivers on scale, that doesn't even compare on what HFT Engineer's have to deal with! But that doesn't mean it's not important, mastering the principles of latency is a craft that affects business performance overall. When it comes to HFT, trading firms are looking to capitalise on market inefficiencies in the most competitive way as possible, so they have adopted certain techniques to squeeze as much performance from their tech stack, as much as possible. In microseconds, their complex algorithms should spare no CPU waste on capitalising on a news reports, such as the US Federal Reserve or the Bank of England raising or decreasing interest rates as soon as the event is announced.&lt;/p&gt;

&lt;p&gt;While a SaaS application might aim for 100ms response times, HFT systems operate in the microsecond range—that's 1000× faster!&lt;/p&gt;

&lt;p&gt;Now, as regular Software Engineer's we may not need to go to the extreme end like our genius counterparts in high finance, but the principles is what can be transferred into our work-life to become more productive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding CPU Cache Hierarchies
&lt;/h2&gt;

&lt;p&gt;In the context of HFT, I mentioned before that it is a very compute heavy task as you're dealing with microseconds worth of transactions/operations that are ultimately responsible of moving billions worth of dollars that move markets. So, as a result, you'd naturally think the computers these professionals operate on are much more powerful than your average tech bro that works in Shoreditch and is too busy debugging a React app hosted on Vercel.&lt;/p&gt;

&lt;p&gt;Modern CPU's are fast, if they weren't fast then a lot of companies would go bankrupt. However, there is a bottleneck, memory access. A CPU can execute an instruction in 1 nanosecond, but accessing memory from the RAM can take almost 100 nanoseconds. In the world of HFT, quite literally, not just one second, but every nanosecond counts!&lt;/p&gt;

&lt;p&gt;Here's where an understanding of cache heirarchy is crucial to combat this.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjd1kfcbwf6r0ylzfprj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwjd1kfcbwf6r0ylzfprj.png" alt="cache_coherency" width="686" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are 3 levels of cache memory&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;L1 : smallest + fastest - stores data the processor is working on&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;L2: data that the processor may need to access soon&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;L3: data that is less likely to be needed by the processor&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The time it takes for a CPU to access L1 is 1 to 2 nanoseconds, for L2 its 5 to 10 nanoseconds and for L3 its 10-20 nanoseconds. Quite literally, every nanosecond counts in the world of HFT!&lt;/p&gt;

&lt;p&gt;Why am I telling you this? Because with this knowledge, HFT devs can optimise their code to ensure data is stored in L1/L2 caches to gain a competitive edge.&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory Ordering and Atomics
&lt;/h2&gt;

&lt;p&gt;Understanding memory ordering and atomics fundamental to low-latency principles.&lt;/p&gt;

&lt;p&gt;Atomic Operations the main building blocks for anything that involves  multiple threads. In Rust, atomic operations are available on the standard atomic types that live in the &lt;code&gt;std::sync::atomic&lt;/code&gt; module.&lt;/p&gt;

&lt;p&gt;When multiple threads need to modify a variable, atomics make sure modifications happen in a defined order without data races. These atomics are implemented using CPU-specific instructions that ensure thread safety, memory access with various memory ordering guarantees.&lt;/p&gt;

&lt;p&gt;There are many Atomics, and each of them have unique use cases in the world of HFT&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;AtomicBool&lt;/code&gt; - use cases are circuit breakers, emergency stop&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AtomicU64/AtomicI64&lt;/code&gt; - use cases are position tracking, price update&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;AtomicUSize&lt;/code&gt; - use cases are message counters, queue indices, resource tracking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these atomics have similar methods attached to them, such as &lt;code&gt;load()&lt;/code&gt; which atomically reads the value, or &lt;code&gt;store()&lt;/code&gt; which atomically writes a value.&lt;/p&gt;

&lt;p&gt;Memory Ordering determines how atomic operations are synchronised between threads. In Rust, these can be used from the Ordering Enum from the &lt;code&gt;std::sync::atomic&lt;/code&gt; module&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pub enum Ordering {&lt;br&gt;
    Relaxed,&lt;br&gt;
    Release,&lt;br&gt;
    Acquire,&lt;br&gt;
    AcqRel,&lt;br&gt;
    SeqCst,&lt;br&gt;
}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Each of these memory orderings have different guarantees. What do I mean by guarantees? &lt;/p&gt;

&lt;p&gt;Memory ordering guarantees control how operations on atomic variables become visible to other threads. Let's break down each ordering option:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Relaxed&lt;/strong&gt;: The weakest ordering - only guarantees that the operation itself is atomic. There are no synchronization guarantees between threads. This is the fastest option but provides minimal safety.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Acquire&lt;/strong&gt;: Used for loads (reading). Ensures that subsequent operations in the same thread cannot be reordered before this load. Essentially saying "any operations after this read must happen after the read."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Release&lt;/strong&gt;: Used for stores (writing). Ensures that preceding operations in the same thread cannot be reordered after this store. Tells other threads "any operations before this write must be visible before the write becomes visible."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;AcqRel&lt;/strong&gt;: Combines Acquire and Release semantics. Used for operations that both read and modify, like compare-and-swap. Provides bidirectional ordering guarantees.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SeqCst&lt;/strong&gt;: The strongest guarantee - Sequential Consistency. Ensures a total ordering of operations across all threads. It's the most intuitive but also the most expensive.&lt;/p&gt;

&lt;p&gt;To summarise, atomics are the building blocks, memory ordering determines the atomics "behaviour"&lt;/p&gt;

&lt;h2&gt;
  
  
  Lock-Free Programming: The Art of Coordination Without Waiting
&lt;/h2&gt;

&lt;p&gt;Lock-Free Programming is a paradigm that allows multiple threads to operate on shared data without traditional "locking mechanisms" like &lt;a href="https://doc.rust-lang.org/std/sync/struct.Mutex.html" rel="noopener noreferrer"&gt;mutexes&lt;/a&gt;. Lock-Free Programming utilises Atomics as building blocks and also careful synchronisation techniques.&lt;/p&gt;

&lt;p&gt;If you want to figure out if your program is "Lock-Free", then look at this code below&lt;/p&gt;

&lt;p&gt;&lt;code&gt;const isLockFree = null;&lt;/code&gt;&lt;br&gt;
&lt;code&gt;if ("Are you programming with multiple threads?") {&lt;/code&gt;&lt;br&gt;
&lt;code&gt;if("Do the threads access shared memory"){&lt;/code&gt;&lt;br&gt;
&lt;code&gt;if("Can the threads operate WITHOUT blocking each other?"){&lt;/code&gt;&lt;br&gt;
&lt;code&gt;isLockFree = true&lt;/code&gt;&lt;br&gt;
&lt;code&gt;}&lt;/code&gt;&lt;br&gt;
&lt;code&gt;}&lt;/code&gt;&lt;br&gt;
&lt;code&gt;}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These are the characteristics of a lock-free program&lt;/p&gt;

&lt;p&gt;1) Non-blocking - at least one thread makes progress even if the others fail&lt;br&gt;
2) Atomicity - operations appear indivisible and are executed without interference from other threads&lt;br&gt;
3) Progress guarantees - ensures the system as a whole makes progress&lt;/p&gt;

&lt;p&gt;Here is an example of a lock-free implementation of a Queue data-structure&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmue868nu1h6h5bs2z6uv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmue868nu1h6h5bs2z6uv.png" alt="lock-free-queue-struct" width="800" height="665"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi765i9qodqvh1eh0ou97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi765i9qodqvh1eh0ou97.png" alt="lock-free-queue-enqueue" width="800" height="729"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kgcmcewxstm9lqh2e1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7kgcmcewxstm9lqh2e1y.png" alt="lock-free-queue-dequeue" width="800" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why use them? Well for the same reason we're discussing this topic in depth, for low latency computations. If you want to be specific, here are the reasons&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;Elimination of Context Switching&lt;/strong&gt; - When threads are blocked on locks, the operating system has to perform a context switch to another thread.&lt;br&gt;
2) &lt;strong&gt;Resilience against thread failures&lt;/strong&gt; - if one thread stalls or fails, other threads continue&lt;br&gt;
3) &lt;strong&gt;Cache efficiency&lt;/strong&gt; - Lock-free data structures and algorithms can be designed to minimise cache-line sharing&lt;/p&gt;

&lt;h2&gt;
  
  
  Multithreading Patterns for Performance
&lt;/h2&gt;

&lt;p&gt;When low-latency is a concern, it isn't just about spinning up multiple threads/processes and hoping for the best, we also have to take in considerations how these threads will need to interact with each other. The right pattern makes a big difference between a system that breaks down when theres traffic in coming and a maintains consistent low latency.&lt;/p&gt;

&lt;p&gt;Lets go over some multithreading patterns to go over what exactly this entails&lt;/p&gt;

&lt;h3&gt;
  
  
  Readers-Writer Pattern
&lt;/h3&gt;

&lt;p&gt;This pattern optimises for scenarios where reads are much more frequent than writes - in the context of HFT systems, it's perfect for order book data where many strategies need to read the current state, but updates happen less frequently.&lt;/p&gt;

&lt;p&gt;Imagine a popular blog post that thousands of people are reading, but occasionally an editor needs to update it. You wouldn't want to lock out all readers while the editor works!&lt;/p&gt;

&lt;p&gt;This pattern is like having a special system where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Multiple readers can view the content simultaneously (like users browsing your website). When an editor needs to make changes, they get exclusive access briefly. Once editing is done, all readers immediately see the new version&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Producer-Consumer Pattern
&lt;/h3&gt;

&lt;p&gt;This is a fundamental pattern for building pipeline-style architectures that minimise latency while maintaining clean separation of concerns.&lt;br&gt;
Using channels &lt;code&gt;(mpsc::channel)&lt;/code&gt; provides:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Lock-free communication between components&lt;/li&gt;
&lt;li&gt;Automatic back-pressure handling&lt;/li&gt;
&lt;li&gt;Clean decoupling between data producers and consumer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Think of this like the relationship between your API backend and frontend. Your backend produces data that your frontend consumes, but they work at different speeds.&lt;br&gt;
This pattern creates a buffer between components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Producers (like your backend) can generate data at their own pace&lt;/li&gt;
&lt;li&gt;Consumers (like your frontend) process it when they're ready&lt;/li&gt;
&lt;li&gt;A channel between them handles timing differences&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Shared Ownership Pattern
&lt;/h3&gt;

&lt;p&gt;Using atomic reference counting &lt;code&gt;(Arc&amp;lt;T&amp;gt;)&lt;/code&gt; is crucial for safe concurrent access to shared resources without the overhead of locks.&lt;br&gt;
This pattern:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Allows safe sharing of read-only or thread-safe data across components&lt;/li&gt;
&lt;li&gt;Eliminates expensive deep copies of data&lt;/li&gt;
&lt;li&gt;Ensures resources are properly cleaned up when no longer needed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you've used React's context API or Redux, you're familiar with the concept of shared state. This pattern is similar but for memory management.&lt;br&gt;
Instead of copying data between components:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Multiple threads share references to the same data&lt;/li&gt;
&lt;li&gt;The system tracks how many threads are using the data&lt;/li&gt;
&lt;li&gt;When no one needs it anymore, it's automatically cleaned up&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This dramatically reduces memory overhead and improves performance by eliminating expensive copies while maintaining safety.&lt;/p&gt;

&lt;h2&gt;
  
  
  NUMA-Aware Application Design
&lt;/h2&gt;

&lt;p&gt;NUMA (Non-Uniform Memory Access) architecture is a fundamental concept when designing systems that need to scale across multiple processors. Let me explain this in a way that builds on our previous discussions.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is NUMA Architecture?
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxx4y9wf63wmc16f5g52z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxx4y9wf63wmc16f5g52z.png" alt="numa-architecture" width="763" height="522"&gt;&lt;/a&gt;&lt;br&gt;
As we discussed earlier, in a NUMA system:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The computer has multiple CPUs (physical processors)&lt;/li&gt;
&lt;li&gt;Each CPU has its own memory controller and directly attached memory&lt;/li&gt;
&lt;li&gt;Each CPU also has its own L1/L2/L3 cache hierarchy&lt;/li&gt;
&lt;li&gt;Memory access speeds vary depending on whether a CPU is accessing its local memory or remote memory (belonging to another CPU)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This creates a critical performance consideration: accessing local memory might be 1.5-3x faster than accessing remote memory, depending on the system.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why NUMA Awareness Matters
&lt;/h3&gt;

&lt;p&gt;In a high-frequency trading system, these access differences can dramatically impact performance:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A thread accessing remote memory might experience 30-100ns additional latency per access. For applications making millions of memory accesses, this adds up quickly&lt;/li&gt;
&lt;li&gt;Memory-intensive operations might run twice as slow if data is placed inappropriately&lt;/li&gt;
&lt;li&gt;Cache coherency traffic across NUMA nodes creates additional overhead&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key NUMA-Aware Design Principles
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Thread and Memory Locality&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep threads and their data on the same NUMA node&lt;/li&gt;
&lt;li&gt;Minimise cross-node communication and data sharing&lt;/li&gt;
&lt;li&gt;Use thread affinity to bind threads to specific CPUs&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Memory Allocation Strategies&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allocate memory on the same node where the processing thread runs&lt;/li&gt;
&lt;li&gt;Use NUMA-specific allocation functions when available&lt;/li&gt;
&lt;li&gt;Consider first-touch policies (memory is allocated on the node of the thread that first writes to it)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Data Partitioning&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Divide work and data along NUMA boundaries&lt;/li&gt;
&lt;li&gt;Each node handles a specific subset of the workload&lt;/li&gt;
&lt;li&gt;Minimize shared data structures that cross NUMA boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NUMA-Aware Data Structures&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Design data structures that respect NUMA topology&lt;/li&gt;
&lt;li&gt;Consider node-local queues feeding into global coordination&lt;/li&gt;
&lt;li&gt;Avoid false sharing across NUMA boundaries&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Translating Low-Latency Principles to AWS
&lt;/h2&gt;

&lt;p&gt;Understanding hardware-level performance concepts doesn't just matter for HFT systems - they can significantly improve latency-sensitive applications in the cloud. Even though HFT developers work on their critical infrastructure on premises instead of the cloud, the principles that they operate on can be translated to the workflow of Backend and Cloud Engineers.&lt;/p&gt;

&lt;p&gt;Here's how to apply these principles when developing on AWS:&lt;/p&gt;

&lt;h3&gt;
  
  
  CPU Cache Optimisation in the Cloud
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Instance Selection:&lt;/strong&gt; Choose compute-optimised instances (c7g, c6g) with higher CPU-to-memory ratios for cache-sensitive workloads&lt;br&gt;
&lt;strong&gt;Data Locality:&lt;/strong&gt; Structure your applications to maintain data locality even in virtualised environments&lt;br&gt;
&lt;strong&gt;Workload Placement:&lt;/strong&gt; Run related services on the same instance to benefit from shared L3 cache&lt;br&gt;
&lt;strong&gt;Bare Metal Options:&lt;/strong&gt; For critical paths, consider AWS bare metal instances to eliminate hypervisor overhead and gain direct access to physical CPU caches&lt;/p&gt;

&lt;h3&gt;
  
  
  Memory Ordering and Atomics in Distributed Systems
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Single-Instance Performance:&lt;/strong&gt; Optimise your critical paths using the atomics and memory ordering techniques on high-CPU instances&lt;br&gt;
&lt;strong&gt;Service Boundaries:&lt;/strong&gt; Design service interfaces to minimise cross-process synchronisation needs&lt;br&gt;
&lt;strong&gt;Local Processing:&lt;/strong&gt; Process data where it's stored whenever possible to avoid network round-trips&lt;br&gt;
&lt;strong&gt;Consistent Instance Types:&lt;/strong&gt; Use the same instance family for services that need predictable memory behaviour&lt;/p&gt;

&lt;h3&gt;
  
  
  Lock-Free Programming in AWS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Serverless Coordination:&lt;/strong&gt; Use DynamoDB with optimistic concurrency control instead of traditional locks&lt;br&gt;
&lt;strong&gt;SQS FIFO Queues:&lt;/strong&gt; Leverage SQS FIFO queues for producer-consumer patterns with guaranteed ordering&lt;br&gt;
&lt;strong&gt;Lambda Concurrency:&lt;/strong&gt; Design Lambda functions to be truly independent, avoiding shared state that requires locking&lt;br&gt;
&lt;strong&gt;ElastiCache Redis:&lt;/strong&gt; Use Redis atomic operations for distributed coordination instead of application-level locks&lt;/p&gt;

&lt;h3&gt;
  
  
  Multithreading Patterns for AWS Services
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Readers-Writer Pattern:&lt;/strong&gt; Implement with DynamoDB's strongly consistent vs. eventually consistent reads&lt;br&gt;
&lt;strong&gt;Producer-Consumer:&lt;/strong&gt; Use SQS and SNS for decoupled, high-throughput communication&lt;br&gt;
&lt;strong&gt;Shared Ownership:&lt;/strong&gt; Leverage ElastiCache for shared state across distributed components&lt;/p&gt;

&lt;h3&gt;
  
  
  NUMA-Aware Design in AWS
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Placement Groups:&lt;/strong&gt; Use cluster placement groups to ensure instances run on the same underlying hardware with low-latency networking&lt;br&gt;
&lt;strong&gt;Enhanced Networking:&lt;/strong&gt; Enable ENA (Elastic Network Adapter) for improved latency between instances&lt;br&gt;
&lt;strong&gt;Topology Awareness:&lt;/strong&gt; Create availability zone-aware designs that keep related components physically close&lt;br&gt;
&lt;strong&gt;Instance Size Selection:&lt;/strong&gt; Choose appropriately sized instances - splitting a workload across multiple smaller instances might hit NUMA-like boundaries&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS-Specific Optimisations
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Direct VPC Endpoints:&lt;/strong&gt; Reduce latency by connecting directly to AWS services without traversing the public internet&lt;br&gt;
&lt;strong&gt;Enhanced Networking:&lt;/strong&gt; Use Elastic Network Adapter (ENA) and Elastic Fabric Adapter (EFA) for high-throughput, low-latency networking&lt;br&gt;
&lt;strong&gt;Nitro System:&lt;/strong&gt; Leverage AWS Nitro-based instances for hardware acceleration and reduced virtualisation overhead&lt;br&gt;
&lt;strong&gt;Graviton Processors:&lt;/strong&gt; Consider ARM-based Graviton instances which can offer better performance characteristics for certain workloads&lt;/p&gt;

&lt;p&gt;By applying these low-level performance principles to your AWS architecture, you can build systems that achieve consistent low latency even in virtualised cloud environments. The key is understanding that while the physical hardware may be abstracted, the principles of CPU caching, memory access patterns, and concurrent programming still directly impact your application's performance.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>aws</category>
      <category>programming</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Zubair Maqsood</dc:creator>
      <pubDate>Fri, 21 Feb 2025 11:48:14 +0000</pubDate>
      <link>https://dev.to/zubairmaqsood866/rust-5fn4</link>
      <guid>https://dev.to/zubairmaqsood866/rust-5fn4</guid>
      <description></description>
    </item>
    <item>
      <title>The Cracked Engineer: From JavaScript to Rust: The Engineer’s Guide to Systems Programming</title>
      <dc:creator>Zubair Maqsood</dc:creator>
      <pubDate>Wed, 12 Feb 2025 02:31:15 +0000</pubDate>
      <link>https://dev.to/zubairmaqsood866/the-cracked-engineer-moving-from-javascript-to-rust-the-basics-3ncl</link>
      <guid>https://dev.to/zubairmaqsood866/the-cracked-engineer-moving-from-javascript-to-rust-the-basics-3ncl</guid>
      <description>&lt;p&gt;&lt;em&gt;In my previous article, I dissected JavaScript's inner workings and mechanics. If you haven't read it yet, I recommend starting &lt;a href="https://dev.to/gho5t_97/the-cracked-engineers-guide-to-javascript-mechanics-magic-and-misconceptions-28jm"&gt;here&lt;/a&gt;. Today, we're diving into Rust, but first, let me explain why I chose it. Coming from a Cloud/Node.js background, I'm intimately familiar with interpreted languages. However, I felt something was missing – a deep understanding of compiled and systems programming languages. While C and C++ were options, their notorious compiler issues steered me away. Enter Rust: a modern systems language that's been gaining significant traction&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Art of Ownership: Memory Management Made Clear
&lt;/h2&gt;

&lt;p&gt;Rust uses the &lt;strong&gt;Ownership&lt;/strong&gt; model. Ownership is a set of rules that sets the show on how a Rust program manages memory. How that is done is with a set of rules that a compiler checks. If these rules are violated, the program itself will not compile. &lt;em&gt;TLDR: There are rules that the Rust compiler has, and if your code breaks them, it simply won't work&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;This &lt;em&gt;"strictness"&lt;/em&gt; allows Rust to have these attributes&lt;br&gt;
1) Control over memory&lt;br&gt;
2) To be Error free&lt;br&gt;
3) Faster runtimes&lt;br&gt;
4) Smaller program size&lt;/p&gt;

&lt;p&gt;At compile time, Rust decides if certain variables and results of functions should be put on 2 core data structures. &lt;strong&gt;The Stack&lt;/strong&gt; and &lt;strong&gt;the Heap&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I went over the Stack data structure briefly. To put it simply, it is a &lt;strong&gt;LIFO (Last in, First Out)&lt;/strong&gt; data structure. The Heap, is something different entirely. It is a tree-like data structure (like a binary search tree) but with a twist. Unlike the binary search tree, it there is no implied ordering between siblings and no implied sequence for in-order traversal.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhoyzhft9z9wkoiqqjagf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhoyzhft9z9wkoiqqjagf.jpg" alt="Binary Search Tree vs Heap" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So at compile time, Rust decides if variables should be pushed onto the stack or heap. For something to be pushed onto the stack, values must have a known size at compile time and/or have a fixed size type &lt;em&gt;(primitives such a bool or char are examples of this)&lt;/em&gt;, where as for something to be pushed onto the heap, it needs to be dynamically sized, usually that means large data, perhaps a batch of JSON returned from an API call that needs to be stored, or it could be a String that can be altered dynamically &lt;em&gt;(we can do this easily using the String::(new) function)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Borrowing: Share Nicely or Don't Share at All
&lt;/h2&gt;

&lt;p&gt;Rust uses a borrowing mechanism to access data without direct ownership of it. Instead of passing objects by value like JavaScript does, objects can be passed by reference by using the &lt;strong&gt;&amp;amp;&lt;/strong&gt; operator&lt;/p&gt;

&lt;p&gt;&lt;code&gt;let message = String::from("Hello");  // message owns this string&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let greeting = message;  // ownership moves to greeting&lt;/code&gt;&lt;br&gt;
&lt;code&gt;// println!("{}", message);  // ❌ Error: message has been moved&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// This is different from JavaScript where:&lt;/code&gt;&lt;br&gt;
&lt;code&gt;// let message = "Hello";&lt;/code&gt;&lt;br&gt;
&lt;code&gt;// let greeting = message;  // Creates a copy/reference&lt;/code&gt;&lt;br&gt;
&lt;code&gt;// console.log(message);    // ✅ Works fine in JS&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// Now let's look at borrowing&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let original = String::from("Hello");&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let borrowed = &amp;amp;original;  // Immutable borrow with &amp;amp;&lt;/code&gt;&lt;br&gt;
&lt;code&gt;println!("{}", borrowed);  // Prints: Hello&lt;/code&gt;&lt;br&gt;
&lt;code&gt;println!("{}", original);  // ✅ Still works! original maintains ownership&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In the example above, Rust initialises the message variable and at the first line, the message variable owns the value String::from("Hello) which is stored on the Heap. Essentially the Heap has a pointer telling the compiler where this value is located. In the second line, the ownership is transferred from the variable message to a new variable called greeting. In the third line, we have an error that signifies the mechanism of ownership, the message variable now does not have access to the string. Contrast this to JavaScript, it works fine because well, there is no ownership mechanism in JavaScript, so the variable greeting merely copies the value from message.&lt;/p&gt;

&lt;p&gt;In the 3rd example, if we wanted to log out both the original and borrowed variables successfully and for same logging, we would need to use the &amp;amp; operator. This does not transfer ownership of the value defined originally, but merely allows the borrowed variable to do exactly what the borrowing mechanism is, &lt;em&gt;to borrow the value&lt;/em&gt;. The original variable still owns the string however!&lt;/p&gt;

&lt;p&gt;Think of this concept as a pencil, you are the owner of it, your friend needs a pencil for something so you let him borrow it, but it is still yours.&lt;/p&gt;

&lt;p&gt;There is one crucial aspect however, using the &amp;amp; operator creates an immutable reference, meaning the variable thats doing the borrowing, cannot mutate the original value. So how can we mutate the value by using the borrower?&lt;/p&gt;

&lt;p&gt;We use the &lt;strong&gt;mut&lt;/strong&gt; keyword&lt;/p&gt;

&lt;p&gt;&lt;code&gt;let mut value = String::from("Hello");&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let mut_borrow = &amp;amp;mut value;  // Mutable borrow with &amp;amp;mut&lt;/code&gt;&lt;br&gt;
&lt;code&gt;mut_borrow.push_str(" World");  // Modify through mutable reference&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;By using the mut keyword, we are essentially allowing the borrower to be able to mutate the original value. So now, if we wanted to log out value, it would output "Hello World"&lt;/p&gt;

&lt;h2&gt;
  
  
  Thread Safety Without the Headaches
&lt;/h2&gt;

&lt;p&gt;In multithreaded languages like Java or C++, the execution of a code is called a process or a thread, in highly performant systems like a trading engine of an FX exchange, you can have multiple threads working at the same time. This multithreading approach can help performance but it can lead to problems such as &lt;strong&gt;Race conditions or Deadlocks&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;But before that we need to become familiar with some modules from commonly used crates in the Rust ecosystem to build thread safe programs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Arc from the &lt;code&gt;std&lt;/code&gt; crate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Arc stands for Atomic Reference Counting. &lt;/p&gt;

&lt;p&gt;&lt;code&gt;use std::sync::Arc;&lt;/code&gt;&lt;br&gt;
&lt;code&gt;// Arc enables multiple ownership of the same data across threads&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let data = Arc::new(vec![1, 2, 3]);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// We can clone the Arc to share ownership&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let data_clone = Arc::clone(&amp;amp;data);  // This is thread-safe&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// Both data and data_clone point to the same memory&lt;/code&gt;&lt;br&gt;
&lt;code&gt;// Arc keeps track of how many references exist&lt;/code&gt;&lt;br&gt;
&lt;code&gt;// Memory is freed when the last Arc is dropped&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Think of Arc as a smart pointer that keeps track of everything thats referencing the data. Its inherent feature is that it allows read only access to multiple threads.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Mutex from the &lt;code&gt;std&lt;/code&gt; crate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mutex stands for mutual exclusion. Its inherent feature that it allows only one thread to access some data at any given time. For a thread to access data in a mutex, it needs the mutex's lock. A lock is a data structure that is part of the mutex that keeps track of who currently has exclusive access to the data. Only one thread access the lock at a time. This prevents data races.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;use std::sync::Mutex;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// Mutex wraps data to ensure only one thread can access it at a time&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let counter = Mutex::new(0);&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// To access data, you must lock() the Mutex&lt;/code&gt;&lt;br&gt;
&lt;code&gt;{&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let mut num = counter.lock().unwrap();&lt;/code&gt;&lt;br&gt;
&lt;code&gt;*num += 1;&lt;/code&gt;&lt;br&gt;
&lt;code&gt;}  // Lock is automatically released here&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// Trying to access while locked blocks the thread until lock is released&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) Channels by using the mspc module from the &lt;code&gt;std&lt;/code&gt; crate&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another way of ensuring thread safety is using channels, where threads can communicate with each other by sending each other messages.&lt;/p&gt;

&lt;p&gt;The best analogy is what I got from the official &lt;a href="https://doc.rust-lang.org/book/ch16-02-message-passing.html" rel="noopener noreferrer"&gt;Rust documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can imagine a channel in programming as being like a directional channel of water, such as a stream or a river. If you put something like a rubber duck into a river, it will travel downstream to the end of the waterway.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A channel has two halves: a transmitter and a receiver. The transmitter half is the upstream location where you put rubber ducks into the river, and the receiver half is where the rubber duck ends up downstream. One part of your code calls methods on the transmitter with the data you want to send, and another part checks the receiving end for arriving messages. A channel is said to be closed if either the transmitter or receiver half is dropped.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;use std::sync::mpsc;  // mpsc = Multi-Producer, Single-Consumer&lt;/code&gt;&lt;br&gt;
&lt;code&gt;use std::thread;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fn main() {&lt;/code&gt;&lt;br&gt;
&lt;code&gt;// Create a channel with sender and receiver&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let (sender, receiver) = mpsc::channel();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// Clone sender for multiple producers&lt;/code&gt;&lt;br&gt;
&lt;code&gt;let sender_clone = sender.clone();&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// Producer thread 1&lt;/code&gt;&lt;br&gt;
&lt;code&gt;thread::spawn(move || {&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sender.send("Hello from thread 1").unwrap();&lt;/code&gt;&lt;br&gt;
&lt;code&gt;});&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// Producer thread 2&lt;/code&gt;&lt;br&gt;
&lt;code&gt;thread::spawn(move || {&lt;/code&gt;&lt;br&gt;
&lt;code&gt;sender_clone.send("Hello from thread 2").unwrap();&lt;/code&gt;&lt;br&gt;
&lt;code&gt;});&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;// Main thread receives&lt;/code&gt;&lt;br&gt;
&lt;code&gt;for message in receiver {&lt;/code&gt;&lt;br&gt;
&lt;code&gt;println!("{}", message);&lt;/code&gt;&lt;br&gt;
&lt;code&gt;}&lt;/code&gt;&lt;br&gt;
&lt;code&gt;}&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Channels are useful for Async communication, event handling, data streaming and message massing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Blocks: Rust's Type System
&lt;/h2&gt;

&lt;p&gt;One thing we haven't gone over in this issue is the Rust syntax, which I've heard is very similar to C or C++ (don't have an opinion as I have never used either of them). So let's go over them briefly and lets also compare them to JavaScript's analogous features.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1) Structs&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A struct in Rust is like a blueprint for creating data objects. Think of it as a more rigid, type-safe version of JavaScript classes/objects:&lt;/p&gt;

&lt;p&gt;Rust enforces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Exact field types (String, bool, u64)&lt;/li&gt;
&lt;li&gt;All fields must be initialised&lt;/li&gt;
&lt;li&gt;Fields can't be added/removed after creation&lt;/li&gt;
&lt;li&gt;Type checking at compile time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;JavaScript classes are more flexible but less safe due to the following reasons&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Dynamic typing&lt;/li&gt;
&lt;li&gt;Properties can be added/removed&lt;/li&gt;
&lt;li&gt;No compile-time type checking (unless using TypeScript)&lt;/li&gt;
&lt;li&gt;More prone to runtime errors&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk43qysqtpe5lzdmkzlfo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk43qysqtpe5lzdmkzlfo.png" alt="Classes vs Structs" width="515" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2) Traits&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Traits are Rust's way of defining shared behavior - similar to interfaces in TypeScript or abstract classes in JavaScript. The key differences are&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Defining behaviour contracts&lt;/li&gt;
&lt;li&gt;Can have default implementations&lt;/li&gt;
&lt;li&gt;Multiple traits can be implemented&lt;/li&gt;
&lt;li&gt;Enforced at compile time&lt;/li&gt;
&lt;li&gt;Used for operator overloading and type conversion&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Tradeable example below shows:&lt;/p&gt;

&lt;p&gt;Required methods (get_price, update_price)&lt;br&gt;
Self reference (&amp;amp;self)&lt;br&gt;
Return type specifications (-&amp;gt; f64)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fehwsza2w3xvngle4i7ag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fehwsza2w3xvngle4i7ag.png" alt="Traits vs Interfaces" width="526" height="710"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3) Implementations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Implementations in Rust separate the data (struct) from behavior (methods). This is different from JavaScript's class-based approach.&lt;/p&gt;

&lt;p&gt;These are the following differences&lt;br&gt;
1) Clear separation of data and behavior&lt;br&gt;
2) Multiple impl blocks allowed&lt;br&gt;
3) Static methods (new)&lt;br&gt;
4) Instance methods with explicit self reference&lt;br&gt;
5) Mutable vs immutable methods (&amp;amp;mut self vs &amp;amp;self)&lt;/p&gt;

&lt;p&gt;JavaScript lumps everything together in the class definition, which can be less organised but more familiar to OOP developers&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcut0wlqdmbxxcv0cfkn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcut0wlqdmbxxcv0cfkn.png" alt="Implementations vs Class methods" width="549" height="587"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4) Attributes&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Attributes in Rust are metadata attached to code elements. They're more powerful than JavaScript decorators. Attributes in Rust are like special tags or labels that give extra instructions or capabilities to your code. Think of them like sticky notes that tell the compiler "hey, do something special with this code."&lt;/p&gt;

&lt;p&gt;These are the common use cases&lt;br&gt;
1) Deriving common traits (#[derive(Debug)])&lt;br&gt;
2) Conditional compilation (#[cfg(test)])&lt;br&gt;
3) Testing (#[test])&lt;br&gt;
4) Documentation&lt;br&gt;
5) Compiler optimisations&lt;/p&gt;

&lt;p&gt;JavaScript decorators are more limited and primarily used for class and method modifications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5eolirckvw7zx4sjuxjq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5eolirckvw7zx4sjuxjq.png" alt="Attributes vs Decorators" width="563" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Putting It All Together
&lt;/h2&gt;

&lt;p&gt;In this deep dive into Rust, we've explored what makes it a powerful systems programming language, especially when compared to interpreted languages like JavaScript. From its strict ownership model to its thread safety guarantees, Rust offers a unique approach to building reliable and performant software.&lt;/p&gt;

&lt;p&gt;Moving from JavaScript to Rust represents more than just learning a new syntax - it's about adopting a different mindset towards software development. Where JavaScript offers flexibility and ease of use, Rust demands precision and thoughtfulness. This trade-off brings us significant rewards: better performance, fewer runtime bugs, and the ability to build systems that are correct by construction.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>systems</category>
      <category>compiling</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Cracked Engineer’s Guide to JavaScript: Mechanics, Magic, and Misconceptions</title>
      <dc:creator>Zubair Maqsood</dc:creator>
      <pubDate>Thu, 06 Feb 2025 01:37:33 +0000</pubDate>
      <link>https://dev.to/zubairmaqsood866/the-cracked-engineers-guide-to-javascript-mechanics-magic-and-misconceptions-28jm</link>
      <guid>https://dev.to/zubairmaqsood866/the-cracked-engineers-guide-to-javascript-mechanics-magic-and-misconceptions-28jm</guid>
      <description>&lt;p&gt;After a career break from tech, I found myself going back to the roots of Software Engineering, exploring different areas such as AI and Rust. Along the way, I realised it would be valuable to document my findings and build a guide on how to be "cracked" as a Software Engineer—not just writing code, but truly understanding how things work under the hood.&lt;/p&gt;

&lt;p&gt;The reason is simple: &lt;em&gt;with the rise of Generative AI, the barrier to entry for software engineers is lower than ever, but at the same time, the demands on existing engineers are only increasing. I believe we are shifting from an era of specialism to an era of breadth.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Before tools like ChatGPT and Claude, companies hired experts in specific frameworks or tech stacks. Now, learning new technologies has become easier and faster, with automation handling many repetitive tasks. But this means that true engineering knowledge is more important than ever.&lt;/p&gt;

&lt;p&gt;Anyone can code. Few can engineer. To stand out, it’s crucial to understand the internals of the technologies we use—how they work, how they scale, and how they solve complex modern-day problems.&lt;br&gt;
With that in mind, let’s begin with JavaScript—the mechanics and magic behind it all.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding JavaScript’s Execution Model
&lt;/h2&gt;

&lt;p&gt;JavaScript is a single threaded programming language. This means it only handles one task at a time, unlike compiled languages such as Rust and C++, it can only execute one operation at a time. Here are some key features of JavaScript to note down&lt;/p&gt;

&lt;p&gt;It uses a call stack to keep track of function execution&lt;br&gt;
If we have a nested function - meaning a function that calls another function, it is added to the call stack, and popped off when the function has completed execution&lt;br&gt;
Blocking code (nested for loops or synchronous network calls) freezes everything as JavaScript can’t move forward until it is finished.&lt;/p&gt;

&lt;p&gt;So the question maybe, how does JavaScript handle tasks like API calls and timers if it is single threaded? Read through the whole page and you will find out!&lt;/p&gt;

&lt;h2&gt;
  
  
  The Call Stack – "The Brain of JavaScript Execution"
&lt;/h2&gt;

&lt;p&gt;Before we jump in on how JavaScript handles things like API calls and timers, it is crucial to understand one core aspect of the language. The Call Stack, you can think of it as the “brain” of JavaScript as the subtitle of this section suggests.&lt;/p&gt;

&lt;p&gt;But before that, let’s briefly go over what a stack is in the realm of Computer Science.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwciq3plbn3jmenf5ympp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwciq3plbn3jmenf5ympp.png" alt=" " width="487" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;stack&lt;/strong&gt; is a data structure that can contain a list of elements, which has two operations. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Push&lt;/strong&gt; - adds an element into the Stack&lt;br&gt;
&lt;strong&gt;Pop&lt;/strong&gt; - removes the recently added element from the Stack.&lt;/p&gt;

&lt;p&gt;When JavaScript executes a function, it adds it to the call stack, if that function calls another function inside, then that function is added on top of the call stack. Only until the execution is finished is when the function is popped off the stack.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajjbty0mbspb295d0ub8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fajjbty0mbspb295d0ub8.png" alt=" " width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So we have a function named addition, when it is called at the bottom of the script, it is the first one added to the stack, inside the addition function, there’s a function called anotherFunction(), it will be the second one added to the stack, then inside that, there is someOtherFunction() being called, which is the last one added to the stack.&lt;/p&gt;

&lt;p&gt;Once &lt;em&gt;someOtherFunction&lt;/em&gt; is finished executing, then it will be the first one to be popped off the stack, and the same goes for the other functions when they are finished with execution.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Event Loop &amp;amp; Async JavaScript – "How JavaScript Never Sleeps"
&lt;/h2&gt;

&lt;p&gt;At the beginning, I asked how JavaScript handles things like API calls if it is single-threaded. Enter the &lt;strong&gt;event loop&lt;/strong&gt;. This is what allows &lt;em&gt;concurrency&lt;/em&gt;, it allows non-blocking code by offloading it to the browser or the Node.js runtime environment.&lt;/p&gt;

&lt;p&gt;So there’s 3 components to bear in mind&lt;/p&gt;

&lt;p&gt;1) &lt;strong&gt;The Call Stack&lt;/strong&gt; - we went over this already in the previous section&lt;br&gt;
2) &lt;strong&gt;Web API’s&lt;/strong&gt; - JavaScript delegates asynchronous tasks (such as setTimeout, fetch, and DOM events) to the Web APIs provided by the browser (or Node.js).&lt;br&gt;
3) &lt;strong&gt;Callback Queue &amp;amp; Microtask Queue&lt;/strong&gt; - Once an async task completes, it can’t execute immediately. Instead, it goes into a task queue and waits for the event loop to process it.&lt;br&gt;
There are two types of queues:&lt;br&gt;
a)  &lt;strong&gt;Callback Queue (Macrotask Queue)&lt;/strong&gt; – Contains:&lt;br&gt;
setTimeout&lt;br&gt;
setInterval&lt;br&gt;
I/O tasks (e.g., file reading in Node.js)&lt;br&gt;
b)  &lt;strong&gt;Microtask Queue (Higher Priority)&lt;/strong&gt; – Contains:&lt;br&gt;
Promise.then() and async/await&lt;br&gt;
MutationObserver (used in the DOM)&lt;br&gt;
How is this all relevant to event loop?&lt;/p&gt;

&lt;p&gt;Well, think of the event loop as a continuous process JavaScript has, it continuously checks if the call stack is empty and if the queue has any pending tasks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Overall Process:&lt;/strong&gt;&lt;br&gt;
1️⃣ The call stack executes all synchronous code.&lt;br&gt;
2️⃣ Once the stack is clear, the event loop checks the microtask queue and runs any pending microtasks.&lt;br&gt;
3️⃣ If the microtask queue is empty, the event loop moves to the callback queue (macrotasks) and executes tasks one by one.&lt;br&gt;
4️⃣ Repeat forever (until the program finishes).&lt;/p&gt;

&lt;h2&gt;
  
  
  Hoisting – "Variables Before They Exist?"
&lt;/h2&gt;

&lt;p&gt;Hoisting is JavaScript's default behaviour of moving all declarations to the top of the current scope.&lt;/p&gt;

&lt;p&gt;This means you can use a function or variable before its declaration without getting a ReferenceError.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7h3o36kt52xbmok1mk1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs7h3o36kt52xbmok1mk1.png" alt=" " width="299" height="95"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you use a variable before it is defined using the &lt;strong&gt;var&lt;/strong&gt; keyword, you can use it, however it will be logged as undefined, as the declaration var a is hoisted at the top of the scope but the expression = 10, is not. &lt;/p&gt;

&lt;p&gt;If you attempt the same thing with &lt;strong&gt;let&lt;/strong&gt; and &lt;strong&gt;const&lt;/strong&gt;, you will get a &lt;strong&gt;ReferenceError&lt;/strong&gt;, as the variable definitions cannot be accessed before initialisation.&lt;/p&gt;

&lt;p&gt;The same behaviour can be seen with &lt;strong&gt;function declarations&lt;/strong&gt; and &lt;strong&gt;function expressions&lt;/strong&gt;. Function declarations are fully hoisted, meaning both the function name and body move to the top of the scope. You can access the function before it is defined. Function expressions are not hoisted, so you cannot access them before it is assigned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The &lt;strong&gt;this&lt;/strong&gt; Keyword – "The Most Misunderstood Keyword in JavaScript"
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;this&lt;/strong&gt; keyword refers to the context where a piece of code is. Usually it is used in object methods. So let me show you an example of this by using the class constructor that JavaScript provides to create objects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykv6dx206us7gnzqgecv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fykv6dx206us7gnzqgecv.png" alt=" " width="717" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside the constructor of the class, we have this.name and this.age being set to the name and age being passed down as arguments. So now at this stage, name and age are set as properties of the Person class, and if you want to reference them anywhere in your code as we are doing it in the introduce() method, you will need to use the &lt;strong&gt;this&lt;/strong&gt; keyword to refer back to the name and age you assigned in the constructor.&lt;/p&gt;

&lt;p&gt;Similarly, if you have another method in the class, and you want to call the introduce() method inside it, then you would have to do &lt;em&gt;this.introduce()&lt;/em&gt; to refer back to the method that was defined inside the class.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Prototype Chain – "How JavaScript Inherits Without Classes"
&lt;/h2&gt;

&lt;p&gt;The prototype chain is a mechanism in JavaScript that allows inheritance and property/method lookup. JavaScript in itself is a prototype-based language, meaning objects can inherit properties and methods from other objects.&lt;/p&gt;

&lt;p&gt;Key Concepts:&lt;br&gt;
1) &lt;strong&gt;Prototype Object&lt;/strong&gt;: Every object in JavaScript has a hidden [[Prototype]] property (accessible via &lt;strong&gt;proto&lt;/strong&gt; or Object.getPrototypeOf()). This property points to another object, which is its prototype.&lt;br&gt;
2) &lt;strong&gt;Prototype Chain&lt;/strong&gt;: When you access a property or method on an object, JavaScript first checks if the object itself has that property. If not, it looks up the prototype chain by following the [[Prototype]] link until it finds the property or reaches the end of the chain (where [[Prototype]] is null).&lt;br&gt;
3) &lt;strong&gt;Constructor Functions&lt;/strong&gt;: Functions in JavaScript can act as constructors. When you create an object using new, the object’s [[Prototype]] is set to the constructor’s prototype property.&lt;br&gt;
4) &lt;strong&gt;Object.prototype&lt;/strong&gt;: This is the top of the prototype chain. Most objects ultimately inherit from Object.prototype, which provides methods like toString(), hasOwnProperty(), etc&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh40klpa5nrukvhfeowlh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh40klpa5nrukvhfeowlh.png" alt=" " width="523" height="286"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Memory Management &amp;amp; Garbage Collection – "The Hidden Cost of JavaScript"
&lt;/h2&gt;

&lt;p&gt;Garbage collection is a process of automatically freeing up memory by removing objects that are not needed by the program. JavaScript handles memory allocation and deallocation automatically, however Cracked Software Engineers should still be mindful of how memory is used to avoid performance issues&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory Lifecycle:&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Allocation&lt;/strong&gt;: Memory is allocated when objects, functions, or primitives are created.&lt;br&gt;
Usage: Memory is used when reading or writing to variables.&lt;br&gt;
&lt;strong&gt;Release&lt;/strong&gt;: Memory is released when objects become unreachable and are garbage collected.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Memory Allocation:&lt;/strong&gt;&lt;br&gt;
 Numbers, strings, booleans, etc., are stored directly in memory.&lt;br&gt;
Objects, arrays, and functions are stored in the heap, and references to them are stored in variables.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion &amp;amp; Takeaways
&lt;/h2&gt;

&lt;p&gt;Now, I have been using JavaScript in my career for almost 4 to 5 years now, it seems easy at first but underneath the hood, there is a lot to know and master to become a Cracked Software Engineer. Truly understanding the internals is what is going to differentiate you from a web developer or a simple coder in the job market. In an era where AI tools like Windsurf and DeepSeek are gaining momentum, truly understanding what is going on underneath is going to differentiate you from the rest.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
