<?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: amBrain</title>
    <description>The latest articles on DEV Community by amBrain (@ambrain).</description>
    <link>https://dev.to/ambrain</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%2F4109909%2Ffb4dd3ef-b04c-4772-a8f3-e72f7b194ece.png</url>
      <title>DEV Community: amBrain</title>
      <link>https://dev.to/ambrain</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ambrain"/>
    <language>en</language>
    <item>
      <title>Pre-Trade Risk Checks Inside the Order Path</title>
      <dc:creator>amBrain</dc:creator>
      <pubDate>Tue, 08 Sep 2026 11:37:00 +0000</pubDate>
      <link>https://dev.to/ambrainorg/pre-trade-risk-checks-inside-the-order-path-5850</link>
      <guid>https://dev.to/ambrainorg/pre-trade-risk-checks-inside-the-order-path-5850</guid>
      <description>&lt;p&gt;An order arrives at the gateway. Before it goes out to the venue, something has to decide whether the account is allowed to send it. That decision runs on every order, including the overwhelming majority that are perfectly fine, so its cost is paid by all normal traffic and not only by the rejects.&lt;/p&gt;

&lt;p&gt;That is the constraint to name first. A risk check placed in the order path is a tax on order entry. The engineering question is not how to make the check clever, it is how to make it small enough that traders do not feel it, and honest enough that it still refuses the orders it must refuse.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The short answer first. The checks that belong in the order path are position and exposure limits, margin, fat-finger bounds, instrument and account state, the kill switch, and duplicate guards. On the risk path amBrain builds for brokers and prop firms, the pre-trade decision on in-memory state completes in under 1 ms - a figure that covers the check itself, not the end-to-end journey of an order. The rest of this article is how that path is built and where it stops.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What actually belongs in the order path
&lt;/h2&gt;

&lt;p&gt;The hot path answers exactly one question: may this order be sent right now, given what we currently know about this account. Checks that answer that question stay in. Checks that answer a different question move out.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Position and exposure limits - the resulting position in the instrument, the group and the account, compared against configured bounds&lt;/li&gt;
&lt;li&gt;Margin or buying power - whether the account still has room for the order under the current margin model&lt;/li&gt;
&lt;li&gt;Fat-finger bounds - order size, notional and price distance from a reference, catching the typo before the venue does&lt;/li&gt;
&lt;li&gt;Instrument and account state - trading halted, account restricted, close-only, product not enabled for this account&lt;/li&gt;
&lt;li&gt;Kill switch state - a single flag that overrides everything above&lt;/li&gt;
&lt;li&gt;Duplicate and self-trade guards where the venue does not provide them&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Everything else runs beside the path, on the same state, without holding the order. It informs the limits that the hot path enforces, but it does not sit between the trader and the venue.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Portfolio risk analytics - scenario runs, stress tests, correlated exposure across accounts&lt;/li&gt;
&lt;li&gt;Margin model re-rating when parameters change, and any recalculation that touches the whole book&lt;/li&gt;
&lt;li&gt;Surveillance and pattern detection, which needs history the hot path deliberately does not carry&lt;/li&gt;
&lt;li&gt;Reporting, reconciliation and anything that talks to a database or an external service&lt;/li&gt;
&lt;li&gt;Credit and counterparty review, which operates on a slower clock by nature&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The dividing line is a question, not a category. In the path: may this order go out. Beside the path: what should the limits be. Anything that answers the second question and still blocks the order is a design mistake, however important the check is.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Where position state lives, and why the database is not on the path
&lt;/h2&gt;

&lt;p&gt;The state a pre-trade check needs - current positions, working orders, used and available margin, limit configuration - lives in the memory of the process that makes the decision. Not in a cache in front of a database, not behind a network call. In the process.&lt;/p&gt;

&lt;p&gt;The reason is not only speed, though a query is orders of magnitude more expensive than a lookup in a local array. The reason is correctness. A database holds the position as it was written. The risk check needs the position including orders sent a moment ago that have not been filled, acknowledged or persisted yet. If you read from storage, you check against a past that has already been overtaken by your own flow.&lt;/p&gt;

&lt;p&gt;Practically, that shapes the process the way any low-latency component gets shaped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;One writer per account. Accounts are sharded across risk instances so that an account's state is never contended, and no lock is taken on the order path&lt;/li&gt;
&lt;li&gt;Flat, pre-allocated structures - fixed-size arrays indexed by account and instrument id, resolved at session start, not hash lookups on strings built per order&lt;/li&gt;
&lt;li&gt;No allocation, no I/O and no logging that blocks on the decision path; the audit record is handed to another thread through a queue&lt;/li&gt;
&lt;li&gt;Configuration that changes without a restart is swapped in as a whole immutable snapshot, so the check never reads a half-updated limit&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The database is where the position is recorded. It is not where the position is known.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Incremental recalculation, not a full pass
&lt;/h2&gt;

&lt;p&gt;A full recomputation of an account's exposure and margin walks every position and every working order. That cost grows with the size of the book, which means the risk check would get slower for exactly the clients who trade the most. So the hot path does not recompute. It applies a delta.&lt;/p&gt;

&lt;p&gt;The account carries running aggregates - net and gross exposure per instrument and per group, used margin, notional in flight. An incoming order produces a small change to those aggregates, the changed values are compared against the limits, and the order is accepted or rejected. The work is proportional to the order, not to the portfolio.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On send, the order's worst-case effect is reserved against the aggregates, so two orders in flight cannot both fit into the same remaining headroom&lt;/li&gt;
&lt;li&gt;On reject, cancel or expiry, the reservation is released; on fill, the reservation is replaced by the realised position change&lt;/li&gt;
&lt;li&gt;Partial fills adjust both sides of that in one step, which is where most bugs in this kind of engine actually live&lt;/li&gt;
&lt;li&gt;Netting and grouping rules are resolved when the instrument is loaded, not per order, so the delta is a handful of arithmetic operations&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Full recomputation still happens - on a schedule, when margin parameters change, and as a periodic self-check against the incremental result. It runs off the path, on a copy, and its result is either swapped in or raised as a discrepancy. Incremental state that silently drifts from the true state is worse than no check at all, so the comparison is not optional.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Measured on the risk path we build, the pre-trade check itself completes in &amp;lt;1 ms. That figure covers the decision on in-memory state, not the full journey of an order from the client to the venue and back.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The kill switch is a separate path
&lt;/h2&gt;

&lt;p&gt;A kill switch is used precisely when something is already wrong. That rules out building it on top of the machinery that may itself be the thing that is wrong. It is a separate path, with its own rules.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is a single atomic flag read at the top of the check, before position state, margin or instrument data is touched - so it works even when those are stale, missing or broken&lt;/li&gt;
&lt;li&gt;It is set from several independent triggers: an operator action, an automated condition, a loss of the market data or fill feed the risk state depends on&lt;/li&gt;
&lt;li&gt;It fails closed. If the risk process cannot establish that it has valid state, the gateway behaves as if the switch is engaged&lt;/li&gt;
&lt;li&gt;It has scopes - the whole firm, one desk, one account, one strategy - because a switch that can only stop everything gets used too late&lt;/li&gt;
&lt;li&gt;Engaging it is one action and one confirmation, not a configuration deploy; disengaging it is deliberate and always recorded&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Stopping new orders is the easy half. The harder half is what the switch does to orders already resting at the venue: pulling quotes and cancelling working orders has to be possible while the sending path is disabled. That cancel path deserves its own testing, because it is exercised on the worst day rather than on a normal one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Restart, and how state comes back
&lt;/h2&gt;

&lt;p&gt;In-memory state is a derived view of a durable record. That is what makes a restart survivable. Every event that changes risk state - an order accepted, a reservation released, a fill applied, a limit changed, the switch engaged - is appended to a journal on the local machine before it is acted upon downstream.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;On start, the process replays the journal to rebuild aggregates, then reconciles against the venue and clearing drop copy for positions and working orders&lt;/li&gt;
&lt;li&gt;Until reconciliation completes, the account is not open for trading. A risk engine that accepts orders while it is still figuring out the position is not a risk engine&lt;/li&gt;
&lt;li&gt;A mismatch between the replayed state and the venue's view stops that account and raises an alert; it is never resolved by quietly preferring one side&lt;/li&gt;
&lt;li&gt;A hot standby follows the same journal, so a failover restores a warm state instead of a cold replay, and the standby is verified by being promoted regularly rather than in theory&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Recovery time then depends on journal length and drop copy availability, not on the size of the book, and the failure mode of every unknown is the same: refuse to trade the account.&lt;/p&gt;

&lt;h2&gt;
  
  
  What this design does not give you
&lt;/h2&gt;

&lt;p&gt;The honest limits are worth stating plainly, because they decide whether this architecture fits at all:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The check is only as correct as the fill feed. If drop copy or execution reports lag, exposure is understated, and the correct response is to degrade to conservative limits or engage the switch rather than to keep trading on stale state&lt;/li&gt;
&lt;li&gt;Portfolio margin models that are genuinely non-additive resist incremental evaluation. What works is a conservative incremental bound on the path plus a full model off the path; the price is that some orders are rejected which a full model would have allowed&lt;/li&gt;
&lt;li&gt;The switch protects against your own flow, not against the market. It cannot prevent a gap or a slippage on positions you already hold&lt;/li&gt;
&lt;li&gt;In-process state means the risk engine and the order gateway share a fate. That buys latency and costs you the ability to scale them independently&lt;/li&gt;
&lt;li&gt;Single-writer sharding by account makes cross-account limits harder, and firm-wide checks need a slower aggregation layer with its own staleness&lt;/li&gt;
&lt;li&gt;It is more operational work than a database-backed check: journals, reconciliation, standby promotion drills. If order entry is not latency-sensitive, this complexity is not worth buying&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;amBrain builds this kind of pre-trade risk path for brokers and prop firms, with the hot paths written in Rust. The team has worked on trading infrastructure from Yerevan, Armenia since 2019.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://ambrain.org/blog/pre-trade-risk-hot-path/" rel="noopener noreferrer"&gt;ambrain.org&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>architecture</category>
      <category>systemdesign</category>
    </item>
    <item>
      <title>Designing a Matching Engine in Rust: Price-Time Priority Without GC Pauses</title>
      <dc:creator>amBrain</dc:creator>
      <pubDate>Tue, 08 Sep 2026 11:29:07 +0000</pubDate>
      <link>https://dev.to/ambrainorg/designing-a-matching-engine-in-rust-price-time-priority-without-gc-pausesr-2h56</link>
      <guid>https://dev.to/ambrainorg/designing-a-matching-engine-in-rust-price-time-priority-without-gc-pausesr-2h56</guid>
      <description>&lt;p&gt;A spot exchange matching engine has a narrow job. It takes an ordered stream of commands - new order, cancel, replace - applies them to an order book under price-time priority, and emits an ordered stream of events: trades, book updates, rejects, acknowledgements.&lt;/p&gt;

&lt;p&gt;Everything hard about it comes from three constraints stacked on top of that job: the result must be identical on every replay of the same input, the remainder of a partially filled order must keep its place in the queue, and the latency tail must not move when a burst arrives.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What amBrain can substantiate publicly: a mini-exchange we built runs in production on MOEX colocation, and the hot paths of our trading systems are written in Rust. The two figures we publish - market data delivered in under 5 ms and pre-trade risk checks completing in under 1 ms - are measured on those paths, not on the matching loop described below. The rest of this article is how we design the engine, not a benchmark of one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The order book is a sorted index of price levels over FIFO queues
&lt;/h2&gt;

&lt;p&gt;The book is two sides, each a price-ordered collection of levels. A level is not a number - it is a queue of resting orders at that price, in arrival order. Matching touches the best level constantly and the deep levels rarely, so the structure is chosen for that access pattern rather than for elegance.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prices are integers in ticks, never floating point - a tick is the unit of the instrument, and comparison and arithmetic on integers are exact&lt;/li&gt;
&lt;li&gt;Each side keeps its levels in price order, with the best price reachable without a search - the top of book is read on every single command&lt;/li&gt;
&lt;li&gt;A level holds a FIFO queue of resting orders, plus the aggregated resting quantity, so the aggregate does not have to be recomputed by walking the queue&lt;/li&gt;
&lt;li&gt;Orders are held in a preallocated slab and referenced by index handles, and the queue is intrusive: the next and previous links live inside the order record itself&lt;/li&gt;
&lt;li&gt;A separate map from client order id to slab handle makes cancel and replace a direct lookup, so a cancel never scans the book&lt;/li&gt;
&lt;li&gt;Removal from a queue is by handle, not by search - a cancel of a deep resting order costs the same as a cancel at the top of book&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The consequence of intrusive queues and index handles is that a resting order never moves in memory while it lives. Its queue position is a property of its links, not of where it happens to sit, which is what makes partial fills cheap later on.&lt;/p&gt;

&lt;h2&gt;
  
  
  Price-time priority is a loop over levels, then over a queue
&lt;/h2&gt;

&lt;p&gt;An incoming aggressive order walks the opposite side from the best price inward. At each level it walks the FIFO queue from the front. It stops when the level price is no longer acceptable to the incoming order or when the incoming quantity reaches zero.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Take the best opposite level; if its price does not cross the incoming limit price, stop&lt;/li&gt;
&lt;li&gt;Take the front order of that level queue - it is the oldest at that price, and time priority means it fills first&lt;/li&gt;
&lt;li&gt;The traded quantity is the smaller of the two remaining quantities; the trade price is the resting order price, because the resting order set the terms&lt;/li&gt;
&lt;li&gt;Decrement both remainders, emit the trade event, and decrement the level aggregate&lt;/li&gt;
&lt;li&gt;If the resting order remainder reaches zero, unlink it from the queue and return its slab slot; if the level becomes empty, remove the level&lt;/li&gt;
&lt;li&gt;Repeat until the incoming quantity is zero or no acceptable level remains&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;A partially filled resting order keeps its place. Its remainder stays at the front of its queue with its original arrival sequence, because a fill changes a quantity and nothing else. A partially filled aggressive order that is a plain limit becomes a resting order at the back of its own price level, with a new arrival sequence - it arrived now, not earlier.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Order type semantics are decisions taken at the boundary of this loop, not inside it. Immediate-or-cancel drops the remainder instead of resting it. Fill-or-kill runs a dry pass first and either executes whole or rejects. Post-only rejects if the order would cross on arrival. Keeping these outside the loop means the loop stays the only place where book state changes.&lt;/p&gt;

&lt;p&gt;Self-trade prevention, minimum quantities, and tick and lot validation belong before the loop as well. An order that reaches matching has already been proven well formed, so the loop has no error branches to slow it down or to disagree about.&lt;/p&gt;

&lt;h2&gt;
  
  
  Determinism is what makes the tail predictable, and allocation is what breaks it
&lt;/h2&gt;

&lt;p&gt;Average latency is rarely the problem. The problem is the worst observation during a burst, which is when the engine matters most and when a stop-the-world pause is most likely to land. Under a managed runtime with a garbage collector, that pause is scheduled by the collector rather than by you, and it lands in the middle of the burst that produced the garbage.&lt;/p&gt;

&lt;p&gt;Manual allocation is a smaller version of the same problem. A general purpose allocator can walk free lists, take a lock, or ask the kernel for more memory, and the call that does so is the call that shows up in the tail. The fix is the same in either case: do not allocate on the hot path at all.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Order records, level records and event buffers come from arenas sized at startup - steady state allocation count on the matching path is zero&lt;/li&gt;
&lt;li&gt;Freed slots return to a free list inside the arena, so a busy instrument recycles the same memory all session&lt;/li&gt;
&lt;li&gt;Structures are fixed shape and fixed size, with capacity limits enforced as a rejection rather than as a growth event&lt;/li&gt;
&lt;li&gt;Outbound events are written into a preallocated ring buffer that another thread drains - the matching thread never blocks on a consumer&lt;/li&gt;
&lt;li&gt;The matching thread is a single writer over the book, so there is no lock on book state and no ordering ambiguity to resolve&lt;/li&gt;
&lt;li&gt;Inputs are sequenced before they reach the engine, and the sequence number, not the arrival time, decides order&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;An engine is deterministic when the same input sequence produces the same output sequence, byte for byte, on a different machine and a year later. Anything that reads wall clock time, thread scheduling or hash iteration order inside the matching path breaks that property.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Timestamps are therefore an input, not something the engine reads for itself. The sequencer stamps a command when it accepts it, and the matching loop treats the stamp as data. Randomness, if any is needed, comes from a seeded generator whose seed is part of the journal.&lt;/p&gt;

&lt;h2&gt;
  
  
  The journal is the engine state, and the book is a cache of it
&lt;/h2&gt;

&lt;p&gt;Recovery is not a feature bolted on after matching works. The engine writes an append-only journal of accepted commands in sequence order, and the in-memory book is nothing more than the result of folding that journal. Rebuilding after a crash means replaying it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A command is journaled and durable before it is matched, so an accepted order cannot be lost by a crash between acknowledgement and execution&lt;/li&gt;
&lt;li&gt;The journal is the input sequence, not the output - outputs are derived, and re-deriving them is exactly what replay does&lt;/li&gt;
&lt;li&gt;Periodic snapshots of the book carry the sequence number they were taken at, so recovery loads a snapshot and replays only the tail of the journal&lt;/li&gt;
&lt;li&gt;Snapshot and replay agreement is checked rather than assumed: replaying from the previous snapshot must reproduce the next one&lt;/li&gt;
&lt;li&gt;The event stream carries the same sequence numbers, so downstream consumers - risk, settlement, market data - can be resumed from a known point instead of resynchronised by hand&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The engine writes the journal, but durability is a property of the storage path and of how many machines have the record before the acknowledgement goes out. That is a replication and hardware decision, and it is where recovery time is actually won or lost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing is deterministic replay plus invariants that must always hold
&lt;/h2&gt;

&lt;p&gt;Determinism is what makes the engine testable. Because the same input gives the same output, a captured session is a regression test, and a failure found once can be reproduced exactly instead of chased.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Replay harness: feed a recorded command sequence, compare the emitted event stream against the stored one, and fail on the first divergence with the sequence number&lt;/li&gt;
&lt;li&gt;Invariant checks after every command in test builds - queues sorted by arrival, level aggregates equal to the sum of their queue, no crossed book, total quantity conserved across every trade&lt;/li&gt;
&lt;li&gt;Property based tests that generate random but well formed command sequences and assert the invariants rather than specific outcomes&lt;/li&gt;
&lt;li&gt;A differential reference model: a slow, obviously correct implementation with naive data structures, run against the same input, with any disagreement treated as a bug in the fast one&lt;/li&gt;
&lt;li&gt;Fuzzing at the decoder boundary, where malformed input arrives from outside and where a panic would take the matching thread down&lt;/li&gt;
&lt;li&gt;Latency measurement under the burst shape that worries you, recording the distribution rather than an average, since the tail is the number that decides the design&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;A reference model is worth more than it looks. Two implementations written from the same specification disagree in exactly the places where the specification was ambiguous, and matching rules are full of ambiguity at the edges - crossed limits, zero remainders, cancels racing fills.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What Rust gives you here, and what it does not
&lt;/h2&gt;

&lt;p&gt;Rust removes a category of problem rather than making the loop faster by itself. There is no garbage collector, so no pause is scheduled behind your back. Ownership makes the single-writer discipline something the compiler enforces instead of something a code review has to notice. Slab handles and intrusive links, which are error prone in a language without lifetimes, are checkable here. Panics on integer overflow in debug builds catch a class of bug that silently corrupts a book.&lt;/p&gt;

&lt;p&gt;The honest boundary is that most of what determines tail latency is not the language:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Kernel scheduling, interrupt handling, CPU pinning and power management move the tail more than the matching code does&lt;/li&gt;
&lt;li&gt;Network interface, kernel bypass or its absence, and the physical path to the venue set a floor the engine cannot go below&lt;/li&gt;
&lt;li&gt;Serialisation and the wire protocol at the boundary are frequently the dominant cost, not the match itself&lt;/li&gt;
&lt;li&gt;Matching semantics, order types, fee and rebate rules and market phases are business decisions - a wrong rule implemented quickly is still wrong&lt;/li&gt;
&lt;li&gt;Risk checks, position limits and the settlement path live outside the engine and have their own latency and their own failure modes&lt;/li&gt;
&lt;li&gt;Operations - deployment, monitoring, the runbook for a failed replay - decide whether the guarantees survive contact with a production incident&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Rust also costs something. The borrow checker slows down the first weeks of a design that is still moving, the ecosystem for exchange specific protocols is thinner than in older languages, and unsafe blocks around lock-free structures need the same review discipline as the equivalent code anywhere else. Choosing it is a decision about the latency tail and about memory safety in a single-writer core, not a decision about developer comfort.&lt;/p&gt;

&lt;p&gt;amBrain has built trading infrastructure in Yerevan, Armenia since 2019, with hot paths in Rust; a mini-exchange we built runs in production on MOEX colocation. If you are designing a matching engine and want to walk through the book structure, the journal format or the replay harness, that conversation is worth having before the first line of the hot path is written.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally published at &lt;a href="https://ambrain.org/blog/matching-engine-rust-design/" rel="noopener noreferrer"&gt;ambrain.org&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>architecture</category>
      <category>systemdesign</category>
      <category>microservices</category>
    </item>
  </channel>
</rss>
