<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="en">
	<title>Irina Scurtu | Blog</title>
	<subtitle>Software architect, independent consultant and .NET developer, speaker, teacher, community conference organizer, and Microsoft MVP for Development Technologies based in Romania</subtitle>
	<link href="https://irina.codes/feed/feed.xml" rel="self"/>
	<link href="https://irina.codes/"/>
	<updated>2026-08-20T00:00:00Z</updated>
	<id>https://irina.codes/</id>
	
	<author>
		<name>Irina Dominte(Scurtu)</name>
		<email>irina@dsisolutions.eu</email>
	</author>
		
		<entry>
			<title>Eventual Consistency Explained</title>
			<link href="https://irina.codes/eventual-consistency-explained/"/>
			<updated>2026-08-20T00:00:00Z</updated>
			<id>https://irina.codes/eventual-consistency-explained/</id>
			<content type="html">&lt;p&gt;&lt;em&gt;Not a bug. A trade-off you need to understand.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Update your profile picture, refresh the page on your phone, and for a second the old one is still there. Is that a bug? Most people&#39;s instinct is yes: I just saved this, why doesn&#39;t everything show it immediately?&lt;/p&gt;
&lt;p&gt;It isn&#39;t a bug. It&#39;s eventual consistency, and if you build distributed systems, you&#39;re relying on it constantly, whether you&#39;ve designed for it or not.&lt;/p&gt;
&lt;h2 id=&quot;the-promise-we-want-and-what-it-costs&quot; tabindex=&quot;-1&quot;&gt;The Promise We Want, and What It Costs &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/eventual-consistency-explained/#the-promise-we-want-and-what-it-costs&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The promise everyone wants is simple: you update something, and every service, every region, every cache sees it instantly. One version of the truth, always. That&#39;s &lt;strong&gt;strong consistency&lt;/strong&gt;, and it&#39;s a completely reasonable thing to want.&lt;/p&gt;
&lt;p&gt;It&#39;s also expensive. Guaranteeing that every node agrees before you consider a write done means coordination. And coordination across a network, especially across regions, means waiting on round trips you don&#39;t control. Do that on every write, at scale, and your system&#39;s latency is now bounded by your slowest node and your worst network hop.&lt;/p&gt;
&lt;p&gt;So most distributed systems don&#39;t do that. They accept a weaker guarantee in exchange for speed and availability, and call it eventual consistency.&lt;/p&gt;
&lt;h2 id=&quot;what-eventual-consistency-actually-means&quot; tabindex=&quot;-1&quot;&gt;What Eventual Consistency Actually Means &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/eventual-consistency-explained/#what-eventual-consistency-actually-means&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s the mechanism: a write lands on one node (or a small subset of nodes). The other nodes find out later, through replication, gossip, or some other propagation mechanism, not as part of the original write. If no new updates come in, every node &lt;strong&gt;eventually&lt;/strong&gt; converges on the same value.&lt;/p&gt;
&lt;p&gt;That word &amp;quot;eventually&amp;quot; is doing a lot of work, and it&#39;s worth being precise about it. It&#39;s a promise about outcome, not about timing. Given enough time and no further writes, all replicas agree. In practice, &amp;quot;eventually&amp;quot; is usually milliseconds, sometimes seconds, and only occasionally something you&#39;d notice. But the guarantee itself says nothing about &lt;em&gt;how fast&lt;/em&gt;. It only says &lt;em&gt;eventually&lt;/em&gt;, which is a liveness guarantee, not a timing one.&lt;/p&gt;
&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;sequenceDiagram&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; Client
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; A as Node A
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; B as Node B
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; C as Node C

    Client&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;A&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Write&lt;span class=&quot;token text string&quot;&gt;(value = X)&lt;/span&gt;
    A&lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&gt;&lt;/span&gt;Client&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Ack &lt;span class=&quot;token text string&quot;&gt;(write accepted)&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; A,C&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Client already sees success.&amp;lt;br/&gt;Replication starts now, not before.
    A&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;B&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Replicate X
    A&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;C&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Replicate X
    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; B&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Still serving the old value&amp;lt;br/&gt;until replication lands
    B&lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&gt;&lt;/span&gt;B&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Converges on X
    C&lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&gt;&lt;/span&gt;C&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Converges on X&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-cap-trade-off&quot; tabindex=&quot;-1&quot;&gt;The CAP Trade-Off &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/eventual-consistency-explained/#the-cap-trade-off&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is where the CAP theorem shows up. It&#39;s less abstract than it sounds once you connect it to a decision you&#39;re already making. CAP says a distributed system can only guarantee two of these three properties at once, and in practice, partition tolerance isn&#39;t optional for any system that spans more than one node:&lt;/p&gt;
&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;graph&lt;/span&gt; LR
    C&lt;span class=&quot;token text string&quot;&gt;((Consistency))&lt;/span&gt;
    A&lt;span class=&quot;token text string&quot;&gt;((Availability))&lt;/span&gt;
    P&lt;span class=&quot;token text string&quot;&gt;((Partition&amp;lt;br/&gt;Tolerance))&lt;/span&gt;

    C &lt;span class=&quot;token arrow operator&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|&quot;CA: no partition tolerance&quot;|&lt;/span&gt; A
    C &lt;span class=&quot;token arrow operator&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|&quot;CP: consistent, may be unavailable&quot;|&lt;/span&gt; P
    A &lt;span class=&quot;token arrow operator&quot;&gt;---&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|&quot;AP: available, eventually consistent&quot;|&lt;/span&gt; P&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That leaves a real choice only once a partition actually happens: stay &lt;strong&gt;consistent&lt;/strong&gt;, where every node sees the same data, or stay &lt;strong&gt;available&lt;/strong&gt;, where every request gets a response, even if it&#39;s not the latest data. You can&#39;t fully have both during that partition.&lt;/p&gt;
&lt;p&gt;Eric Brewer, who proposed CAP back in 2000, revised his own framing in 2012 for exactly this reason. Partition tolerance was never really a dial you could turn off for a system with more than one node, so treating it as an equal third option was misleading. The only decision that matters is what happens to consistency and availability once a partition actually hits.&lt;/p&gt;
&lt;p&gt;Real systems announce which side of that decision they land on. &lt;strong&gt;CP&lt;/strong&gt; (consistency + partition tolerance) systems, like MongoDB in its default configuration or HBase, choose consistency: rather than serve you something wrong, they&#39;d rather reject the request. &lt;strong&gt;AP&lt;/strong&gt; (availability + partition tolerance) systems, like Cassandra or DynamoDB, choose availability: they&#39;ll serve you data that might be a few writes behind rather than nothing at all, and lean on eventual consistency to close the gap afterward. &lt;strong&gt;CA&lt;/strong&gt; (consistency + availability) systems, like a traditional PostgreSQL or MySQL install running in one place, get to keep both, but only because they were never asked to survive a partition between nodes. Spread that same database across two data centers, and CA quietly stops being on the table.&lt;/p&gt;
&lt;p&gt;Eventual consistency is the deliberate choice of availability over strong consistency. You&#39;re saying: I&#39;d rather my system keep responding, even with slightly stale data somewhere, than refuse to answer. It shouldn&#39;t have to wait until everyone agrees. For a lot of systems, that&#39;s exactly the right call. It&#39;s worth naming it as a call, not something that just happens to you.&lt;/p&gt;
&lt;h2 id=&quot;where-you-re-already-using-it&quot; tabindex=&quot;-1&quot;&gt;Where You&#39;re Already Using It &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/eventual-consistency-explained/#where-you-re-already-using-it&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This isn&#39;t exotic. You might assume eventual consistency is something only distributed-systems specialists deal with. Consider how much of your daily infrastructure already works this way.&lt;/p&gt;
&lt;p&gt;DNS is the oldest example: update a record and it can take minutes to propagate everywhere, not milliseconds. CDNs cache content at edge nodes and take time to invalidate it after a change. Social media feeds (likes, comments, view counts) propagate asynchronously. Nobody expects a like count to update everywhere in the same instant. And if you run a primary/replica database setup, your read replicas are, by construction, eventually consistent with the primary. There&#39;s always some lag, even if it&#39;s usually small enough that nobody notices.&lt;/p&gt;
&lt;p&gt;You&#39;ve been relying on eventual consistency for years. The only question is whether you&#39;ve been designing for it on purpose.&lt;/p&gt;
&lt;h2 id=&quot;what-this-means-in-practice&quot; tabindex=&quot;-1&quot;&gt;What This Means in Practice &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/eventual-consistency-explained/#what-this-means-in-practice&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Designing for eventual consistency on purpose looks different from tolerating it by accident. A few things that help:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Show what the user submitted, not what&#39;s confirmed.&lt;/strong&gt; This is optimistic UI. Update the screen right after the action. Don&#39;t wait on a round trip to every replica before showing the change.&lt;/li&gt;
&lt;li&gt;Retries should be idempotent. If an operation might have partially succeeded, retrying it again needs to be safe, not additive.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Version your data.&lt;/strong&gt; ETags, sequence numbers, or vector clocks let you detect conflicts instead of silently losing one write to another.&lt;/li&gt;
&lt;li&gt;Conflicts need to be resolvable, not just detectable. &amp;quot;Last write wins&amp;quot; is the easy default, but it isn&#39;t always the right one. Know when it&#39;s acceptable and when it isn&#39;t.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of this has to be all-or-nothing at the system level, either. Most distributed databases let you dial consistency per operation instead of per deployment. Cassandra lets you ask for a &lt;code&gt;QUORUM&lt;/code&gt; read on an account balance and an &lt;code&gt;ONE&lt;/code&gt; read on an activity feed, in the same application. DynamoDB defaults to eventually consistent reads but gives you a strongly consistent read on request, at roughly double the read cost. MongoDB&#39;s &lt;code&gt;writeConcern&lt;/code&gt; and &lt;code&gt;readPreference&lt;/code&gt; settings do the same job. Pick the guarantee per query, not per database.&lt;/p&gt;
&lt;p&gt;Know the specific failure patterns to watch for too. They&#39;re the same handful every time:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reading your own write and getting the stale value back (you just saved this, why doesn&#39;t it show?).&lt;/li&gt;
&lt;li&gt;Stale aggregates, where a count or total lags behind the records it&#39;s summarizing.&lt;/li&gt;
&lt;li&gt;Two users updating the same record concurrently, with one silently overwriting the other.&lt;/li&gt;
&lt;li&gt;Downstream actions (an email, a notification) firing off the back of data that&#39;s already out of date by the time they run.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these are exotic bugs. They&#39;re the predictable consequences of eventual consistency meeting code that was written as if it were strongly consistent.&lt;/p&gt;
&lt;p&gt;Not every part of your system should take the eventually-consistent trade, either. Financial transactions (debits and credits) need to be atomic. Inventory reservation needs strong consistency, or two customers can both &amp;quot;successfully&amp;quot; claim the last item in stock. A revoked authentication token needs to be invalid immediately, not eventually. Eventual consistency is a sensible default for a lot of your system. It isn&#39;t a universal rule. Treat it as one, and you&#39;ll end up debugging a double-sold item at 2am.&lt;/p&gt;
&lt;h2 id=&quot;closing&quot; tabindex=&quot;-1&quot;&gt;Closing &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/eventual-consistency-explained/#closing&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Eventual consistency trades &amp;quot;always correct&amp;quot; for &amp;quot;always available.&amp;quot; For most of what you build, that&#39;s the right trade. The mistake isn&#39;t choosing it. The mistake is not knowing you&#39;ve chosen it.&lt;/p&gt;
&lt;p&gt;The worst eventual consistency bugs I&#39;ve seen didn&#39;t come from the trade-off itself. They came from nobody realizing they&#39;d made a trade-off, until a user noticed their own change had disappeared.&lt;/p&gt;
&lt;p&gt;PS: Let me know if I missed anything, ping me on &lt;a href=&quot;https://twitter.com/irina_scurtu&quot;&gt;Twitter/X&lt;/a&gt; or &lt;a href=&quot;https://linkedin.com/in/irinascurtu&quot;&gt;LinkedIn&lt;/a&gt;, and follow along there if you&#39;d like more of this. Let&#39;s chat.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>REST APIs in Production: Pagination, HATEOAS, and the Patterns That Actually Matter (Part 3 of 3)</title>
			<link href="https://irina.codes/rest-api-production-patterns/"/>
			<updated>2026-08-13T00:00:00Z</updated>
			<id>https://irina.codes/rest-api-production-patterns/</id>
			<content type="html">&lt;p&gt;This is the last post in a three-part series on REST API design. &lt;a href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/&quot;&gt;The first post&lt;/a&gt; covered REST&#39;s constraints and HTTP semantics. &lt;a href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/&quot;&gt;The second&lt;/a&gt; covered resource design, versioning, and authentication. This one covers what separates a demo API from a production one.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;pagination&quot; tabindex=&quot;-1&quot;&gt;Pagination &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#pagination&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Always paginate collections that can grow without bound. Returning 50,000 records in one response is a denial-of-service problem waiting to happen.&lt;/p&gt;
&lt;h3 id=&quot;offset-based-pagination&quot; tabindex=&quot;-1&quot;&gt;Offset-Based Pagination &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#offset-based-pagination&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /orders?offset&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;
GET /orders?offset&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;
GET /orders?page&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;per_page&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple to implement and query. Allows jumping to arbitrary pages.&lt;/p&gt;
&lt;p&gt;Weakness: inconsistent results under concurrent writes. A record inserted between page 1 and page 2 requests can show up as a duplicate. If one is deleted, you may skip a record. Performance also degrades at high offsets in most databases.&lt;/p&gt;
&lt;h3 id=&quot;cursor-based-pagination&quot; tabindex=&quot;-1&quot;&gt;Cursor-Based Pagination &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#cursor-based-pagination&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /orders?limit&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Returns: { &quot;data&quot;: [...], &quot;cursor&quot;: &quot;eyJpZCI6MjB9&quot; }&lt;/span&gt;

GET /orders?after&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;eyJpZCI6MjB9&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The cursor encodes a position in the result set. It&#39;s typically the ID or sort key of the last item returned. Each page request is a stable query from a known position.&lt;/p&gt;
&lt;p&gt;Strengths: consistent under concurrent writes, efficient at any depth, suits real-time feeds.&lt;/p&gt;
&lt;p&gt;Weaknesses: cannot jump to arbitrary pages (only forward/backward), and clients must treat the cursor as opaque.&lt;/p&gt;
&lt;h3 id=&quot;keyset-pagination&quot; tabindex=&quot;-1&quot;&gt;Keyset Pagination &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#keyset-pagination&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A variant of cursor pagination that expresses the position as an explicit field value:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /orders?after_id&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;789&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Less opaque than encoded cursors, but ties pagination semantics to visible fields.&lt;/p&gt;
&lt;h3 id=&quot;response-envelope-for-pagination&quot; tabindex=&quot;-1&quot;&gt;Response Envelope for Pagination &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#response-envelope-for-pagination&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Always include pagination metadata:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt; ... &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;pagination&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;total&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1540&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;limit&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;has_next&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;has_previous&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;links&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;self&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;/orders?offset=40&amp;amp;limit=20&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;next&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;/orders?offset=60&amp;amp;limit=20&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;prev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;/orders?offset=20&amp;amp;limit=20&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;first&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/orders?offset=0&amp;amp;limit=20&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;last&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;token string&quot;&gt;&quot;/orders?offset=1520&amp;amp;limit=20&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;links&lt;/code&gt; object connects this to HATEOAS, more on that next.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;hateoas&quot; tabindex=&quot;-1&quot;&gt;HATEOAS &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#hateoas&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;HATEOAS (Hypermedia as the Engine of Application State) is REST&#39;s most misunderstood, most often omitted constraint. According to Fielding himself, it&#39;s also the part that makes an API truly RESTful.&lt;/p&gt;
&lt;p&gt;The idea: a client moves through an API using links in responses. It needs little upfront knowledge of URI structure. A human clicks through a website by following links. A REST client should do the same instead of hardcoding URI structure it was never given.&lt;/p&gt;
&lt;h3 id=&quot;what-it-looks-like&quot; tabindex=&quot;-1&quot;&gt;What It Looks Like &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#what-it-looks-like&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-json&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;GET /orders/&lt;span class=&quot;token number&quot;&gt;789&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;789&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pending&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;total&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;142.50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;customer&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;42&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Alice&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;_links&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;self&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/orders/789&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;cancel&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/orders/789/cancel&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;method&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;POST&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;customer&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/users/42&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;payment&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;href&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/payments?orderId=789&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server controls what actions are available. If the order is already cancelled, the &lt;code&gt;cancel&lt;/code&gt; link disappears. The client doesn&#39;t need to know the cancellation rules. It just checks whether the link is present.&lt;/p&gt;
&lt;h3 id=&quot;media-types-and-hateoas&quot; tabindex=&quot;-1&quot;&gt;Media Types and HATEOAS &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#media-types-and-hateoas&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;_links&lt;/code&gt; pattern above is informal. There are standardized hypermedia formats:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;HAL (Hypertext Application Language)&lt;/strong&gt;: uses &lt;code&gt;_links&lt;/code&gt; and &lt;code&gt;_embedded&lt;/code&gt;, widely adopted&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JSON:API&lt;/strong&gt;: a more opinionated full specification for request/response format&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Siren&lt;/strong&gt;: includes actions with typed fields, more expressive for write operations&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Collection+JSON&lt;/strong&gt;: designed specifically for collection resources&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;the-reality-of-hateoas-adoption&quot; tabindex=&quot;-1&quot;&gt;The Reality of HATEOAS Adoption &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#the-reality-of-hateoas-adoption&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;HATEOAS is theoretically correct and has genuine benefits for client decoupling. In practice, most production APIs don&#39;t implement it. Most clients don&#39;t use it even when offered. The reasons are pragmatic. Statically typed clients often prefer a well-documented contract over dynamic link discovery. Tooling for hypermedia clients also isn&#39;t as mature as tooling for OpenAPI-described APIs.&lt;/p&gt;
&lt;p&gt;Know this so you decide deliberately, not by default. For public APIs with diverse clients and long lifespans, HATEOAS is worth the investment. For internal APIs between services you control, a well-maintained OpenAPI spec may be more practical.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;filtering-sorting-and-searching&quot; tabindex=&quot;-1&quot;&gt;Filtering, Sorting, and Searching &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#filtering-sorting-and-searching&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;filtering&quot; tabindex=&quot;-1&quot;&gt;Filtering &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#filtering&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use query parameters to filter collections:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /orders?status&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;pending
GET /orders?status&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;pending&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;customerId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;
GET /products?minPrice&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;maxPrice&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;category&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;electronics&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For complex filters, some APIs use a structured query language:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /products?filter&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;price:lt:100,category:eq:electronics&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or a JSON-encoded filter (though this can be awkward in URLs):&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /products?filter&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;price&quot;&lt;/span&gt;:&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;lt&quot;&lt;/span&gt;:100&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,&lt;span class=&quot;token string&quot;&gt;&quot;category&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;electronics&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Be consistent and document your filter syntax explicitly.&lt;/p&gt;
&lt;h3 id=&quot;sorting&quot; tabindex=&quot;-1&quot;&gt;Sorting &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#sorting&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /orders?sort&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;createdAt
GET /orders?sort&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;createdAt:desc
GET /orders?sort&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;-createdAt          &lt;span class=&quot;token comment&quot;&gt;# minus prefix for descending&lt;/span&gt;
GET /orders?sort&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;status,createdAt:desc&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Always document the default sort order, and make it stable. Include a unique field as a tiebreaker.&lt;/p&gt;
&lt;h3 id=&quot;searching&quot; tabindex=&quot;-1&quot;&gt;Searching &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#searching&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Full-text search usually warrants a dedicated endpoint or query parameter:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /products?q&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;wireless+headphones
GET /search?q&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;wireless+headphones&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;products&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For sophisticated search requirements, dedicated search endpoints with POST bodies can be appropriate:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;POST /products/search

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;query&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;wireless headphones&quot;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;filters&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;inStock&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; true, &lt;span class=&quot;token string&quot;&gt;&quot;priceMax&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;200&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;sort&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;field&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;relevance&quot;&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;order&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;desc&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;pagination&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;limit&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;, &lt;span class=&quot;token string&quot;&gt;&quot;offset&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;POST works for search when the query is too complex for a URL. It&#39;s pragmatic, but it sacrifices cacheability.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;sparse-fieldsets-and-field-selection&quot; tabindex=&quot;-1&quot;&gt;Sparse Fieldsets and Field Selection &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#sparse-fieldsets-and-field-selection&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Large resources with many fields are expensive to transfer. They often contain fields a given client doesn&#39;t need. Field selection lets clients request only what they need:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /users/42?fields&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;id,name,email
GET /orders?fields&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;id,status,total&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token assign-left variable&quot;&gt;include&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;customer.name&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is particularly important for mobile clients on limited bandwidth.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;content-negotiation&quot; tabindex=&quot;-1&quot;&gt;Content Negotiation &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#content-negotiation&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;REST embraces HTTP&#39;s content negotiation mechanism. Clients can request specific response formats:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Request JSON&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Accept: application/json&quot;&lt;/span&gt; https://api.example.com/users/42

&lt;span class=&quot;token comment&quot;&gt;# Request XML&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Accept: application/xml&quot;&lt;/span&gt; https://api.example.com/users/42&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server indicates what it returned:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Content-Type: application/json; charset=utf-8
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the server cannot produce the requested format, it returns &lt;code&gt;406 Not Acceptable&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For versioning via content type:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Accept: application/vnd.myapi.v2+json&quot;&lt;/span&gt; https://api.example.com/users/42&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;rate-limiting&quot; tabindex=&quot;-1&quot;&gt;Rate Limiting &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#rate-limiting&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every public API needs rate limiting. Return standard headers so clients can manage their behavior:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 742
X-RateLimit-Reset: 1735689600
Retry-After: 3600
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When a client exceeds the limit, return &lt;code&gt;429 Too Many Requests&lt;/code&gt; with a &lt;code&gt;Retry-After&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;Common rate limiting strategies:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Fixed window&lt;/strong&gt;: count requests in a fixed time window (simple, but allows burst at window boundary)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sliding window&lt;/strong&gt;: rolling time window (smoother, more expensive)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Token bucket&lt;/strong&gt;: tokens replenish at a fixed rate, bursts allowed up to bucket size&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Leaky bucket&lt;/strong&gt;: requests processed at a fixed rate, excess queued or dropped&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Provide different rate limit tiers for different clients (authenticated vs. anonymous, free vs. paid).&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;idempotency-keys&quot; tabindex=&quot;-1&quot;&gt;Idempotency Keys &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#idempotency-keys&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;For non-idempotent operations like POST, idempotency keys let clients retry safely. There&#39;s no risk of duplicate side effects:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-X&lt;/span&gt; POST https://api.example.com/payments &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Idempotency-Key: 7f3a2b1c-4d5e-6f7g-8h9i-0j1k2l3m4n5o&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Content-Type: application/json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{&quot;amount&quot;: 9900, &quot;currency&quot;: &quot;USD&quot;}&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The server caches the response for the idempotency key for a defined window. A retry with the same key returns the cached response without reprocessing the payment.&lt;/p&gt;
&lt;p&gt;This pattern is essential for payment APIs and order creation. It matters for any operation with financial or irreversible consequences.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;conditional-requests-and-optimistic-concurrency&quot; tabindex=&quot;-1&quot;&gt;Conditional Requests and Optimistic Concurrency &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#conditional-requests-and-optimistic-concurrency&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;HTTP provides mechanisms for conditional operations via ETags and timestamps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ETags&lt;/strong&gt; are version identifiers for a resource:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /orders/789

HTTP/1.1 200 OK
ETag: &amp;quot;abc123&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A client can make conditional requests using &lt;code&gt;If-Match&lt;/code&gt; to prevent lost updates:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Only update if the resource still has this ETag (optimistic locking)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-X&lt;/span&gt; PUT https://api.example.com/orders/789 &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;If-Match: &lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt;abc123&lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&#39;{ &quot;status&quot;: &quot;processing&quot; }&#39;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Someone else may have modified the resource since the client retrieved it. If so, the server returns &lt;code&gt;412 Precondition Failed&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;For cache revalidation, clients use &lt;code&gt;If-None-Match&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Only send the body if the resource has changed&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;If-None-Match: &lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt;abc123&lt;span class=&quot;token entity&quot; title=&quot;&#92;&amp;quot;&quot;&gt;&#92;&quot;&lt;/span&gt;&quot;&lt;/span&gt; https://api.example.com/orders/789
&lt;span class=&quot;token comment&quot;&gt;# Returns 304 Not Modified if unchanged&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;If-Modified-Since&lt;/code&gt; / &lt;code&gt;Last-Modified&lt;/code&gt; work similarly but with timestamps.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;long-running-operations&quot; tabindex=&quot;-1&quot;&gt;Long-Running Operations &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#long-running-operations&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some operations take seconds or minutes. Don&#39;t block. Return &lt;code&gt;202 Accepted&lt;/code&gt; and provide a way to track progress:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;POST /reports/generate

HTTP/1.1 &lt;span class=&quot;token number&quot;&gt;202&lt;/span&gt; Accepted
Location: /jobs/report-abc123

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;jobId&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;report-abc123&quot;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pending&quot;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;statusUrl&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/jobs/report-abc123&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /jobs/report-abc123

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;jobId&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;report-abc123&quot;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;running&quot;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;progress&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;startedAt&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2026-03-27T09:00:00Z&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;GET /jobs/report-abc123

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;jobId&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;report-abc123&quot;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;completed&quot;&lt;/span&gt;,
  &lt;span class=&quot;token string&quot;&gt;&quot;result&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/reports/2026-q1.pdf&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alternatively, use webhooks to push completion notifications rather than requiring polling.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;api-design-best-practices&quot; tabindex=&quot;-1&quot;&gt;API Design Best Practices &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#api-design-best-practices&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;be-consistent&quot; tabindex=&quot;-1&quot;&gt;Be Consistent &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#be-consistent&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Inconsistency is the number one user-experience failure in APIs. Pick conventions for naming, casing, error format, date format, pagination, and versioning, then apply them everywhere.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Field names: &lt;code&gt;camelCase&lt;/code&gt; or &lt;code&gt;snake_case&lt;/code&gt;, pick one&lt;/li&gt;
&lt;li&gt;Dates: ISO 8601 (&lt;code&gt;2026-03-27T09:00:00Z&lt;/code&gt;) always, never Unix timestamps in mixed APIs&lt;/li&gt;
&lt;li&gt;Booleans: &lt;code&gt;isActive&lt;/code&gt;, not &lt;code&gt;active&lt;/code&gt;, &lt;code&gt;enabled&lt;/code&gt;, or &lt;code&gt;status: &amp;quot;true&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;document-everything&quot; tabindex=&quot;-1&quot;&gt;Document Everything &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#document-everything&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An API without documentation is incomplete. Use OpenAPI 3.x to describe your API. It enables generated documentation, client SDK generation, request validation, and contract testing.&lt;/p&gt;
&lt;p&gt;Document:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every endpoint, method, and path parameter&lt;/li&gt;
&lt;li&gt;Every request body field (required vs. optional, constraints)&lt;/li&gt;
&lt;li&gt;Every response status code and body&lt;/li&gt;
&lt;li&gt;Authentication requirements&lt;/li&gt;
&lt;li&gt;Rate limits&lt;/li&gt;
&lt;li&gt;Deprecation timelines&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;use-https-exclusively&quot; tabindex=&quot;-1&quot;&gt;Use HTTPS Exclusively &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#use-https-exclusively&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Never offer plain HTTP. Redirect all HTTP traffic to HTTPS. Use HSTS (&lt;code&gt;Strict-Transport-Security&lt;/code&gt;) to prevent downgrade attacks.&lt;/p&gt;
&lt;h3 id=&quot;design-for-your-consumer&quot; tabindex=&quot;-1&quot;&gt;Design for Your Consumer &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#design-for-your-consumer&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;APIs are products. Talk to the developers who will use your API. Understand their workflows. APIs that are technically correct but operationally awkward get abandoned. I always advise treating your API contract like a public function signature in a library.&lt;/p&gt;
&lt;h3 id=&quot;backward-compatibility-is-a-promise&quot; tabindex=&quot;-1&quot;&gt;Backward Compatibility is a Promise &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#backward-compatibility-is-a-promise&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once you publish an endpoint, clients depend on it. Make breaking changes rare, communicate them well, and support them with a migration path and a generous deprecation window.&lt;/p&gt;
&lt;h3 id=&quot;keep-actions-atomic&quot; tabindex=&quot;-1&quot;&gt;Keep Actions Atomic &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#keep-actions-atomic&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;An endpoint should either succeed completely or fail completely. Partial success is a consistency nightmare. If a batch operation partially fails, return a clear, structured response. It should identify which items succeeded and which failed, with specific error details.&lt;/p&gt;
&lt;h3 id=&quot;dont-return-200-with-an-error-body&quot; tabindex=&quot;-1&quot;&gt;Don&#39;t Return 200 with an Error Body &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#dont-return-200-with-an-error-body&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This bears repeating. Use the HTTP status code correctly. Your monitoring, your clients, and your own sanity will thank you.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;closing&quot; tabindex=&quot;-1&quot;&gt;Closing &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-production-patterns/#closing&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most &amp;quot;REST APIs&amp;quot; in production implement a subset of the constraints. They ignore the rest, particularly HATEOAS. That&#39;s a pragmatic reality of software engineering. It&#39;s fine, as long as it&#39;s a deliberate choice.&lt;/p&gt;
&lt;p&gt;Think about it. The difference between a good API and a frustrating one usually isn&#39;t the technology. It&#39;s the care taken with every small decision. The status code, the error message, the version strategy, the pagination contract. Those decisions compound. Clients you don&#39;t know yet will inherit them.&lt;/p&gt;
&lt;p&gt;Build APIs you&#39;d want to consume yourself.&lt;/p&gt;
&lt;p&gt;PS: Let me know if I missed anything. Ping me on &lt;a href=&quot;https://twitter.com/irina_scurtu&quot;&gt;Twitter/X&lt;/a&gt; or &lt;a href=&quot;https://linkedin.com/in/irinascurtu&quot;&gt;LinkedIn&lt;/a&gt;, and follow along there if you&#39;d like more of this. Let&#39;s chat.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>REST API Design: Resources, Versioning, and Auth That Hold Up (Part 2 of 3)</title>
			<link href="https://irina.codes/rest-api-resource-design-versioning-auth/"/>
			<updated>2026-08-06T00:00:00Z</updated>
			<id>https://irina.codes/rest-api-resource-design-versioning-auth/</id>
			<content type="html">&lt;p&gt;In &lt;a href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/&quot;&gt;the first post in this series&lt;/a&gt;, I covered REST&#39;s architectural constraints and the HTTP semantics (methods, status codes) that most APIs get subtly wrong. Constraints and semantics get you a technically correct API. They don&#39;t get you a good one.&lt;/p&gt;
&lt;p&gt;A good API needs resources named sensibly and a versioning strategy that won&#39;t break your clients every quarter. It also needs an authentication pattern matching your threat model, and errors that tell the caller something useful. That&#39;s what this post covers.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;rest-api-resource-design&quot; tabindex=&quot;-1&quot;&gt;REST API Resource Design &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#rest-api-resource-design&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Resources are the nouns of your API. Good resource design is the single biggest factor in how usable and evolvable your API will be.&lt;/p&gt;
&lt;h3 id=&quot;uris-should-identify-resources-not-actions&quot; tabindex=&quot;-1&quot;&gt;URIs Should Identify Resources, Not Actions &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#uris-should-identify-resources-not-actions&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;# Good - noun-based, resource-centric
GET  /orders
GET  /orders/789
POST /orders
PUT  /orders/789
DELETE /orders/789

# Bad - verb-based, RPC-style
POST /getOrder
POST /createOrder
POST /deleteOrder
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The HTTP method carries the action. The URI carries the identity of the resource the action applies to.&lt;/p&gt;
&lt;h3 id=&quot;singular-vs-plural&quot; tabindex=&quot;-1&quot;&gt;Singular vs. Plural &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#singular-vs-plural&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Use plurals for collections consistently:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/users          # collection
/users/42       # single resource
/users/42/posts # sub-collection
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Mixing singular and plural is a maintenance and documentation headache.&lt;/p&gt;
&lt;h3 id=&quot;hierarchical-relationships&quot; tabindex=&quot;-1&quot;&gt;Hierarchical Relationships &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#hierarchical-relationships&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Nest resources to express ownership or containment, but limit nesting depth:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;/users/42/orders         # orders belonging to user 42
/users/42/orders/789     # order 789 belonging to user 42
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Deep nesting (more than two levels) is a sign that you may need to reconsider your resource model. &lt;code&gt;/users/42/orders/789/items/1/reviews/5&lt;/code&gt; is hard to work with. In my opinion, it&#39;s better to use query parameters to filter at the top-level collection:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /orders?userId=42
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;resource-identifiers&quot; tabindex=&quot;-1&quot;&gt;Resource Identifiers &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#resource-identifiers&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Prefer opaque, stable identifiers. UUIDs or ULIDs are better than auto-increment integers for several reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They don&#39;t expose record counts (a security and competitive concern)&lt;/li&gt;
&lt;li&gt;They can be generated client-side without a round-trip&lt;/li&gt;
&lt;li&gt;They&#39;re safe to use across environments without collision&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you publish a URI, it is a contract. Change it only with proper versioning and redirect support.&lt;/p&gt;
&lt;h3 id=&quot;controller-resources&quot; tabindex=&quot;-1&quot;&gt;Controller Resources &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#controller-resources&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some operations don&#39;t map cleanly onto CRUD. Use controller resources for these, named with a verb and invoked with POST:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;POST /orders/789/cancel
POST /accounts/42/verify-email
POST /payments/refund
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is better than bending PUT or PATCH into something unnatural.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;versioning-strategies&quot; tabindex=&quot;-1&quot;&gt;Versioning Strategies &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#versioning-strategies&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;APIs change. Resources get new fields, old fields get removed, behavior changes. Versioning is how you manage change without breaking existing clients.&lt;/p&gt;
&lt;h3 id=&quot;uri-versioning&quot; tabindex=&quot;-1&quot;&gt;URI Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#uri-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;/v1/users
/v2/users
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pros: explicit, easy to route at the infrastructure level, easy to document and test separately, shows up clearly in logs.&lt;/p&gt;
&lt;p&gt;Cons: technically violates REST&#39;s uniform interface (the same resource should have one canonical URI), pollutes the URI space, encourages &amp;quot;big bang&amp;quot; version increments rather than incremental evolution.&lt;/p&gt;
&lt;p&gt;This is the most pragmatic approach and the most widely used.&lt;/p&gt;
&lt;h3 id=&quot;header-versioning&quot; tabindex=&quot;-1&quot;&gt;Header Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#header-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;GET /users
Accept: application/vnd.myapi.v2+json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pros: URIs remain stable, aligns with HTTP content negotiation, cleaner theoretically.&lt;/p&gt;
&lt;p&gt;Cons: harder to test in a browser, not visible in logs without extra work, requires clients to set headers correctly, harder to cache with some intermediaries.&lt;/p&gt;
&lt;h3 id=&quot;query-parameter-versioning&quot; tabindex=&quot;-1&quot;&gt;Query Parameter Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#query-parameter-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;GET /users?version=2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pros: easy to test, explicit, no header management.&lt;/p&gt;
&lt;p&gt;Cons: version in the query string is semantically awkward (it&#39;s not a filter parameter), can interfere with caching.&lt;/p&gt;
&lt;h3 id=&quot;additive-changes-vs-breaking-changes&quot; tabindex=&quot;-1&quot;&gt;Additive Changes vs. Breaking Changes &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#additive-changes-vs-breaking-changes&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The best versioning strategy is to not break clients in the first place. Additive changes are generally safe:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Adding new fields to a response&lt;/li&gt;
&lt;li&gt;Adding new optional request parameters&lt;/li&gt;
&lt;li&gt;Adding new endpoints&lt;/li&gt;
&lt;li&gt;Adding new values to an enum (this can break strict deserializers, so communicate it)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Breaking changes require a new version:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Removing or renaming fields&lt;/li&gt;
&lt;li&gt;Changing field types&lt;/li&gt;
&lt;li&gt;Changing URI structure&lt;/li&gt;
&lt;li&gt;Changing status codes for existing operations&lt;/li&gt;
&lt;li&gt;Removing endpoints&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Be pragmatic about it, and don&#39;t take it to the extreme. An API version number is not a build number. Don&#39;t do that.&lt;/p&gt;
&lt;h3 id=&quot;versioning-in-practice&quot; tabindex=&quot;-1&quot;&gt;Versioning in Practice &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#versioning-in-practice&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Requesting a specific version via header&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Accept: application/vnd.myapi.v2+json&quot;&lt;/span&gt; https://api.example.com/users

&lt;span class=&quot;token comment&quot;&gt;# URI versioning&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; https://api.example.com/v2/users&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Maintain multiple versions simultaneously during a deprecation window. Communicate deprecation timelines clearly via &lt;code&gt;Deprecation&lt;/code&gt; and &lt;code&gt;Sunset&lt;/code&gt; response headers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Deprecation: true
Sunset: Sat, 31 Dec 2026 23:59:59 GMT
Link: &amp;lt;https://api.example.com/v2/users&amp;gt;; rel=&amp;quot;successor-version&amp;quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;authentication-patterns&quot; tabindex=&quot;-1&quot;&gt;Authentication Patterns &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#authentication-patterns&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;REST is stateless, which means credentials travel with every request. There are several established patterns.&lt;/p&gt;
&lt;h3 id=&quot;api-keys&quot; tabindex=&quot;-1&quot;&gt;API Keys &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#api-keys&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The simplest approach. You issue a key to a client, who sends it with every request, typically in a header:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;X-API-Key: abc123xyz&quot;&lt;/span&gt; https://api.example.com/data&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or as a Bearer token variant:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Authorization: ApiKey abc123xyz&quot;&lt;/span&gt; https://api.example.com/data&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Pros: simple to implement and use, easy to rotate.
Cons: no expiration built in, broad access (all-or-nothing unless scoped), key must be kept secret.&lt;/p&gt;
&lt;h3 id=&quot;http-basic-authentication&quot; tabindex=&quot;-1&quot;&gt;HTTP Basic Authentication &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#http-basic-authentication&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Username and password encoded as Base64 in the Authorization header:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-u&lt;/span&gt; username:password https://api.example.com/data
&lt;span class=&quot;token comment&quot;&gt;# Which sends:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Never use Basic auth over plain HTTP. Base64 only obscures credentials; it doesn&#39;t encrypt them. Only acceptable over HTTPS.&lt;/p&gt;
&lt;h3 id=&quot;bearer-tokens-jwt-oauth-2-0&quot; tabindex=&quot;-1&quot;&gt;Bearer Tokens (JWT / OAuth 2.0) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#bearer-tokens-jwt-oauth-2-0&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The dominant pattern for modern APIs. You obtain a token through an authentication flow and include it as a Bearer token:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;curl&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-H&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Authorization: Bearer eyJhbGciOiJSUzI1NiJ9...&quot;&lt;/span&gt; https://api.example.com/data&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;JWTs (JSON Web Tokens) are self-contained: they encode claims (user ID, roles, expiry) and are cryptographically signed. The server can validate them without a database lookup.&lt;/p&gt;
&lt;p&gt;Key properties to verify when processing JWTs:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Signature validity (using the correct public key or shared secret)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;exp&lt;/code&gt; claim (not expired)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;iss&lt;/code&gt; claim (issued by the expected authority)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;aud&lt;/code&gt; claim (intended for your service)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OAuth 2.0 defines flows for obtaining tokens. The authorization code flow is appropriate for delegated access on behalf of a user. Client credentials flow is appropriate for machine-to-machine APIs.&lt;/p&gt;
&lt;h3 id=&quot;mutual-tls-mtls&quot; tabindex=&quot;-1&quot;&gt;Mutual TLS (mTLS) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#mutual-tls-mtls&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Both client and server present certificates. Strong authentication with no bearer token to steal, but operationally complex (certificate management, rotation). Common in zero-trust internal service meshes.&lt;/p&gt;
&lt;h3 id=&quot;choosing-a-pattern&quot; tabindex=&quot;-1&quot;&gt;Choosing a Pattern &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#choosing-a-pattern&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Public API, external developers&lt;/strong&gt;: OAuth 2.0 with API keys for non-delegated access&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Service-to-service&lt;/strong&gt;: mTLS or client credentials flow&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Internal tooling&lt;/strong&gt;: API keys or JWT with short expiry&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;User-facing mobile/web&lt;/strong&gt;: OAuth 2.0 authorization code flow with PKCE&lt;/li&gt;
&lt;/ul&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;error-handling&quot; tabindex=&quot;-1&quot;&gt;Error Handling &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#error-handling&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Error responses deserve as much design attention as success responses. A client that receives an error needs to know what went wrong and why. It also needs to know what, if anything, it can do about it.&lt;/p&gt;
&lt;h3 id=&quot;structure-your-error-responses&quot; tabindex=&quot;-1&quot;&gt;Structure Your Error Responses &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#structure-your-error-responses&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ad-hoc error messages are hard to handle programmatically. Define a consistent error structure and use it everywhere:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://api.example.com/errors/validation-failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Validation Failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;422&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;detail&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;The request payload contains invalid values.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;instance&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/orders/create&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;errors&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;field&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;email&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;INVALID_FORMAT&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Email address is not in a valid format.&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;field&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;quantity&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;code&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;OUT_OF_RANGE&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Quantity must be between 1 and 100.&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This follows &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc7807&quot;&gt;RFC 7807 (Problem Details for HTTP APIs)&lt;/a&gt;, a standard worth adopting. The &lt;code&gt;type&lt;/code&gt; field is a URI that identifies the error type (it should resolve to documentation). The &lt;code&gt;instance&lt;/code&gt; field identifies the specific occurrence.&lt;/p&gt;
&lt;h3 id=&quot;be-specific&quot; tabindex=&quot;-1&quot;&gt;Be Specific &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#be-specific&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A 400 with &amp;quot;bad request&amp;quot; is nearly useless. Tell the client:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which field(s) failed validation&lt;/li&gt;
&lt;li&gt;What constraint was violated&lt;/li&gt;
&lt;li&gt;What the expected format or range is&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;dont-leak-internals&quot; tabindex=&quot;-1&quot;&gt;Don&#39;t Leak Internals &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#dont-leak-internals&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Error responses must never expose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stack traces&lt;/li&gt;
&lt;li&gt;Database error messages&lt;/li&gt;
&lt;li&gt;Internal server paths&lt;/li&gt;
&lt;li&gt;Framework-level exceptions&lt;/li&gt;
&lt;li&gt;Secrets or keys&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Log those internally. Return only what the client needs to understand and act on the error.&lt;/p&gt;
&lt;h3 id=&quot;distinguish-permanent-from-transient-errors&quot; tabindex=&quot;-1&quot;&gt;Distinguish Permanent from Transient Errors &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#distinguish-permanent-from-transient-errors&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;4xx errors are the client&#39;s fault. Retrying the same request unchanged is pointless.&lt;/li&gt;
&lt;li&gt;5xx errors are the server&#39;s fault. The client may retry, ideally with exponential backoff and jitter.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Make this distinction explicit in your API documentation and, where possible, in the response itself.&lt;/p&gt;
&lt;h3 id=&quot;correlation-ids&quot; tabindex=&quot;-1&quot;&gt;Correlation IDs &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#correlation-ids&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Return a correlation ID in every error response (and ideally every response):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;X-Correlation-Id: 7f3a2b1c-4d5e-6f7g-8h9i-0j1k2l3m4n5o
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This allows support teams to trace a client-reported error back to server-side logs without guessing.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;closing&quot; tabindex=&quot;-1&quot;&gt;Closing &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/#closing&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Resource design, versioning, and auth get your API to a place where it&#39;s usable and safe to evolve. What&#39;s still missing is what happens once real traffic hits it: pagination, discoverability, rate limits, and idempotency.&lt;/p&gt;
&lt;p&gt;That&#39;s what the &lt;a href=&quot;https://irina.codes/rest-api-production-patterns/&quot;&gt;last post in this series&lt;/a&gt; covers.&lt;/p&gt;
&lt;p&gt;PS: Let me know if I missed anything. Ping me on &lt;a href=&quot;https://twitter.com/irina_scurtu&quot;&gt;Twitter/X&lt;/a&gt; or &lt;a href=&quot;https://linkedin.com/in/irinascurtu&quot;&gt;LinkedIn&lt;/a&gt;, and follow along there if you&#39;d like more of this. Let&#39;s chat.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>REST API Design: The Constraints and HTTP Semantics Everyone Skips (Part 1 of 3)</title>
			<link href="https://irina.codes/rest-api-constraints-and-http-semantics/"/>
			<updated>2026-07-30T00:00:00Z</updated>
			<id>https://irina.codes/rest-api-constraints-and-http-semantics/</id>
			<content type="html">&lt;p&gt;REST is everywhere. Almost every web application, mobile app, and microservice you interact with today exposes or consumes a REST API. Yet most developers learn REST by cargo-culting patterns they see in tutorials. They slap HTTP verbs on endpoints and call it done.&lt;/p&gt;
&lt;p&gt;This is the first post in a series on REST API design that goes deeper than the tutorials. We&#39;ll start with the part everyone skips: what REST actually is, the constraints Roy Fielding defined it by, and the HTTP semantics that most APIs get subtly wrong. That means methods and status codes.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;what-rest-actually-is&quot; tabindex=&quot;-1&quot;&gt;What REST Actually Is &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#what-rest-actually-is&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;REST stands for &lt;strong&gt;Representational State Transfer&lt;/strong&gt;. Roy Fielding defined it in his 2000 doctoral dissertation, where he described the architectural style that underpins the web itself.&lt;/p&gt;
&lt;p&gt;The key insight in Fielding&#39;s thesis is that REST is not a protocol or a standard. It is an &lt;strong&gt;architectural style&lt;/strong&gt;: a set of constraints that, applied together, produce desirable properties in a distributed hypermedia system. If your API doesn&#39;t satisfy those constraints, it isn&#39;t REST. It may still be a perfectly useful HTTP API, but calling it REST is technically incorrect.&lt;/p&gt;
&lt;p&gt;This distinction matters more than it might seem. Fielding designed REST to be highly scalable, loosely coupled, and evolvable over time. Those properties emerge from the constraints, not from any single choice like &amp;quot;use JSON&amp;quot; or &amp;quot;put the ID in the URL&amp;quot;. Understanding the constraints is understanding the &lt;em&gt;why&lt;/em&gt; behind every REST best practice.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;the-six-architectural-constraints&quot; tabindex=&quot;-1&quot;&gt;The Six Architectural Constraints &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#the-six-architectural-constraints&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Fielding defined six constraints. The first five are mandatory; the sixth is optional.&lt;/p&gt;
&lt;h3 id=&quot;1-client-server&quot; tabindex=&quot;-1&quot;&gt;1. Client-Server &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#1-client-server&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A uniform interface separates the client and server. The client is responsible for the user interface and user experience. The server is responsible for data storage, business logic, and security.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; This separation improves portability of the UI across platforms and scalability of the server components. Neither side needs to know the implementation details of the other. A web client, a mobile client, and a CLI tool can all consume the same API. The server doesn&#39;t care which one is making the request.&lt;/p&gt;
&lt;h3 id=&quot;2-stateless&quot; tabindex=&quot;-1&quot;&gt;2. Stateless &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#2-stateless&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each request from a client to a server must contain all the information necessary to process it. The client keeps all session state. The server stores no client context between requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Statelessness enables horizontal scaling. Any server in a pool can handle any request because no server holds session state. It also improves visibility: you can understand a single request in isolation, which helps with debugging and monitoring. And it improves reliability, since partial failures don&#39;t leave dangling server-side sessions.&lt;/p&gt;
&lt;p&gt;The practical consequence: if a user is authenticated, the authentication token travels with every single request. The server never says &amp;quot;I remember you from last time.&amp;quot; It re-validates the token on every call.&lt;/p&gt;
&lt;h3 id=&quot;3-cacheable&quot; tabindex=&quot;-1&quot;&gt;3. Cacheable &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#3-cacheable&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Responses must define themselves as cacheable or non-cacheable. If a response is cacheable, the client (or an intermediate proxy) can reuse that response for equivalent future requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Caching eliminates some client-server interactions entirely, improving efficiency, scalability, and perceived performance. HTTP already has a rich caching model (&lt;code&gt;Cache-Control&lt;/code&gt;, &lt;code&gt;ETag&lt;/code&gt;, &lt;code&gt;Last-Modified&lt;/code&gt;, &lt;code&gt;Expires&lt;/code&gt;), and REST makes full use of it.&lt;/p&gt;
&lt;p&gt;Getting caching right requires deliberate thought about:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which resources change frequently vs. infrequently&lt;/li&gt;
&lt;li&gt;Whether two requests are truly equivalent (same query parameters, same &lt;code&gt;Accept&lt;/code&gt; header, etc.)&lt;/li&gt;
&lt;li&gt;Conditional requests (&lt;code&gt;If-None-Match&lt;/code&gt;, &lt;code&gt;If-Modified-Since&lt;/code&gt;) for cheap revalidation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;4-uniform-interface&quot; tabindex=&quot;-1&quot;&gt;4. Uniform Interface &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#4-uniform-interface&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is the central constraint that distinguishes REST from other network-based styles. It has four sub-constraints:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Identification of resources.&lt;/strong&gt; Stable identifiers (URIs) identify each resource. The resource and its representation are distinct. The server might store user data in a relational database. It can represent that data as JSON, XML, or HTML, depending on what the client requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Manipulation of resources through representations.&lt;/strong&gt; When a client holds a representation of a resource, including any metadata, it has enough information to modify or delete the resource. It just needs permission.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Self-descriptive messages.&lt;/strong&gt; Each message includes enough information to describe how to process it. A &lt;code&gt;Content-Type: application/json&lt;/code&gt; header tells the recipient exactly how to interpret the body. This decouples message processing from the application logic.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hypermedia as the engine of application state (HATEOAS).&lt;/strong&gt; Clients interact with the application entirely through hypermedia that the server provides dynamically. I cover this one in detail in the last post of this series.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; A uniform interface simplifies the architecture and makes it easier to evolve parts of the system independently. The cost is efficiency: the interface generalizes rather than optimizing for any specific use case. That is an explicit and intentional trade-off in REST.&lt;/p&gt;
&lt;h3 id=&quot;5-layered-system&quot; tabindex=&quot;-1&quot;&gt;5. Layered System &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#5-layered-system&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The architecture must be composable from hierarchical layers. A client cannot tell whether it&#39;s talking directly to the server or to an intermediary. You can insert intermediaries, such as load balancers, API gateways, caches, and security proxies, transparently.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why it matters:&lt;/strong&gt; Layers let you enforce security policies at the boundary, cache aggressively at edge nodes, and load-balance without clients knowing. They also let you evolve infrastructure independently from application logic. HTTPS termination at a load balancer is a layered system in action.&lt;/p&gt;
&lt;h3 id=&quot;6-code-on-demand-optional&quot; tabindex=&quot;-1&quot;&gt;6. Code on Demand (Optional) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#6-code-on-demand-optional&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Servers can extend client functionality by transferring executable code, like JavaScript running in a browser. This constraint is optional because it reduces visibility. You can&#39;t easily audit what code a client will execute, and it creates coupling.&lt;/p&gt;
&lt;p&gt;Most API design work ignores code on demand. It shows up primarily in browser-based applications, not in service-to-service APIs.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;http-methods&quot; tabindex=&quot;-1&quot;&gt;HTTP Methods &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#http-methods&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;HTTP provides a small vocabulary of methods (also called verbs). REST maps operations on resources to this vocabulary. Each method carries a defined semantic and a set of properties.&lt;/p&gt;
&lt;h3 id=&quot;the-core-methods&quot; tabindex=&quot;-1&quot;&gt;The Core Methods &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#the-core-methods&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Method&lt;/th&gt;
&lt;th&gt;Semantics&lt;/th&gt;
&lt;th&gt;Safe&lt;/th&gt;
&lt;th&gt;Idempotent&lt;/th&gt;
&lt;th&gt;Request Body&lt;/th&gt;
&lt;th&gt;Response Body&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;GET&lt;/td&gt;
&lt;td&gt;Retrieve a resource or collection&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;POST&lt;/td&gt;
&lt;td&gt;Create a subordinate resource, or trigger a process&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PUT&lt;/td&gt;
&lt;td&gt;Replace a resource entirely&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PATCH&lt;/td&gt;
&lt;td&gt;Partially update a resource&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No (usually)&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DELETE&lt;/td&gt;
&lt;td&gt;Remove a resource&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;td&gt;Optional&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;HEAD&lt;/td&gt;
&lt;td&gt;Same as GET but body omitted&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;OPTIONS&lt;/td&gt;
&lt;td&gt;Retrieve supported methods and CORS preflight&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Safe&lt;/strong&gt; means the method should not have any observable side effects on the server. Clients (and proxies) can prefetch or retry safe methods freely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Idempotent&lt;/strong&gt; means that multiple identical requests produce the same server state as a single request. A &lt;code&gt;DELETE /users/42&lt;/code&gt; should have the same effect whether called once or ten times. This property is what makes retries safe.&lt;/p&gt;
&lt;h3 id=&quot;common-misuses&quot; tabindex=&quot;-1&quot;&gt;Common Misuses &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#common-misuses&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;POST for everything.&lt;/strong&gt; Using POST for reads, updates, and deletes is a common anti-pattern. It throws away idempotency and prevents HTTP-layer caching.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PUT when you mean PATCH.&lt;/strong&gt; PUT replaces the entire resource. If a client sends a PUT with a partial payload and the server accepts it, the result behaves like PATCH. On paper, though, it&#39;s still PUT. This creates subtle bugs when clients omit fields they didn&#39;t intend to clear.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DELETE with a body.&lt;/strong&gt; While technically possible, HTTP clients and intermediaries poorly support bodies on DELETE requests. If you need to delete multiple resources, prefer a separate endpoint or use PATCH on a collection.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;POST vs. PUT for creation.&lt;/strong&gt; Use POST when the server assigns the identifier: you POST to &lt;code&gt;/orders&lt;/code&gt; and the server returns &lt;code&gt;/orders/789&lt;/code&gt;. Use PUT when the client specifies the identifier: you PUT to &lt;code&gt;/users/alice&lt;/code&gt; to create or replace that resource.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;http-status-codes&quot; tabindex=&quot;-1&quot;&gt;HTTP Status Codes &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#http-status-codes&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Status codes are not decoration. They carry semantic meaning and drive client behavior. Using the wrong status code is like raising the wrong exception type. Technically it works, but everyone downstream makes the wrong decisions because of it.&lt;/p&gt;
&lt;h3 id=&quot;the-five-classes&quot; tabindex=&quot;-1&quot;&gt;The Five Classes &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#the-five-classes&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1xx, Informational&lt;/strong&gt;: Rarely used in REST APIs. &lt;code&gt;100 Continue&lt;/code&gt; tells the client it can proceed with a large request body.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2xx, Success&lt;/strong&gt;: The request was received, understood, and accepted.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3xx, Redirection&lt;/strong&gt;: Further action needs to be taken to complete the request.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4xx, Client Error&lt;/strong&gt;: The client sent an invalid request. Retrying the same request unchanged will not succeed.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5xx, Server Error&lt;/strong&gt;: The server failed to process a valid request. The client may retry.&lt;/p&gt;
&lt;h3 id=&quot;key-codes-in-detail&quot; tabindex=&quot;-1&quot;&gt;Key Codes in Detail &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#key-codes-in-detail&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;200 OK&lt;/strong&gt;: General success. The response body contains the result. Appropriate for GET, PUT, PATCH.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;201 Created&lt;/strong&gt;: A new resource was created. The &lt;code&gt;Location&lt;/code&gt; header should point to the new resource URI. Appropriate for POST and PUT when creating.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;202 Accepted&lt;/strong&gt;: The server accepted the request for processing, but processing isn&#39;t complete. Used for async operations. The response should explain how the client can check status.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;204 No Content&lt;/strong&gt;: Success, but there is nothing to return. Common for DELETE and some PUTs.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;301 Moved Permanently&lt;/strong&gt;: The resource has a new permanent URI. Clients should update their bookmarks.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;304 Not Modified&lt;/strong&gt;: The client&#39;s cached copy is still fresh. No body is returned. Used with conditional requests.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;400 Bad Request&lt;/strong&gt;: The server cannot process the request because of a client error (malformed JSON, invalid field values, missing required parameters). Return a body that explains what is wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;401 Unauthorized&lt;/strong&gt;: The request lacks valid authentication credentials. Despite the name, this code means &amp;quot;unauthenticated&amp;quot;: the client needs to provide credentials.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;403 Forbidden&lt;/strong&gt;: The client is authenticated but does not have permission to access the resource. Don&#39;t return 404 as a security measure unless you want to hide the existence of the resource.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;404 Not Found&lt;/strong&gt;: The resource does not exist. Also used intentionally when a 403 would reveal information you want to protect.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;405 Method Not Allowed&lt;/strong&gt;: The HTTP method is not supported for this resource. The response must include an &lt;code&gt;Allow&lt;/code&gt; header listing valid methods.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;409 Conflict&lt;/strong&gt;: The request conflicts with the current state of the resource. Common for optimistic concurrency failures or duplicate creation attempts.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;410 Gone&lt;/strong&gt;: The resource existed but has been permanently deleted. Distinct from 404; useful for caches and clients to know not to ask again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;422 Unprocessable Entity&lt;/strong&gt;: The request is syntactically valid but semantically wrong (valid JSON, but the business rules reject it). Preferred over 400 for domain validation failures.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;429 Too Many Requests&lt;/strong&gt;: Rate limit exceeded. Include a &lt;code&gt;Retry-After&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;500 Internal Server Error&lt;/strong&gt;: Generic server failure. Never expose stack traces or internal details.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;503 Service Unavailable&lt;/strong&gt;: The server is temporarily unable to handle requests (overloaded, maintenance). Include &lt;code&gt;Retry-After&lt;/code&gt; if known.&lt;/p&gt;
&lt;h3 id=&quot;the-mistake-of-returning-200-for-everything&quot; tabindex=&quot;-1&quot;&gt;The Mistake of Returning 200 for Everything &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#the-mistake-of-returning-200-for-everything&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A common anti-pattern is returning &lt;code&gt;200 OK&lt;/code&gt; with a body that contains an error flag:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;HTTP/1.1 200 OK

{ &amp;quot;success&amp;quot;: false, &amp;quot;error&amp;quot;: &amp;quot;User not found&amp;quot; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This forces every client to parse the body to know whether the request succeeded. It breaks HTTP-aware intermediaries (caches will cache failure responses), APM tools, and alerting systems. Use the status code as it was designed to be used.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;closing&quot; tabindex=&quot;-1&quot;&gt;Closing &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/rest-api-constraints-and-http-semantics/#closing&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Get the constraints and the HTTP semantics right, and you&#39;ve built the foundation. Neither one is optional trivia. The rest of REST design only works if two things hold: a client that trusts your status codes, and an architecture that respects statelessness.&lt;/p&gt;
&lt;p&gt;Next in this series: &lt;a href=&quot;https://irina.codes/rest-api-resource-design-versioning-auth/&quot;&gt;how to design the resources themselves, version them without breaking clients, and choose an authentication pattern that matches your actual threat model&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;PS: Let me know if I missed anything, ping me on &lt;a href=&quot;https://twitter.com/irina_scurtu&quot;&gt;Twitter/X&lt;/a&gt; or &lt;a href=&quot;https://linkedin.com/in/irinascurtu&quot;&gt;LinkedIn&lt;/a&gt;, and follow along there if you&#39;d like more of this. Let&#39;s chat.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Load Shedding: Dropping Requests to Save Your System</title>
			<link href="https://irina.codes/load-shedding-dropping-requests-to-save-your-system/"/>
			<updated>2026-07-23T00:00:00Z</updated>
			<id>https://irina.codes/load-shedding-dropping-requests-to-save-your-system/</id>
			<content type="html">&lt;p&gt;&lt;em&gt;Your system under pressure will either shed load deliberately or collapse accidentally.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Traffic spikes. Response times climb. Clients start retrying. The retries add more load. Response times climb further. At some point, the whole thing tips over, and every user gets nothing instead of something.&lt;/p&gt;
&lt;p&gt;Most teams I talk to reach for circuit breakers or rate limiters when this happens. Both are good tools. Neither solves this problem. The missing piece, the one that keeps your system alive when everything else is failing, is &lt;strong&gt;load shedding&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;And the reason most teams don&#39;t have it is that they think they already do.&lt;/p&gt;
&lt;h2 id=&quot;what-load-shedding-actually-is-and-what-it-isn-t&quot; tabindex=&quot;-1&quot;&gt;What load shedding actually is (and what it isn&#39;t) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/load-shedding-dropping-requests-to-save-your-system/#what-load-shedding-actually-is-and-what-it-isn-t&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let me be precise about terms, because this is where most confusion starts.&lt;/p&gt;
&lt;p&gt;Rate limiting is about &lt;em&gt;identity and quota&lt;/em&gt;. You allow 100 requests per second per client. It doesn&#39;t matter how healthy your system is: if a client is over quota, they&#39;re rejected. Rate limiting protects you from individual bad actors or runaway clients.&lt;/p&gt;
&lt;p&gt;Circuit breakers are about &lt;em&gt;outbound calls&lt;/em&gt;. When a downstream service is failing, you stop calling it. The circuit breaker sits between you and your dependencies.&lt;/p&gt;
&lt;p&gt;Load shedding is different. It responds to your system&#39;s health right now. When CPU is saturating, memory is tight, or your request queue is backing up, you drop requests. It doesn&#39;t matter who sent them. Not because of policy. Because you physically cannot serve them well.&lt;/p&gt;
&lt;p&gt;The correct HTTP response for a shed request is &lt;code&gt;503 Service Unavailable&lt;/code&gt; with a &lt;code&gt;Retry-After&lt;/code&gt; header. Not &lt;code&gt;429 Too Many Requests&lt;/code&gt;: that implies the caller is the problem. &lt;code&gt;503&lt;/code&gt; says: it&#39;s us, not you, try again in a moment.&lt;/p&gt;
&lt;p&gt;A minimal implementation in ASP.NET Core 7+ looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; memoryInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; GC&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetGCMemoryInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; load &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;memoryInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MemoryLoadBytes &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt; memoryInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;HighMemoryLoadThresholdBytes&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;load &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0.85&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; _activeRequests &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; _maxConcurrent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusCode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;503&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Headers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Retry-After&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    Interlocked&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Increment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; _activeRequests&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;finally&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Interlocked&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Decrement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; _activeRequests&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Simple. But where you put this matters more than you might think.&lt;/p&gt;
&lt;h2 id=&quot;the-cost-of-where-you-shed&quot; tabindex=&quot;-1&quot;&gt;The cost of where you shed &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/load-shedding-dropping-requests-to-save-your-system/#the-cost-of-where-you-shed&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Think about it: by the time your middleware runs, your system has already done a lot of work.&lt;/p&gt;
&lt;p&gt;The TCP connection was accepted. TLS was negotiated. HTTP/2 frames were parsed. Request headers were read. The ASP.NET Core pipeline started. Middleware before yours executed. Dependency injection resolved your services. Authentication ran.&lt;/p&gt;
&lt;p&gt;All of that happened before you decided to return a 503.&lt;/p&gt;
&lt;p&gt;Imagine a restaurant that seats guests, hands them menus, and takes their drink order before sending it to the kitchen. Only &lt;em&gt;then&lt;/em&gt; does it tell them the kitchen is closed. You&#39;ve wasted everyone&#39;s time, including your own. &lt;strong&gt;The earlier you shed, the cheaper each rejection is.&lt;/strong&gt;&lt;/p&gt;
&lt;!-- DIAGRAM: Layered stack diagram showing Kestrel → Middleware Pipeline → Controller, with a &quot;shed here&quot; arrow at each layer and the cost already paid highlighted at each level; export from draw.io or Excalidraw --&gt;
&lt;p&gt;Kestrel exposes &lt;code&gt;MaxConcurrentConnections&lt;/code&gt; and &lt;code&gt;MaxConcurrentUpgradedConnections&lt;/code&gt; for connection-level limiting, before any HTTP parsing happens. That&#39;s the cheapest possible rejection. Your custom middleware is next. Your controller is the most expensive place to shed, because by then you&#39;ve done almost everything.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;WebHost&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConfigureKestrel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Limits&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MaxConcurrentConnections &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In practice, you often need both: Kestrel limits as a hard ceiling, middleware for health-signal-based shedding. The point is to push the decision as far left in the stack as you can.&lt;/p&gt;
&lt;h2 id=&quot;not-all-requests-deserve-equal-treatment&quot; tabindex=&quot;-1&quot;&gt;Not all requests deserve equal treatment &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/load-shedding-dropping-requests-to-save-your-system/#not-all-requests-deserve-equal-treatment&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s where most load shedding implementations leave a lot on the table.&lt;/p&gt;
&lt;p&gt;Standard shedding is binary: you&#39;re over threshold, so you reject the next request in the queue. But think about what&#39;s in that queue. Health check probes from your orchestrator. Background telemetry events. An analytics batch job. And a user trying to complete a payment.&lt;/p&gt;
&lt;p&gt;These are not the same. Dropping a telemetry flush is free. Dropping a payment request has a real cost.&lt;/p&gt;
&lt;p&gt;Google&#39;s SRE team calls these &lt;em&gt;criticality labels&lt;/em&gt;: not all RPCs are equal, so you shouldn&#39;t shed them equally. In .NET 7+, you can implement the same idea with endpoint metadata and a priority-aware middleware.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SheddingPriorityAttribute&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Attribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IEndpointMetadata&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;SheddingPriority&lt;/span&gt; Priority &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SheddingPriorityAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SheddingPriority&lt;/span&gt; priority&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; Priority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; priority&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SheddingPriority&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ShedFirst&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Normal&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Protected &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in your middleware, read the priority before deciding whether to shed:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; next&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsOverloaded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; priority &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetEndpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;Metadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;GetMetadata&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;SheddingPriorityAttribute&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;Priority &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; SheddingPriority&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Normal&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;priority &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; SheddingPriority&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Protected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;StatusCode &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;503&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Headers&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Retry-After&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;5&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Apply it on your endpoints:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MapGet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/metrics&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithMetadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;SheddingPriorityAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SheddingPriority&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ShedFirst&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MapPost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/checkout&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithMetadata&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;SheddingPriorityAttribute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;SheddingPriority&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Protected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Uber described something similar in 2019. They shed roughly 10% of requests during peak events, with no measurable customer impact. The reason? They shed the &lt;em&gt;right&lt;/em&gt; 10%.&lt;/p&gt;
&lt;h2 id=&quot;static-thresholds-will-betray-you&quot; tabindex=&quot;-1&quot;&gt;Static thresholds will betray you &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/load-shedding-dropping-requests-to-save-your-system/#static-thresholds-will-betray-you&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most implementations I see use a static threshold: if CPU &amp;gt; 80%, start shedding. In my opinion, this is better than nothing, but it&#39;s a guess, and often a late one.&lt;/p&gt;
&lt;p&gt;Queueing theory (the M/M/1 model, if you want to look it up) tells us why. Latency doesn&#39;t degrade linearly as utilisation climbs. It degrades super-linearly. At 70% utilisation things feel fine. At 85% they start to slip. By 95% you&#39;re already in trouble, and that&#39;s when the threshold fires.&lt;/p&gt;
&lt;p&gt;The smarter signal isn&#39;t the absolute CPU value. It&#39;s the &lt;em&gt;gradient&lt;/em&gt;: is latency getting worse? By how much, and how fast?&lt;/p&gt;
&lt;p&gt;Netflix&#39;s Concurrency Limits library, which powers parts of their resilience infrastructure, uses TCP Vegas for exactly this. TCP Vegas was designed for network congestion control. Instead of detecting packet loss (the cliff), it detects the &lt;em&gt;onset&lt;/em&gt; of congestion by tracking latency increase. Load shedding can work the same way.&lt;/p&gt;
&lt;p&gt;There&#39;s no standard .NET library that does adaptive shedding like this. It&#39;s a genuine gap in the ecosystem. But you can get close with &lt;code&gt;System.Diagnostics.Metrics&lt;/code&gt; in .NET 8. Track a rolling P99 latency histogram and engage shedding when the gradient turns positive:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; histogram &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; meter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;CreateHistogram&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;request.duration.ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Record on every request: histogram.Record(elapsedMs);&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// In your shedding middleware: compare rolling P99 against baseline&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// When P99 rises &gt;20% over baseline, start shedding&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Start shedding when things are &lt;em&gt;getting worse&lt;/em&gt;, not when they&#39;re already broken. That&#39;s the difference between a controlled degradation and a collapse.&lt;/p&gt;
&lt;h2 id=&quot;what-this-means-in-practice&quot; tabindex=&quot;-1&quot;&gt;What this means in practice &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/load-shedding-dropping-requests-to-save-your-system/#what-this-means-in-practice&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Load shedding isn&#39;t one decision: it&#39;s a stack of decisions. Here&#39;s how I&#39;d approach it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shed at the lowest layer you can&lt;/strong&gt;: Kestrel limits before middleware, middleware before controllers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Assign criticality to endpoint groups&lt;/strong&gt;: protect payments, shed telemetry and batch jobs first&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use health signals, not static thresholds&lt;/strong&gt;: track latency gradient, not just CPU percentage&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Return &lt;code&gt;503 + Retry-After&lt;/code&gt;, never &lt;code&gt;429&lt;/code&gt;, for load shedding.&lt;/strong&gt; The semantics matter&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test it under load&lt;/strong&gt; by simulating overload in staging and verifying that P99 for accepted requests improves; if it doesn&#39;t, your threshold is wrong&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Layer it with your other resilience tools.&lt;/strong&gt; Load shedding doesn&#39;t replace rate limiting, circuit breakers, or backpressure; it completes them&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Remember: properly implemented load shedding &lt;em&gt;improves&lt;/em&gt; the experience for the requests you do accept. By refusing to queue work you can&#39;t process quickly, you keep latency tight for everything you take on. AWS and Google both document this. It&#39;s counterintuitive until you see the numbers.&lt;/p&gt;
&lt;p&gt;Load shedding feels like giving up. It feels like admitting your system can&#39;t handle the load, that you&#39;re turning users away.&lt;/p&gt;
&lt;p&gt;In reality, it&#39;s the opposite. It&#39;s your system making a deliberate, informed choice about who it can serve &lt;em&gt;well&lt;/em&gt; right now. That&#39;s better than trying to serve everyone badly and tipping into total failure. Systems that shed deliberately stay alive. Systems that don&#39;t shed eventually serve no one.&lt;/p&gt;
&lt;p&gt;The question worth asking: has a retry storm ever taken down one of your services? Because load shedding is precisely the thing that would have stopped it.&lt;/p&gt;
&lt;p&gt;If you find this useful, ping me on &lt;a href=&quot;https://twitter.com/irina_scurtu&quot;&gt;Twitter/X&lt;/a&gt; or &lt;a href=&quot;https://linkedin.com/in/irinascurtu&quot;&gt;LinkedIn&lt;/a&gt;. A follow there is always appreciated if you&#39;d like more of this. I&#39;m always curious what resilience patterns teams use in production.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>When Business Processes Outlive Your Request</title>
			<link href="https://irina.codes/when-business-processes-outlive-your-request/"/>
			<updated>2026-04-14T00:00:00Z</updated>
			<id>https://irina.codes/when-business-processes-outlive-your-request/</id>
			<content type="html">&lt;p&gt;&lt;em&gt;A request has a lifecycle measured in milliseconds. A business process has one measured in minutes, hours, or days - and the gap between those two timelines is where most distributed systems go wrong.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-millisecond-and-the-multi-day-process&quot; tabindex=&quot;-1&quot;&gt;The Millisecond and the Multi-Day Process &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/when-business-processes-outlive-your-request/#the-millisecond-and-the-multi-day-process&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Think about what happens when a customer places an order on an e-commerce platform. From the user&#39;s perspective, they click &amp;quot;Buy&amp;quot; and wait a few seconds for a confirmation. But behind the scenes, that single click might kick off a workflow that takes days to fully complete: payment authorisation, inventory reservation, warehouse picking, carrier handoff, customs clearance if it&#39;s international, and eventually delivery confirmation.&lt;/p&gt;
&lt;p&gt;That workflow doesn&#39;t live inside a single HTTP request. It can&#39;t - not because of any technical limitation on our end, but because the world it&#39;s coordinating with operates on a different clock. The payment provider has its own settlement windows. The warehouse runs on shifts. The carrier updates its tracking on its own schedule. And if any of those steps fail mid-flight, the system needs to know what to do, even though the original HTTP connection closed three days ago.&lt;/p&gt;
&lt;p&gt;This isn&#39;t unique to e-commerce. Loan approval workflows sit in review queues for hours before an underwriter picks them up. User onboarding flows wait for email verification and background checks before granting access. Subscription billing retries over days when a card declines. &lt;strong&gt;The moment a business process outlives its triggering request, you have a different class of problem&lt;/strong&gt; - and request-response thinking is the wrong tool for it.&lt;/p&gt;
&lt;h2 id=&quot;why-request-response-thinking-breaks-down&quot; tabindex=&quot;-1&quot;&gt;Why Request-Response Thinking Breaks Down &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/when-business-processes-outlive-your-request/#why-request-response-thinking-breaks-down&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The request-response model is beautiful in its simplicity. A client sends a request, the server does work, the server responds. The connection is open the whole time; if something fails, you get an exception; if everything succeeds, you get data back. It&#39;s easy to reason about because one thing happens at a time, in one place, under one transaction.&lt;/p&gt;
&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;sequenceDiagram&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; Client
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; Server

    Client&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;Server&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Request
    Server&lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&gt;&lt;/span&gt;Client&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Response&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Distribute that workflow across multiple services, though, and the guarantees evaporate fast. Consider an order fulfillment flow that calls three services: one charges the card, one reserves inventory, and one sends a confirmation email. If the inventory service fails after the card charge goes through, what state are you in? The database transaction that might have protected you in a monolith doesn&#39;t span service boundaries. Each service has already committed its own local transaction. You can&#39;t &amp;quot;rollback&amp;quot; a charge that&#39;s already been authorised. You can&#39;t &amp;quot;un-send&amp;quot; an email.&lt;/p&gt;
&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;sequenceDiagram&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; Client
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; OrderService as Order Service
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; PaymentService as Payment Service
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; InventoryService as Inventory Service
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; EmailService as Email Service

    Client&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;OrderService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; POST /orders
    &lt;span class=&quot;token keyword&quot;&gt;activate&lt;/span&gt; OrderService
    OrderService&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;PaymentService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Charge card
    PaymentService&lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&gt;&lt;/span&gt;OrderService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ✓ Authorised
    OrderService&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;InventoryService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Reserve stock
    InventoryService&lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&gt;&lt;/span&gt;OrderService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ✗ Out of stock
    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; PaymentService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Card already charged
    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; EmailService&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Never reached
    OrderService&lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&gt;&lt;/span&gt;Client&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; 500 Error
    &lt;span class=&quot;token keyword&quot;&gt;deactivate&lt;/span&gt; OrderService&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What you get instead is &lt;strong&gt;partial completion&lt;/strong&gt; - a system where some steps succeeded, some failed, and the overall state is inconsistent in ways that are not immediately obvious. And this isn&#39;t a corner case you can architect away. Services fail mid-flight. Networks drop connections. Deployments happen. The question isn&#39;t whether partial completion will occur; it&#39;s whether your system knows how to recover when it does.&lt;/p&gt;
&lt;p&gt;There&#39;s also the &lt;strong&gt;state drift problem&lt;/strong&gt;. In a long-running workflow, every service involved maintains its own view of the process state. If those views diverge - because a message was lost, a retry was missed, or a timeout fired at the wrong moment - you end up with services that disagree about where the workflow is. The payment service thinks the order is complete. The inventory service thinks it was cancelled. Nobody is wrong according to their own data. The system is just inconsistent.&lt;/p&gt;
&lt;p class=&quot;newsletter-inline&quot; style=&quot;border-left:3px solid #666;padding-left:0.75rem;font-style:italic&quot;&gt;Want more like this? &lt;a href=&quot;https://irinacodes.kit.com/&quot; target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot;&gt;Get the newsletter&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;sagas-a-fast-recap&quot; tabindex=&quot;-1&quot;&gt;Sagas: A Fast Recap &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/when-business-processes-outlive-your-request/#sagas-a-fast-recap&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Saga pattern was formalised by Hector Garcia-Molina and Kenneth Salem back in 1987, originally in the context of long-lived database transactions. The core idea is simple: instead of one large distributed transaction that would require coordination across all participants (which doesn&#39;t scale and is largely impractical in practice), you break the workflow into a &lt;strong&gt;sequence of smaller local transactions&lt;/strong&gt;, each of which commits independently. And for each of those steps, you define a &lt;strong&gt;compensating transaction&lt;/strong&gt; - a business action that semantically reverses what that step did, should something go wrong later.&lt;/p&gt;
&lt;p&gt;If step 4 fails in a five-step workflow, you run the compensating transactions for steps 3, 2, and 1 in reverse order. You end up in a state that&#39;s consistent again - not identical to where you started (the compensation itself is a new business event), but coherent.&lt;/p&gt;
&lt;p&gt;That&#39;s the pattern in one paragraph. Two things to hold onto: the sequence of local commits, and the compensations that undo them. Everything else - orchestration vs choreography, timeout handling, observability - is about how you implement that idea well in production.&lt;/p&gt;
&lt;h2 id=&quot;designing-the-saga-orchestration-vs-choreography&quot; tabindex=&quot;-1&quot;&gt;Designing the Saga: Orchestration vs Choreography &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/when-business-processes-outlive-your-request/#designing-the-saga-orchestration-vs-choreography&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There are two ways to coordinate a saga, and choosing the wrong one for your context creates pain that&#39;s hard to unwind.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Orchestration&lt;/strong&gt; means there&#39;s a central coordinator - a saga state machine or process manager - that drives each step. It sends commands to services, waits for responses, tracks the overall state, and decides what happens next. The workflow logic lives in one place. If you need to know where a given order is in the fulfillment process, you ask the coordinator. This is intuitive. It maps well onto how humans think about workflows, and it makes complex compensations straightforward to implement because the coordinator has the full context.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Orchestration&lt;/strong&gt; - workflow logic lives in the coordinator:&lt;/p&gt;
&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;flowchart&lt;/span&gt; TD
    C&lt;span class=&quot;token text string&quot;&gt;([Coordinator])&lt;/span&gt;
    C &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|1. ChargeCard|&lt;/span&gt; P&lt;span class=&quot;token text string&quot;&gt;[Payment]&lt;/span&gt;
    C &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|2. ReserveStock|&lt;/span&gt; I&lt;span class=&quot;token text string&quot;&gt;[Inventory]&lt;/span&gt;
    C &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|3. CreateShipment|&lt;/span&gt; S&lt;span class=&quot;token text string&quot;&gt;[Shipping]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Choreography&lt;/strong&gt; means there&#39;s no central coordinator. Each service reacts to events published by previous steps. Service A completes its work and publishes &lt;code&gt;OrderValidated&lt;/code&gt;. Service B subscribes to that event, does its part, and publishes &lt;code&gt;StockReserved&lt;/code&gt;. Service C picks that up and publishes &lt;code&gt;PaymentProcessed&lt;/code&gt;. The workflow emerges from the interactions - no single service orchestrates it. This scales better, removes a central point of coupling, and lets each team own their domain completely.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Choreography&lt;/strong&gt; - workflow emerges from events, no coordinator:&lt;/p&gt;
&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;sequenceDiagram&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; P as Payment
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; I as Inventory
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; S as Shipping

    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; P,S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; No coordinator — services react to events
    P&lt;span class=&quot;token arrow operator&quot;&gt;-)&lt;/span&gt;I&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; OrderPaid
    I&lt;span class=&quot;token arrow operator&quot;&gt;-)&lt;/span&gt;S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; StockReserved
    S&lt;span class=&quot;token arrow operator&quot;&gt;-)&lt;/span&gt;P&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ShipmentCreated&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So how do you choose? I look at a few things. If the workflow has &lt;strong&gt;complex branching logic&lt;/strong&gt; - &amp;quot;if the payment fails, route to retry; if the retry budget is exhausted, escalate to manual review; if manual review times out, cancel and notify&amp;quot; - that logic has to live somewhere. In choreography, it ends up scattered across services that need to know about each other&#39;s states, which tends to create hidden coupling. Orchestration keeps it in one place. On the other hand, if the steps are genuinely &lt;strong&gt;independent domain reactions&lt;/strong&gt; - each service truly just needs to know &amp;quot;this thing happened, now I do my part&amp;quot; - choreography gives you better isolation and resilience.&lt;/p&gt;
&lt;p&gt;Where I see teams go wrong is mixing both styles within a single workflow. An orchestrator that also listens to events from services it&#39;s supposed to be coordinating creates a tangle. Pick one model per workflow, and be deliberate about it. Choreography is not &amp;quot;orchestration without writing a coordinator&amp;quot; - it&#39;s a different design choice with different trade-offs.&lt;/p&gt;
&lt;h2 id=&quot;compensating-transactions-done-right&quot; tabindex=&quot;-1&quot;&gt;Compensating Transactions Done Right &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/when-business-processes-outlive-your-request/#compensating-transactions-done-right&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s the thing that trips up almost every team the first time they design a saga: &lt;strong&gt;compensation is not rollback&lt;/strong&gt;. They look similar on the surface - something went wrong, we go back to where we were - but they&#39;re fundamentally different operations.&lt;/p&gt;
&lt;p&gt;Rollback is a database primitive. When a transaction fails, the database engine atomically reverses every change as if the transaction never happened. It&#39;s invisible to the business. It cannot fail. It&#39;s symmetric - the before and after states are identical.&lt;/p&gt;
&lt;p&gt;Compensation is a business action. Issuing a refund is not &amp;quot;un-charging&amp;quot; a card - it&#39;s a new transaction that creates a new fact in the payment ledger. Cancelling a reservation is not &amp;quot;un-reserving&amp;quot; inventory - it&#39;s releasing stock back to the available pool, which may then be picked up by another order. Sending a cancellation email is not &amp;quot;un-sending&amp;quot; the confirmation - it&#39;s a new message with a new meaning. Compensation can fail. It can be partial. It&#39;s visible to the business and to the customer.&lt;/p&gt;
&lt;p&gt;This matters because if you design compensations as if they were rollbacks, you&#39;ll miss important cases. The most important question to ask when designing a compensating transaction is: &lt;strong&gt;&amp;quot;What if this compensation fails?&amp;quot;&lt;/strong&gt; Most teams never ask it. A failed compensation is its own error case that needs its own handling - possibly a manual intervention workflow, an alert to an operations team, or a retry with a longer delay.&lt;/p&gt;
&lt;p&gt;Design compensations upfront, not as afterthoughts. For every step in your saga, before you write the forward path, write the compensating action. Name it explicitly: &lt;code&gt;RefundPayment&lt;/code&gt;, &lt;code&gt;ReleaseReservation&lt;/code&gt;, &lt;code&gt;SendCancellationNotification&lt;/code&gt;. Some steps won&#39;t have a neat compensation - sending an SMS is very hard to undo. Model those as non-compensatable steps and think carefully about where in the saga sequence they appear.&lt;/p&gt;
&lt;p&gt;Every compensating transaction must also be &lt;strong&gt;idempotent&lt;/strong&gt;. When your saga state machine executes a compensation, it might execute it more than once - due to a retry after a timeout, or a network blip that prevented the acknowledgment from reaching the coordinator. Executing the same compensation twice must produce the same result as executing it once. The refund must not be issued twice. The reservation must not be released twice and then fail on a third attempt. Design for this from the start, not after you find the bug in production.&lt;/p&gt;
&lt;h2 id=&quot;timeouts-retries-and-the-messy-middle&quot; tabindex=&quot;-1&quot;&gt;Timeouts, Retries, and the Messy Middle &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/when-business-processes-outlive-your-request/#timeouts-retries-and-the-messy-middle&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Long-running workflows spend a lot of time in a state I call the messy middle: you&#39;ve sent a command to a service, and you don&#39;t know what happened to it.&lt;/p&gt;
&lt;p&gt;The message might have been delivered and processed successfully but the acknowledgment was lost in transit. The message might never have arrived at all. The service might have received it, started processing, crashed halfway through, and is now recovering. The service might have processed it and responded, but your network blipped and you missed the response. From the saga coordinator&#39;s perspective, all of these look identical: silence.&lt;/p&gt;
&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;sequenceDiagram&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; C as Coordinator
    &lt;span class=&quot;token keyword&quot;&gt;participant&lt;/span&gt; S as Service

    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; C,S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Scenario A — message never arrived
    C&lt;span class=&quot;token arrow operator&quot;&gt;--x&lt;/span&gt;S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Command
    C&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;C&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ⏱ Timeout

    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; C,S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Scenario B — service crashed mid-processing
    C&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Command
    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Crash
    C&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;C&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ⏱ Timeout

    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; C,S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Scenario C — acknowledgment lost
    C&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Command
    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; S&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ✓ Processed
    S&lt;span class=&quot;token arrow operator&quot;&gt;--x&lt;/span&gt;C&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Ack
    C&lt;span class=&quot;token arrow operator&quot;&gt;-&gt;&gt;&lt;/span&gt;C&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; ⏱ Timeout

    &lt;span class=&quot;token keyword&quot;&gt;Note over&lt;/span&gt; C&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; All three look identical to the coordinator&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the fundamental challenge that makes distributed systems hard. The timeout fires, and you need to make a decision: did that step complete or not?&lt;/p&gt;
&lt;p&gt;The answer is almost always: &lt;strong&gt;assume it might have completed, and design your retry as if it did&lt;/strong&gt;. Send the same command again with the same idempotency key. A well-designed service will recognise the duplicate and return the result of the first execution without re-processing. A poorly designed service will execute the operation twice. This is why idempotency isn&#39;t optional in saga steps - it&#39;s the mechanism that makes the messy middle safe to navigate.&lt;/p&gt;
&lt;p&gt;For retry policies within sagas, I think in terms of three categories. &lt;strong&gt;Transient failures&lt;/strong&gt; - network blips, brief overloads - should be retried quickly, with exponential backoff and jitter to avoid thundering-herd effects. &lt;strong&gt;Uncertain states&lt;/strong&gt; - where you timed out and don&#39;t know the outcome - should be retried with idempotency keys. &lt;strong&gt;Permanent failures&lt;/strong&gt; - a 400 Bad Request, a business rule violation, an external service that&#39;s permanently shut down - should not be retried at all. Retrying a permanent failure is just executing the same bug repeatedly.&lt;/p&gt;
&lt;p&gt;Timeout policies should be defined per step, based on that step&#39;s expected latency SLA. Don&#39;t use a single global timeout for everything - a warehouse reservation might legitimately take 30 seconds while a payment authorisation should complete in under 3. When a timeout fires in a saga, you have three options: retry the step, escalate to manual review, or start the compensation sequence. Which one you choose depends on the business rules - and those rules should be explicit in your saga design, not emergent from whatever happens to work in tests.&lt;/p&gt;
&lt;h2 id=&quot;observability-seeing-inside-the-black-box&quot; tabindex=&quot;-1&quot;&gt;Observability: Seeing Inside the Black Box &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/when-business-processes-outlive-your-request/#observability-seeing-inside-the-black-box&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A long-running workflow that spans multiple services and days of calendar time is, without the right observability, a black box. You know when it started, and you know when it ends. Everything in between is inference.&lt;/p&gt;
&lt;p&gt;The most important thing you can do is establish a &lt;strong&gt;correlation ID&lt;/strong&gt; from the very first event, and propagate it through every message, every log line, every trace span for the entire lifetime of the workflow. The correlation ID ties together every bit of activity - the payment charge, the inventory update, the shipping label generation - into a single coherent narrative. Without it, debugging a workflow failure means manually correlating timestamps across five services&#39; logs, which is miserable and error-prone.&lt;/p&gt;
&lt;p&gt;Beyond correlation, you need &lt;strong&gt;saga state snapshots&lt;/strong&gt; - a persistent record of what state the saga is in at each point in time. Every time the saga transitions - a step completes, a timeout fires, a compensation begins - record that transition with a timestamp. This gives you a queryable audit log: &amp;quot;where is order 12345 in the workflow right now?&amp;quot; and &amp;quot;what was the sequence of events that led to this saga being stuck in the compensation phase?&amp;quot; A good saga state machine will handle this automatically; if you&#39;re rolling your own, it&#39;s the first thing to build.&lt;/p&gt;
&lt;p&gt;Think about what you need to monitor operationally:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How many saga instances are currently in-flight, by workflow type and by state?&lt;/li&gt;
&lt;li&gt;What&#39;s the p99 completion time for each workflow type, and how has it changed over time?&lt;/li&gt;
&lt;li&gt;How many sagas are currently stuck - in a state they haven&#39;t left for longer than expected?&lt;/li&gt;
&lt;li&gt;What&#39;s the rate of compensations being triggered, and which saga steps are causing them most often?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;That last metric is particularly valuable. If you start seeing a spike in compensations triggered from a specific step, that step is degrading. You can catch this before it turns into a customer-visible incident.&lt;/p&gt;
&lt;h2 id=&quot;five-things-worth-remembering&quot; tabindex=&quot;-1&quot;&gt;Five Things Worth Remembering &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/when-business-processes-outlive-your-request/#five-things-worth-remembering&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Model the unhappy paths first.&lt;/strong&gt; The happy path is easy - services cooperate, everything succeeds. The value of saga design is in making failure explicit: defining what &amp;quot;undo step 3&amp;quot; means as a named business operation before you write step 3.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Compensation is not rollback.&lt;/strong&gt; It&#39;s a new forward action with its own failure modes. Ask &amp;quot;what if this compensation fails?&amp;quot; for every step, and have an answer.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose orchestration or choreography deliberately.&lt;/strong&gt; Both are valid. Orchestration wins for complex branching and human-in-loop steps. Choreography wins for genuinely independent domain reactions. Mixing both within a single workflow is a sign you haven&#39;t committed to a model.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Every saga step must be idempotent.&lt;/strong&gt; The messy middle is unavoidable in distributed systems. The mechanism that makes retries safe is idempotency, and it needs to be designed in from the start, not retrofitted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Observability is not optional.&lt;/strong&gt; A workflow you can&#39;t see is a workflow you can&#39;t operate. Correlation IDs, state snapshots, and operational metrics are the minimum.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Whatever your stack - MassTransit, NServiceBus, Temporal, AWS Step Functions, or something you&#39;ve built yourself - these patterns apply. The frameworks help with the plumbing, but the design thinking is on you.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;If any of this resonates with problems you&#39;re currently dealing with, or if you disagree with any of my choices here, reach out. I&#39;d genuinely like to hear how your team has approached it.&lt;/p&gt;
&lt;p&gt;PS: Let me know if I forgot anything.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Keep Your Claude Code Agents Out of the Team&#39;s Repo</title>
			<link href="https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/"/>
			<updated>2026-04-06T00:00:00Z</updated>
			<id>https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/</id>
			<content type="html">&lt;p&gt;&lt;em&gt;Your Claude Code setup is yours. Your team&#39;s repo shouldn&#39;t have to carry it.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You&#39;ve spent time building up a nice set of Claude Code agents and skills. Custom reviewers, project-specific helpers, the kind of thing that makes your day a bit smoother. Then you open a pull request and realise: that &lt;code&gt;.claude/&lt;/code&gt; folder is sitting right there in the diff, ready to land in a shared repository that your whole team owns.&lt;/p&gt;
&lt;p&gt;Now you have a decision to make, and neither option feels great.&lt;/p&gt;
&lt;h2 id=&quot;the-problem-with-the-obvious-choices&quot; tabindex=&quot;-1&quot;&gt;The Problem With the Obvious Choices &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#the-problem-with-the-obvious-choices&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You can commit your &lt;code&gt;.claude/&lt;/code&gt; folder to the repo. It works. But now you&#39;re adding personal AI tooling to a shared codebase that your team didn&#39;t ask for, and the next time a colleague clones the repo they get your configuration whether they want it or not. That&#39;s not a disaster, but it&#39;s noise - and noise compounds.&lt;/p&gt;
&lt;p&gt;The other option is to leave it local. Simple. Except local means you lose it the moment you switch machines, rebuild your environment, or hand this work off to someone else. &lt;strong&gt;Local-only configuration doesn&#39;t travel with you.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;There&#39;s a third path that most people don&#39;t think of: keep your agents and skills in a separate, private repository and use symbolic links to make Claude discover them as if they lived inside each project.&lt;/p&gt;
&lt;h2 id=&quot;the-setup-one-config-repo-many-projects&quot; tabindex=&quot;-1&quot;&gt;The Setup: One Config Repo, Many Projects &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#the-setup-one-config-repo-many-projects&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The idea is straightforward. Instead of scattering &lt;code&gt;.claude/&lt;/code&gt; folders across every repo you work in, you maintain a single &lt;code&gt;claude-configs&lt;/code&gt; repository that mirrors your project structure. Each project gets a folder in there, and your actual project repos get a symlink that points back to it.&lt;/p&gt;
&lt;p&gt;Let&#39;s take a childish example to make it concrete. Say you have two projects: &lt;code&gt;my-api&lt;/code&gt; and &lt;code&gt;my-worker&lt;/code&gt;. Your layout ends up looking like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;workspace/
  my-api/                  ← shared, committed, team-owned
  my-worker/               ← shared, committed, team-owned
  claude-configs/          ← private, yours, committed to your own repo
    my-api/
      .claude/
        agents/
        skills/
    my-worker/
      .claude/
        agents/
        skills/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude Code sees &lt;code&gt;my-api/.claude/agents/&lt;/code&gt; and reads your agents from it. It has no idea - and doesn&#39;t need to know - that the folder is actually a symlink into &lt;code&gt;claude-configs&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;step-1-create-the-config-repo&quot; tabindex=&quot;-1&quot;&gt;Step 1 - Create the Config Repo &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#step-1-create-the-config-repo&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Start by creating the structure and moving your existing agents and skills over.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; claude-configs
&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; claude-configs
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; init

&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; my-api/.claude/agents
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; my-api/.claude/skills
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; my-worker/.claude/agents
&lt;span class=&quot;token function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; my-worker/.claude/skills&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then copy your existing agents and skills into their new home (run these from your &lt;code&gt;workspace/&lt;/code&gt; root, not from inside &lt;code&gt;claude-configs&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; my-api/.claude/agents/* claude-configs/my-api/.claude/agents/
&lt;span class=&quot;token function&quot;&gt;cp&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-r&lt;/span&gt; my-api/.claude/skills/* claude-configs/my-api/.claude/skills/&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now the source of truth lives in &lt;code&gt;claude-configs&lt;/code&gt;. Your projects will just point to it.&lt;/p&gt;
&lt;h2 id=&quot;step-2-replace-the-folders-with-symlinks&quot; tabindex=&quot;-1&quot;&gt;Step 2 - Replace the Folders With Symlinks &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#step-2-replace-the-folders-with-symlinks&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Remove the real folders from your project and replace them with symbolic links.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;On Linux, macOS, or WSL:&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; my-api

&lt;span class=&quot;token comment&quot;&gt;# Remove the original folders (content is already in claude-configs)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-rf&lt;/span&gt; .claude/agents .claude/skills

&lt;span class=&quot;token comment&quot;&gt;# Create the symlinks&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/claude-configs/my-api/.claude/agents .claude/agents
&lt;span class=&quot;token function&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;/claude-configs/my-api/.claude/skills .claude/skills&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;On Windows (PowerShell - requires Developer Mode or Administrator):&lt;/strong&gt;&lt;/p&gt;
&lt;pre class=&quot;language-powershell&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-powershell&quot;&gt;cd my-api

&lt;span class=&quot;token function&quot;&gt;Remove-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Recurse &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Force &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;claude&#92;agents&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;claude&#92;skills

&lt;span class=&quot;token function&quot;&gt;New-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ItemType SymbolicLink &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;.claude&#92;agents&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Target &lt;span class=&quot;token string&quot;&gt;&quot;..&#92;..&#92;claude-configs&#92;my-api&#92;.claude&#92;agents&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;New-Item&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;ItemType SymbolicLink &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Path &lt;span class=&quot;token string&quot;&gt;&quot;.claude&#92;skills&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Target &lt;span class=&quot;token string&quot;&gt;&quot;..&#92;..&#92;claude-configs&#92;my-api&#92;.claude&#92;skills&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Use absolute paths on Windows if relative paths give you trouble. The target has to exist before you create the link, so make sure &lt;code&gt;claude-configs&lt;/code&gt; is set up first.&lt;/p&gt;
&lt;h2 id=&quot;step-3-hide-the-symlinks-from-git-without-touching-gitignore&quot; tabindex=&quot;-1&quot;&gt;Step 3 - Hide the Symlinks From Git, Without Touching .gitignore &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#step-3-hide-the-symlinks-from-git-without-touching-gitignore&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here&#39;s the part that trips people up. If you add &lt;code&gt;.claude/agents&lt;/code&gt; to &lt;code&gt;.gitignore&lt;/code&gt;, that file gets committed and your teammates see it. That&#39;s exactly what we&#39;re trying to avoid.&lt;/p&gt;
&lt;p&gt;The right tool is &lt;strong&gt;&lt;code&gt;.git/info/exclude&lt;/code&gt;&lt;/strong&gt;. It behaves like &lt;code&gt;.gitignore&lt;/code&gt; but it&#39;s machine-local and never committed. Your colleagues will never know it exists.&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# Inside my-api&lt;/span&gt;
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.claude/agents&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; .git/info/exclude
&lt;span class=&quot;token builtin class-name&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;.claude/skills&quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; .git/info/exclude&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;On Windows, Git sometimes treats directory symlinks as a single file entry rather than a directory. Add both forms to cover your bases:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;.claude/agents
.claude/agents/
.claude/skills
.claude/skills/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then verify:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; status&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;agents/&lt;/code&gt; and &lt;code&gt;skills/&lt;/code&gt; folders should not appear. If they do, double-check the entries in &lt;code&gt;.git/info/exclude&lt;/code&gt; - a typo there is the usual culprit.&lt;/p&gt;
&lt;h2 id=&quot;step-4-commit-the-config-repo&quot; tabindex=&quot;-1&quot;&gt;Step 4 - Commit the Config Repo &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#step-4-commit-the-config-repo&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token builtin class-name&quot;&gt;cd&lt;/span&gt; claude-configs
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; &lt;span class=&quot;token builtin class-name&quot;&gt;.&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit &lt;span class=&quot;token parameter variable&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Add Claude agents and skills for my-api and my-worker&quot;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; remote &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; origin &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;your-private-repo-url&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; push &lt;span class=&quot;token parameter variable&quot;&gt;-u&lt;/span&gt; origin main&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You now have version history, a backup, and a clean way to reproduce your setup on any machine. Clone &lt;code&gt;claude-configs&lt;/code&gt;, re-run the &lt;code&gt;ln -s&lt;/code&gt; commands, and you&#39;re back to where you were.&lt;/p&gt;
&lt;h2 id=&quot;day-to-day-workflow&quot; tabindex=&quot;-1&quot;&gt;Day-to-Day Workflow &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#day-to-day-workflow&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once it&#39;s running, you mostly forget it&#39;s there. You edit files in &lt;code&gt;claude-configs&lt;/code&gt;, commit inside that repo, and the changes are immediately visible through the symlink. No sync step, no copy-paste.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Action&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Edit an agent or skill&lt;/td&gt;
&lt;td&gt;&lt;code&gt;claude-configs/my-api/.claude/agents/&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Save changes&lt;/td&gt;
&lt;td&gt;&lt;code&gt;git commit&lt;/code&gt; inside &lt;code&gt;claude-configs&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Use on a new machine&lt;/td&gt;
&lt;td&gt;Clone &lt;code&gt;claude-configs&lt;/code&gt;, re-run the &lt;code&gt;ln -s&lt;/code&gt; commands&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Add a new project&lt;/td&gt;
&lt;td&gt;Create the folder in &lt;code&gt;claude-configs&lt;/code&gt;, symlink, exclude&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;why-git-info-exclude-and-not-gitignore&quot; tabindex=&quot;-1&quot;&gt;Why &lt;code&gt;.git/info/exclude&lt;/code&gt; and not &lt;code&gt;.gitignore&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#why-git-info-exclude-and-not-gitignore&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This deserves a proper answer because the difference matters.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;.gitignore&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;.git/info/exclude&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Committed to repo&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Affects teammates&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reversible without a commit&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;.gitignore&lt;/code&gt; is a shared agreement between everyone on the team. When you add something there, you&#39;re making a statement on behalf of the project. &lt;code&gt;.git/info/exclude&lt;/code&gt; is for exactly this kind of case: personal, local configuration that is nobody else&#39;s business.&lt;/p&gt;
&lt;h2 id=&quot;a-note-for-windows-users&quot; tabindex=&quot;-1&quot;&gt;A Note for Windows Users &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#a-note-for-windows-users&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Windows requires &lt;strong&gt;Developer Mode&lt;/strong&gt; or an Administrator terminal to create symlinks. If you get an access error, that&#39;s why.&lt;/p&gt;
&lt;p&gt;You also need to make sure Git is configured to handle symlinks correctly:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config &lt;span class=&quot;token parameter variable&quot;&gt;--global&lt;/span&gt; core.symlinks &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without this, Git may check out a symlink as a plain text file containing the target path as a string. The folder won&#39;t exist, Claude won&#39;t find anything, and you&#39;ll spend twenty minutes wondering what went wrong. Set it once, globally, and move on.&lt;/p&gt;
&lt;h2 id=&quot;credit&quot; tabindex=&quot;-1&quot;&gt;Credit &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/keep-your-claude-code-agents-out-of-the-teams-repo/#credit&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This pattern is adapted from &lt;a href=&quot;https://www.tamirdresher.com/blog/2026/02/17/trying-squad-without-touching-your-repo&quot;&gt;Tamir Dresher&#39;s approach&lt;/a&gt; to using GitHub Copilot&#39;s agent framework without touching shared repositories. The same idea maps cleanly onto Claude Code&#39;s &lt;code&gt;.claude/agents&lt;/code&gt; and &lt;code&gt;.claude/skills&lt;/code&gt; structure.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Your tooling is part of how you work, not part of what the team ships. Keeping those things separate has always been the right instinct - symlinks just make it practical.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Your Database Can&#39;t Read. Vector Databases Can.</title>
			<link href="https://irina.codes/your-database-cant-read-vector-databases-can/"/>
			<updated>2026-03-30T00:00:00Z</updated>
			<id>https://irina.codes/your-database-cant-read-vector-databases-can/</id>
			<content type="html">&lt;p&gt;&lt;em&gt;Vector databases are everywhere right now. Before you add one to your architecture, it is worth understanding what problem they actually solve.&lt;/em&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-search-problem-nobody-talks-about&quot; tabindex=&quot;-1&quot;&gt;The Search Problem Nobody Talks About &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/your-database-cant-read-vector-databases-can/#the-search-problem-nobody-talks-about&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Think about it - when you search for &amp;quot;cheap hotels near the beach&amp;quot;, a traditional database has no idea what you mean. It looks for rows where columns contain the words &amp;quot;cheap&amp;quot;, &amp;quot;hotels&amp;quot;, &amp;quot;near&amp;quot;, &amp;quot;beach&amp;quot;. Exact matches, maybe with some LIKE wildcards. It is matching characters, not meaning.&lt;/p&gt;
&lt;p&gt;That works fine for structured queries. &amp;quot;Give me all orders where status = &#39;pending&#39;&amp;quot; - a relational database handles that brilliantly. But the moment your search depends on &lt;em&gt;meaning&lt;/em&gt; rather than &lt;em&gt;exact text&lt;/em&gt;, you are fighting the tool.&lt;/p&gt;
&lt;p&gt;This is the gap vector databases fill. They do not store rows and columns. They store meaning.&lt;/p&gt;
&lt;h2 id=&quot;what-is-a-vector-really&quot; tabindex=&quot;-1&quot;&gt;What Is a Vector, Really? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/your-database-cant-read-vector-databases-can/#what-is-a-vector-really&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A vector is just a list of numbers. That is it. But those numbers are meaningful - they are the output of a machine learning model trained to capture the &lt;em&gt;semantic content&lt;/em&gt; of whatever you fed it.&lt;/p&gt;
&lt;p&gt;Give a text embedding model the sentence &amp;quot;the cat sat on the mat&amp;quot; and you will get back something like &lt;code&gt;[0.12, -0.87, 0.45, ...]&lt;/code&gt; - hundreds or thousands of numbers. Feed it &amp;quot;a kitten rested on the rug&amp;quot; and you will get a &lt;em&gt;different&lt;/em&gt; list, but one that is &lt;em&gt;close&lt;/em&gt; to the first one in mathematical space. The model learned that cats and kittens are related, that sitting and resting are similar, that mats and rugs belong to the same world.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;This is the core idea&lt;/strong&gt;: things that mean similar things end up &lt;em&gt;near each other&lt;/em&gt; in vector space. And &amp;quot;near&amp;quot; is something we can compute very fast.&lt;/p&gt;
&lt;p&gt;The same principle works for images, audio, code snippets, user behaviour - anything you can run through a model that produces embeddings.&lt;/p&gt;
&lt;h2 id=&quot;how-similarity-search-actually-works&quot; tabindex=&quot;-1&quot;&gt;How Similarity Search Actually Works &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/your-database-cant-read-vector-databases-can/#how-similarity-search-actually-works&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once you have vectors, finding the most similar ones is a geometry problem. The most common approach is &lt;strong&gt;cosine similarity&lt;/strong&gt; - measuring the angle between two vectors. Two vectors pointing in roughly the same direction are semantically similar. Two vectors pointing in opposite directions are not.&lt;/p&gt;
&lt;p&gt;In theory you could compare a query vector against every stored vector. At a thousand entries, that is fine. At a million, it is slow. At a billion, it is not going to work.&lt;/p&gt;
&lt;p&gt;This is where indexing algorithms come in. &lt;strong&gt;HNSW&lt;/strong&gt; (Hierarchical Navigable Small World) is the most widely used - it builds a graph structure that lets you skip most of the search space and find approximate nearest neighbours in milliseconds. The trade-off is that it is approximate - you might miss the single closest match in exchange for speed. In practice, for most use cases, the top 5 results from an approximate search are indistinguishable from the exact top 5.&lt;/p&gt;
&lt;p&gt;Simply put: vector databases are optimised for this specific operation in a way general-purpose databases are not. You &lt;em&gt;can&lt;/em&gt; store vectors in PostgreSQL and compute cosine similarity with &lt;code&gt;pgvector&lt;/code&gt;. For moderate scale, that works well. But a dedicated vector database gives you purpose-built indexing, filtering, and metadata handling that a SQL extension cannot match at scale.&lt;/p&gt;
&lt;p&gt;One thing worth naming explicitly: your existing ORM does not know what cosine similarity is. Entity Framework Core, Dapper, whatever you use - none of it has a concept of &amp;quot;find me the rows most similar to this query&amp;quot;. This is not a gap in those tools; it is a different paradigm entirely. The mental model shift from &amp;quot;find this exact thing&amp;quot; to &amp;quot;find things similar to this thing&amp;quot; is the hardest part of adding vector search to an existing system - not the database choice.&lt;/p&gt;
&lt;h2 id=&quot;rag-the-use-case-that-made-vector-dbs-famous&quot; tabindex=&quot;-1&quot;&gt;RAG: The Use Case That Made Vector DBs Famous &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/your-database-cant-read-vector-databases-can/#rag-the-use-case-that-made-vector-dbs-famous&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have worked with LLMs, you have probably heard of &lt;strong&gt;Retrieval-Augmented Generation&lt;/strong&gt;. The problem it solves: LLMs have a knowledge cutoff. They do not know your internal documentation, your latest product specs, your customer&#39;s order history. And you cannot dump it all into the prompt - context windows have limits, and at scale this gets expensive fast.&lt;/p&gt;
&lt;p&gt;The solution: store that knowledge as embeddings in a vector database. When a user asks a question, convert the question to a vector, find the most relevant chunks from your database, and inject them into the prompt. The LLM answers based on &lt;em&gt;your&lt;/em&gt; data, not just its training.&lt;/p&gt;
&lt;p&gt;This is why every AI feature announcement mentions vector databases right now. They are the memory layer for LLM-powered applications.&lt;/p&gt;
&lt;p&gt;But RAG is not the only use case worth knowing about.&lt;/p&gt;
&lt;h2 id=&quot;other-use-cases-worth-knowing&quot; tabindex=&quot;-1&quot;&gt;Other Use Cases Worth Knowing &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/your-database-cant-read-vector-databases-can/#other-use-cases-worth-knowing&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Semantic search&lt;/strong&gt; is the obvious one - search that understands meaning. &amp;quot;Show me articles about concurrency issues&amp;quot; finds posts about race conditions and deadlocks even if they do not contain those exact words.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Recommendation systems&lt;/strong&gt; are another strong fit. If you embed your products and your users&#39; purchase history into the same vector space, you can find &amp;quot;users whose taste is similar to this user&amp;quot; or &amp;quot;products similar to this one&amp;quot; with a nearest-neighbour query.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Anomaly detection&lt;/strong&gt; works similarly - embed your normal traffic patterns, then flag incoming requests whose vectors are far from any known cluster. Duplicate detection, image search, code similarity - they all reduce to the same operation: embed, store, query by proximity.&lt;/p&gt;
&lt;h2 id=&quot;the-landscape-which-one-do-you-pick&quot; tabindex=&quot;-1&quot;&gt;The Landscape: Which One Do You Pick? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/your-database-cant-read-vector-databases-can/#the-landscape-which-one-do-you-pick&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here is a quick honest comparison of the options you will actually encounter:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.pinecone.io/&quot;&gt;Pinecone&lt;/a&gt;&lt;/strong&gt; - fully managed, serverless pricing, minimal ops overhead. Good if you want to ship fast and do not want to think about infrastructure. Vendor lock-in is real.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://qdrant.tech/&quot;&gt;Qdrant&lt;/a&gt;&lt;/strong&gt; - open source, written in Rust, excellent performance. My personal preference for self-hosted setups. Great filtering support and a clean API, plus an official .NET SDK (&lt;code&gt;Qdrant.Client&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://weaviate.io/&quot;&gt;Weaviate&lt;/a&gt;&lt;/strong&gt; - open source, schema-oriented, supports hybrid search (vector + keyword) out of the box. Good if you need both semantic and structured queries in one place.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://www.trychroma.com/&quot;&gt;Chroma&lt;/a&gt;&lt;/strong&gt; - lightweight, easy local setup, popular for prototypes and small projects. Not what you would put in front of production traffic at scale.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/pgvector/pgvector&quot;&gt;pgvector&lt;/a&gt;&lt;/strong&gt; - a PostgreSQL extension. If you already run Postgres and your scale is moderate (think hundreds of thousands of vectors, not hundreds of millions), this is often the pragmatic choice. No new infrastructure, no new operational complexity, familiar tooling. With &lt;code&gt;Pgvector.EntityFrameworkCore&lt;/code&gt; you get a &lt;code&gt;Vector&lt;/code&gt; column type and LINQ methods like &lt;code&gt;CosineDistance()&lt;/code&gt; and &lt;code&gt;L2Distance()&lt;/code&gt; - you can be running similarity queries in EF Core in an afternoon.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In my opinion, the biggest mistake teams make is reaching for Pinecone or Qdrant before they have validated that pgvector cannot handle their load. For most .NET teams already on Postgres/EF Core, pgvector is the right first step. The dedicated vector database conversation should happen at scale, not day one.&lt;/p&gt;
&lt;h2 id=&quot;what-this-means-in-practice&quot; tabindex=&quot;-1&quot;&gt;What This Means in Practice &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/your-database-cant-read-vector-databases-can/#what-this-means-in-practice&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are evaluating vector databases, a few things are worth keeping in mind.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Your embeddings are only as good as your model.&lt;/strong&gt; A bad embedding model produces vectors where similar things end up far apart - no indexing algorithm fixes that. Test your retrieval quality before committing to a database choice.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Embeddings go stale.&lt;/strong&gt; If your underlying data changes, the vectors need to be regenerated. This is an operational concern most architecture diagrams skip over.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Hybrid search matters.&lt;/strong&gt; Pure semantic search sometimes misses exact matches (product SKUs, proper nouns, IDs). Most real production systems combine vector similarity with keyword search. Some databases (Weaviate, Elasticsearch with kNN) support this natively; others you wire up yourself.&lt;/p&gt;
&lt;p&gt;And finally - &lt;strong&gt;you do not need a vector database to use embeddings&lt;/strong&gt;. If you are building a small RAG prototype, start with pgvector or even SQLite with a vector extension. Add the dedicated infrastructure when you have a reason to.&lt;/p&gt;
&lt;h2 id=&quot;useful-net-tools-and-packages&quot; tabindex=&quot;-1&quot;&gt;Useful .NET Tools and Packages &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/your-database-cant-read-vector-databases-can/#useful-net-tools-and-packages&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you are building on .NET, these are the packages and tools you will reach for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Pgvector.EntityFrameworkCore&lt;/code&gt;&lt;/strong&gt; - adds &lt;code&gt;Vector&lt;/code&gt; column type to EF Core with &lt;code&gt;CosineDistance()&lt;/code&gt; and &lt;code&gt;L2Distance()&lt;/code&gt; LINQ methods. Start here if you are already on Postgres.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Qdrant.Client&lt;/code&gt;&lt;/strong&gt; - official Qdrant .NET SDK for working with a dedicated Qdrant instance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Microsoft.SemanticKernel.Connectors.Qdrant&lt;/code&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;code&gt;Microsoft.SemanticKernel.Connectors.Postgres&lt;/code&gt;&lt;/strong&gt; - Semantic Kernel connectors for using Qdrant or pgvector as the vector store in an SK-based application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://github.com/petabridge/memorizer&quot;&gt;Memorizer&lt;/a&gt;&lt;/strong&gt; - a .NET service that gives AI agents persistent semantic memory using pgvector. Supports workspaces, tagging, versioning, knowledge graph relationships between memories, and a web UI. Ships as a Docker image and exposes an MCP server, so any MCP-compatible agent can use it as a memory layer out of the box.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;closing&quot; tabindex=&quot;-1&quot;&gt;Closing &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/your-database-cant-read-vector-databases-can/#closing&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Vector databases are a genuine new primitive in the software stack, not just AI hype with a database attached. They solve a real problem - matching by meaning, at scale - that relational databases were never designed for.&lt;/p&gt;
&lt;p&gt;But like any tool, the question is not &amp;quot;should I use a vector database?&amp;quot; It is &amp;quot;does my problem require this, and at what scale?&amp;quot; Start simple. Measure. Add complexity when the simpler thing runs out of road.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Async Does Not Mean Scalable</title>
			<link href="https://irina.codes/async-does-not-mean-scalable/"/>
			<updated>2026-03-04T00:00:00Z</updated>
			<id>https://irina.codes/async-does-not-mean-scalable/</id>
			<content type="html">&lt;p&gt;We keep hearing about &lt;code&gt;async/await&lt;/code&gt; syntactic sugar and &lt;strong&gt;message queues&lt;/strong&gt; and they make our applications faster, more responsive, more scalable. Teams rewrite synchronous endpoints to async ones and feel better about the decision. And when that&#39;s not enough, someone adds a message queue — and suddenly the system feels &lt;em&gt;&lt;strong&gt;fixed&lt;/strong&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But what if it wasn&#39;t broken in the way you thought?&lt;/p&gt;
&lt;h2 id=&quot;the-reflex&quot; tabindex=&quot;-1&quot;&gt;The Reflex &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/async-does-not-mean-scalable/#the-reflex&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Every system that starts struggling under load goes through the same ritual. Traffic climbs, latency spikes, and someone in the room says: &amp;quot;let&#39;s make it async.&amp;quot; The team nods. It sounds right. And for a while, it works — or at least, the dashboard stops screaming.&lt;/p&gt;
&lt;p&gt;Then the queue backs up. Or the thread pool saturates. Or the database starts timing out. And the team discovers that async didn&#39;t fix the problem. It just changed where the problem lived.&lt;/p&gt;
&lt;p&gt;This isn&#39;t a criticism of &lt;code&gt;async/await&lt;/code&gt; or message queues. They&#39;re genuinely useful - especially the &lt;strong&gt;message queues&lt;/strong&gt; . But there&#39;s a persistent confusion in how teams reach for them — as a scaling tool rather than a decoupling tool — and it leads to systems that feel fast right up until they don&#39;t.&lt;/p&gt;
&lt;h2 id=&quot;why-async-feels-like-a-scaling-solution&quot; tabindex=&quot;-1&quot;&gt;Why async feels like a scaling solution &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/async-does-not-mean-scalable/#why-async-feels-like-a-scaling-solution&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Imagine a restaurant where the kitchen is slow. Orders pile up, customers wait, tables sit occupied for too long.&lt;/p&gt;
&lt;p&gt;Someone has an idea: hire a host to take orders at the door and give customers a buzzer. Now people don&#39;t stand in line at the counter — they sit down, browse their phones, and the kitchen calls them when food is ready. The entrance is clear. The restaurant feels &lt;em&gt;more efficient&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;But the kitchen is still slow. The same number of meals gets cooked per hour. The queue just moved from the door to the pager system — and now it&#39;s harder to see.&lt;/p&gt;
&lt;p&gt;This is exactly what happens when you reach for &lt;code&gt;async/await&lt;/code&gt; or a message queue as a scaling solution.&lt;/p&gt;
&lt;h2 id=&quot;what-async-await-actually-does&quot; tabindex=&quot;-1&quot;&gt;What async/await actually does &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/async-does-not-mean-scalable/#what-async-await-actually-does&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you &lt;code&gt;await&lt;/code&gt; an HTTP request or a database call in .NET, the current thread is released back to the thread pool while the work happens. Once it completes, the continuation resumes on another thread.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Order&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetOrderAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Thread is released here while the DB query runs&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; _db&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Orders&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FindAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s genuinely useful. In ASP.NET Core, a server handling thousands of concurrent I/O-bound requests with a small thread pool instead of spinning up thousands of threads is a better server. It&#39;s the right answer to thread pool exhaustion.&lt;/p&gt;
&lt;p&gt;But notice what &lt;code&gt;async&lt;/code&gt; doesn&#39;t do: it doesn&#39;t make the database query faster. It doesn&#39;t reduce the number of queries. If 1,000 requests come in simultaneously, the database still gets 1,000 queries — they just don&#39;t each hold a thread while waiting.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The thread is free. The work is not.&lt;/strong&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-adding-a-queue-does&quot; tabindex=&quot;-1&quot;&gt;What adding a queue does &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/async-does-not-mean-scalable/#what-adding-a-queue-does&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The queue is a debt counter, not a buffer&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Adding a message queue feels like a more decisive fix. The API returns instantly. Traffic spikes are absorbed. The team ships it and calls it done.&lt;/p&gt;
&lt;p&gt;But every message still needs a consumer. The consumer still hits the database, calls the downstream service, runs the business logic. What changed is &lt;em&gt;where&lt;/em&gt; the pressure accumulates.&lt;/p&gt;
&lt;p&gt;Think of it like a debt. If your producer sends 1,000 messages per second and your consumer handles 200, you&#39;re accumulating 800 messages of debt every second. After ten minutes: 480,000 unprocessed messages. The producer side looks fine. The queue is quietly growing.&lt;/p&gt;
&lt;p&gt;When will you notice? When redelivery storms start hitting your consumer. When messages expire. When the lag is hours, not seconds.&lt;/p&gt;
&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;xychart-beta
    title &lt;span class=&quot;token string&quot;&gt;&quot;Queue debt over time (producer: 1000/s, consumer: 200/s)&quot;&lt;/span&gt;
    &lt;span class=&quot;token arrow operator&quot;&gt;x-&lt;/span&gt;axis &lt;span class=&quot;token text string&quot;&gt;[&quot;0m&quot;, &quot;2m&quot;, &quot;4m&quot;, &quot;6m&quot;, &quot;8m&quot;, &quot;10m&quot;]&lt;/span&gt;
    y-axis &lt;span class=&quot;token string&quot;&gt;&quot;Unprocessed messages&quot;&lt;/span&gt; 0 &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt; 500000
    bar &lt;span class=&quot;token text string&quot;&gt;[0, 96000, 192000, 288000, 384000, 480000]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-bottlenecks-async-doesnt-touch&quot; tabindex=&quot;-1&quot;&gt;The bottlenecks async doesn&#39;t touch &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/async-does-not-mean-scalable/#the-bottlenecks-async-doesnt-touch&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In my experience, most throughput and scalability problems aren&#39;t thread problems at all. They&#39;re resource contention problems — and no amount of concurrency management fixes a contention problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Database connection pool&lt;/strong&gt; — finite by design. Callers queue for a connection whether the calling code is async or not.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Row-level locks&lt;/strong&gt; — concurrent writers serialize. An &lt;code&gt;await&lt;/code&gt; doesn&#39;t skip the queue.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;External rate limits&lt;/strong&gt; — the third-party API that allows 100 requests per second doesn&#39;t care that your code is elegant.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shared mutable state&lt;/strong&gt; — still needs coordination. The concurrency model doesn&#39;t change because you added &lt;code&gt;async&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Making the calling code async moves the problem one layer down. It doesn&#39;t remove it — it just makes it harder to see because you&#39;ve separated the producer from the consumer.&lt;/p&gt;
&lt;h2 id=&quot;when-to-actually-reach-for-async&quot; tabindex=&quot;-1&quot;&gt;When to actually reach for async &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/async-does-not-mean-scalable/#when-to-actually-reach-for-async&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;None of this is a reason to avoid &lt;code&gt;async/await&lt;/code&gt;. It&#39;s a reason to reach for it &lt;em&gt;for the right reason&lt;/em&gt;: genuine thread pool pressure in high-concurrency I/O-bound workloads.&lt;/p&gt;
&lt;p&gt;Message queues have their place too — decoupling services with different uptime requirements, absorbing traffic spikes without dropping requests, building retry logic for unreliable operations. These are real problems worth solving.&lt;/p&gt;
&lt;p&gt;Just don&#39;t confuse those benefits with throughput. &lt;strong&gt;Decoupling and throughput are different properties.&lt;/strong&gt; A well-decoupled system can still be slow. A fast system doesn&#39;t require a queue.&lt;/p&gt;
&lt;h2 id=&quot;what-actually-improves-throughput&quot; tabindex=&quot;-1&quot;&gt;What actually improves throughput &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/async-does-not-mean-scalable/#what-actually-improves-throughput&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When throughput is the real problem, the levers are different:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;More consumer instances&lt;/strong&gt; — horizontal scaling works when consumers are stateless and independent. If processing one message takes 50ms and you need to handle 500 messages/second, you need at least 25 concurrent consumers. Spin up more; the math is simple.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Partitioning&lt;/strong&gt; — route work by a meaningful key (customer ID, tenant ID) so consumers operate on non-overlapping subsets without competing for the same rows. This is where real parallelism comes from.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-mermaid&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-mermaid&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;graph&lt;/span&gt; LR
    Q&lt;span class=&quot;token text string&quot;&gt;[Queue]&lt;/span&gt; &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|customer A–M|&lt;/span&gt; C1&lt;span class=&quot;token text string&quot;&gt;[Consumer 1]&lt;/span&gt;
    Q &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt;&lt;span class=&quot;token label property&quot;&gt;|customer N–Z|&lt;/span&gt; C2&lt;span class=&quot;token text string&quot;&gt;[Consumer 2]&lt;/span&gt;
    C1 &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt; DB1&lt;span class=&quot;token text string&quot;&gt;[(DB shard 1)]&lt;/span&gt;
    C2 &lt;span class=&quot;token arrow operator&quot;&gt;--&gt;&lt;/span&gt; DB2&lt;span class=&quot;token text string&quot;&gt;[(DB shard 2)]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Keeping handlers lean&lt;/strong&gt; — shared mutable state limits how many you can run in parallel. A handler that locks a shared cache or calls a single-instance service will serialize regardless of how many instances you run.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And measure the right thing. &lt;strong&gt;Consumer lag&lt;/strong&gt;, not queue depth. Queue depth is a snapshot — it tells you how much work is waiting right now. Consumer lag tells you how fast you&#39;re falling behind. One is a count. The other is a velocity. You need the velocity to plan capacity.&lt;/p&gt;
&lt;h2 id=&quot;async-await-scalability-and-what-to-do-instead&quot; tabindex=&quot;-1&quot;&gt;async/await, scalability, and what to do instead &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/async-does-not-mean-scalable/#async-await-scalability-and-what-to-do-instead&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;async/await&lt;/code&gt; is a thread management tool. Message queues are a decoupling tool. Neither is a throughput tool on its own.&lt;/p&gt;
&lt;p&gt;If your system is slow, profile before you refactor. Measure before you add infrastructure. The database index you&#39;re not adding, the N+1 query you haven&#39;t noticed, the downstream service with no timeout — these are more likely your problem than the absence of &lt;code&gt;await&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Async shifts the work. Scale requires designing &lt;em&gt;where&lt;/em&gt; the work goes.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;If this resonates with you, share it with someone who just added a queue to a slow service and called it a day. I&#39;d love to hear your thoughts — find me on &lt;a href=&quot;https://www.linkedin.com/in/irinascurtu&quot;&gt;LinkedIn&lt;/a&gt; and drop a comment.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>OpenTelemetry in C#: Setup, Spans, and Custom Instrumentation</title>
			<link href="https://irina.codes/posts/2026/opentelemetry-setup/"/>
			<updated>2026-02-07T00:00:00Z</updated>
			<id>https://irina.codes/posts/2026/opentelemetry-setup/</id>
			<content type="html">&lt;h1 id=&quot;opentelemetry-in-c-setup-spans-and-custom-instrumentation&quot; tabindex=&quot;-1&quot;&gt;OpenTelemetry in C#: Setup, Spans, and Custom Instrumentation &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-setup/#opentelemetry-in-c-setup-spans-and-custom-instrumentation&quot;&gt;🔗&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;If you&#39;ve landed here directly, consider reading &lt;a href=&quot;https://irina.codes/opentelemetry-csharp-beginners/&quot;&gt;Part 1&lt;/a&gt; first. It covers what OpenTelemetry is, what traces, metrics, and logs actually mean, and why the instrumentation/exporter separation matters. This post picks up right where that one ends - concepts assumed, code begins.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;opentelemetry-c-setup-in-asp-net-core&quot; tabindex=&quot;-1&quot;&gt;OpenTelemetry C# Setup in ASP.NET Core &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-setup/#opentelemetry-c-setup-in-asp-net-core&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Enough theory. Let&#39;s get something running.&lt;/p&gt;
&lt;p&gt;Add the NuGet packages:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;dotnet &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; package OpenTelemetry.Extensions.Hosting
dotnet &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; package OpenTelemetry.Instrumentation.AspNetCore
dotnet &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; package OpenTelemetry.Instrumentation.Http
dotnet &lt;span class=&quot;token function&quot;&gt;add&lt;/span&gt; package OpenTelemetry.Exporter.OpenTelemetryProtocol&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Wire it up in &lt;code&gt;Program.cs&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddOpenTelemetry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithTracing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tracing &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; tracing
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;SetResourceBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ResourceBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;CreateDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my-api&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token named-parameter punctuation&quot;&gt;serviceVersion&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddAspNetCoreInstrumentation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// auto-traces every incoming HTTP request&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddHttpClientInstrumentation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;token comment&quot;&gt;// auto-traces outgoing HttpClient calls&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddSource&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MyApp.Orders&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;        &lt;span class=&quot;token comment&quot;&gt;// register your custom ActivitySource&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddOtlpExporter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;               &lt;span class=&quot;token comment&quot;&gt;// ship traces to Jaeger or a collector&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithMetrics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;metrics &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; metrics
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddAspNetCoreInstrumentation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddRuntimeInstrumentation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;      &lt;span class=&quot;token comment&quot;&gt;// GC, thread pool, memory - free signal&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddMeter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MyApp.Orders&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;         &lt;span class=&quot;token comment&quot;&gt;// register your custom Meter&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddOtlpExporter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WithLogging&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;logging &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; logging
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddOtlpExporter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That&#39;s the complete wiring. The ASP.NET Core instrumentation alone gives you traces for every incoming HTTP request and all outgoing &lt;code&gt;HttpClient&lt;/code&gt; calls - zero manual code required. Add &lt;code&gt;OpenTelemetry.Instrumentation.SqlClient&lt;/code&gt; if you want SQL queries in there too.&lt;/p&gt;
&lt;p&gt;Now spin up Jaeger locally:&lt;/p&gt;
&lt;pre class=&quot;language-bash&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;docker&lt;/span&gt; run &lt;span class=&quot;token parameter variable&quot;&gt;-d&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;--name&lt;/span&gt; jaeger &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16686&lt;/span&gt;:16686 &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  &lt;span class=&quot;token parameter variable&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4317&lt;/span&gt;:4317 &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
  jaegertracing/all-in-one:latest&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Open &lt;code&gt;http://localhost:16686&lt;/code&gt;. Start your application, hit any endpoint, refresh Jaeger. You&#39;ll see your first trace - a complete timeline of what happened inside your API for that single request. That moment is genuinely satisfying.&lt;/p&gt;
&lt;p&gt;To configure the OTLP exporter endpoint explicitly:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddOtlpExporter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;opts &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    opts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Endpoint &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:4317&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    opts&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Protocol &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; OtlpExportProtocol&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Grpc&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;custom-spans&quot; tabindex=&quot;-1&quot;&gt;Custom Spans &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-setup/#custom-spans&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Auto-instrumentation covers the framework-level operations: the HTTP request arrived, the SQL query ran, the &lt;code&gt;HttpClient&lt;/code&gt; call went out. Useful - but it knows nothing about &lt;em&gt;your&lt;/em&gt; business logic.&lt;/p&gt;
&lt;p&gt;A custom span lets you draw your own box around any operation that matters to you. When you start a span, the clock starts. When the &lt;code&gt;using&lt;/code&gt; block exits - whether normally or because an exception was thrown - the span closes and its duration is recorded. Everything that happens inside that block - child spans, log lines, events - is linked together by the same &lt;code&gt;TraceId&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You control the name (&lt;code&gt;&amp;quot;PlaceOrder&amp;quot;&lt;/code&gt;), the attributes (&lt;code&gt;customer.id&lt;/code&gt;, &lt;code&gt;order.id&lt;/code&gt;), and the status (OK or ERROR). This is where generic &amp;quot;OpenTelemetry&amp;quot; becomes &lt;em&gt;your&lt;/em&gt; telemetry - shaped around the operations that actually matter in your domain.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderService&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ActivitySource&lt;/span&gt; Source &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MyApp.Orders&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ILogger&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;OrderService&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; _logger&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;OrderService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ILogger&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;OrderService&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; logger&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; _logger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; logger&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Order&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;PlaceOrderAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; customerId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; productId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; activity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;StartActivity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;PlaceOrder&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        activity&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;SetTag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;customer.id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; customerId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        activity&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;SetTag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;product.id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; productId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            _logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Placing order for customer {CustomerId}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; customerId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; inventory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CheckInventoryAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;productId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;inventory&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Available&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                activity&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;SetStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ActivityStatusCode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Out of stock&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                activity&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;ActivityEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;inventory.unavailable&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;InvalidOperationException&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Product out of stock&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

            &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; order &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CreateOrderAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;customerId&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; productId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            activity&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;SetTag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;order.id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; order&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; order&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Exception&lt;/span&gt; ex&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            activity&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;SetStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ActivityStatusCode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ex&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;throw&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;InventoryResult&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;CheckInventoryAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; productId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; activity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;StartActivity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;CheckInventory&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        activity&lt;span class=&quot;token punctuation&quot;&gt;?.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;SetTag&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;product.id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; productId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// database call&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;InventoryResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token named-parameter punctuation&quot;&gt;Available&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A few things worth knowing here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ActivitySource&lt;/code&gt; is &lt;code&gt;static readonly&lt;/code&gt; - create it once, reuse it everywhere across the lifetime of the application&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;using&lt;/code&gt; on &lt;code&gt;activity&lt;/code&gt; ensures the span closes (and its duration is recorded) when the block exits, even on exception&lt;/li&gt;
&lt;li&gt;Tags via &lt;code&gt;SetTag&lt;/code&gt; become searchable attributes in your backend - filter traces by &lt;code&gt;customer.id&lt;/code&gt; or &lt;code&gt;order.id&lt;/code&gt; directly in Jaeger&#39;s UI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;SetStatus(ActivityStatusCode.Error)&lt;/code&gt; marks the span red in the Jaeger UI, making failures immediately obvious in the trace timeline&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AddEvent&lt;/code&gt; adds a timestamped annotation to the span - useful for checkpoints inside a long operation (&amp;quot;inventory checked&amp;quot;, &amp;quot;payment initiated&amp;quot;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The child span &lt;code&gt;CheckInventory&lt;/code&gt; automatically becomes a child of &lt;code&gt;PlaceOrder&lt;/code&gt; because OTel tracks the ambient activity context via &lt;code&gt;AsyncLocal&lt;/code&gt;. You don&#39;t pass parent IDs manually - it just works across &lt;code&gt;await&lt;/code&gt; boundaries.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;custom-metrics&quot; tabindex=&quot;-1&quot;&gt;Custom Metrics &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-setup/#custom-metrics&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OrderMetrics&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Meter&lt;/span&gt; Meter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MyApp.Orders&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;1.0.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Counter&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; _ordersPlaced&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Counter&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; _ordersFailed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Histogram&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; _orderProcessingTime&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;OrderMetrics&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        _ordersPlaced &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Meter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;CreateCounter&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;orders.placed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token named-parameter punctuation&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Total number of orders placed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        _ordersFailed &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Meter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;CreateCounter&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;orders.failed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token named-parameter punctuation&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Total number of failed orders&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        _orderProcessingTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Meter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;CreateHistogram&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token string&quot;&gt;&quot;orders.processing_duration_ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token named-parameter punctuation&quot;&gt;unit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ms&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token named-parameter punctuation&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Time to process an order end-to-end&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;RecordOrderPlaced&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; region&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _ordersPlaced&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;KeyValuePair&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;region&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; region&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;RecordOrderFailed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; reason&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _ordersFailed&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;KeyValuePair&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;reason&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reason&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;RecordProcessingTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;double&lt;/span&gt;&lt;/span&gt; ms&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
        _orderProcessingTime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Record&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ms&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Register &lt;code&gt;OrderMetrics&lt;/code&gt; as a singleton in DI and inject it into &lt;code&gt;OrderService&lt;/code&gt;. Now you have business-level signal - not just &amp;quot;endpoint returned 200&amp;quot; but &amp;quot;347 orders processed in the last minute, 4 failed, p99 processing time was 430ms.&amp;quot;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;region&lt;/code&gt; parameter on &lt;code&gt;RecordOrderPlaced&lt;/code&gt; is an &lt;strong&gt;attribute&lt;/strong&gt; on the metric data point. It lets you slice the counter: &amp;quot;EU orders&amp;quot; vs &amp;quot;US orders&amp;quot;. Watch cardinality here - don&#39;t use a user ID or request ID as an attribute or you&#39;ll create millions of unique time series and make your metrics backend very unhappy.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;log-correlation-nothing-to-change&quot; tabindex=&quot;-1&quot;&gt;Log Correlation - Nothing to Change &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-setup/#log-correlation-nothing-to-change&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;When you emit a log inside an active span, OTel automatically enriches it with &lt;code&gt;TraceId&lt;/code&gt; and &lt;code&gt;SpanId&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ProcessAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; activity &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Source&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;StartActivity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Process&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Both log lines automatically carry TraceId and SpanId - no changes needed&lt;/span&gt;
    _logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Processing started&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;DoWorkAsync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    _logger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;LogInformation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Processing completed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With a structured logger (Serilog, for example), the output looks like:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;message&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Processing started&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;traceId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;4bf92f3577b34da6a3ce929d0e0e4736&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;spanId&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;00f067aa0ba902b7&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;timestamp&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2026-04-03T14:32:05.123Z&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you spot an error in your log aggregator (Seq, Elasticsearch, Loki), copy the &lt;code&gt;traceId&lt;/code&gt;, paste it into Jaeger&#39;s search, and you immediately see everything that was happening in the system when that log line was written. This is the connection between the three pillars that makes observability actually useful - any signal leads you to the others.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;where-to-go-next&quot; tabindex=&quot;-1&quot;&gt;Where to Go Next &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-setup/#where-to-go-next&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;The OTel Collector&lt;/strong&gt; - rather than shipping telemetry directly from your application to a backend, the Collector acts as a pipeline: your app sends to the Collector, the Collector fans out to multiple backends, applies filters, redacts sensitive fields, and batches efficiently. For anything beyond local development, this is the recommended architecture.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sampling strategies&lt;/strong&gt; - head-based sampling (decide at trace start) vs tail-based sampling (decide after the full trace is captured, so you can always keep error traces). The OTel Collector supports both. In development, sample 100%. In production, start at 10% and tune from there.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cloud backends&lt;/strong&gt; - Azure Monitor has supported OTLP natively since 2023. AWS X-Ray, Google Cloud Trace, Datadog, Dynatrace, New Relic, and Honeycomb all accept OTLP. Switching backends is one changed exporter configuration in &lt;code&gt;Program.cs&lt;/code&gt; - your instrumentation code stays untouched.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;More library instrumentation&lt;/strong&gt; - there are OTel packages for Entity Framework Core, Redis, RabbitMQ, gRPC, MassTransit, and more. The pattern is always the same: one &lt;code&gt;AddXInstrumentation()&lt;/code&gt; call in your setup.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://opentelemetry.io/docs/languages/net/&quot;&gt;OpenTelemetry .NET documentation&lt;/a&gt; is thorough and the GitHub examples cover every major scenario.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;start-simple-add-signal&quot; tabindex=&quot;-1&quot;&gt;Start Simple, Add Signal &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-setup/#start-simple-add-signal&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You don&#39;t need to instrument everything on day one.&lt;/p&gt;
&lt;p&gt;Start with auto-instrumentation for ASP.NET Core and &lt;code&gt;HttpClient&lt;/code&gt;. Get traces into Jaeger. Spend five minutes clicking through a real trace from your application - look at what it already knows about your system without a single line of custom code.&lt;/p&gt;
&lt;p&gt;Then add one custom span around the most important operation in your codebase - the thing that, when it is slow, everyone notices. Attach the domain attributes that matter: order IDs, customer IDs, the names of the external services you call.&lt;/p&gt;
&lt;p&gt;That single span will pay for itself the first time something breaks at 2am and you can see exactly where to look.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Have you added OpenTelemetry to a .NET project? I&#39;d love to hear what backend you&#39;re using and what surprised you. Drop a comment below or reach me on &lt;a href=&quot;https://linkedin.com/in/irinascurtu&quot;&gt;LinkedIn&lt;/a&gt; or &lt;a href=&quot;https://twitter.com/irina_scurtu&quot;&gt;X&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For more .NET deep-dives on ASP.NET Core, distributed systems, and architecture, check out the rest of the posts on &lt;a href=&quot;https://irina.codes/&quot;&gt;irina.codes&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>OpenTelemetry for Developers: The Concepts Explained Simply</title>
			<link href="https://irina.codes/posts/2026/opentelemetry-beginners/"/>
			<updated>2026-02-03T00:00:00Z</updated>
			<id>https://irina.codes/posts/2026/opentelemetry-beginners/</id>
			<content type="html">&lt;h1 id=&quot;opentelemetry-for-net-developers-the-concepts-explained-simply&quot; tabindex=&quot;-1&quot;&gt;OpenTelemetry for .NET Developers: The Concepts Explained Simply &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#opentelemetry-for-net-developers-the-concepts-explained-simply&quot;&gt;🔗&lt;/a&gt;&lt;/h1&gt;
&lt;h2 id=&quot;its-2am-your-phone-is-ringing&quot; tabindex=&quot;-1&quot;&gt;It&#39;s 2am. Your Phone is Ringing. &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#its-2am-your-phone-is-ringing&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Production is down. The checkout flow is broken. Customers can&#39;t complete orders.&lt;/p&gt;
&lt;p&gt;You open the logs. There are thousands of lines - INFO, DEBUG, ERROR messages from a dozen services - and none of them clearly say &lt;em&gt;this is where it broke&lt;/em&gt;. You start guessing. Was it the payment service? The inventory API? The database connection pool? You SSH into servers, tail logs, grep for exceptions, and hope something jumps out.&lt;/p&gt;
&lt;p&gt;Meanwhile, your on-call rotation is burning, your boss is messaging you, and every minute of downtime has a dollar value attached to it.&lt;/p&gt;
&lt;p&gt;This is the problem observability solves. Not &amp;quot;do we have logs?&amp;quot; - you have plenty of those. The real question is: &amp;quot;can we actually understand what our system is doing from the outside?&amp;quot; That&#39;s a very different thing.&lt;/p&gt;
&lt;p&gt;If you&#39;ve never set up observability before - if words like &amp;quot;distributed tracing&amp;quot; or &amp;quot;metrics cardinality&amp;quot; make you glaze over - this post is for you. We&#39;ll start from first principles and use everyday analogies to make the concepts stick. The code comes in &lt;a href=&quot;https://irina.codes/opentelemetry-csharp-setup-and-instrumentation/&quot;&gt;Part 2&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;what-is-observability&quot; tabindex=&quot;-1&quot;&gt;What is Observability? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#what-is-observability&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Observability is a term borrowed from control theory. A system is &amp;quot;observable&amp;quot; if you can determine its internal state by examining its external outputs. For software, the question becomes: can you figure out what is happening inside your application by looking at the data it produces?&lt;/p&gt;
&lt;p&gt;The classic alternative is monitoring - you set up dashboards and alerts for problems you &lt;em&gt;know&lt;/em&gt; might happen. CPU over 90%? Alert. Error rate over 1%? Alert. Monitoring only tells you about problems you anticipated. Observability lets you ask &lt;em&gt;new&lt;/em&gt; questions about your running system, even for failures you never predicted.&lt;/p&gt;
&lt;p&gt;A good analogy is a visit to the doctor.&lt;/p&gt;
&lt;p&gt;When you feel unwell, the doctor doesn&#39;t open you up to look inside. Instead, they gather external signals:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;They ask you to describe your symptoms - &amp;quot;it hurts here, it started two days ago, it gets worse when I move&amp;quot; - that&#39;s your &lt;strong&gt;logs&lt;/strong&gt;: a timestamped record of events that happened&lt;/li&gt;
&lt;li&gt;They take your temperature, blood pressure, and heart rate - numbers tracked over time - that&#39;s your &lt;strong&gt;metrics&lt;/strong&gt;: gauges and counters that show the system&#39;s health at a glance&lt;/li&gt;
&lt;li&gt;They trace where the pain originates, step by step - that&#39;s your &lt;strong&gt;traces&lt;/strong&gt;: the complete journey of a request through your system&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;OpenTelemetry is the industry-standard toolkit that lets your software produce all three signals in a consistent, vendor-neutral way. It is the second most active CNCF (Cloud Native Computing Foundation) project after Kubernetes - and that momentum is there for a reason.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;enter-opentelemetry&quot; tabindex=&quot;-1&quot;&gt;Enter OpenTelemetry &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#enter-opentelemetry&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;OpenTelemetry (OTel) is a CNCF project born in 2019 from the merger of two earlier standards: OpenCensus (Google) and OpenTracing (community). The goal was to stop fragmenting the ecosystem. Before OTel, every vendor had their own SDK, their own agent, their own wire format. You&#39;d instrument your app for Datadog, then decide to try Honeycomb, and find yourself rewriting everything.&lt;/p&gt;
&lt;p&gt;OTel solves that by owning the instrumentation layer - the code that lives in your application and produces telemetry data. Where that data goes (Jaeger, Datadog, Azure Monitor, Honeycomb, Zipkin) is a separate concern, handled by swappable exporters. You change one line in &lt;code&gt;Program.cs&lt;/code&gt;, not your entire codebase.&lt;/p&gt;
&lt;p&gt;Think of it like a flight recorder - the black box on commercial aircraft. Every plane has one that records:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All notable events (logs)&lt;/li&gt;
&lt;li&gt;Instrument readings every second (metrics)&lt;/li&gt;
&lt;li&gt;The full flight path and timeline (traces)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The black box standard defines exactly &lt;em&gt;what&lt;/em&gt; gets recorded, &lt;em&gt;how&lt;/em&gt; it&#39;s encoded, and &lt;em&gt;where&lt;/em&gt; it goes - so any airline (your application) can connect to any air traffic control system (any observability backend) without rebuilding the recorder. OTel is that standard for software.&lt;/p&gt;
&lt;p&gt;One distinction worth locking in early: &lt;strong&gt;OpenTelemetry is not Jaeger. It is not Datadog. It is not your observability backend.&lt;/strong&gt; Those tools store and visualize telemetry. OTel produces and ships it. This confusion derails a lot of developers when they first encounter the space.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;what-is-instrumentation&quot; tabindex=&quot;-1&quot;&gt;What is Instrumentation? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#what-is-instrumentation&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Instrumentation is the act of adding code to your application that captures what is happening and turns it into data - traces, metrics, and logs.&lt;/p&gt;
&lt;p&gt;Think of a busy kitchen in a restaurant. Without instrumentation, you just know that food goes in and plates come out. With instrumentation, you have a timer on every dish, a counter for every order, and a note every time something is returned. You can now answer questions you couldn&#39;t before: which dish takes the longest? When does the kitchen slow down? Which orders triggered a complaint?&lt;/p&gt;
&lt;p&gt;There are two kinds of instrumentation:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Automatic instrumentation&lt;/strong&gt; is when a library does the work for you. Packages like &lt;code&gt;OpenTelemetry.Instrumentation.AspNetCore&lt;/code&gt; trace every incoming HTTP request, and &lt;code&gt;OpenTelemetry.Instrumentation.Http&lt;/code&gt; traces every outgoing &lt;code&gt;HttpClient&lt;/code&gt; call - without you writing a single line for each one. The library knows where the interesting things happen, so it instruments them on your behalf.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Manual instrumentation&lt;/strong&gt; (or custom instrumentation) is when you do it yourself - for the parts that only &lt;em&gt;you&lt;/em&gt; understand. No framework knows what a &amp;quot;PlaceOrder&amp;quot; operation is, or that you care about &lt;code&gt;customer.id&lt;/code&gt; and &lt;code&gt;order.id&lt;/code&gt;. That domain knowledge only lives in your code. In .NET, you reach for &lt;code&gt;ActivitySource&lt;/code&gt; (for traces) and &lt;code&gt;Meter&lt;/code&gt; (for metrics) from the &lt;code&gt;System.Diagnostics&lt;/code&gt; namespace. More on that in &lt;a href=&quot;https://irina.codes/opentelemetry-csharp-setup-and-instrumentation/&quot;&gt;Part 2&lt;/a&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;the-three-pillars&quot; tabindex=&quot;-1&quot;&gt;The Three Pillars &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#the-three-pillars&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;traces-the-parcel-tracker&quot; tabindex=&quot;-1&quot;&gt;Traces: The Parcel Tracker &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#traces-the-parcel-tracker&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When you order something online, you can track it. You see: picked up at warehouse - arrived at regional hub - out for delivery - delivered. Each step has a timestamp and a duration. If the parcel goes missing, you know exactly which step failed.&lt;/p&gt;
&lt;p&gt;Distributed tracing works the same way for requests in your software.&lt;/p&gt;
&lt;p&gt;A &lt;strong&gt;trace&lt;/strong&gt; is the complete record of a single request&#39;s journey - from the moment it hits your API, through every service call, database query, cache lookup, and background job it touches, until a response goes back to the caller. Each individual step is called a &lt;strong&gt;span&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Every trace carries a &lt;code&gt;TraceId&lt;/code&gt; that ties the whole journey together across all services. Every span carries its own &lt;code&gt;SpanId&lt;/code&gt; and a &lt;code&gt;ParentSpanId&lt;/code&gt; that links it back to whichever span called it - allowing you to reconstruct the exact call tree. Spans can also carry attributes (key-value pairs like &lt;code&gt;customer.id&lt;/code&gt; or &lt;code&gt;http.status_code&lt;/code&gt;) and events (timestamped annotations like &amp;quot;cache miss&amp;quot;).&lt;/p&gt;
&lt;p&gt;In .NET, the native API for this is &lt;code&gt;System.Diagnostics.Activity&lt;/code&gt; and &lt;code&gt;System.Diagnostics.ActivitySource&lt;/code&gt; - designed to be OTel-compatible from the start. In OTel terms: &lt;code&gt;Activity&lt;/code&gt; = Span, &lt;code&gt;ActivitySource&lt;/code&gt; = Tracer.&lt;/p&gt;
&lt;h3 id=&quot;metrics-the-car-dashboard&quot; tabindex=&quot;-1&quot;&gt;Metrics: The Car Dashboard &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#metrics-the-car-dashboard&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Your car doesn&#39;t send you a notification every time a cylinder fires or the alternator charges the battery. You get a dashboard: RPM, speed, fuel level, engine temperature. Numbers that summarize the state of the system so you can glance at them while driving.&lt;/p&gt;
&lt;p&gt;Metrics do the same for your application.&lt;/p&gt;
&lt;p&gt;Metrics are &lt;strong&gt;numbers measured or aggregated over time&lt;/strong&gt;. Unlike traces (which follow individual requests) or logs (which capture individual events), metrics are cheap to store, fast to query, and easy to alert on. You don&#39;t record every request - you record the count, the rate, the percentile distribution.&lt;/p&gt;
&lt;p&gt;The OTel SDK defines four instrument types: a &lt;code&gt;Counter&lt;/code&gt; (only increases - total orders placed), an &lt;code&gt;UpDownCounter&lt;/code&gt; (increases or decreases - active connections), a &lt;code&gt;Histogram&lt;/code&gt; (distribution of values - request duration, giving you p50/p95/p99), and an &lt;code&gt;ObservableGauge&lt;/code&gt; (polled at collection time - current memory usage). In .NET, these map directly to &lt;code&gt;System.Diagnostics.Metrics.Meter&lt;/code&gt; and its family of types, available since .NET 6.&lt;/p&gt;
&lt;h3 id=&quot;logs-the-ships-logbook&quot; tabindex=&quot;-1&quot;&gt;Logs: The Ship&#39;s Logbook &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#logs-the-ships-logbook&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A ship&#39;s captain keeps a logbook. Every notable event goes in, in order: &amp;quot;14:32 - departed port&amp;quot;, &amp;quot;15:10 - storm began&amp;quot;, &amp;quot;16:45 - storm passed&amp;quot;. A chronological record of what happened and when. When something goes wrong, you read the logbook.&lt;/p&gt;
&lt;p&gt;Your application&#39;s logs work the same way. The problem in distributed systems is context: you have log lines from five services, all interleaved by timestamp, with no way to tell which lines belong to the same user request.&lt;/p&gt;
&lt;p&gt;OTel solves this cleanly. When you emit a log inside an active span, OTel automatically injects the &lt;code&gt;TraceId&lt;/code&gt; and &lt;code&gt;SpanId&lt;/code&gt; into the log entry. You can jump from a single log line to the full trace it belongs to.&lt;/p&gt;
&lt;p&gt;And here is the best part for existing .NET codebases: if you already use &lt;code&gt;Microsoft.Extensions.Logging&lt;/code&gt; (&lt;code&gt;ILogger&lt;/code&gt;), you do not need to change any of your existing log calls. Add the OTel logging exporter once, and all your log calls automatically carry trace context from that point forward.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;what-is-an-exporter&quot; tabindex=&quot;-1&quot;&gt;What is an Exporter? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#what-is-an-exporter&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;An exporter is the component that takes the telemetry data your application produces and ships it somewhere useful.&lt;/p&gt;
&lt;p&gt;Think of it as a courier service. Your application boxes up the data - spans, metrics, log entries. The exporter is the courier that picks up those boxes and delivers them to wherever you want: Jaeger running on your laptop, a managed Datadog account, Azure Monitor, whatever fits your setup.&lt;/p&gt;
&lt;p&gt;Because OTel separates the &lt;em&gt;producing&lt;/em&gt; step from the &lt;em&gt;shipping&lt;/em&gt; step, you can swap exporters without touching your instrumentation code. This is the whole point of vendor neutrality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Console exporter&lt;/strong&gt; - prints telemetry to stdout. Great for local development: you see spans appear in your terminal as requests come in. Zero infrastructure needed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OTLP exporter&lt;/strong&gt; (OpenTelemetry Protocol) - the standard wire format. Works with any OTLP-compatible backend (Jaeger, Grafana, Datadog, Honeycomb, Azure Monitor, and more). This is the one you&#39;ll use in real environments.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;In-memory exporter&lt;/strong&gt; - stores telemetry in memory. Mostly used in tests to assert that specific spans or metrics were recorded.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Switching from Jaeger to Datadog? You change the exporter configuration - one or two lines in &lt;code&gt;Program.cs&lt;/code&gt;. The rest of your instrumentation code stays identical.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;busting-the-myths&quot; tabindex=&quot;-1&quot;&gt;Busting the Myths &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#busting-the-myths&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;OpenTelemetry replaces Jaeger / Datadog / Prometheus&amp;quot;&lt;/strong&gt;
No. OTel is the instrumentation and wire format layer. Jaeger, Datadog, and Prometheus are the storage, query, and visualization layer. They complement each other. OTel means you are not locked into any single backend.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;I will have to rewrite all my logging code&amp;quot;&lt;/strong&gt;
No. OTel integrates with &lt;code&gt;Microsoft.Extensions.Logging&lt;/code&gt;. Your existing &lt;code&gt;ILogger&lt;/code&gt; calls get trace context automatically once you add the OTel logging exporter. You do not touch the call sites.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;Tracing is only useful for microservices&amp;quot;&lt;/strong&gt;
Monoliths benefit too - sometimes more immediately. Slow database queries, external API latency, CPU-heavy code paths - traces expose all of this in a single-process application. If you have a database and an &lt;code&gt;HttpClient&lt;/code&gt;, tracing is already useful.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;The performance overhead is not worth it&amp;quot;&lt;/strong&gt;
OTel sampling lets you record 1% of traces in production with near-zero overhead. You configure the sampling ratio. Use 100% in development. The SDK is designed to be a no-op when no exporter is attached.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;I need a paid service&amp;quot;&lt;/strong&gt;
Jaeger is free and open source. One Docker command and it is running. Prometheus is free. The OTel Collector is free. You only pay for a managed service if you decide you no longer want to run the infrastructure yourself - and that is a problem for later.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;whats-next&quot; tabindex=&quot;-1&quot;&gt;What&#39;s Next &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/posts/2026/opentelemetry-beginners/#whats-next&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://irina.codes/opentelemetry-csharp-setup-and-instrumentation/&quot;&gt;Part 2&lt;/a&gt; picks up from here with the hands-on side: wiring up OpenTelemetry in an ASP.NET Core application from scratch, running Jaeger locally in one Docker command, and adding custom spans and metrics that reflect your own business domain. If the concepts clicked here, the code in Part 2 will feel obvious.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;&lt;em&gt;Have you started thinking about observability for your .NET services? Drop a comment or reach me on &lt;a href=&quot;https://linkedin.com/in/irinascurtu&quot;&gt;LinkedIn&lt;/a&gt; or &lt;a href=&quot;https://twitter.com/irina_scurtu&quot;&gt;X&lt;/a&gt; - I&#39;d love to hear where you are in the journey.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;For more .NET deep-dives on ASP.NET Core, distributed systems, and architecture, check out the rest of the posts on &lt;a href=&quot;https://irina.codes/&quot;&gt;irina.codes&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Api Versioning - A deep dive</title>
			<link href="https://irina.codes/api-versioning-a-deep-dive/"/>
			<updated>2025-11-25T00:00:00Z</updated>
			<id>https://irina.codes/api-versioning-a-deep-dive/</id>
			<content type="html">&lt;p class=&quot;original-post-note&quot; style=&quot;text-align:right&quot; target=&quot;_blank&quot;&gt;
  This article first appeared on &lt;a href=&quot;https://newsletter.systemdesign.one/p/api-versioning&quot;&gt;systemdesign.one Newsletter&lt;/a&gt;.
&lt;/p&gt;
&lt;hr /&gt;
Building Application Programming Interfaces ( APIs) is easy. Keeping them stable and predictable as the system evolves over time - that’s the real challenge.
&lt;p&gt;API consumers depend on API contracts^1, and even the slightest change can break dozens of integrations. Rename a field, change a response format, change an endpoint entirely, and suddenly someone’s production system might stop working correctly.  Systems grow, business requirements change all the time, and security or regulatory updates force adjustments that need to be communicated clearly.&lt;/p&gt;
&lt;p&gt;That’s where &lt;strong&gt;versioning&lt;/strong&gt; comes in. Versioning provides a structured approach to evolving APIs without leaving our API consumers behind. It sets the guidelines for how changes are introduced, how extensive those changes are,  and how consumers can transition to new versions at their own pace.&lt;/p&gt;
&lt;p&gt;But here’s the catch - just adding a version number in the URL doesn’t automatically prevent all the problems that might appear.&lt;/p&gt;
&lt;p&gt;I’ve personally seen numerous APIs across different domains that expose a /v1/ in their URLs. Not that it’s a terrible idea, but years later, they are displaying the same version even though the API has changed a lot.&lt;/p&gt;
&lt;p&gt;I find it hard to believe that nothing has changed in an API contract since they were first created. We often add new data, change property names, modify their types, remove fields, and so on. If so, why add a version at all?&lt;/p&gt;
&lt;p&gt;The truth is, many APIs end up with “v1” in the URL forever because the same organization or teams control the consumer apps**.**&lt;/p&gt;
&lt;p&gt;When the API owners and the API consumers are developed by different teams working at the same company, version upgrades are “just” code changes. Teams can synchronize deployments, push breaking changes, and update consumers without much risk of breaking changes.&lt;/p&gt;
&lt;p&gt;However, the scenario changes completely when the API is exposed publicly, and you don’t own and control the consumers. Suddenly, you cannot just break compatibility.&lt;/p&gt;
&lt;p&gt;You may be constrained by:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SLAs&lt;/strong&gt; (Service Level Agreements)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Legal contracts&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Third-party dependencies&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;…all of which force you to design carefully and communicate transparently.&lt;/p&gt;
&lt;p&gt;You don’t want to spend nights answering angry support calls because someone’s app broke after you changed an endpoint. That pressure alone is often enough to influence &lt;strong&gt;how you version&lt;/strong&gt;.&lt;/p&gt;
&lt;hr /&gt;
&lt;h2 id=&quot;api-as-a-contract&quot; tabindex=&quot;-1&quot;&gt;API as a Contract &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#api-as-a-contract&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s helpful to think about APIs as contracts. The contract describes the &lt;strong&gt;shape of the data, the form of the endpoints, and the behavior of the API&lt;/strong&gt;. The client and the provider both agree to this contract.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/img/posts/2025/Contract.png&quot; alt=&quot;Contract.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;However, contracts aren’t fixed forever - they evolve. A new field is added, a bug is fixed, or sometimes, a breaking change sneaks in. Versioning is our way of saying:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;“Here’s the old contract — still valid.”&lt;/li&gt;
&lt;li&gt;“Here’s the new one — with improvements.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The idea of a ‘contract’ helps both parties—the consumer and the API—understand each other. This way, they can communicate effectively.&lt;/p&gt;
&lt;p&gt;When an API evolves, we usually have three paths forward:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Release a new version in a new location.&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This means creating something like /api/v2/orders while keeping /api/v1/orders around. Current users can keep using the old version as is. They will switch only when they want the new features. This approach is safe and straightforward for clients, but it creates a lot of work for API owners. They now need to maintain several versions at the same time. This means fixing bugs, applying security updates, or implementing features for all supported versions. Over time, this can get very expensive and slow down future improvements.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Release a backward-compatible version&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this approach, the API changes in a way that doesn’t break existing clients. Typically, this means &lt;em&gt;additive&lt;/em&gt; changes, such as new fields in a response, new optional parameters, or new endpoints. Consumers don’t need to update anything, since everything they used before still works the same way. The rules are tight. Even a tiny change, such as changing givenName to firstName, can cause compatibility issues. This happens because current clients might rely on the original name. This strategy works well for gradual growth but can be limiting when bigger design shifts are needed.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Break compatibility entirely&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In this scenario, the API makes changes that require and force &lt;em&gt;every client&lt;/em&gt; to upgrade.&lt;/p&gt;
&lt;p&gt;At first glance, this sounds like the worst option—and often it is, because it forces all consumers to adapt quickly. But sometimes it’s unavoidable. For example, new rules might need a different data model. A serious security flaw could require significant changes. Also, the original API design may have issues that can&#39;t be fixed easily.&lt;/p&gt;
&lt;p&gt;In reality, &lt;strong&gt;we need a mix of all three&lt;/strong&gt;, depending on the type and impact of changes. And that’s where rules and versioning strategies come in.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Additive vs. Explicit Versioning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;When you make &lt;strong&gt;additive changes only&lt;/strong&gt;, your API evolves without incrementing its version. You add new fields, parameters, or endpoints, but never remove or rename anything. Additive changes are easy for consumers. They don’t have to change their integrations—but they force API designers to carry legacy choices forever.&lt;/p&gt;
&lt;p&gt;By contrast, &lt;strong&gt;explicit versioning&lt;/strong&gt; acknowledges when a change breaks compatibility. You create a new version using a URL, header, or parameter. This gives clients a clear boundary. They can see old behavior in one version and new behavior in another. Explicit versioning may require more maintenance, but it allows for bigger, cleaner design changes when needed.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Additive Versioning&lt;/th&gt;
&lt;th&gt;Explicit Versioning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What it is&lt;/td&gt;
&lt;td&gt;Evolve the API only by adding fields, parameters, or endpoints&lt;/td&gt;
&lt;td&gt;Introduce a new API version when breaking changes are needed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backward compatibility&lt;/td&gt;
&lt;td&gt;Fully preserved (no removals or renames)&lt;/td&gt;
&lt;td&gt;Not preserved between versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Impact on consumers&lt;/td&gt;
&lt;td&gt;Very low — integrations continue to work without changes&lt;/td&gt;
&lt;td&gt;Consumers must choose and migrate to the new version&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Design flexibility&lt;/td&gt;
&lt;td&gt;Limited — must keep all legacy choices forever&lt;/td&gt;
&lt;td&gt;High — allows major redesigns or cleanup&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Maintenance overhead&lt;/td&gt;
&lt;td&gt;Lower initially, but grows with legacy baggage&lt;/td&gt;
&lt;td&gt;Higher — must maintain multiple API versions&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ideal for&lt;/td&gt;
&lt;td&gt;Stable APIs with infrequent changes&lt;/td&gt;
&lt;td&gt;APIs that evolve rapidly or require periodic breaking improvements&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Version indicators&lt;/td&gt;
&lt;td&gt;None (single evolving API)&lt;/td&gt;
&lt;td&gt;URL path (&lt;code&gt;/v2&lt;/code&gt;), header, or query parameter&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h1 id=&quot;types-of-api-versioning&quot; tabindex=&quot;-1&quot;&gt;Types of API Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#types-of-api-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;Let’s walk through the different approaches to versioning an API, their benefits, and their drawbacks.&lt;/p&gt;
&lt;h2 id=&quot;1-path-based-versioning&quot; tabindex=&quot;-1&quot;&gt;1. Path-Based Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#1-path-based-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is the most common approach:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://coolapi.com/api/v1/orders
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Path-based versioning solves the problem of &lt;strong&gt;clarity and visibility&lt;/strong&gt;. Both developers and clients can easily identify which version they are using because it is visible upfront, in the path. This makes it easier for new consumers to follow documentation and quicker for developers to configure routing logic.&lt;/p&gt;
&lt;p&gt;However, this clarity comes at the cost of stability. URIs change with every major version, which means clients may break unless the provider maintains multiple versions at the same time. For API owners, that means patching, monitoring, and duplicating logic across &lt;code&gt;/v1&lt;/code&gt;, &lt;code&gt;/v2&lt;/code&gt;, and beyond.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easy to see which version you’re using.&lt;/li&gt;
&lt;li&gt;Simple to implement and route.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pollutes URLs with version info.&lt;/li&gt;
&lt;li&gt;Every new version means new endpoints, potentially breaking existing clients.&lt;/li&gt;
&lt;li&gt;Maintaining multiple versions creates redundancy and overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It seems easy enough, but here’s the challenge:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Roy Fielding&lt;/strong&gt;, the inventor of REST, reminded us that REST should imply evolvability,
&lt;img src=&quot;https://irina.codes/img/posts/2025/roy_fielding.avif&quot; alt=&quot;roy_fielding.avif&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If we correlate that with what Tim Berners-Lee &lt;a href=&quot;https://www.w3.org/Provider/Style/URI?utm_source=chatgpt.com&quot;&gt;said in 1998, that” Cool URIs don’t change,”&lt;/a&gt;  we might have no reason to version in the URL. Not in the path anyway.&lt;/p&gt;
&lt;p&gt;Let’s take a step back for a moment. A REST API is designed to work with &lt;strong&gt;representations of resources.&lt;/strong&gt; For example, imagine your system has a domain with entities such as &lt;em&gt;Orders&lt;/em&gt; or &lt;em&gt;Cookies&lt;/em&gt;. You will have endpoints that manage them, allowing you to add, delete, or edit these items as needed. Once the API evolves, the new version of the API will change how the resource is represented. It can have new fields, or the structure may be slightly different, but the core concept will be the same. An &lt;em&gt;Order&lt;/em&gt; will be an &lt;em&gt;Order&lt;/em&gt; object, whether you’re looking at version 1 or version n of the API. It doesn’t turn into a Cookie object because you moved from v1 to v2.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best practice:&lt;/strong&gt; If you adopt path-based versioning, always keep older versions alive for a transition period. Announce clear deprecation timelines and provide migration guides. Use shared libraries for common logic so that bug fixes don’t need to be copied everywhere.&lt;/p&gt;
&lt;p&gt;HTTP itself provides mechanisms for communicating versioning and deprecation. Two of the most useful are the &lt;strong&gt;&lt;code&gt;Sunset&lt;/code&gt; header&lt;/strong&gt; and &lt;strong&gt;3xx redirects&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc8594&quot;&gt;**Sunset Header (RFC 8594)&lt;/a&gt;: lets an API signal when a version will no longer be available.**&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;Sunset&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Wed&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;01&lt;/span&gt; Jul &lt;span class=&quot;token number&quot;&gt;2026&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;00&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;00&lt;/span&gt; GMT
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This communicates to consumers that the current version is scheduled to be retired on July 1, 2026. Combined with other headers like &lt;code&gt;Deprecation: true&lt;/code&gt;, it gives clients time to plan migration instead of being caught off guard.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;3xx Redirects
Redirects are another simple way to guide clients from one version to another. For example, you can respond to a request for &lt;code&gt;/api/v1/orders&lt;/code&gt; with a 301 Status code and a Location header that specifies where the resource can be found.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token number&quot;&gt;301&lt;/span&gt; Moved &lt;span class=&quot;token class-name&quot;&gt;Permanently&lt;/span&gt;
Location&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;api&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;v2&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;orders&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This helps consumers who aren’t ready to update their code right away. It also guides them toward the newer version. Redirects work very well when endpoints haven’t changed much—otherwise, clients may still need code changes.&lt;/p&gt;
&lt;h2 id=&quot;query-parameter-versioning&quot; tabindex=&quot;-1&quot;&gt;Query Parameter Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#query-parameter-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Instead of including the version in the path, the  base URL  is kept stable and the version is included as a parameter:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;https://coolapi.com/api/orders?api-version=1.2.3

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;****This approach solves the problem of &lt;strong&gt;flexibility at the resource level&lt;/strong&gt;. Developers can version specific endpoints or even individual resources without changing the overall API structure. Clients don’t need to update/store base URLs, and multiple versions can coexist on the same path.&lt;/p&gt;
&lt;p&gt;Routers typically rely on paths, not query strings, so you’ll need custom logic in your gateway or application to handle version dispatch. Documentation must also be precise; otherwise, developers may forget to include the query parameter and end up confused.&lt;/p&gt;
&lt;p&gt;Another important consideration is &lt;strong&gt;caching&lt;/strong&gt;. To prevent serving the wrong version, ensure that CDNs and proxies cache ?version=1 and ?version=2 separately. This is because many treat query strings as part of the cache key.   This also brings risks. Caches might not normalize parameters the same way, like ?version=2 and ?version=02. This can lead to cache pollution and lower hit rates. In poorly set up systems, some intermediaries might ignore query parameters. This can lead to clients getting the wrong version of a response.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;URL remains stable.&lt;/li&gt;
&lt;li&gt;Versioning can be granular, down to a single resource.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Routing becomes more complex (logic shifts to the handler).&lt;/li&gt;
&lt;li&gt;Documentation and discoverability are trickier.&lt;/li&gt;
&lt;li&gt;Caching can cause issues (pollution, misconfiguration, or wrong cache hits).&lt;/li&gt;
&lt;li&gt;It can get messy when multiple parameters pile up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Best practice:&lt;/strong&gt; If you choose query parameters, configure your CDN or cache &lt;strong&gt;always to include them in the cache key&lt;/strong&gt;. Document the parameter in each example call. Also, check its presence on the server side. This way, clients get clear error messages instead of silent issues. Normalize query strings where possible to avoid duplicate cache entries.&lt;/p&gt;
&lt;p&gt;This works well when you want resource-level control, but it can complicate your codebase and caching strategy.&lt;/p&gt;
&lt;h2 id=&quot;message-payload-versioning&quot; tabindex=&quot;-1&quot;&gt;Message Payload Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#message-payload-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here, the version is part of the request or response body itself:&lt;/p&gt;
&lt;pre class=&quot;language-json&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;v1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;data&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;status&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;shipped&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Payload versioning facilitates the management of &lt;strong&gt;long-lived data in asynchronous systems&lt;/strong&gt;. In event-driven or queue-based architectures, payloads can be stored for later replay. Including the version in the message allows consumers to know which schema to use, even after a long time.&lt;/p&gt;
&lt;p&gt;However, this approach mixes concerns. Versioning is now part of business data. Each consumer must manage multiple schemas. For REST APIs with short request/response lifecycles, this adds extra complexity.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Best practice:&lt;/strong&gt; Use payload versioning only for asynchronous or event-driven systems. Treat messages as fixed facts and create schema registries. This helps consumers safely deserialize old formats. Don&#39;t use this for standard REST APIs unless you need messages that last beyond your endpoints.&lt;/p&gt;
&lt;h2 id=&quot;header-based-versioning&quot; tabindex=&quot;-1&quot;&gt;Header-Based Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#header-based-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here, the URL stays clean, and the version is passed in the request headers:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;GET /api/orders
api-version: 2
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another approach is to version APIs through the &lt;strong&gt;&lt;code&gt;Accept&lt;/code&gt; header&lt;/strong&gt;. Instead of changing the URL or adding query parameters, the client specifies the resource version by requesting a specific media type. This is known as &lt;strong&gt;Media Type Versioning.&lt;/strong&gt; For example, the client might use an Accept header value. The API will then respond with the resource formatted according to version 2 of the contract.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Accept: application/vnd.example+json;api-version=2
Accept: application/vnd.github.v2+json
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Header-based versioning lets clients upgrade to new API versions at their own pace. This means they don’t have to rewrite or disrupt their integrations.&lt;/p&gt;
&lt;p&gt;But there is a catch. Because version information is hidden in headers, it is less visible in logs or browser address bars. s. Debugging is more difficult if logging and tracing systems aren&#39;t set to record these headers. Proxies or middleware may even strip out unknown headers if not set up correctly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pros:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Keeps URIs stable and self-describing.&lt;/li&gt;
&lt;li&gt;Aligns with HTTP semantics (metadata belongs in headers).&lt;/li&gt;
&lt;li&gt;Works great with content negotiation.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Cons:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Version isn’t visible in the URL (harder for debugging, logging, and caching).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Some proxies or middlewares might strip or block custom headers.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Caching introduces some challenges because the version is shown in the Accept header. Caches (such as CDNs, reverse proxies, and browsers) require the correct setup to use this header when storing and delivering responses.&lt;/p&gt;
&lt;p&gt;To solve this, add a Vary: Accept header. This tells caches that different &lt;strong&gt;Accept&lt;/strong&gt; values lead to different responses. As a best practice, we should always include a Vary header in our responses (e.g., V&lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc7231?utm_source=chatgpt.com#page-70&quot;&gt;ary: api-version or Vary: Accept&lt;/a&gt;) to prevent caches from mixing responses across different versions.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Adopting this option may seem complex, but it’s the cleanest and most sustainable choice long-term. Header-based versioning keeps your URLs stable. For example, the/orders endpoint will always point to the same resource, no matter the version. Instead of using different endpoints like /v1/orders or /v2/orders, versioning becomes part of the resource&#39;s representation. This fits well with HTTP and REST design principles.&lt;/p&gt;
&lt;p&gt;This method also helps your API adapt to future changes. You can update representations without breaking links. It maintains consistency across endpoints and uses HTTP&#39;s content negotiation tools, such as Accept and Content-Type. This reduces fragmentation, keeps your API smaller, and offers a clearer contract for users.&lt;/p&gt;
&lt;p&gt;The initial setup may require more effort. Teams need to configure caching properly with Vary: Accept and adjust their tools. However, the long-term benefits include a cleaner, more maintainable API that avoids piling up technical debt with each new version.&lt;/p&gt;
&lt;p&gt;Now that we&#39;ve covered versioning types, let&#39;s explore our options for versioning parameter format.&lt;/p&gt;
&lt;h3 id=&quot;versioning-formats&quot; tabindex=&quot;-1&quot;&gt;Versioning formats &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#versioning-formats&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;h3 id=&quot;1-semantic-versioning-semver&quot; tabindex=&quot;-1&quot;&gt;1. Semantic Versioning(&lt;a href=&quot;https://semver.org/&quot;&gt;SemVer&lt;/a&gt;) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#1-semantic-versioning-semver&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Semantic Versioning (SemVer) is a system that uses the format MAJOR.MINOR.PATCH. This helps illustrate the changes included in a release.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;MAJOR&lt;/strong&gt; - Breaking changes (clients must actively migrate).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MINOR&lt;/strong&gt; - Backward-compatible changes (new fields, endpoints).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;PATCH -&lt;/strong&gt;  Bug fixes only.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Example: &lt;code&gt;2.4.1&lt;/code&gt; → second major release, fourth minor, first patch.&lt;/p&gt;
&lt;p&gt;This gives consumers expectations: a jump from 1.9 to 2.0 is a big deal, but a rise from 2.4 to 2.5 should be considered safe.&lt;/p&gt;
&lt;h3 id=&quot;2-calendar-versioning-calver&quot; tabindex=&quot;-1&quot;&gt;2. Calendar Versioning(&lt;a href=&quot;https://calver.org/&quot;&gt;CalVer&lt;/a&gt; ) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#2-calendar-versioning-calver&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;CalVer&lt;/em&gt; is a versioning convention that uses dates instead of semantic numbers. Sometimes it is based on your project&#39;s release calendar.&lt;/p&gt;
&lt;p&gt;Example:
&lt;strong&gt;Ubuntu 24.04&lt;/strong&gt; - released in April 2024 (YY.MM format).
&lt;strong&gt;Python 3.12.20231001&lt;/strong&gt;  - could mean a release from October 1st, 2023 (YYYYMMDD format).&lt;/p&gt;
&lt;p&gt;The version shows when the software was released. This helps us track freshness more easily than compatibility.&lt;/p&gt;
&lt;h3 id=&quot;3-hash-versioning-hashver&quot; tabindex=&quot;-1&quot;&gt;3. Hash Versioning(&lt;a href=&quot;https://miniscruff.github.io/hashver/&quot;&gt;HashVer&lt;/a&gt;) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#3-hash-versioning-hashver&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Hash versioning is a versioning scheme that uses &lt;strong&gt;hashes&lt;/strong&gt; (like Git commit IDs) instead of numbers or dates. It allows us to reference a point in time for our software, hardware, or anything in between.
Example:
v-237a2b4f -could be the shortened Git commit hash where the build was cut.&lt;/p&gt;
&lt;p&gt;The challenge with versioning is conveying a potentially large set of API changes into a single version label.&lt;/p&gt;
&lt;h2 id=&quot;real-world-case-study-stripe&quot; tabindex=&quot;-1&quot;&gt;Real-World Case Study: Stripe &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#real-world-case-study-stripe&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s one thing to talk theory, but let’s look at how a company like Stripe is designing its API.
Stripe uses a &lt;strong&gt;header-based versioning&lt;/strong&gt; approach: clients send a &lt;code&gt;Stripe-Version&lt;/code&gt; header to indicate which &lt;a href=&quot;https://docs.stripe.com/api/versioning&quot;&gt;API version&lt;/a&gt; they want. When a new user first makes a request, Stripe “pins” that account to the latest available version at that time. All future API calls from that account use that version by default unless the client overrides it.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;Stripe&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;Version&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2023&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Stripe also releases &lt;strong&gt;&lt;em&gt;rolling versions^2&lt;/em&gt;,&lt;/strong&gt; named by date, with most releases being backward-compatible. Only certain “major” or “release train” versions introduce breaking changes. Versions are named with dates (e.g. 2025-08-27.basil) rather than simple numeric “v1, v2” semantics&lt;/p&gt;
&lt;p&gt;Why Stripe chose this approach — strengths &amp;amp; trade-offs&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Strengths/rationale:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stability for clients&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Pinning an account to a version helps Stripe keep existing integrations safe during updates. Clients can upgrade when convenient. &lt;a href=&quot;https://stripe.com/blog/api-versioning&quot;&gt;Stripe+1&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Controlled breaking changes&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most changes are backward-compatible; breaking changes are introduced only at specific release points. This smooths the migration path. &lt;a href=&quot;https://docs.stripe.com/upgrades&quot;&gt;Stripe Docs+2Stripe Docs+2&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Flexible overrides &amp;amp; experimentation&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Clients can set the Stripe-Version header for each request. This allows them to test new versions without a full migration. &lt;a href=&quot;https://stripe.com/blog/api-versioning&quot;&gt;Stripe+2Stripe Docs+2&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Decouples API routing from versioning&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Since the version is in the header, the URL doesn’t need to change. The path stays clean and meaningful.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In other words, Stripe turned versioning into a &lt;strong&gt;safe, consumer-driven choice&lt;/strong&gt;. You decide when to upgrade, not Stripe. This aligns perfectly with the principle: &lt;em&gt;don’t break your consumers without their consent&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;real-world-study-case-github&quot; tabindex=&quot;-1&quot;&gt;Real-World Study case: GitHub &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#real-world-study-case-github&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another interesting case is &lt;strong&gt;GitHub’s REST API&lt;/strong&gt;. GitHub uses &lt;strong&gt;header-based versioning&lt;/strong&gt; with &lt;strong&gt;calendar-date identifiers&lt;/strong&gt;. The base URL remains fixed: &lt;a href=&quot;https://api.github.com/%5D(https://api.github.com/)&quot;&gt;https://api.github.com/&lt;/a&gt;. Clients specify the version they want via the custom header:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;X-GitHub-Api-Version: 2022-11-28
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;Notice that there is no /v1/, /v2/, etc. included in the path.&lt;/li&gt;
&lt;li&gt;If no version header is provided, GitHub defaults to the latest stable version.&lt;/li&gt;
&lt;li&gt;Each version is supported for &lt;strong&gt;at least 24 months&lt;/strong&gt;, which means developers have a guaranteed migration window.&lt;/li&gt;
&lt;li&gt;Breaking changes, like removing a field or changing behavior, trigger a new version. Non-breaking, additive changes roll out to all active versions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Why this works well for them&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Stable URLs :
The resource path (&lt;code&gt;/users/{username}&lt;/code&gt;, &lt;code&gt;/repos/{owner}/{repo}&lt;/code&gt;) never changes. Versioning lives in headers, which keeps URIs clean and consistent with Tim Berners-Lee’s principle that &lt;em&gt;“Cool URIs don’t change.”&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Clear upgrade path: GitHub offers 24 months of support for each version. This helps ease the burden on large integrations. Teams can plan migrations with confidence, rather than scrambling every time a new release is introduced.&lt;/li&gt;
&lt;li&gt;Clear boundary for breaking changes: Small, additive improvements don’t require a new version. This avoids “version churn,” where every little change creates another version to manage. At the same time, bigger shifts have a well-defined process.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GitHub’s approach highlights another philosophy: instead of thinking in &lt;strong&gt;v1/v2 jumps&lt;/strong&gt;, think of your API as &lt;strong&gt;incremental snapshots in time&lt;/strong&gt;. Clients pin to a snapshot and move forward when ready.&lt;/p&gt;
&lt;h1 id=&quot;when-to-increase-the-version&quot; tabindex=&quot;-1&quot;&gt;When to Increase the Version? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#when-to-increase-the-version&quot;&gt;🔗&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;There’s no strict rule for when to raise a version number. It depends on many factors. Consider the following guiding questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;How many consumers do you have?&lt;/li&gt;
&lt;li&gt;How sensitive are they to breaking changes?&lt;/li&gt;
&lt;li&gt;Are you bound by SLAs or legal obligations?&lt;/li&gt;
&lt;li&gt;Can you realistically maintain multiple active versions of your software?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Be pragmatic. Don’t bump the version with every deployment. Only bump when the &lt;strong&gt;contract changes&lt;/strong&gt; in a meaningful way.&lt;/p&gt;
&lt;h2 id=&quot;tooling-for-managing-api-versioning&quot; tabindex=&quot;-1&quot;&gt;&lt;strong&gt;Tooling for Managing API Versioning&lt;/strong&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#tooling-for-managing-api-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Versioning isn’t just a design choice—it also needs operational support. Fortunately, there are plenty of tools that can help automate and manage the complexity:&lt;/p&gt;
&lt;h3 id=&quot;api-gateways&quot; tabindex=&quot;-1&quot;&gt;API Gateways &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#api-gateways&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/img/posts/2025/API-diagrams-gateway.png&quot; alt=&quot;Gateway.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Gateways act as the &lt;strong&gt;traffic managers&lt;/strong&gt; for your APIs. They sit in front of your services, receive incoming requests, and decide how to route them. This makes it much easier to run multiple API versions in parallel.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Examples&lt;/strong&gt;:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://konghq.com/&quot;&gt;Kong API&lt;/a&gt; Gateway lets you deploy plugins for version routing, deprecations, and logging.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloud.google.com/apigee&quot;&gt;Apigee&lt;/a&gt; – supports versioned API proxies and detailed analytics.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://aws.amazon.com/api-gateway/&quot;&gt;AWS API Gateway&lt;/a&gt; – enables you to run different &lt;strong&gt;stages&lt;/strong&gt; (like &lt;code&gt;v1&lt;/code&gt;, &lt;code&gt;v2&lt;/code&gt;) and control access with IAM policies.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://learn.microsoft.com/en-us/azure/api-management/api-management-key-concepts&quot;&gt;Azure API Management&lt;/a&gt; – has first-class support for multiple versions and revisions, with portal-based documentation for each.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Usage scenario&lt;/strong&gt;: You can keep &lt;code&gt;/v1/orders&lt;/code&gt; active while routing &lt;code&gt;/v2/orders&lt;/code&gt; to a new backend service, then gradually phase out/v1 once consumers have migrated.&lt;/p&gt;
&lt;h3 id=&quot;api-documentation-frameworks&quot; tabindex=&quot;-1&quot;&gt;API Documentation Frameworks &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#api-documentation-frameworks&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Good versioning isn’t just about routing traffic - it’s also about communicating and making the changes visible to consumers. Documentation frameworks make sure developers know which version they’re using and what has changed.:&lt;/p&gt;
&lt;p&gt;Examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://swagger.io/specification/&quot;&gt;OpenAPI&lt;/a&gt; / Swagger: you can publish multiple YAML/JSON specs (v1, v2, etc.), generate docs with Swagger UI, and even compare schemas between versions.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.postman.com/&quot;&gt;Postman&lt;/a&gt; lets you group API requests by version and share version-specific collections with consumers. Also, it simplifies API testing.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://redocly.com/&quot;&gt;Redoc&lt;/a&gt; - renders OpenAPI specs beautifully, making it easy to maintain docs for multiple versions.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Example in practice&lt;/strong&gt;: You might publish &lt;code&gt;orders-v1.yaml&lt;/code&gt; and &lt;code&gt;orders-v2.yaml&lt;/code&gt; side by side. Swagger UI then shows consumers exactly what fields changed between versions, instead of leaving them to guess.&lt;/p&gt;
&lt;p&gt;Usage scenario: You might publish &lt;code&gt;orders-v1.yaml&lt;/code&gt; and &lt;code&gt;orders-v2.yaml&lt;/code&gt; side by side. Swagger UI then shows consumers exactly what fields changed between versions, instead of leaving them to guess.&lt;/p&gt;
&lt;h3 id=&quot;versioning-libraries&quot; tabindex=&quot;-1&quot;&gt;Versioning Libraries &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#versioning-libraries&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Depending on your ecosystem, you might find a library that handles the plumbing for different API versions. This means you won&#39;t need to reinvent the wheel. These can be useful. They cut down on boilerplate code and ensure consistent versioning across your endpoints.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/dotnet/aspnet-api-versioning&quot;&gt;.NET API Versioning&lt;/a&gt; – a widely used library that lets you define versions via routes (&lt;code&gt;/v1/orders&lt;/code&gt;), query strings (&lt;code&gt;?api-version=1.0&lt;/code&gt;), or headers.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reflectoring.io/spring-boot-api-versioning/&quot;&gt;Spring Boot API Versioning&lt;/a&gt; Approaches – a guide showing how to implement versioning in Java/Spring apps (header-based, URI-based, or content negotiation).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/express-routes-versioning&quot;&gt;Express.js Versioning Middleware&lt;/a&gt; – allows Node.js developers to map different versions of handlers to the same endpoint.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Usage scenario:  In ASP.NET Core, you can decorate a controller with &lt;code&gt;[ApiVersion(&amp;quot;1.0&amp;quot;)]&lt;/code&gt; and &lt;code&gt;[ApiVersion(&amp;quot;2.0&amp;quot;)]&lt;/code&gt;, then route clients to the right version based on the &lt;code&gt;api-version&lt;/code&gt; header&lt;/p&gt;
&lt;p&gt;Tools don’t replace a good versioning strategy, but they make the work manageable at scale. Gateways manage routing and deprecation. Documentation frameworks provide clarity for users. Libraries ensure code consistency. Tools like Postman and Swagger UI show what’s changed.&lt;/p&gt;
&lt;h1 id=&quot;takeaway-when-to-use-which-strategy&quot; tabindex=&quot;-1&quot;&gt;Takeaway: When to Use Which Strategy &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/api-versioning-a-deep-dive/#takeaway-when-to-use-which-strategy&quot;&gt;🔗&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;There’s no single “best” way to version APIs. Each strategy exists because it solves a different problem. The key is matching the &lt;strong&gt;strategy&lt;/strong&gt; to your &lt;strong&gt;context&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Path-based versioning&lt;/strong&gt; is best when you &lt;strong&gt;control all the consumers&lt;/strong&gt;. It’s easy to read, easy to route, and works fine for internal systems where you can update clients in lockstep.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Query parameter versioning&lt;/strong&gt; makes sense if you want &lt;strong&gt;granularity at the resource level&lt;/strong&gt;. Use it when you need flexible evolution but can tolerate a bit more routing complexity.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Header-based versioning&lt;/strong&gt; is the safest choice when you have &lt;strong&gt;external or long-lived clients you don’t control&lt;/strong&gt;. It keeps URLs stable and allows consumers to decide when to upgrade.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Payload versioning&lt;/strong&gt; works well for asynchronous, event-driven systems. In these systems, messages might be stored or replayed even years later. It’s overkill for typical REST APIs but powerful in streaming or messaging contexts.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And when it comes to &lt;strong&gt;version labels&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use &lt;strong&gt;SemVer&lt;/strong&gt; if you want to communicate &lt;em&gt;the impact (breaking vs, additive vs.&lt;/em&gt; bugfix).&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;CalVer&lt;/strong&gt; if you want to communicate &lt;em&gt;freshness&lt;/em&gt; (when it was released).&lt;/li&gt;
&lt;li&gt;Use &lt;strong&gt;HashVer&lt;/strong&gt; if you care about &lt;em&gt;traceability&lt;/em&gt; (exactly which commit produced this build).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the end, choose &lt;strong&gt;the versioning strategy that surprises your consumers the least&lt;/strong&gt;. If you ensure their stability and make migration paths clear, they’ll trust your API. That trust is more valuable than any technical detail.&lt;/p&gt;
&lt;p&gt;Footnotes&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;^1 - &lt;strong&gt;API Contract&lt;/strong&gt; - the formal agreement that defines how clients interact with an API—covering its endpoints, methods, request and response formats, and error handling—and breaking it means existing integrations may fail.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;rolling versions^2 -&lt;/strong&gt;&lt;/em&gt;&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Competing Consumers Pattern: The Underrated Key to handling more load</title>
			<link href="https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/"/>
			<updated>2025-03-25T00:00:00Z</updated>
			<id>https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/</id>
			<content type="html">&lt;p&gt;As a software architect, I&#39;m always looking for patterns that reduce complexity, improve throughput, and make systems more resilient. One of the most powerful — and often overlooked pattern in distributed systems is the Competing Consumers Pattern.&lt;/p&gt;
&lt;p&gt;If you have a system that has the right prequisites, aka, uses queues, has async messaging or it is event-driven, this pattern deserves a spot in your architecture toolbox.&lt;/p&gt;
&lt;p&gt;In this post, I’ll break down:&lt;/p&gt;
&lt;p&gt;✅ What the Competing Consumers Pattern is&lt;/p&gt;
&lt;p&gt;✅ When and why to use it&lt;/p&gt;
&lt;p&gt;✅ Key benefits and trade-offs&lt;/p&gt;
&lt;h2 id=&quot;what-is-the-competing-consumers-pattern&quot; tabindex=&quot;-1&quot;&gt;🔍 What Is the Competing Consumers Pattern? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#what-is-the-competing-consumers-pattern&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.enterpriseintegrationpatterns.com/patterns/messaging/CompetingConsumers.html&quot;&gt;Competing Consumers is a design pattern&lt;/a&gt; in asynchronous message-based systems where multiple consumers pull messages from the same message queue. Each message is being proccessed by only one consumer, and the consumers &lt;strong&gt;“compete”&lt;/strong&gt; to process messages off the queue as they arrive.&lt;/p&gt;
&lt;p&gt;This approach allows us to scale horizontally,without too much hassle, automatically balancing load across multiple worker instances.&lt;/p&gt;
&lt;h2 id=&quot;why-use-the-competing-consumers-pattern&quot; tabindex=&quot;-1&quot;&gt;Why Use the Competing Consumers Pattern? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#why-use-the-competing-consumers-pattern&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here are the core problems this pattern solves:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Handling Variable Workloads&lt;/strong&gt; -
Message queues act as a buffer between producers and consumers, leveling out the load. By increasing the number of consumers, you process more messages, without changing the producer logic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Scalability&lt;/strong&gt; -
You can add or remove consumer instances to scale horizontally, when needed, based on traffic. This is great for cloud-native, serverless, or containerized environments.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Resilience&lt;/strong&gt; -
If one consumer crashes, others continue processing messages without interruption, giving you a bit of redundancy when it comes to processing messages.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;how-does-it-look-in-practice&quot; tabindex=&quot;-1&quot;&gt;How does it look in practice &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#how-does-it-look-in-practice&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Producers send messages sends messages to a queue.(e.g., an API or upstream service)&lt;/li&gt;
&lt;li&gt;The queue (or broker) stores the messages until a consumer is ready to process them.&lt;/li&gt;
&lt;li&gt;Multiple consumer instances pull/get messages from the same queue and process messages independently.&lt;/li&gt;
&lt;li&gt;Each message is guaranteed to be processed by only one consumer, and as it is processed is not accessible by other consumers.
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/2025/cc-black.svg&quot; /&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;trade-offs-and-design-considerations&quot; tabindex=&quot;-1&quot;&gt;Trade-Offs and Design Considerations &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#trade-offs-and-design-considerations&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Like any architectural pattern, Competing Consumers comes with its own set of challenges:&lt;/p&gt;
&lt;h3 id=&quot;message-ordering&quot; tabindex=&quot;-1&quot;&gt;Message Ordering &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#message-ordering&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When multiple consumers are involved, FIFO order is not guaranteed unless explicitly implemented (e.g., with message sessions in Azure or partition keys in Kafka).&lt;/p&gt;
&lt;h3 id=&quot;idempotency-and-isolated-independent-message-processing&quot; tabindex=&quot;-1&quot;&gt;🔁 Idempotency &amp;amp; isolated/independent message processing &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#idempotency-and-isolated-independent-message-processing&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Consumers should be idempotent—able to process the same message more than once without side effects. This is crucial when retries happen. Stricly speaking, in an asyncronous messaging world, you should always strive for that.
Also, each message should be able to be processed independent of other messages. Just as we talk in REST API that requests/responses should be able to be processed in isolation. Same here.
Consumers shouldn&#39;t need to have context about what messages have been processed before, or of what comes after.&lt;/p&gt;
&lt;h3 id=&quot;poison-messages&quot; tabindex=&quot;-1&quot;&gt;Poison Messages &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#poison-messages&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some messages might always fail to process. Leverage dead-letter queues (DLQs) with a suitable error management strategy to isolate and analyze them quickly. On top of that, having an &lt;a href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/&quot;&gt;Error management platform&lt;/a&gt; might save you valuable time and aid in implementing recoverability.&lt;/p&gt;
&lt;h3 id=&quot;observability&quot; tabindex=&quot;-1&quot;&gt;📈 Observability &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#observability&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As observability should be a first-class citizen in any system, the &lt;strong&gt;Consumer throughput&lt;/strong&gt;, &lt;strong&gt;queue length&lt;/strong&gt;, and &lt;strong&gt;error management&lt;/strong&gt; should be monitored.
Having some metrics ensures that you can smartly auto-scale your consumers. For example, if you notice that messages are piling up in a queue, up to a threshold, without receiving errors. It might indicate that you need more &#39;workforce,&#39; you could add a new consumer to the queue to help spread the load.&lt;/p&gt;
&lt;h2 id=&quot;when-should-you-use-this-pattern&quot; tabindex=&quot;-1&quot;&gt;🧩 When Should You Use This Pattern? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#when-should-you-use-this-pattern&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The short answer an architect would give is: it depends. Of course, it depends on several factors:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;your workload is bursty or unpredictable, or it is predictable. You will know you will have an event that will cause an increase in the processing need—for example, a Black Friday sale.&lt;/li&gt;
&lt;li&gt;You have some long-running processing to do, and you need to process more. In this case, you could dynamically scale&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;don-t-use-it-when&quot; tabindex=&quot;-1&quot;&gt;Don’t use it when: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#don-t-use-it-when&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;You require strict message ordering&lt;/li&gt;
&lt;li&gt;Messages must be processed exactly once with no duplicates (hard guarantee)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;final-thoughts&quot; tabindex=&quot;-1&quot;&gt;💬 Final Thoughts &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/competing-consumers-pattern-the-underrated-key-to-handling-more-load/#final-thoughts&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The Competing Consumers Pattern is a fantastic way to handle more load and temporarily scale parts of your system. It’s simple to implement and powerful in practice—especially in cloud-native architectures.&lt;/p&gt;
&lt;p&gt;Whether you’re processing millions of messages daily or want more resilience by adding a bit of redundancy to your consumers, this pattern has your back.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>MassTransit with Azure Service Bus - Error management</title>
			<link href="https://irina.codes/masstransit-with-azure-service-bus-error-management/"/>
			<updated>2025-02-13T00:00:00Z</updated>
			<id>https://irina.codes/masstransit-with-azure-service-bus-error-management/</id>
			<content type="html">&lt;!-- &lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/masstransit-errormanagement_wvjhw0.png&quot;
  srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/masstransit-errormanagement_wvjhw0.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/masstransit-errormanagement_wvjhw0.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/masstransit-errormanagement_wvjhw0.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/masstransit-errormanagement_wvjhw0.png 1856w&quot;
  sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot;
  alt=&quot;MassTransit with Azure Service Bus - Error management&quot;
  importance=&quot;high&quot;
  class=&quot;img-responsive&quot;
  style=&quot;display:none&quot;
  width=&quot;780&quot;&gt;  --&gt;
&lt;p&gt;When working with distributed messaging systems, failures are inevitable. Whether it&#39;s transient network issues, processing errors, or service unavailability, handling  and addressing failures gracefully is crucial.&lt;/p&gt;
&lt;p&gt;In this post we explore how with can deal with messages that can’t be processed by MassTransit endpoints and how to recover messages that end up in erorr quues or DLQs.&lt;/p&gt;
&lt;h3 id=&quot;handling-failures-in-masstransit&quot; tabindex=&quot;-1&quot;&gt;Handling failures in MassTransit &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/#handling-failures-in-masstransit&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;MassTransit allows us to configure different policies for handling transient failures:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Retries&lt;/strong&gt;: Automatically retry a failing message with exponential backoff. Has some policies we can apply&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Circuit Breakers&lt;/strong&gt;: Prevent overwhelming a failing service with continuous retries.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Redelivery&lt;/strong&gt; : Move a message to an error queue after exceeding retry attempts, for a much later redelivery&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These mechanisms are the first step in ensuring resilience against transient failures and preventing message loss. However, they can’t guarantee that a message will be successfully processed before the retry attempt is exhausted.&lt;/p&gt;
&lt;p&gt;If that happens, the messages are moved either an &lt;strong&gt;Error&lt;/strong&gt; queue, or a &lt;strong&gt;Dead-Letter queue&lt;/strong&gt;.  That’s the last stop for them, they go dormant until we wake them up and reprocess them. MassTransit, by convention, will create  &lt;code&gt;&amp;lt;queuename&amp;gt;_error&lt;/code&gt; for any queue from where the message couldn&#39;t be processed sucessfully, unless explicitly told otherwise.&lt;/p&gt;
&lt;h3 id=&quot;recovering-failed-messages&quot; tabindex=&quot;-1&quot;&gt;Recovering failed messages &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/#recovering-failed-messages&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;All messages that end up in error queues should be considered valuable to the business. They can end up there for various reasons: the database is down, there is a network glitch, the consumer server is slow, etc.
Let’s say that the consumer endpoint was down and is now up and running again, and we are ready to replay all the failed messages. From this point on, nothing could automagically start the processing flow from where it failed.&lt;/p&gt;
&lt;p&gt;We would have to dig through the failed messages and restart the flow.
If you have one or a few messages, the process can be painless enough, but what if you have hundreds? Or thousands? What then?&lt;/p&gt;
&lt;p&gt;Luckily, the recoverability processes can be done with the help of &lt;a href=&quot;https://learn.microsoft.com/en-us/azure/service-bus-messaging/explorer&quot;&gt;Service Bus Explorer &lt;/a&gt;or by setting up a dedicated consumer.&lt;/p&gt;
&lt;p&gt;Although this works like a charm, it has basic capabilities. It forces you to click through a not-so-simple interface endlessly and to think in engineering terms.&lt;/p&gt;
&lt;p&gt;More than that, it is almost impossible if you want to give better visibility over the system to someone not involved in the development process or allow anyone to help correct the errors. Let’s face it:  Azure Portal is not the friendliest place to be. I am sometimes scared not to click the wrong button.&lt;/p&gt;
&lt;p&gt;With this aside, reprocessing a large number of messages from the &lt;strong&gt;dead-letter queue (DLQ) or an error queue&lt;/strong&gt; can be challenging for anyone, especially when there is no way to &lt;strong&gt;group&lt;/strong&gt; them efficiently.  Each message must be manually retrieved and inspected without any categorization or sorting capabilities.  &lt;/p&gt;
&lt;p&gt;This lack of features increases operational overhead and slows recovery efforts, making it difficult to restore normal processing quickly. We do want to visibility and avoid tedious work right, right?&lt;/p&gt;
&lt;p&gt;How about if I told you we have a platform that can be easily integrated with &lt;strong&gt;MassTransit endpoints that use Azure Service Bus&lt;/strong&gt;, making message reprocessing effortless?&lt;/p&gt;
&lt;h3 id=&quot;recovering-failed-messages-the-easy-way&quot; tabindex=&quot;-1&quot;&gt;Recovering failed messages the easy way &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/#recovering-failed-messages-the-easy-way&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.particular.net/platform/masstransit?utm_source=irinacodes&amp;amp;utm_medium=blog&amp;amp;utm_campaign=irina&quot;&gt;MassTransit Error Management&lt;/a&gt; is a platform that can be easily set up, and removed, runs in Docker, and knows how to talk error queues, and DQLs.&lt;/p&gt;
&lt;p&gt;The Particular team set up &lt;a href=&quot;https://bit.ly/error-management-showcase&quot;&gt;an interesting demo&lt;/a&gt;, very well documented that works with Azure Service Bus and RabbitMQ. Feeel free to have a look.&lt;/p&gt;
&lt;p&gt;Now, let’s explore the platform named ServicePulse. This integrates with MassTrasit endpoints and allows you to specify which queues you want to monitor.
As soon as it detects failed messages in those queues, it will transport these messages in this centralized dashboard where you can easily group, sort, retry, edit, and even delete the failed messages.&lt;/p&gt;
&lt;p&gt;So let&#39;s see what it brings to the table:&lt;/p&gt;
&lt;h3 id=&quot;visualizing-all-messages&quot; tabindex=&quot;-1&quot;&gt;Visualizing all messages &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/#visualizing-all-messages&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The platform exposes different views for failed messages: &lt;em&gt;Groups&lt;/em&gt;, &lt;em&gt;All, Deleted&lt;/em&gt;, and &lt;em&gt;All Deleted&lt;/em&gt;, all in a very friendly and interface.&lt;/p&gt;
&lt;p&gt;It gives you an birds-eye view of the:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Type of the message that failed.&lt;/li&gt;
&lt;li&gt;Time of failing&lt;/li&gt;
&lt;li&gt;Endpoint where the message processing failed&lt;/li&gt;
&lt;li&gt;Machine that processed the message&lt;/li&gt;
&lt;li&gt;The exception of the failing message&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;From here you can select the messages that you want, and you can retry them or delete them.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/2025/mt-asb-recov/all-failed.png&quot; /&gt;
&lt;h3 id=&quot;grouping-messages-and-sorting&quot; tabindex=&quot;-1&quot;&gt;Grouping messages and sorting &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/#grouping-messages-and-sorting&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Grouping and sorting is an importan feature as it can really help you save time. When you have many error message types and you want to skim trough them fast. If you know that a Shipping consumer failed to process the messages, then you can retry the entire group.
In the image below you will see 3 distinct groups.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/2025/mt-asb-recov/groupandsort.png&quot; /&gt;
&lt;h3 id=&quot;visualizing-and-editing-individual-messages&quot; tabindex=&quot;-1&quot;&gt;Visualizing and editing individual messages &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/#visualizing-and-editing-individual-messages&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Each message can be viewed independently too. We can see the &lt;code&gt;Stacktrace&lt;/code&gt;, &lt;code&gt;Headers&lt;/code&gt; and the &lt;code&gt;Body&lt;/code&gt; of the message with all the details.&lt;/p&gt;
&lt;p&gt;The message can be retried as it is, or we can &lt;em&gt;Edit &amp;amp; Retry&lt;/em&gt; to apply changes to the body or header.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/2025/mt-asb-recov/individual-message-overview.png&quot; /&gt;
&lt;h3 id=&quot;retrying-groups-of-messages&quot; tabindex=&quot;-1&quot;&gt;Retrying groups of messages &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/#retrying-groups-of-messages&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Retrying groups of messages when you don&#39;t want to edit anything is very straightforward. Below you can see two groups of messages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the first one was already retried, and had 2 messages&lt;/li&gt;
&lt;li&gt;the second one is queued for retrying, and it has 3 messages&lt;/li&gt;
&lt;/ul&gt;
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/2025/mt-asb-recov/retry-by-group.png&quot; /&gt;
&lt;h3 id=&quot;deleting-the-failed-messages&quot; tabindex=&quot;-1&quot;&gt;Deleting the failed messages &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/#deleting-the-failed-messages&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although all messages are valuable, since they contain business data, there might be cases when you would want to discard &lt;strong&gt;some of the messages.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If anyone deletes a message by mistake, the message is gone forever, without any backup. You would have not way of recovering that.&lt;/p&gt;
&lt;p&gt;The Particular platform keeps the deleted messages for 15 days, just in case you change your mind and you want to restore them, or in case some were deleted by mistake.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/2025/mt-asb-recov/deleted-messages.png&quot; /&gt;
&lt;p&gt;We like it or not, in distributed system errors are inevitable and we shouldn&#39;t be scared of those. What we should do instead is to try and adopt &lt;strong&gt;the strategy and tools&lt;/strong&gt; to allow us to move fast enough and recover from them.&lt;/p&gt;
&lt;p&gt;I&#39;ve seen teams struggling to recover messages because they didn&#39;t have the right permissions or weren&#39;t used to the Azure Portal.&lt;/p&gt;
&lt;p&gt;Sometimes not using the right tools can lead to delays, inconsistencies, and additional complexity in handling error messages effectively.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;Summary &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/masstransit-with-azure-service-bus-error-management/#summary&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Recoverability&lt;/strong&gt; is a quality attribute that is not easy to obtain and often overlooked, but the MassTransit erorr manangement platform helps with this.&lt;/p&gt;
&lt;p&gt;Instead of manually handling each message one by one, this solution provides &lt;strong&gt;intelligent grouping, batch processing&lt;/strong&gt; significantly reducing operational overhead. With built-in &lt;strong&gt;visibility, filtering, and retry controls&lt;/strong&gt;, you can quickly identify failed messages, apply corrective actions, and ensure seamless recovery— all without disrupting your existing system.  Plus, is user-friendly!&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Getting started: Messaging in .NET with MassTransit</title>
			<link href="https://irina.codes/getting-started-messaging-in-net-with-masstransit/"/>
			<updated>2024-07-10T00:00:00Z</updated>
			<id>https://irina.codes/getting-started-messaging-in-net-with-masstransit/</id>
			<content type="html">&lt;!-- &lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/fzth-mess-mass_x5chav.png&quot;
  srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/fzth-mess-mass_x5chav.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/fzth-mess-mass_x5chav.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/fzth-mess-mass_x5chav.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/fzth-mess-mass_x5chav.png 1856w&quot;
  sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot;
  alt=&quot;From zero to hero messaging with MassTransit - Irina Scurtu&quot;
  importance=&quot;high&quot;
  class=&quot;img-responsive&quot;
  style=&quot;display:none&quot;
  width=&quot;780&quot;&gt;  --&gt;
&lt;p&gt;&lt;a href=&quot;https://dometrain.com/course/getting-started-messaging-in-net-with-masstransit/?utm_medium=bloghome&amp;utm_source=irina.codes&amp;utm_campaign=blog&amp;ref=irina-scurtu&quot; target=&quot;_blank&quot;&gt;&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/gtst-masstransit.jpg&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I&#39;m thrilled to announce that my course &lt;strong&gt;&amp;quot;From zero to hero messaging in .NET with MassTransit&amp;quot;&lt;/strong&gt; was released on &lt;a href=&quot;https://dometrain.com/course/from-zero-to-hero-messaging-in-net-with-masstransit/?utm_medium=bloghome&amp;amp;utm_source=irina.codes&amp;amp;utm_campaign=blog&quot;&gt;Dometrain&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;i-this-course-for-you&quot; tabindex=&quot;-1&quot;&gt;I this course for you? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/getting-started-messaging-in-net-with-masstransit/#i-this-course-for-you&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In this course, we’ll explore the ins and outs of asynchronous communication patterns, with a focus on &lt;strong&gt;MassTransit&lt;/strong&gt; —a powerful message broker for .NET applications.
Have you ever dreamt of building applications that can handle complex workloads with ease?&lt;/p&gt;
&lt;p&gt;Well, buckle up, because asynchronous messaging with MassTransit is about to become your new best friend!&lt;/p&gt;
&lt;p&gt;This course is designed for three types of heroes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;C# Software Developers&lt;/strong&gt;: If you&#39;re already slinging code in C#, you&#39;ve got a great foundation for diving into asynchronous messaging.&lt;/li&gt;
&lt;li&gt;C# &lt;strong&gt;Architects&lt;/strong&gt;: Looking to elevate your design skills and craft scalable, performant applications? This course will equip you with the knowledge to do just that.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MassTransit and asyncronous messaging Newbies&lt;/strong&gt;: Maybe you&#39;ve heard the buzz around MassTransit, but aren&#39;t sure where to start.
Fear not, curious minds! This course will guide you every step of the way.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As a hands-on software architect, I witnessed first-hand how people in different teams struggle to shift their mind-set to adopt an asyncronous messaging approach.&lt;/p&gt;
&lt;p&gt;At first, is a bit unusual to think in terms of messages, react to those messages in different components, and not have status codes to guide you.&lt;/p&gt;
&lt;p&gt;I know it is hard too, but sometimes we have to unlearn what we know from building traditional  APIs in order to design better ones.  Reality tells us that  the APIs that respond to ever increasing throughput needs, are resilient and more robust.&lt;/p&gt;
&lt;p&gt;In the course, we will start by learning the basic concepts about asynchronous messaging, how it is different than REST and then progress towards concepts like Pub/sub, request/reply, different architecture types, pipes &amp;amp; filters, Pull&amp;amp; Push consumption models and many more.&lt;/p&gt;
&lt;p&gt;And if you ask me, designing and building a good system sometimes  reduces to applying well tried recipes, others were using before you, like Outbox Pattern and Saga Pattern.&lt;/p&gt;
&lt;p&gt;You can see &lt;a href=&quot;https://dometrain.com/course/getting-started-messaging-in-net-with-masstransit/?utm_medium=bloghome&amp;utm_source=irina.codes&amp;utm_campaign=blog&amp;ref=irina-scurtu&quot;&gt; here&lt;/a&gt; the list  of topics that are covered in the course. And of course, you will also get the source code as a resource for the course.&lt;/p&gt;
&lt;h2 id=&quot;outcome&quot; tabindex=&quot;-1&quot;&gt;Outcome &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/getting-started-messaging-in-net-with-masstransit/#outcome&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By the end of this course you will be able to apply asynchronous messaging concepts in enterprise applications. You will be able to extrapolate the examples and use them in your scenarios.&lt;/p&gt;
&lt;p&gt;So, if you&#39;re ready to unlock the power of asynchronous messaging and build next-level applications with MassTransit, then &lt;a href=&quot;https://dometrain.com/course/getting-started-messaging-in-net-with-masstransit/?utm_medium=bloghome&amp;utm_source=irina.codes&amp;utm_campaign=blog&amp;ref=irina-scurtu&quot;&gt; check it out&lt;/a&gt;   and let&#39;s get started!&lt;/p&gt;
&lt;p&gt;PS: Don’t forget to &lt;a href=&quot;https://www.linkedin.com/in/irinascurtu/&quot; target=&quot;_blank&quot;&gt;connect&lt;/a&gt; , and let me know what you think about the course.&lt;/p&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;You can click  &lt;a target=&quot;_blank&quot; href=&quot;https://orders.dometrain.com/b/bIY3e79KsetE9I4aJs&quot;&gt;&lt;strong&gt;here to checkout the course directly&lt;/strong&gt; &lt;/a&gt;&lt;/p&gt;
&lt;hr /&gt;
</content>
		</entry>
		
		<entry>
			<title>Adapter design pattern</title>
			<link href="https://irina.codes/adapter-design-pattern/"/>
			<updated>2024-02-24T00:00:00Z</updated>
			<id>https://irina.codes/adapter-design-pattern/</id>
			<content type="html">&lt;p&gt;&lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/Adapter_gcfbfv.png&quot; srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/Adapter_gcfbfv.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/Adapter_gcfbfv.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/Adapter_gcfbfv.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/Adapter_gcfbfv.png 1856w&quot; sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot; alt=&quot;adapter design pattern in C# - Irina Scurtu&quot; importance=&quot;high&quot; class=&quot;img-responsive&quot; style=&quot;display:none&quot; width=&quot;780&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;adapter-design-pattern&quot; tabindex=&quot;-1&quot;&gt;Adapter design pattern &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#adapter-design-pattern&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Name: Adapter&lt;/p&gt;
&lt;p&gt;Type: Structural&lt;/p&gt;
&lt;h3 id=&quot;intent&quot; tabindex=&quot;-1&quot;&gt;Intent: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#intent&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;“Convert the interface of a class into another interface the client expects. Adapter lets classes work together that couldn’t otherwise because of incompatible interfaces.”&lt;/p&gt;
&lt;p&gt;In other words, this pattern makes incompatible interfaces compatible.&lt;/p&gt;
&lt;h3 id=&quot;when-to-use&quot; tabindex=&quot;-1&quot;&gt;When to use: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#when-to-use&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sometimes, objects just don’t fit together as they should.  Either the object is too complex to work with or their interfaces are incompatible.
The adapter design pattern lets you adapt what an object or class has to offer, by converting the interface of a class into another one.
There are some common use cases like: integrating external libraries with different interfaces, working with legacy systems or adapting data formats&lt;/p&gt;
&lt;h3 id=&quot;real-life-analogy&quot; tabindex=&quot;-1&quot;&gt;Real-life analogy &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#real-life-analogy&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;One of the real-life encounters with an Adapter is with headphone jacks. These jacks aren’t universal, and we won’t find the same jacks in all phone models.  In these cases we might need adapters to convert a 3.5mm jack to a USB-C or Lightning connector, allowing headphones to work with different devices.&lt;/p&gt;
&lt;p&gt;In all of these cases, the adapter is a middleman that translates the signals or data from one system to another, making it possible to connect incompatible objects or interfaces.&lt;/p&gt;
&lt;p&gt;Imagine two people who speak different languages trying to have a conversation. The Adapter acts like a helpful translator, listening to one person (object 1) in their language and then seamlessly converting their message into terms understandable by the other person (object 2). This allows them to communicate effectively despite their initial incompatibility.&lt;/p&gt;
&lt;h3 id=&quot;structure&quot; tabindex=&quot;-1&quot;&gt;Structure &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#structure&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For the Adapter design pattern, we have three main entities involved.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The interfaces are the abstractions that need to be made compatible.&lt;/li&gt;
&lt;li&gt;The  Adaptee -  is the original class that needs to be adapted. In our case, the &lt;code&gt;iPhone&lt;/code&gt; needs to be charged with a different charger&lt;/li&gt;
&lt;li&gt;The Adapter - &lt;code&gt;LightningToTypeCAdapter&lt;/code&gt; is the class that knows how to talk with both the client and the Adaptee(i.e knows how to adapt a Lightning port to type C one)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see in the diagram below the interaction between classes.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/adapter-structure.png&quot; /&gt;
&lt;h3 id=&quot;implementation&quot; tabindex=&quot;-1&quot;&gt;Implementation &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#implementation&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ITypeCPort&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Recharge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UseTypeC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ILightningPort&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Recharge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UseLightning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we have an &lt;code&gt;IPhone&lt;/code&gt; that implements the &lt;code&gt;ILightningPort&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOSPhone&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ILightningPort&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; isConnected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UseLightning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            isConnected &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Lightning connection established&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Recharge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isConnected&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Recharging iPhone...&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Please connect Lightning first&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we need to have an adapter.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LightningToTypeCAdapter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ITypeCPort&lt;/span&gt;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ILightningPort&lt;/span&gt; lightningPhone&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;LightningToTypeCAdapter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ILightningPort&lt;/span&gt; lightningPhone&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lightningPhone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; lightningPhone&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UseTypeC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Adapter converts Type-C to Lightning&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
         lightningPhone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseLightning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Recharge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         lightningPhone&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Recharge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the client&#39;s perspective, now we can have an iPhone, use an adapter and recharge our phone.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;
&lt;span class=&quot;token class-name&quot;&gt;IOSPhone&lt;/span&gt; myIPhone &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;IOSPhone&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;LightningToTypeCAdapter&lt;/span&gt; typeCAdapter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;LightningToTypeCAdapter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myIPhone&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Attempting to recharge iPhone with Type-C via adapter:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
typeCAdapter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseTypeC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
typeCAdapter&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Recharge&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Structurally speaking, the Adapter design pattern is very similar to the &lt;a href=&quot;https://irina.codes/proxy-design-pattern/&quot; target=&quot;_blank&quot;&gt;Proxy design pattern&lt;/a&gt;. But unlike Proxy, The Adapter pattern is visible to the client, and the client knows that it interacts with an adapter.&lt;/p&gt;
&lt;h3 id=&quot;pros-and-cons&quot; tabindex=&quot;-1&quot;&gt;Pros &amp;amp; Cons &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#pros-and-cons&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;h3 id=&quot;pro&quot; tabindex=&quot;-1&quot;&gt;Pro &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#pro&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;enables integration of systems with incompatible interfaces with the rest of the codebase&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Code reuse&lt;/strong&gt; - can reuse existing functionality that is not compatible with the desired interfaces by using adapters&lt;/li&gt;
&lt;li&gt;enhances &lt;strong&gt;flexibility&lt;/strong&gt; and &lt;strong&gt;maintainability&lt;/strong&gt; by decoupling the client code from specific implementations of the Adaptees&lt;/li&gt;
&lt;li&gt;promotes ** loose coupling **&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extensibility&lt;/strong&gt; : adapters can be easily extended to accommodate new interfaces without modifying the adaptee code&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reusability&lt;/strong&gt;: Adapters can be reused with different Adaptees&lt;/li&gt;
&lt;li&gt;Open/ Closed principle - we can introduce new adapter types without modifying existing code&lt;/li&gt;
&lt;li&gt;the Adapter pattern hides the implementation details of the Adaptee, shielding client code from the complexities of its internal workings. This promotes abstraction and makes the code more &lt;strong&gt;maintainable&lt;/strong&gt; and &lt;strong&gt;testable&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;cons&quot; tabindex=&quot;-1&quot;&gt;cons: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#cons&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;can introduce complexity in the system due to adapter classes, and the code can be harder to understand&lt;/li&gt;
&lt;li&gt;Requires thoughtful design considerations to ensure that adapters are appropriately implemented and don&#39;t introduce unintended consequences.&lt;/li&gt;
&lt;li&gt;Requires more testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;Summary &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/adapter-design-pattern/#summary&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
</content>
		</entry>
		
		<entry>
			<title>Proxy design pattern</title>
			<link href="https://irina.codes/proxy-design-pattern/"/>
			<updated>2024-02-13T00:00:00Z</updated>
			<id>https://irina.codes/proxy-design-pattern/</id>
			<content type="html">&lt;!-- &lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/hproxy-pattern_jof6vv.png&quot;
  srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/proxy-pattern_jof6vv.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/proxy-pattern_jof6vv.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/proxy-pattern_jof6vv.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/proxy-pattern_jof6vv.png 1856w&quot;
  sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot;
  alt=&quot;Proxy design pattern - Irina Scurtu&quot;
  importance=&quot;high&quot;
  class=&quot;img-responsive&quot;
  width=&quot;780&quot;&gt; --&gt;
&lt;h3 id=&quot;proxy-design-pattern&quot; tabindex=&quot;-1&quot;&gt;Proxy design pattern &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#proxy-design-pattern&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Name: Proxy
Type: Structural&lt;/p&gt;
&lt;h3 id=&quot;intent&quot; tabindex=&quot;-1&quot;&gt;Intent: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#intent&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Proxy design pattern is a structural pattern that provides an in-between, or placeholder for another object.
They are closely related in structure, to Adapters and &lt;a target=&quot;_blank&quot; href=&quot;https://irina.codes/decorator-design-pattern/&quot;&gt; Decorators&lt;/a&gt; but they have different purposes.&lt;/p&gt;
&lt;h3 id=&quot;when-to-use&quot; tabindex=&quot;-1&quot;&gt;When to use &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#when-to-use&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The Proxy design pattern can be used in a variety of scenarios, but its goal is generally to preserve exactly (or as closely as possible) the API that is being used while offering certain internal enhancements.&lt;/p&gt;
&lt;p&gt;It&#39;s useful when we want to add a layer of control over the interaction with an object. In the context of C#, implementing the Proxy pattern involves creating a proxy class that wraps an object and controls access to it.&lt;/p&gt;
&lt;h3 id=&quot;real-life-analogy&quot; tabindex=&quot;-1&quot;&gt;Real-life analogy: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#real-life-analogy&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Imagine you are at a restaurant that has a limited number of tables. The restaurant has a hostess who stands at the door and checks if there is a table available before seating a customer.
The hostess acts as a proxy for the tables. She controls access to the tables and chairs and ensures that the tables are distributed as best as possible.&lt;/p&gt;
&lt;p&gt;Another example we often encounter in real life is when we order coffee.
When we want to order a coffee, we don&#39;t go directly to the barista behind the counter or make it ourselves. Instead, we place the order with a person, who then relays the order to the barista. The person taking our order acts as a proxy for us, handling the communication and coordination between us and the barista.&lt;/p&gt;
&lt;h3 id=&quot;structure&quot; tabindex=&quot;-1&quot;&gt;Structure: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#structure&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For the proxy design pattern, we have three main entities involved.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;One is the abstraction which is usually an interface that defines the operations&lt;/li&gt;
&lt;li&gt;The real Subject - a class that implements the interface and usually has more operations than is specified there&lt;/li&gt;
&lt;li&gt;The Proxy acts as an intermediary between the Client and the Real Subject. This too implements the interface and has-a Real Subject instance. It is the common ground between the Client(consumer) and the Subject, providing an indirection layer.&lt;/li&gt;
&lt;li&gt;The client is the one interacting with the Proxy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can see in the diagram below the interaction between classes.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/Proxy-structure.png&quot; /&gt;
&lt;h3 id=&quot;implementation&quot; tabindex=&quot;-1&quot;&gt;Implementation &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#implementation&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For our example implementation we have the following components:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;IOrderService&lt;/code&gt; interface represents the service interface for placing food orders.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;RestaurantKitchen&lt;/code&gt; class is the real subject that prepares the food. This is the actual code that we want to interact with.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Waiter&lt;/code&gt; class acts as a proxy for the &lt;code&gt;RestaurantKitchen&lt;/code&gt;. When a client wants to place an order, the waiter can perform various tasks (like confirming the order, checking special requests, etc.) before delegating the actual food preparation to the &lt;code&gt;RestaurantKitchen&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The client interacts with the &lt;code&gt;IOrderService&lt;/code&gt; interface and uses the &lt;code&gt;Waiter&lt;/code&gt; to place orders. The client is unaware of the direct interaction with the &lt;code&gt;RestaurantKitchen&lt;/code&gt; which illustrates the encapsulation provided by the Proxy pattern.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This design allows for additional logic and features to be implemented in the &lt;code&gt;Waiter&lt;/code&gt; without modifying the underlying &lt;code&gt;RestaurantKitchen&lt;/code&gt; or the client&#39;s code.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Service Interface&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IOrderService&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; dish&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Real Subject&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RestaurantKitchen&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IOrderService&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; dish&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;$&quot;Cooking &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token expression language-csharp&quot;&gt;dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; in the kitchen.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Waiter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IOrderService&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RestaurantKitchen&lt;/span&gt; _kitchen&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Waiter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RestaurantKitchen&lt;/span&gt; kitchen&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          _kitchen &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; kitchen&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; dish&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;$&quot;Waiter is taking order for &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token expression language-csharp&quot;&gt;dish&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;.&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token comment&quot;&gt;// Additional checks or preparations can be done here.&lt;/span&gt;
          _kitchen&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PlaceOrder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;dish&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;pros-and-cons&quot; tabindex=&quot;-1&quot;&gt;Pros &amp;amp; Cons &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#pros-and-cons&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;h3 id=&quot;pro&quot; tabindex=&quot;-1&quot;&gt;Pro &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#pro&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Control Access&lt;/strong&gt;: The proxy can control access to the real subject(&lt;code&gt;RestaurantKitchen&lt;/code&gt;), which is useful for lazy initialization, permissions, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Additional Functionality&lt;/strong&gt;: You can add additional functionality (e.g., caching, logging, monitoring) before/after the request to the real subject.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Open/Closed Principle&lt;/strong&gt;: You can introduce new proxies without changing the subject or client&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h4 id=&quot;cons&quot; tabindex=&quot;-1&quot;&gt;CONS: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#cons&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Code Complexity&lt;/strong&gt;: Code may become complicated, if both the real subject and proxy implement the same interface.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Response Time&lt;/strong&gt;: The proxy can add a delay to the response time due to additional processing.&lt;/li&gt;
&lt;li&gt;adding a layer of indirection&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The Proxy Design Pattern stands out as it does not conform to a single, uniform structure.
There is a wide variety of proxy types that serve distinct functions. The choice of which one to implement depends on the specific requirements of the software design problem at hand. Below there are just a few of them:&lt;/p&gt;
&lt;h2 id=&quot;proxy-types&quot; tabindex=&quot;-1&quot;&gt;Proxy types: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#proxy-types&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Virtual Proxy&lt;/strong&gt;: This type of proxy creates expensive objects on demand. The proxy contains a placeholder for an object that is extremely resource-intensive to create. The actual object is only created when its functionality is absolutely needed. This is useful in scenarios like lazy initialization of a large image or a file.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Remote Proxy&lt;/strong&gt;: This proxy provides a local representative for an object that resides in a different address space (like another computer or server). It&#39;s used in distributed systems to hide the details of remote interaction. The proxy takes care of the details of working with remote methods, such as handling network communication and marshaling.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Protective Proxy&lt;/strong&gt;: This proxy controls access to a sensitive master object. It&#39;s used to manage the different access rights of clients and to protect the target object from unauthorized access. For example, it might check if a client has the necessary permissions to perform an action.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smart Proxy&lt;/strong&gt;: A smart proxy adds extra layers of functionality when an object is accessed. This can include additional steps when an object is accessed, such as reference counting, logging, or locking to manage concurrent access to an object. It can also perform tasks that are needed for housekeeping purposes, like freeing resources or initializing objects upon access.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Caching Proxy&lt;/strong&gt;: This proxy can store recent or frequent requests and provide cached responses to reduce the operation&#39;s time or complexity. It&#39;s commonly used in systems where fetching or calculating the original data is very resource-intensive, like in web content delivery networks.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each of these proxy types serves a different purpose, and the choice of which one to implement depends on the specific requirements of the software design problem at hand.&lt;/p&gt;
&lt;h3 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;Summary &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/proxy-design-pattern/#summary&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In essence, the Proxy design pattern is a powerful tool that can take many forms. It doesn’t try to expand the public API surface of an object by adding new members but it can control access and add supplementary behavior to objects without changing the object&#39;s code or the client&#39;s code.&lt;/p&gt;
&lt;p&gt;P.S. Want the source code or level up your coding? Consider joining my monthly newsletter. Sign up here!&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Decorator design pattern</title>
			<link href="https://irina.codes/decorator-design-pattern/"/>
			<updated>2024-02-05T00:00:00Z</updated>
			<id>https://irina.codes/decorator-design-pattern/</id>
			<content type="html">&lt;p&gt;&lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/decorator-pattern_wr6qua.png&quot; srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/decorator-pattern_wr6qua.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/decorator-pattern_wr6qua.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/decorator-pattern_wr6qua.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/decorator-pattern_wr6qua.png 1856w&quot; sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot; alt=&quot;Decorator design pattern in C# - Irina Scurtu&quot; importance=&quot;high&quot; class=&quot;img-responsive&quot; style=&quot;display:none&quot; width=&quot;780&quot; /&gt;&lt;/p&gt;
&lt;h3 id=&quot;decorator-design-pattern&quot; tabindex=&quot;-1&quot;&gt;Decorator design pattern &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#decorator-design-pattern&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Name: Decorator&lt;/p&gt;
&lt;p&gt;Alternative name: Wrapper&lt;/p&gt;
&lt;p&gt;Type: Structural&lt;/p&gt;
&lt;h3 id=&quot;intent&quot; tabindex=&quot;-1&quot;&gt;Intent: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#intent&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.&lt;/p&gt;
&lt;h3 id=&quot;when-to-use&quot; tabindex=&quot;-1&quot;&gt;When to use: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#when-to-use&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The decorator pattern is particularly useful when we want to provide a myriad of customization options for a basic/base object because it allows us to add new functionalities to an object without altering its internal structure.&lt;/p&gt;
&lt;h3 id=&quot;real-life-analogy&quot; tabindex=&quot;-1&quot;&gt;Real-life analogy: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#real-life-analogy&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For example, when ordering a coffee, we might like that coffee with sugar, milk, or soy milk and we might add some cinnamon or whipped cream. Or, we might want some nice sprinkles on top. The sky is the limit here.&lt;/p&gt;
&lt;p&gt;Another example is cake 🎂. We all love cakes,(I definitely do) and the variations are endless. Any cake can be customized in numerous ways to create a unique and memorable treat.&lt;/p&gt;
&lt;p&gt;IT can start from a base cake and take different forms and shapes depending on who orders or eats it.
Variations encompass diverse cake bases, tantalizing flavors, fillings, enticing fruit layers, rich icings, and eye-catching decorations🧁.&lt;/p&gt;
&lt;p&gt;Imagine how such a scenario would look in terms of code.&lt;/p&gt;
&lt;p&gt;Usually, when it comes to altering an object&#39;s structure or behavior, inheritance might be the first thing that comes to mind, but it might not be the best thing. In this scenario, subclassing wouldn’t be a great idea at all. Moreover, in time, we would have so many classes and spaghetti code that it would become impossible to manage.&lt;/p&gt;
&lt;p&gt;By using inheritance, it would mean changing an object with another one, and it would only allow us to have one single base class. Inheritance is static and we need a way to alter the object’s structure, &lt;strong&gt;dynamically&lt;/strong&gt;, at runtime.&lt;/p&gt;
&lt;p&gt;One way to overcome this static issue is to use &lt;strong&gt;Aggregation&lt;/strong&gt; or &lt;strong&gt;Composition&lt;/strong&gt;. (remember- there is also a design principle that tells us to favor composition over inheritance.!!).&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;it is better to compose objects to achieve polymorphic behavior and code reuse rather than inheriting from a base class.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;These two concepts sit behind many design patterns, and this happens here too.
So, let’s see how to model such a thing in C#.&lt;/p&gt;
&lt;h3 id=&quot;structure&quot; tabindex=&quot;-1&quot;&gt;Structure: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#structure&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/decorator-diagram1.png&quot; /&gt;
&lt;h3 id=&quot;implementation&quot; tabindex=&quot;-1&quot;&gt;Implementation &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#implementation&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;First, let’s have a look at the components needed:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ICake&lt;/code&gt; - the abstraction common&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BaseCake&lt;/code&gt; class - that will implement the interface and act as  our base object to which we will add enhancements to&lt;/li&gt;
&lt;li&gt;&lt;code&gt;BaseCakeDecorator&lt;/code&gt; - an abstract class that contains the object that we wrap as a field. The field’s type should be declared as the component interface so it can contain both concrete components and decorators. The base decorator delegates all operations to the wrapped object.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChocolateFilling&lt;/code&gt; - is a concrete decorator (i.e. a concrete implementation of the &lt;code&gt;BaseCakeDecorator&lt;/code&gt;) that we will use to wrap our &lt;code&gt;BaseCake&lt;/code&gt;, to customize it as we want&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Spinkles&lt;/code&gt; - it can be added as decoration too&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetCost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BasicCake&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Basic Cake&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetCost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10.0m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/// Decorator abstract class&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BaseCakeDecorator&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt; decoratedCake&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;BaseCakeDecorator&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt; cake&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        decoratedCake &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; cake&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; decoratedCake&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;virtual&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetCost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; decoratedCake&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetCost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;
&lt;span class=&quot;token comment&quot;&gt;// Concrete decorator: ChocolateFilling&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ChocolateFilling&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseCakeDecorator&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ChocolateFilling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt; cake&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cake&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token expression language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; with Chocolate Filling&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetCost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetCost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5.0m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Concrete decorator: ChocolateFilling&lt;/span&gt;

 &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sprinkles&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;BaseCakeDecorator&lt;/span&gt;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Sprinkles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt; cake&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;cake&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;$&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token expression language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetDescription&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt; with Sprinkles&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

     &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetCost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetCost&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2.0m&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we can create complex variations by using the concrete decorators that we have, starting with the base. Then, we can take the base and apply different decorators. The example below is exaggerated, but I hope it gives you an idea about how it can be used&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;
&lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt; basic &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;BasicCake&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt; basicWithChoolateFilling &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;ChocolateFilling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basicCake&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt; basicWithChoolateFillingAndFruitFilling &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;FruitFilling&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basicWithChoolateFilling&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;ICake&lt;/span&gt; basicWithChoolateFillingAndFruitFillingAndSprinkles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Sprinkles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;basicWithChoolateFillingAndFruitFilling&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;pros-and-cons&quot; tabindex=&quot;-1&quot;&gt;Pros &amp;amp; Cons &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#pros-and-cons&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;h3 id=&quot;pro&quot; tabindex=&quot;-1&quot;&gt;Pro &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#pro&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;We can give objects new responsibilities without making any code changes to the underlying classes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Wearing clothes is an example of using decorators. If it is cold, we might layer ourselves with a few more clothes than in the summer. This is an example of using decorators at runtime. We are not born with clothes, and we don’t become different people just because we wear different clothes(if we were to use inheritance), we use clothes when we need them, as many as we need them.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Helps us &#39;achieve&#39; Open/Closed principle
The decorator has the same supertype as the object it decorates, we can pass around a decorated object in place of the original&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;encourages us to write code that adheres to the SOLID design principles.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;gives us more flexibility than static inheritance&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;because decorators only rely on the interface of decorated objects (injected through the constructor of the decorator class), decorators can be unit tested independently.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;cons&quot; tabindex=&quot;-1&quot;&gt;CONS: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#cons&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;All methods specified in the interface must be implemented in the concrete decorators, and if we don’t add additional functionality, we must use those as pass-through
-** Lots of little objects**. A design that uses the Decorator design pattern often results in systems composed of lots of little objects that all look alike. The objects differ only in the way they are interconnected, not in their class or the value of their variables. Although these systems are easy to customize by those who understand them, they can be hard to learn and debug&lt;/li&gt;
&lt;li&gt;Decorators can be tested in isolation of decorated objects but subclasses cannot be tested in isolation of their parent.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;Summary &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/decorator-design-pattern/#summary&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;strong&gt;Decorator design pattern&lt;/strong&gt; acts like a wrapper, giving us the ability to enhance base objects. This enhancement is dynamic, happening at run-time, without altering the base object’s structure.  I can say that the decorator behaves very much like an extension method, that allows us to add extra functionality where we can’t or won’t change the base class.
In essence, the decorator design pattern provides us a flexible mechanism to extend the functionality of existing objects, without changing their internal structure. It is like changing the case of your phone, not the internal structure of it&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Hosting gRPC in Azure</title>
			<link href="https://irina.codes/hosting-grpc-in-azure/"/>
			<updated>2024-01-18T00:00:00Z</updated>
			<id>https://irina.codes/hosting-grpc-in-azure/</id>
			<content type="html">&lt;p&gt;&lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/hosting-grpc-in-azure_zuzlj4.png&quot; srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/hosting-grpc-in-azure_zuzlj4.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/hosting-grpc-in-azure_zuzlj4.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/hosting-grpc-in-azure_zuzlj4.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/hosting-grpc-in-azure_zuzlj4.png 1856w&quot; sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot; alt=&quot;Laws of software development&quot; importance=&quot;high&quot; class=&quot;img-responsive&quot; width=&quot;780&quot; height=&quot;500&quot; style=&quot;display: none;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;How to host a gRPC service in Azure? gRPC on Linux?&lt;/p&gt;
&lt;p&gt;When it comes to hosting a gRPC service to Azure, things are pretty straightforward, but there are a few tips and tricks that we will cover.&lt;/p&gt;
&lt;p&gt;If you have, like me, a not-so-complicated system, or you simply want to play around with the gRPC service deployed in the cloud, then this is for you.&lt;/p&gt;
&lt;p&gt;Make sure you have an app Service plan ready and a Web App with Linux. Currently, only Linux is supported for gRPC.&lt;/p&gt;
&lt;p&gt;So, first, a gRPC service is nothing more than a regular API with a few particularities, that need to be set in the Azure Web App prior to deployment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;uses HTTP/2&lt;/li&gt;
&lt;li&gt;uses a binary serialization&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;How you get your code source in the Cloud it&#39;s up to you. You can publish directly from the IDE, or have a pipeline that runs when you push to GitHub. As long as you will be able to move your source code to the cloud, then you are all set.&lt;/p&gt;
&lt;p&gt;Since we are dealing with a gRPC service, we need to ensure that we have &lt;em&gt;HTTP version 2&lt;/em&gt;, and that the &lt;em&gt;HTTP 2.0 Proxy is enabled&lt;/em&gt;, as shown in the picture below.&lt;/p&gt;
&lt;p&gt;&lt;img loading=&quot;lazy&quot; src=&quot;https://irina.codes/img/posts/deploy-grpc-to-azure/image-2.png&quot; class=&quot;img-responsive card&quot; width=&quot;780&quot; height=&quot;500&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The next thing we will need to do is to add an application setting named &lt;code&gt;HTTP20_ONLY_PORT&lt;/code&gt; with the value of &lt;code&gt;8585&lt;/code&gt;.&lt;/p&gt;
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/deploy-grpc-to-azure/image.png&quot; /&gt;
&lt;p&gt;With these things done, we should be able to test our service, from our local machine, by instantiating a client.
But if we want to use Postman, for example, is advisable to install &lt;code&gt;Grpc.AspNetCore.Server.Reflection&lt;/code&gt; package and enable the middlewares.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;
builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddGrpcReflection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MapGrpcReflectionService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Make sure the application is up and running, by accessing the URL provided in Azure: e.g: &lt;code&gt;https://yourgrpcwebiste.azurewebsites.net/&lt;/code&gt;, and now you can use Postman for example to make requests.&lt;/p&gt;
&lt;p&gt;In Postman, select the &#39;use server reflection` option to make sure the operations are discoverable. After that, we can make requests, just as we would do with a regular Web API.
&lt;img loading=&quot;lazy&quot; class=&quot;col-md-12&quot; src=&quot;https://irina.codes/img/posts/deploy-grpc-to-azure/image-1.png&quot; /&gt;&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>The laws of software development</title>
			<link href="https://irina.codes/the-laws-of-software-development/"/>
			<updated>2023-12-14T00:00:00Z</updated>
			<id>https://irina.codes/the-laws-of-software-development/</id>
			<content type="html">&lt;p&gt;&lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/laws-of-software_lhms4g.png&quot; srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/laws-of-software_lhms4g.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/laws-of-software_lhms4g.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/laws-of-software_lhms4g.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/laws-of-software_lhms4g.png 1856w&quot; sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot; alt=&quot;Laws of software development&quot; importance=&quot;high&quot; class=&quot;img-responsive&quot; width=&quot;780&quot; height=&quot;500&quot; style=&quot;display: none;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Unlike legal statutes, software development &#39;laws&#39; are not binding regulations or practices. These are rather distilled insights into the intricacies of software creation. These principles serve as valuable guides, steering developers away from common pitfalls and promoting effective practices.&lt;/p&gt;
&lt;p&gt;We can look at these ‘laws’ as popular knowledge, that captures common observations and best practices. Many software development laws apply in different areas. Some apply to project management, some apply to software architecture, some to ways of working or organizations.&lt;/p&gt;
&lt;p&gt;Below we will cover the most common ones.&lt;/p&gt;
&lt;h4 id=&quot;table-of-contents&quot; tabindex=&quot;-1&quot;&gt;Table of Contents &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#table-of-contents&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#pareto-principle-80-20-rule&quot;&gt;Pareto Principle (80-20 Rule)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#conway-law&quot;&gt;Conway Law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#postels-law-of-communication-and-robustness&quot;&gt;Postel&#39;s Law of Communication and Robustness&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#murphys-law-of-software-development&quot;&gt;Murphy&#39;s Law of Software Development&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#law-of-demeter-principle-of-least-knowledge&quot;&gt;Law of Demeter (Principle of Least Knowledge)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#amdahls-law&quot;&gt;Amdahl&#39;s Law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#galls-law&quot;&gt;Gall’s Law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#brookss-law&quot;&gt;Brooks&#39;s Law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#linuss-law&quot;&gt;Linus&#39;s Law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#knuths-optimization-principle-or-the-premature-optimization-principle&quot;&gt;Knuth’s Optimization Principle or the Premature Optimization principle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#parkinsons-law&quot;&gt;Parkinson’s Law&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#conclusion&quot;&gt;Conclusion&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;pareto-principle-80-20-rule&quot; tabindex=&quot;-1&quot;&gt;Pareto Principle (80-20 Rule) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#pareto-principle-80-20-rule&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement:&lt;/strong&gt; 80% of the effects come from 20% of the causes&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: This principle is named after Vilfredo Pareto, an Italian economist and sociologist who observed that 80% of the land in Italy was owned by 20% of the population.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;: While not specific to software development, the Pareto Principle is often applied in various contexts, including software. The stated idea is that a significant portion of results or outcomes often come from a relatively small portion of inputs or factors. In our field, it might suggest that focusing on fixing the most critical 20% of software bugs can eliminate 80% of the issues.&lt;/p&gt;
&lt;h2 id=&quot;conway-law&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#conway-law&quot; id=&quot;conway-law&quot;&gt;Conway Law&lt;/a&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#conway-law&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;: Any organization that designs a system (defined broadly) will produce a design whose structure is a copy of the organization&#39;s communication structure.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;:
The source for Conway&#39;s law is an&lt;a target=&quot;_blank&quot; href=&quot;https://www.melconway.com/Home/Committees_Paper.html&quot;&gt; article &lt;/a&gt;written by  Melvin Conway in 1968. It was later dubbed “Conway’s Law” by Fred Brooks in his hugely influential book &lt;a target=&quot;_blank&quot; href=&quot;https://www.amazon.com/gp/product/0201835959/&quot;&gt;The Mythical Man-Month &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;: The idea of this law is that consciously or not, organizations that have small distributed teams tend to develop systems that are modular and use service-based architectures. On the other hand, organizations that have large and colocated teams tend to develop monolith-like architectures.
Conway&#39;s Law highlights the idea that communication and collaboration patterns within an organization have a direct impact on the architecture and design of the systems they create. It&#39;s a reminder that we should consider the organizational structure when we plan and design complex systems.&lt;/p&gt;
&lt;p&gt;Understanding Conway&#39;s Law can be valuable for organizations looking to improve their software development processes. By aligning team structures and communication patterns with the desired system architecture, organizations can work more efficiently and produce systems that better meet their needs.&lt;/p&gt;
&lt;h2 id=&quot;postels-law-of-communication-and-robustness&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#postels-law-of-communication-and-robustness&quot; id=&quot;postels-law-of-communication-and-robustness&quot;&gt;Postel&#39;s Law of Communication and Robustness&lt;/a&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#postels-law-of-communication-and-robustness&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;: &amp;quot;Be conservative in what you send, liberal in what you accept.”&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: It was coined by Jon Postel, a computer scientist who played a key role in the development of the early internet.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;:
It states that when a system sends data, it should send it in a well-defined manner and adhere strictly to the specifications and standards governing the communication protocol. On the receiving end, the system should be flexible in interpreting the received data. It should accept and interpret inputs even if they deviate from the standard too. This flexibility promotes interoperability and allows systems to work together effectively, even if they have minor differences. This law promotes preventing situations where strict adherence to standards might lead to communication failures, especially in a networked environment where different systems with different implementations need to interact.&lt;/p&gt;
&lt;h2 id=&quot;murphys-law-of-software-development&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#murphys-law-of-software-development&quot; id=&quot;murphys-law-of-software-development&quot;&gt;Murphy&#39;s Law of Software Development&lt;/a&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#murphys-law-of-software-development&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;: If anything can go wrong, it will, and often at the most inopportune moment.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source:&lt;/strong&gt; Everyone&#39;s heard of &amp;quot;Murphy&#39;s Law,&amp;quot; right? The whole &amp;quot;if anything can go wrong, it will&amp;quot; thing. Well, it&#39;s named after an aerospace engineer named Edward Murphy Jr., who worked at Edwards Air Force Base in the 1940s. He was testing a braking system for a rocket sled when a cable snapped, sending the sled haywire and injuring the pilot. Murphy said something like &amp;quot;If there&#39;s any way for things to go wrong, they will.&amp;quot; His buddy Capt. John Saintsbury shortened it to &amp;quot;Murphy&#39;s Law,&amp;quot; and it&#39;s been a part of our vernacular ever since. It&#39;s a reminder that things don&#39;t always go according to plan, especially when you&#39;re dealing with complex machinery or high-stakes situations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;: This law highlights the inherent unpredictability of software development. It reminds us that even the most carefully planned and executed projects can encounter unexpected problems, and we can&#39;t prevent all of them.&lt;/p&gt;
&lt;h2 id=&quot;law-of-demeter-principle-of-least-knowledge&quot; tabindex=&quot;-1&quot;&gt;Law of Demeter (Principle of Least Knowledge) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#law-of-demeter-principle-of-least-knowledge&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;: &amp;quot;Each unit should have only limited knowledge about other units: only units &#39;closely&#39; related to the current unit.&amp;quot;
or  &amp;quot;Only talk to your immediate friends.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: The Law of Demeter, also known as the Principle of Least Knowledge, originated from the work of Ian Holland in 1987 as presented in his paper titled &amp;quot;On the Criteria To Be Used in Decomposing Systems into Modules.&amp;quot; Karl Lieberherr later coined the term &amp;quot;Law of Demeter&amp;quot; in homage to the Demeter project, initiated by Lieberherr and colleagues in the 1980s, which aimed to apply the principle to achieve more modular and maintainable software designs. Ian Holland formulated the principles, while the Demeter project significantly contributed to the principle&#39;s popularization and application within the software engineering community.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;:&lt;/p&gt;
&lt;p&gt;We can split this law into 3 parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Only talk to your immediate neighbors:&lt;/strong&gt; An object should interact with its immediate neighbors and not with distant or unrelated objects. This means that an object should not have detailed knowledge about the inner workings or structures of other objects that are not directly related to its purpose. If we think about SRP, this makes total sense.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Don&#39;t talk to strangers:&lt;/strong&gt; When an object needs something done, it should delegate the task to its immediate friends (i.e., the objects it holds references to), rather than reaching out to objects it doesn&#39;t know about.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Limit the interaction and knowledge hierarchy:&lt;/strong&gt; An object should not expose its internal structure or the structure of objects it interacts with to the outside world. It should only reveal a minimal, well-defined interface. Focus on Encapsulation and prevent chattiness.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This law advocates that we should aim to reduce coupling between different parts of a system, making each component more independent and easier to maintain. In other words, it emphasizes the importance of minimizing dependencies and promoting modular, loosely coupled system designs in object-oriented programming.&lt;/p&gt;
&lt;h2 id=&quot;amdahls-law&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#amdahls-law&quot; id=&quot;amdahls-law&quot;&gt;Amdahl&#39;s Law&lt;/a&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#amdahls-law&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;: The law states that the maximum speedup achievable by a parallel program is limited by the fraction of the program that cannot be parallelized, known as the sequential fraction (S). The formula for Amdahl&#39;s Law is:&lt;/p&gt;
&lt;pre class=&quot;language-markdown&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-markdown&quot;&gt;Speedup = 1 / (1 - S + S/P)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;where P is the number of processors used.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: Amdahl&#39;s Law is a principle in computer architecture and parallel computing named after computer architect Gene Amdahl. Formulated in 1967, Amdahl&#39;s Law quantifies the potential speedup of a computation when parts of the computation are parallelized.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;:
Amdahl&#39;s Law can be applied to a wide variety of parallel computing applications like data processing, database systems and computing in general.
But, no matter where we try to apply it, mathematically speaking we will never be able to parallelize a program 100% and there will always be a limit to the speedup. This limit is given by the small things that are sequential: input/output, control flow, memory management, etc. When it comes to optimizing, we should look out for these things.
Simply put, Amdahl&#39;s Law says that only certain parts of a program can run faster on multiple computers. Focus on optimizing those parts to get the most speed from your parallel system.&lt;/p&gt;
&lt;h2 id=&quot;gall-s-law&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#galls-law&quot; id=&quot;galls-law&quot;&gt;Gall’s Law&lt;/a&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#gall-s-law&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement:&lt;/strong&gt; &amp;quot;A complex system that works is invariably found to have evolved from a simple system that worked.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: The law was developed by American author John Gall in his 1977 book &amp;quot;Systemantics: How Systems Really Work and Why They Fail&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;: Gall&#39;s Law highlights the importance of starting with a simple working system and gradually adding complexity as needed. This approach can help to reduce the risk of introducing bugs and making the system more difficult to understand and maintain. For example, we could start developing our systems as monoliths and slowly transition them towards complex microservices, instead of diving right into a complex microservice architecture. :)&lt;/p&gt;
&lt;h2 id=&quot;brookss-law&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#brooks-law&quot; id=&quot;brooks-law&quot;&gt;Brooks&#39;s Law&lt;/a&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#brookss-law&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;: Adding manpower to a late software project makes it later.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: Fred Brooks, an American computer scientist, coined Brooks&#39;s Law in his 1975 book The Mythical Man-Month: Essays on Software Engineering.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;: This law suggests that adding more people to a project that is already behind schedule is likely to further delay its completion. Communication overhead and the time required for new team members to become productive can contribute to this delay. And as the team grows, the organizational structure becomes more complex. This can lead to confusion, bureaucracy, and decision-making delays. In this case, it is harder to keep everyone on the same page, distribute work effectively and be productive because it is most likely that some work causes friction inside different teams, or between teams.&lt;/p&gt;
&lt;h2 id=&quot;linuss-law&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#linuss-law&quot; id=&quot;linuss-law&quot;&gt;Linus&#39;s Law&lt;/a&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#linuss-law&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;: &amp;quot;Given enough eyeballs, all bugs are shallow&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;:
Linus&#39;s Law was coined by Eric S. Raymond in his 1997 essay &lt;a target=&quot;_blank&quot; href=&quot;https://en.wikipedia.org/wiki/The_Cathedral_and_the_Bazaar&quot;&gt;&amp;quot;The Cathedral and the Bazaar: Musings on Linux and Open Source by an Accidental Revolutionary&amp;quot;.&lt;/a&gt; It is named after Linus Torvalds, the creator of the Linux kernel. Despite the name, the book talks about two different free software development models.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Cathedral model,&lt;/strong&gt; in which source code is available with each software release, but code developed between releases is restricted to an exclusive group of software developers&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bazaar model&lt;/strong&gt; - in which the code is developed over the Internet in view of the public. Here, he credits Linus Torvalds, leader of the Linux kernel project, as the inventor of this model. (hence the given law name)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;: Linus&#39;s Law is closely tied to the open-source software development model, where the source code of a program is made available to the public. By allowing a broad community of developers to scrutinize and contribute to the code, the theory is that the software can benefit from a collective effort to identify and resolve problems. In other words, the more people who have access to and review the source code, the more likely it is that any flaws will be identified and addressed, so the more eyes on a given source code, the fewer errors.&lt;/p&gt;
&lt;p&gt;I think we can also extrapolate this to something related to the usage of a particular library. The more people use it, the more likely it is to create a stable library. This is one of the reasons that as an architect, I always advise and recommend using well-established libraries and packages.&lt;/p&gt;
&lt;h2 id=&quot;knuth-s-optimization-principle-or-the-premature-optimization-principle&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#knuths-optimization-principle-or-the-premature-optimization-principle&quot; id=&quot;knuths-optimization-principle-or-the-premature-optimization-principle&quot;&gt;Knuth’s Optimization Principle or the Premature Optimization principle&lt;/a&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#knuth-s-optimization-principle-or-the-premature-optimization-principle&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;:  &amp;quot;Premature optimization is the root of all evil&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: Coined by Donald Knuth, an American computer scientist who is considered one of the fathers of computer science.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;:
It is often cited as a warning against premature optimization, which is the practice of optimizing code before it is necessary to do so.&lt;/p&gt;
&lt;p&gt;The principle states that it is usually better to write straightforward, understandable code first, and then only optimize it if the performance is not good enough. This is because premature optimization can often lead to buggy code that is difficult to maintain.&lt;/p&gt;
&lt;p&gt;I think this goes hand in hand with YAGNI, stating that we need to optimize when we really need to optimize, and not anticipate the optimizations that we will (maybe) need. In regards to this rule, but not necessarily related to optimizing code, I always advise teams to keep things simple when it comes to writing code.&lt;/p&gt;
&lt;h2 id=&quot;parkinson-s-law&quot; tabindex=&quot;-1&quot;&gt;&lt;a href=&quot;https://irina.codes/the-laws-of-software-development/#parkinsons-law&quot; id=&quot;parkinsons-law&quot;&gt;Parkinson’s Law&lt;/a&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#parkinson-s-law&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Statement&lt;/strong&gt;: &amp;quot;Work expands so as to fill the time available for its completion&amp;quot; or &amp;quot;Work expands so as to fill the allotted time.&amp;quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Source&lt;/strong&gt;: It was formulated by Cyril Northcote Parkinson, a British historian and author, in his 1955 essay &amp;quot;Parkinson&#39;s Law or The Pursuit of Progress&amp;quot;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;: Parkinson&#39;s Law is often cited as a way of explaining why projects often take longer than expected. It is also used to argue against the idea of setting unrealistic deadlines. This law says that if you give people more time to do something, they will take more time to do it. This applies to software development because when we have a lot of time to complete a task, we may procrastinate or do unnecessary work to fill the time. This can lead to delays, cost overruns, and poor quality. Crazy deadlines should be the solution to prevent this &#39;rule&#39; from being applied. A way to prevent this is thorough planning, task breakdown, delegation, discussion and estimations.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/the-laws-of-software-development/#conclusion&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These are just a few of the laws we encounter in our software development world. These are some nice-to-know principles that some smart people came up with. If you have other favorite laws that I didn&#39;t cover,  &lt;a target=&quot;_blank&quot; href=&quot;https://twitter.com/irina_scurtu&quot;&gt; drop me a message&lt;/a&gt; and I&#39;ll add them.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Code-first gRPC</title>
			<link href="https://irina.codes/code-first-grpc/"/>
			<updated>2023-12-05T00:00:00Z</updated>
			<id>https://irina.codes/code-first-grpc/</id>
			<content type="html">&lt;p&gt;&lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/rcodefirst_qkozml.png&quot; srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/codefirst_qkozml.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/codefirst_qkozml.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/codefirst_qkozml.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/codefirst_qkozml.png 1856w&quot; sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot; alt=&quot;Code-first gRPC&quot; importance=&quot;high&quot; class=&quot;img-responsive&quot; width=&quot;780&quot; height=&quot;500&quot; style=&quot;display: none;&quot; /&gt;&lt;/p&gt;
&lt;h1 id=&quot;code-first-grpc-or-how-to-add-grpc-to-an-existing-app&quot; tabindex=&quot;-1&quot;&gt;Code-first gRPC or how to add gRPC to an existing app &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/code-first-grpc/#code-first-grpc-or-how-to-add-grpc-to-an-existing-app&quot;&gt;🔗&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;There are cases when we are not lucky enough to start green-field projects, and we are not able to add the technologies we want or need from the project inception. In these cases, when it comes to using gRPC we can still retrofit it by leveraging an awesome library build by Mark Gravell.&lt;/p&gt;
&lt;h2 id=&quot;exposing-code-first-grpc-data-types&quot; tabindex=&quot;-1&quot;&gt;Exposing code-first gRPC data types &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/code-first-grpc/#exposing-code-first-grpc-data-types&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To add gRPC support for an existing app we would need to install &lt;code&gt;protobuf-net.Grpc&lt;/code&gt; package and to expose the types and operations by annotating them with some attributes. Below there are two types that we will use to be our request/response types. These are annotated  with &lt;code&gt;[DataContract]&lt;/code&gt;, and  &lt;code&gt;[DataMember(Order = 1)]&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;code-block-with-title&quot;&gt;
  &lt;div class=&quot;code-block-title&quot;&gt;Defining types&lt;/div&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;DataContract&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonRequest&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;DataMember&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Order &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Name &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;DataContract&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonResponse&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;DataMember&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Order &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Message &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;Once we define the types that we will use, we would need to define an interface that exposes our operations. In our case, we will have only one operation that will behave like a classic request/response. And by that I mean that is not streaming from client or server-side.&lt;/p&gt;
&lt;div class=&quot;code-block-with-title&quot;&gt;
  &lt;div class=&quot;code-block-title&quot;&gt;Title&lt;/div&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ServiceContract&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IPersonContract&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;PersonResponse&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PersonRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CallContext&lt;/span&gt; context &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This interface is our contract, and what we want to expose to consumers. It is up to you how you pack these types, either in the same gRPC project, or as a separate class library. I choose to have a separate class library because it is easier to share between different projects. Another reason is that I didn&#39;t want to reference the gRPC service directly in the consumer.&lt;/p&gt;
&lt;p&gt;The next step is to take this interface and actually provide an implementation for the operations listed. This project is a regular gRPC service template, in which we install the &lt;code&gt;protobuf-net.Grpc.AspNetCore&lt;/code&gt; NuGet package and enable the corresponding middleware &lt;code&gt;builder.Services.AddCodeFirstGrpc();&lt;/code&gt; in &lt;em&gt;Program.cs&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;You will notice that unlike using gRPC in a new project, in here we won&#39;t have code that is generated for us, but we will this interface that will be the &#39;definition&#39; for our gRPC service.
So when we give the actual implementation, we will implement what is specified in the interface, and not extend a generated base class as before.&lt;/p&gt;
&lt;div class=&quot;code-block-with-title&quot;&gt;
  &lt;div class=&quot;code-block-title&quot;&gt;Title&lt;/div&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;
 &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PersonService&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IPersonContract&lt;/span&gt;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;PersonResponse&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SayHi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PersonRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CallContext&lt;/span&gt; context &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Task&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FromResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
              &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;PersonResponse&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                  Message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;$&quot;Hello &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token expression language-csharp&quot;&gt;request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;h2 id=&quot;consuming-the-code-first-grpc-service&quot; tabindex=&quot;-1&quot;&gt;Consuming the code-first gRPC service &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/code-first-grpc/#consuming-the-code-first-grpc-service&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Once we provide an actual implementation we can create a project that consumes our gRPC service. In this case I will use a console app and install 2 NuGet packages: &lt;code&gt;protobuf-net.Grpc&lt;/code&gt; and &lt;code&gt;Grpc.Net.Client&lt;/code&gt;.
The first one allows us to work with gRPC code-first approach. The second allows us to call to call gRPC services.&lt;/p&gt;
&lt;p&gt;After package install step we need to make sure we have access to the interface that exposes the types and operations. Depending on how you organized your code you need add a project reference. In my case I have a separate project that holds these types, and I needed to add the reference to the &lt;code&gt;SharedContracts&lt;/code&gt; class library. With all being done, now we can call the gRPC service.&lt;/p&gt;
&lt;div class=&quot;code-block-with-title&quot;&gt;
  &lt;div class=&quot;code-block-title&quot;&gt;Title&lt;/div&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;Grpc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Net&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;ProtoBuf&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Grpc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;SharedContracts&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello, World!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; channel &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; GrpcChannel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ForAddress&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://localhost:7251&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; channel&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;CreateGrpcService&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;IPersonContract&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; reply &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Unary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;PersonRequest&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Gigi Dev&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token interpolation-string&quot;&gt;&lt;span class=&quot;token string&quot;&gt;$&quot;Greeting: &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token expression language-csharp&quot;&gt;reply&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Message&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;As you notice, we still need a channel that points to the gRPC service location, but we don&#39;t use the base client that is generated for us by the protoc compiler, we obtain a service instance ourselves by calling &lt;code&gt;.CreateGrpcService&amp;lt;IPersonContract&amp;gt;();&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Another difference will be that we don&#39;t have a .proto file in our application that is shared between the service and the consumer of our service. Instead, we share C# code, by referencing it inside our consumer.&lt;/p&gt;
&lt;h2 id=&quot;summary&quot; tabindex=&quot;-1&quot;&gt;Summary &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/code-first-grpc/#summary&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you want to add gRPC to an existing project, let&#39;s say, a WCF one, you can do it without writing from scratch a .proto file that replicates your existing types and operations. This save some precious time and still gives you the perks of working with  &lt;a href=&quot;https://irina.codes/grpc-vs-rest/&quot;&gt;gRPC&lt;/a&gt;. The downside is that you won&#39;t have the interoperability you get when you work with Protocol Buffers. Those .proto files are agnostic and can be interpreted by different programming languages, giving you some flexibility. When you use the Code-First gRPC approach, you won&#39;t have that, since you are sharing C# code between your projects.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;PS: If you want to actually execute the code I used, fill &lt;a target=&quot;_blank&quot; href=&quot;https://mailchi.mp/irina.codes/code-first-grpc&quot;&gt;this form&lt;/a&gt; and you will receive the zip file directly in your inbox.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>How to mimic response headers in gRPC</title>
			<link href="https://irina.codes/how-to-mimic-response-headers-in-grpc/"/>
			<updated>2023-11-13T00:00:00Z</updated>
			<id>https://irina.codes/how-to-mimic-response-headers-in-grpc/</id>
			<content type="html">&lt;p&gt;&lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/responseheaders1_kf3vbh.png&quot; srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/responseheaders1_kf3vbh.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/responseheaders1_kf3vbh.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/responseheaders1_kf3vbh.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/responseheaders1_kf3vbh.png 1856w&quot; sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot; alt=&quot;Trailers in a Http response context&quot; importance=&quot;high&quot; class=&quot;img-responsive&quot; width=&quot;780&quot; height=&quot;500&quot; /&gt;&lt;/p&gt;
&lt;h1 id=&quot;how-to-mimic-response-headers-in-grpc&quot; tabindex=&quot;-1&quot;&gt;How to mimic response headers in gRPC &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/how-to-mimic-response-headers-in-grpc/#how-to-mimic-response-headers-in-grpc&quot;&gt;🔗&lt;/a&gt;&lt;/h1&gt;
&lt;p&gt;gRPC trailers are a fascinating aspect of the gRPC that often receives less attention than the core request-response mechanism.
They play a role in communication between gRPC clients and servers by providing additional metadata or information after the main response message.&lt;/p&gt;
&lt;h2 id=&quot;what-are-grpc-trailers&quot; tabindex=&quot;-1&quot;&gt;What Are gRPC Trailers? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/how-to-mimic-response-headers-in-grpc/#what-are-grpc-trailers&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Trailers&lt;/strong&gt; are part of the HTTP/2 protocol that carry additional metadata about a resonse. They allow servers to send extra information with the response. Usually, this information doesn&#39;t fit in the response message itself and can&#39;t be added to HTTP protocol headers collection. This is because the protocol is one level above the gRPC response and we are interested in the gRPC context and content.&lt;/p&gt;
&lt;p&gt;gRPC uses trailers by default for two scenarios:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;to support streaming&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;to send a grpc status code after the content&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For example, when an error occurs, the Status(grpc-status) and Status-Message are delivered in the &lt;a href=&quot;https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#errors&quot;&gt;Trailers section&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;So basically, if we remember how a HTTP response looks like, we have:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Headers (or header fiels/header properties)&lt;/li&gt;
&lt;li&gt;Body - the actual response content&lt;/li&gt;
&lt;li&gt;Status code - indicating the status of our Response( 404, 200, etc.)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If we look at the picture below, we will notice that there are several gRPC responses inside the same response body. This is dependent of the gRPC method type that we have. For a &lt;code&gt;Server-streaming method&lt;/code&gt; type we will have several reponses, and for all the rest, just one.&lt;/p&gt;
&lt;!-- ![Trailers in a response](/img/posts/trailers.png) --&gt;
&lt;!-- &lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;/img/7C999BMs_w-530.avif 530w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;/img/7C999BMs_w-530.webp 530w&quot;&gt;&lt;img alt=&quot;Photo of a cat named Shakespeare sitting on a window sill&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;img-fluid&quot; src=&quot;/img/7C999BMs_w-530.png&quot; width=&quot;530&quot; height=&quot;720&quot;&gt;&lt;/picture&gt; --&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://irina.codes/img/7C999BMs_w-530.avif 530w&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://irina.codes/img/7C999BMs_w-530.webp 530w&quot; /&gt;&lt;img alt=&quot;A possum parent and two possum kids hanging from the iconic red balloon&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;img-fluid&quot; src=&quot;https://irina.codes/img/7C999BMs_w-530.png&quot; width=&quot;530&quot; height=&quot;720&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;After all the responses, we see the gRPC trailers, which is sent only after all the response messages are sent.&lt;/p&gt;
&lt;h2 id=&quot;adding-trailers-in-net&quot; tabindex=&quot;-1&quot;&gt;Adding trailers in .NET &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/how-to-mimic-response-headers-in-grpc/#adding-trailers-in-net&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In .NET gRPC trailers are represented like a &lt;code&gt;Metadata&lt;/code&gt; type, which is basically a dictionary that allows us to add key-value pairs from the server implementation.
In our code, the type &lt;code&gt;ServerCallContext&lt;/code&gt; contains info about both the current request and the response, and allows us to manage a few things. One of those things is &lt;strong&gt;ResponseTrailers&lt;/strong&gt;.
So, to add something in the &lt;strong&gt;ResponseTrailers&lt;/strong&gt;, we simply create a &lt;code&gt;Metadata.Entry&lt;/code&gt; object and we pass the key and value. If we need to add more than one key and value, we can also do that.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Task&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;GrpcResponse&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;SayHello&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;GrpcRequest&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ServerCallContext&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Metadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Entry&lt;/span&gt; myHeader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Metadata&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Entry&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my-fake-header&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;grpc-header&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ResponseTrailers&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;myHeader&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Task&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FromResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;GrpcResponse&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                Message &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Name
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/how-to-mimic-response-headers-in-grpc/#conclusion&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;gRPC trailers are an often overlooked but invaluable part of gRPC. Their ability to carry additional metadata beyond the main response is instrumental in improving error handling, providing contextual information, and enhancing the flexibility of the protocol. Developers leveraging gRPC can harness trailers to transmit crucial details, streamline error handling, and enrich the client-server communication with valuable context.
Understanding and utilizing gRPC trailers can significantly enhance the efficiency and effectiveness of gRPC-based applications.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>gRPC vs REST</title>
			<link href="https://irina.codes/grpc-vs-rest/"/>
			<updated>2023-11-02T00:00:00Z</updated>
			<id>https://irina.codes/grpc-vs-rest/</id>
			<content type="html">&lt;p&gt;&lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/Articole.png&quot; srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/grpcvsrest.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/grpcvsrest.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/grpcvsrest.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/grpcvsrest.png 1856w&quot; sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot; alt=&quot;gRPC vs REST by Irina Scurtu&quot; importance=&quot;high&quot; class=&quot;img-responsive&quot; width=&quot;941&quot; height=&quot;627&quot; /&gt;&lt;/p&gt;
&lt;h2 id=&quot;context&quot; tabindex=&quot;-1&quot;&gt;Context &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#context&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Both gRPC and REST are different architectural approaches for building distributed systems, each with its own fundamental characteristics. Both have their own place in the application ecosystem. The main visible difference being the context in which they are used. REST is more for public-facing APIs and gRPC for downstream APIs.
In modern architectures, both gRPC and REST have their rightful place. Even though gRPC is fairly new to the game, it has it&#39;s own merits and it will never replace REST.
REST(or HTTP APIs to be more specific) will continue to be the &#39;de facto&#39; standard for creating APIs in the WEB world.&lt;/p&gt;
&lt;h2 id=&quot;underlying-principles-and-architecture&quot; tabindex=&quot;-1&quot;&gt;Underlying Principles and Architecture &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#underlying-principles-and-architecture&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Explain the fundamental architectural differences between gRPC and REST. Discuss how gRPC is based on Protocol Buffers and HTTP/2, while REST uses standard HTTP methods and typically exchanges data in formats like JSON or XML.&lt;/p&gt;
&lt;h3 id=&quot;grpc&quot; tabindex=&quot;-1&quot;&gt;gRPC &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#grpc&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;gRPC uses HTTP/2 protocol. This gives us all the perks of using a new protocol version like: performance, reduced latency, and optimized network utilization.&lt;/li&gt;
&lt;li&gt;gRPC uses Protocol Buffers (protobuf) as its Interface Definition Language (IDL). This is a special language in which we define the types(messages) that can be handled by the server, and service operations. This allows us to define what the server exposes in a language-agnostic way.&lt;/li&gt;
&lt;li&gt;gRPC requests can be only of type POST&lt;/li&gt;
&lt;li&gt;Call-a-method look and feel - for us, developers, with gRPC we call methods and pass parameters to the methods, and we get objects as a response&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rest&quot; tabindex=&quot;-1&quot;&gt;REST &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#rest&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;REST APIs can use HTTP/1 or HTTP/2&lt;/li&gt;
&lt;li&gt;The definition of a REST API is usually exposed trough a standard like Open API, and tools like Swagger&lt;/li&gt;
&lt;li&gt;REST APIs use the standard HTTP methods (GET, POST, PUT, DELETE, etc.) to manipulate resources identified by URIs. Often these are used to perform CRUD(Create, Read, Update, Delete) operations&lt;/li&gt;
&lt;li&gt;Make-a-request-to-an endpoint approach - for us, developers with REST APIs, we call endpoints utilizing HTTPClients, and passing in querystrings, request bodies and expecting the response back&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;performance-and-efficiency-comparison&quot; tabindex=&quot;-1&quot;&gt;Performance and Efficiency Comparison &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#performance-and-efficiency-comparison&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;protocol-buffers&quot; tabindex=&quot;-1&quot;&gt;Protocol Buffers &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#protocol-buffers&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;uses binary serialization format which is more compact and ligthweight(in some cases)&lt;/li&gt;
&lt;li&gt;is not always human-readable( but we have tools to aid with that) e.g: &lt;a href=&quot;https://www.postman.com/&quot;&gt;Postman&lt;/a&gt;, &lt;a href=&quot;https://kreya.app/&quot;&gt;Kreya&lt;/a&gt;, &lt;a href=&quot;https://kalisto.app/&quot;&gt;Kalisto&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;is lightweight when it comes to data transmission over the network(due to its binary format and compression)&lt;/li&gt;
&lt;li&gt;offer a high level of schema flexibility and extensibility. They allows for backward and forward compatibility through versioning and evolving message schemas without breaking changes, making them suitable for applications where data structures might evolve over time.&lt;/li&gt;
&lt;li&gt;steep and complex learning curve, more setup&lt;/li&gt;
&lt;li&gt;strongly typed&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rest-1&quot; tabindex=&quot;-1&quot;&gt;REST &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#rest-1&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;is a text-based serialisation format&lt;/li&gt;
&lt;li&gt;is human readable usually using JSON or XML&lt;/li&gt;
&lt;li&gt;more heavy-weight for network data transfer due to the format&lt;/li&gt;
&lt;li&gt;offer a high level of schema flexibility and extensibility. Thisflexibility allows for easy serialization of different types of data structures, it can lead to challenges in maintaining data consistency and handling schema evolution across different versions of an API.&lt;/li&gt;
&lt;li&gt;easy to understand and simple structure&lt;/li&gt;
&lt;li&gt;lack of strict typing&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;service-contracts-and-code-generation&quot; tabindex=&quot;-1&quot;&gt;Service Contracts and Code Generation &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#service-contracts-and-code-generation&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;grpc-1&quot; tabindex=&quot;-1&quot;&gt;gRPC &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#grpc-1&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;has a strict and well-defined service contract, in which we can specify the methods and their parameters and the message types.&lt;/li&gt;
&lt;li&gt;code-generation out of the box&lt;/li&gt;
&lt;li&gt;promotes a rigid an strongly typed API definition.&lt;/li&gt;
&lt;li&gt;straightforward interoperability between different programming languages due to the consistent code generation from the well-defined service contract.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rest-2&quot; tabindex=&quot;-1&quot;&gt;REST &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#rest-2&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;has flexible and loosely defined service contracts relying on HTTP methods and standard data formats like JSON or XML&lt;/li&gt;
&lt;li&gt;no strict standards or structured schema definitions allowing us to define and design services how we want&lt;/li&gt;
&lt;li&gt;due to less strict service definitions, it can lead to less consistency in how APIs are implemented in various programming languages. This is usually done using third-party tooling&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;tooling-and-ecosystem-support&quot; tabindex=&quot;-1&quot;&gt;Tooling and Ecosystem Support &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#tooling-and-ecosystem-support&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;gRPC provides a more standardized and uniform way of defining APIs and generating code across multiple languages, leading to consistent development experiences.
REST ecosystem is more descentralized. We can find a wide array of frameworks and tooling, providing flexibility and options for developers but may lack uniformity.&lt;/p&gt;
&lt;p&gt;while gRPC&#39;s ecosystem is more standardized and centralized, providing uniform tooling and library support, the REST ecosystem offers extensive diversity and flexibility, leading to a more varied developer experience based on chosen frameworks and tools. Both ecosystems have their respective strengths and benefits, allowing developers to choose based on their specific needs, preferences, and project requirements.&lt;/p&gt;
&lt;h2 id=&quot;use-cases-and-suitability&quot; tabindex=&quot;-1&quot;&gt;Use Cases and Suitability &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#use-cases-and-suitability&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;grpc-2&quot; tabindex=&quot;-1&quot;&gt;gRPC &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#grpc-2&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;high-performance applications&lt;/strong&gt; where low latency and high throughput are essential. It uses HTTP/2 and binary serialization, making it faster and more efficient than REST in terms of data transfer.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;microservices environments&lt;/strong&gt;, gRPC&#39;s strong typing and well-defined service contracts streamline communication between microservices. We basically have to have access to the .profo file and we are good to consume the API that exposes the file. It supports bidirectional streaming that can be an advantage for distributed systems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Polyglot/wanna be polyglot environments&lt;/strong&gt; - the code-generation for various languages from the service contract(.proto file) is makes a consistent experience accross different languages. This allows expose and consume services from different ecosystems.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;limited browser support&lt;/strong&gt; - We have &lt;code&gt;gRPC-Web&lt;/code&gt; that extends gRPC support to browser-based applications, its full capabilities and features might not be readily available or as seamless as with native gRPC usage in non-browser environments.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;rest-3&quot; tabindex=&quot;-1&quot;&gt;REST &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#rest-3&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;due to the textual representation we can say that it has higher overhead and slower performance compared to gRPC&lt;/li&gt;
&lt;li&gt;for all apps, and public-facing APIs&lt;/li&gt;
&lt;li&gt;the format of the request/response must be known by the parts trying to communicate&lt;/li&gt;
&lt;li&gt;fully supported in browser apps without third party tooling&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;conclusion-and-recommendations&quot; tabindex=&quot;-1&quot;&gt;Conclusion and Recommendations: &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/grpc-vs-rest/#conclusion-and-recommendations&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In summary, the choice between gRPC and REST depends on various factors such as performance requirements, interoperability, ease of use, and the specific needs of the application. gRPC is ideal for high-performance, microservices, and polyglot environments, whereas REST is more suitable for simpler, publicly accessible APIs and when browser compatibility and human readability are prioritized. Understanding the trade-offs and specific use cases helps in making an informed decision regarding the best-fit technology for a given scenario.&lt;/p&gt;
&lt;p&gt;PS: Let me know if I forgot anything&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Configuring gRPC clients in .NET</title>
			<link href="https://irina.codes/configuring-grpc-clients-in-net/"/>
			<updated>2023-06-13T00:00:00Z</updated>
			<id>https://irina.codes/configuring-grpc-clients-in-net/</id>
			<content type="html">&lt;p&gt;&lt;img loading=&quot;lazy&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/Articole.png&quot; srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/Articole.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/Articole.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/Articole.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/Articole.png 1856w&quot; sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot; alt=&quot;Configuring gRPC Clients by Irina Scurtu&quot; importance=&quot;high&quot; class=&quot;img-responsive&quot; width=&quot;941&quot; height=&quot;627&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Configuring &lt;a href=&quot;https://grpc.io/docs/languages/csharp/&quot;&gt;gRPC&lt;/a&gt; clients in .NET is similar to what we would configure when we use HTTP Clients. The NuGet package that will allow us to use gRPC clients, is &lt;code&gt;Grpc.NET.ClientFactory.&lt;/code&gt; This allows us to build on top of the IHttpClientFactory by providing us with the necessary &lt;code&gt;GrpcClientFactoryOptions&lt;/code&gt; we use to customize our globally scoped clients.&lt;/p&gt;
&lt;p&gt;Underneath it all, a gRPC client is a special kind of HTTPClient that knows how to handle messages at a lower level, in a construct called &#39;channel&#39;.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;  services
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddHttpClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConfigurePrimaryHttpMessageHandler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// Set PrimaryHandler to null so we can track whether the user&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// set a value or not. If they didn&#39;t set their own handler then&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// one will be created by PostConfigure.&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We configure our gRPC clients globally in Program.cs, just as we configure any regular HTTP Client, by using the extension method, and pointing it to the right server address.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;
builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Services
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;AddGrpcClient&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;SpeakerServiceDefinition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SpeakerServiceDefinitionClient&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    o &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://localhost:7226&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;By default, the &lt;code&gt;AddGrpcClient&lt;/code&gt;, configures the gRPC clients as being transient.&lt;/p&gt;
&lt;h3 id=&quot;configuring-grpc-client-channels&quot; tabindex=&quot;-1&quot;&gt;Configuring gRPC client channels &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/configuring-grpc-clients-in-net/#configuring-grpc-client-channels&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We can configure several aspects of the gRPC channels, which resemble the configurations available for Kestrel. Most of these configurations pertain to the size of the transported data in bytes.&lt;/p&gt;
&lt;p&gt;For example, below, you can see that the &lt;code&gt;MaxReceiveMessageSize&lt;/code&gt; - referring to gRPC data is configured to be 100 bytes. This means that if the server sends a message that exceeds this limit, it will cause a &lt;code&gt;ResourceExhausted&lt;/code&gt; exception in the consumer app.&lt;code&gt;RpcException: Status(StatusCode=&amp;quot;ResourceExhausted&amp;quot;, Detail=&amp;quot;Received message exceeds the maximum configured message size.&amp;quot;)&lt;/code&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Services 
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;AddGrpcClient&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;SpeakerServiceDefinition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SpeakerServiceDefinitionClient&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; 
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt; 
     o &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://localhost:7226&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; 
     &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConfigureChannel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;o &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; 
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  
         o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MaxReceiveMessageSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
         o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MaxRetryBufferSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  
         o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;MaxSendMessageSize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; 
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a similar way, we can configure the &lt;code&gt;MaxSendMessageSize&lt;/code&gt; property. This will prevent us from sending payloads that exceed what is configured globally.&lt;/p&gt;
&lt;p&gt;We will get an exception that has the &lt;code&gt;ResourceExhausted&lt;/code&gt; status code. This time, the exception happens when we call the server, and not when we get a response from it.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Grpc.Core.RpcException: &#39;Status(StatusCode=&amp;quot;ResourceExhausted&amp;quot;, Detail=&amp;quot;Sending message exceeds the maximum configured message size.&amp;quot;)&#39;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;MaxRetryBufferSize&lt;/code&gt; is used when we have a retry policy in place, as a safety measure.&lt;/p&gt;
&lt;p&gt;When a request fails, it gets added to the retry buffer. Then, the requests in the buffer are attempted again during subsequent retries. If the specified &lt;code&gt;MaxRetryBufferSize&lt;/code&gt; is exceeded by the buffer size, new requests are not added to the buffer, and retries are not attempted.&lt;/p&gt;
&lt;p&gt;This way, excessive memory consumption is prevented, and the buffer it&#39;s not allowed to grow indefinitely.&lt;/p&gt;
&lt;p&gt;We can also configure the &lt;code&gt;CompressionProviders&lt;/code&gt; and channel-level &lt;code&gt;Credentials&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;specifying-a-retry-or-a-hedging-policy&quot; tabindex=&quot;-1&quot;&gt;Specifying a retry or a hedging policy &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/configuring-grpc-clients-in-net/#specifying-a-retry-or-a-hedging-policy&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;When configuring gRPC clients, we have the ability to specify whether a retry or a hedging policy is being used. All we need to do is create a policy and pass it as an option.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; retryPolicy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;MethodConfig&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Names &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; MethodName&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Default &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    RetryPolicy &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;RetryPolicy&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        MaxAttempts &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        InitialBackoff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; TimeSpan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        MaxBackoff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; TimeSpan&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;FromSeconds&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0.5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        BackoffMultiplier &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        RetryableStatusCodes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; StatusCode&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Internal &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;


builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Services
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;AddGrpcClient&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;SpeakerServiceDefinition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SpeakerServiceDefinitionClient&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    o &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Address &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;https://localhost:7226&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConfigureChannel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;o &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ServiceConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;ServiceConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
             MethodConfigs &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                 retryPolicy
             &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      
     &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With everything configured we now can inject our client in controllers:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpeakerServiceDefinition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SpeakerServiceDefinitionClient&lt;/span&gt; speakerService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;HomeController&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;SpeakerServiceDefinition&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;SpeakerServiceDefinitionClient&lt;/span&gt; speakerService&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;speakerService &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; speakerService&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now that you know how to configure gRPC clients, you can enable &lt;a href=&quot;https://irina.codes/enabling-grpc-server-reflection/&quot;&gt;gRPC server reflection&lt;/a&gt; on your server.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Enabling gRPC server reflection</title>
			<link href="https://irina.codes/enabling-grpc-server-reflection/"/>
			<updated>2022-12-13T00:00:00Z</updated>
			<id>https://irina.codes/enabling-grpc-server-reflection/</id>
			<content type="html">&lt;p&gt;&lt;img loading=&quot;lazy&quot; class=&quot;img-fluid&quot; src=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_680/v1695118159/posts/Articole-800--450.png&quot; srcset=&quot;https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_300/v1695118159/posts/Articole-800-_-450.png 300w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_600/v1695118159/posts/Articole-800-_-450.png 600w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_928/v1695118159/posts/Articole-800-_-450.png 928w, https://res.cloudinary.com/irinacodes/image/upload/q_auto,f_auto,w_1856/v1695118159/posts/Articole-800-_-450.png 1856w&quot; sizes=&quot;(min-width: 980px) 928px, calc(95.15vw + 15px)&quot; alt=&quot;Enabling grpc serv er reflection by Irina Scurtu&quot; importance=&quot;high&quot; width=&quot;941&quot; height=&quot;627&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Enabling gRPC server reflection will allow you to easily call your gRPC services from &lt;a href=&quot;https://www.postman.com/&quot;&gt;Postman&lt;/a&gt; without actually loading the protofile .&lt;/p&gt;
&lt;p&gt;PostMan will help you test your gRPC services, by giving you the same look and feel as a you would have with a traditional HTTP API.&lt;/p&gt;
&lt;p&gt;gRPC uses binary serialization, which means that is not human readable as JSON is. Is still possible to use grpcurl or similar tools, but you will not be able to decipher binary. Having the right tools will help you understand gRPC faster, because it has a slightly different vocabulary.&lt;/p&gt;
&lt;p&gt;When you open PostMan and click the New button , you can choose to issue gRPC requests. Thankfully, this is not in beta anymore.&lt;/p&gt;
&lt;!-- ![](image-1024x650.png) --&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://irina.codes/img/zM3OR56o-t-1024.avif 1024w&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://irina.codes/img/zM3OR56o-t-1024.webp 1024w&quot; /&gt;&lt;img alt=&quot;A possum parent and two possum kids hanging from the iconic red balloon&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;img-fluid&quot; src=&quot;https://irina.codes/img/zM3OR56o-t-1024.png&quot; width=&quot;1024&quot; height=&quot;650&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;PostMan New menu options&lt;/p&gt;
&lt;p&gt;To issue a gRPC request, you will need to specify the URL of the server (as you would do with a regular RESTful API), and then give PostMan access to the proto file that is implemented on the server. In this case, you need to help the tool a bit, so you can gain access to available functionality.&lt;/p&gt;
&lt;p&gt;To do that you can simply import the proto file or let PostMan discover what is available.&lt;/p&gt;
&lt;p&gt;In this case we will use the second option, because we would want our service operations to be somehow discoverable.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://irina.codes/img/iaQiRU2eME-1024.avif 1024w&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://irina.codes/img/iaQiRU2eME-1024.webp 1024w&quot; /&gt;&lt;img alt=&quot;A possum parent and two possum kids hanging from the iconic red balloon&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;img-fluid&quot; src=&quot;https://irina.codes/img/iaQiRU2eME-1024.png&quot; width=&quot;1024&quot; height=&quot;465&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Different ways of &#39;accessing&#39; the proto file contract&lt;/p&gt;
&lt;p&gt;Now, if we try to click the highlighted option, we will get a big red error. This is as if our server throws an Unimplemented exception from a method.&lt;/p&gt;
&lt;!-- ![](images/image-4.png) --&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://irina.codes/img/ihcP20e2TK-677.avif 677w&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://irina.codes/img/ihcP20e2TK-677.webp 677w&quot; /&gt;&lt;img alt=&quot;A possum parent and two possum kids hanging from the iconic red balloon&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;img-fluid&quot; src=&quot;https://irina.codes/img/ihcP20e2TK-677.png&quot; width=&quot;677&quot; height=&quot;155&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;In reality, if we look at the logs, it actually does throw that kind of exception. :)&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;info&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; Grpc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AspNetCore&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Internal&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ServerCallHandlerFactory&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      Service &#39;grpc&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;reflection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;v1alpha&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ServerReflection&#39; &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;unimplemented&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Enabling gRPC server reflection requires a few code tweaks because we actually need to an implementation that allows reflection over our grPC services.&lt;/p&gt;
&lt;p&gt;First, install &lt;code&gt;Grpc.AspNetCore.Server.Reflection&lt;/code&gt; NuGet package. This will contain the ServerReflection implementation that will we need, that allows external services and tools to discover existing methods&lt;/p&gt;
&lt;p&gt;Next, we need to add the middleware and then map the incoming gRPC requests to the Reflection Service.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddGrpcReflection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;IWebHostEnvironment&lt;/span&gt; env &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Environment&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsDevelopment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MapGrpcReflectionService&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you did this, PostMan will successfully discover the available gRPC methods, and you will be able to call invoke these from the UI&lt;/p&gt;
&lt;!-- ![Alt text](image-4-1.png) --&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://irina.codes/img/ihcP20e2TK-677.avif 677w&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://irina.codes/img/ihcP20e2TK-677.webp 677w&quot; /&gt;&lt;img alt=&quot;A possum parent and two possum kids hanging from the iconic red balloon&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;img-fluid&quot; src=&quot;https://irina.codes/img/ihcP20e2TK-677.png&quot; width=&quot;677&quot; height=&quot;155&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;!-- ![](images/image-5-1024x295.png) --&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://irina.codes/img/fHZxo-crRI-1024.avif 1024w&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://irina.codes/img/fHZxo-crRI-1024.webp 1024w&quot; /&gt;&lt;img alt=&quot;A possum parent and two possum kids hanging from the iconic red balloon&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;img-fluid&quot; src=&quot;https://irina.codes/img/fHZxo-crRI-1024.png&quot; width=&quot;1024&quot; height=&quot;295&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;gRPC methods&lt;/p&gt;
&lt;p&gt;That&#39;s it! A package and a few lines of code and you will enable gRPC server reflection.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Using custom media types in .NET</title>
			<link href="https://irina.codes/using-custom-media-types-in-net/"/>
			<updated>2022-04-01T00:00:00Z</updated>
			<id>https://irina.codes/using-custom-media-types-in-net/</id>
			<content type="html">&lt;p&gt;How to use and create custom mime types in .NET? How to respond to different mime-types in your Web API? Use the same HTTP Verb but hit different actions based on the Content-Type header value? Have 2 methods that have the same number of parameters and respond to the same HTTP Verb?&lt;/p&gt;
&lt;p&gt;These are questions that some of the developers seek answers to.&lt;/p&gt;
&lt;h3 id=&quot;accept-and-content-type&quot; tabindex=&quot;-1&quot;&gt;Accept &amp;amp; Content-Type &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/using-custom-media-types-in-net/#accept-and-content-type&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are 2 very important headers that are often overlooked, &lt;em&gt;Accept&lt;/em&gt; and &lt;em&gt;Content-Type&lt;/em&gt;, and play a major role in a process called &lt;em&gt;Content-Negotiation.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;Accept&lt;/em&gt; header&lt;/strong&gt;&lt;br /&gt;
With this header, the API consumer tells the server what is the mime type it expects as the format for the response.&lt;br /&gt;
eg. Accept: application/json or Accept: application/xml.&lt;br /&gt;
If the server can&#39;t build a response with the requested format, it will return a 406 - Not Acceptable status code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Content-Type&lt;/strong&gt;&lt;br /&gt;
Describes the format of the body of the request that is sent to the server or to the client. This tells the server: &amp;quot;Hey, the body you will receive has this format, can you interpret it?&amp;quot;&lt;br /&gt;
If the server can&#39;t read the content of the body, it will return a &lt;em&gt;415 Unsupported Media Type&lt;/em&gt; status code_._&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Content-Negotiation&lt;/em&gt; is the process that happens with every request, where basically the two parties involved (i.e the Server and the Consumer/Client) do exactly that. : negotiate on the content. Depending on the format send as value for these 2 header properties, the result can differ.&lt;/p&gt;
&lt;p&gt;When we talk about APIs or &lt;a href=&quot;https://irina.codes/how-to-build-rest-ful-apis/&quot;&gt;REST-ful APIs&lt;/a&gt; we implicitly talk about different representations of resources.&lt;/p&gt;
&lt;h2 id=&quot;introducing-the-problem&quot; tabindex=&quot;-1&quot;&gt;Introducing the problem &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/using-custom-media-types-in-net/#introducing-the-problem&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let&#39;s say we have an API that deals with speakers. For these, we store all kinds of info, but not all are needed every time. Maybe you need to return a full object in one scenario, and just a few properties in another one, like below.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;   &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpeakerModel&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;    
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; Id &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; FirstName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; LastName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Bio &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; JobTitle &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Website &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Email &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; TwitterHandle &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; CompanyName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Address &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Phone &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; PassportDetails &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TrimmedSpeakerModel&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;    
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; Id &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;      
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; FirstName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; LastName &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To achieve this, people end up creating new endpoints because the platform forces us to do so, in a way. We have the controller and in the controller we implement implement 2 actions, with different name, and different routes.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/api/speakers&lt;/code&gt; and &lt;code&gt;/api/speakers/trimmed&lt;/code&gt; are basically two representations of the same resource. It shouldn&#39;t matter what scenario you are in, the endpoint should still be something similar to &lt;code&gt;/api/speakers&lt;/code&gt; to the outside world.&lt;/p&gt;
&lt;p&gt;In the example below we focus on the output, and not the logic in the action.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpGet&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{speakerId}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;IActionResult&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetSpeakerFull&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;FromQuery&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; speakerId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
           &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;SpeakerModel&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpGet&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{speakerId}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;IActionResult&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetSpeakerTrimmed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;FromQuery&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; speakerId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;SpeakerTrimmedDto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Something like this would be possible without getting an error from the routing mechanism. We know we have a GET request, but which one of the two actions should be selected? We need to help the framework to distinguish between the two of them.&lt;/p&gt;
&lt;h2 id=&quot;introducing-media-types-mime-types&quot; tabindex=&quot;-1&quot;&gt;Introducing media types(MIME types) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/using-custom-media-types-in-net/#introducing-media-types-mime-types&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A media type (also known as a Multipurpose Internet Mail Extensions or MIME type) indicates the nature and format of a document, file, or assortment of bytes. MIME types are defined and standardized in IETF&#39;s &lt;a href=&quot;https://datatracker.ietf.org/doc/html/rfc6838&quot;&gt;RFC 6838&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;custom-media-types&quot; tabindex=&quot;-1&quot;&gt;Custom media types &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/using-custom-media-types-in-net/#custom-media-types&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A custom media type is a media type that we can create, often containg vnd as . This media type is significant to the specific business or domain.&lt;br /&gt;
You will often find vendor-specific media types used by different businesses as a mean to address specific customers trough the same api. There are hundreds of vendor-specific media types registered with IANA. Such example can be &lt;em&gt;application/vnd.mspowerpoint&lt;/em&gt;.&lt;br /&gt;
GitHub uses them in their API, and a lot of other businesses.&lt;/p&gt;
&lt;h2 id=&quot;media-type-structure&quot; tabindex=&quot;-1&quot;&gt;Media type structure &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/using-custom-media-types-in-net/#media-type-structure&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://irina.codes/img/Rvsc_fpPD5-809.avif 809w&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://irina.codes/img/Rvsc_fpPD5-809.webp 809w&quot; /&gt;&lt;img alt=&quot;Media types&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;img-fluid&quot; src=&quot;https://irina.codes/img/Rvsc_fpPD5-809.png&quot; width=&quot;809&quot; height=&quot;328&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;!-- ![](/04/media-type.png) --&gt;
&lt;p&gt;Media type structure&lt;/p&gt;
&lt;h2 id=&quot;responding-to-media-types-in-controllers&quot; tabindex=&quot;-1&quot;&gt;Responding to media types in controllers &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/using-custom-media-types-in-net/#responding-to-media-types-in-controllers&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To respond to a specific media type in our action, all you have to do is annotate with the &lt;code&gt;[Consumes(&amp;quot;&amp;quot;)]&lt;/code&gt; attribute, passing the value of your media type.&lt;/p&gt;
&lt;p&gt;This attribute will allow us to have actions that return different representations of the same resource.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;        &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpGet&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;{speakerId}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Consumes&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;vnd.speaker.trimmed&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;IActionResult&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetSpeakerTrimmed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;FromQuery&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; speakerId&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;SpeakerTrimmedDto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You will have to send Content-Type : vnd.speaker.trimmed when you make the request, to hit the action.&lt;/p&gt;
&lt;p&gt;In a similar manner you can use the &lt;code&gt;[Produces(&amp;quot;&amp;quot;)]&lt;/code&gt; attribute to help the routing mechanism when you want a specific format returned. This attribute will look after &lt;em&gt;Accept&lt;/em&gt; header value. Bear in mind that it will work out of the box only with the mime types supported by the framework. Otherwise you will need to create an &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/web-api/advanced/custom-formatters?view=aspnetcore-6.0&quot;&gt;OutputFormatter&lt;/a&gt;, and register it.&lt;/p&gt;
&lt;h2 id=&quot;in-summary&quot; tabindex=&quot;-1&quot;&gt;In summary &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/using-custom-media-types-in-net/#in-summary&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The approach with media types can be used for versioning too. You can read more about versioning your API &lt;a href=&quot;https://irina.codes/versioning-rest-apis/&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can create your custom vendor-specific mime types, and include versions in them. From there you leave the routing mechanism to do its job.&lt;/p&gt;
&lt;p&gt;Use this wisely, because currently It will cause a swagger loading error. I&#39;m still looking for a way around it. If you know how, drop a comment or a DM&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>The .NET Docs Show - Versioning REST APIs</title>
			<link href="https://irina.codes/the-net-docs-show-versioning-rest-apis/"/>
			<updated>2021-05-11T00:00:00Z</updated>
			<id>https://irina.codes/the-net-docs-show-versioning-rest-apis/</id>
			<content type="html">&lt;!-- In any other file (e.g., a blog post): --&gt;
&lt;script src=&quot;https://irina.codes/public/js/lite-yt-embed.js&quot;&gt;&lt;/script&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://irina.codes/css/lite-yt-embed.css&quot; /&gt;
&lt;lite-youtube videoid=&quot;4fw2c_ukABM&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/4fw2c_ukABM/hqdefault.jpg&#39;);&quot; params=&quot;start=0&quot;&gt;
    &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
        &lt;span class=&quot;lyt-visually-hidden&quot;&gt;Play Video: Irina Scurtu on .NET &lt;/span&gt;
    &lt;/button&gt;
&lt;/lite-youtube&gt;
</content>
		</entry>
		
		<entry>
			<title>How to build REST-ful APIs</title>
			<link href="https://irina.codes/how-to-build-rest-ful-apis/"/>
			<updated>2021-03-02T00:00:00Z</updated>
			<id>https://irina.codes/how-to-build-rest-ful-apis/</id>
			<content type="html">&lt;p&gt;Thank you Edwin van Wijk and Sander Molenkamp for inviting me.&lt;/p&gt;
&lt;p&gt;You can see more interesting videos on &lt;a href=&quot;https://www.youtube.com/channel/UCFz17EP7PE2ouhPuVmu__kA&quot;&gt;DotNETFlix channel&lt;/a&gt; on different topics, or &lt;a href=&quot;https://irina.codes/category/rest/&quot;&gt;read about REST&lt;/a&gt;&lt;/p&gt;
&lt;!-- In any other file (e.g., a blog post): --&gt;
&lt;script src=&quot;https://irina.codes/public/js/lite-yt-embed.js&quot;&gt;&lt;/script&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://irina.codes/css/lite-yt-embed.css&quot; /&gt;
&lt;lite-youtube videoid=&quot;Igh2KfwOT-Q&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/Igh2KfwOT-Q/hqdefault.jpg&#39;);&quot; params=&quot;start=0&quot;&gt;
    &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
        &lt;span class=&quot;lyt-visually-hidden&quot;&gt;Play Video: Irina Scurtu - How to build RESTful APIs the right way&lt;/span&gt;
    &lt;/button&gt;
&lt;/lite-youtube&gt;
&lt;!-- https://www.youtube.com/watch?v=Igh2KfwOT-Q&amp;ab&#92;_channel=dotnetFlix --&gt;
</content>
		</entry>
		
		<entry>
			<title>Naming your REST endpoints</title>
			<link href="https://irina.codes/naming-your-rest-endpoints/"/>
			<updated>2020-11-19T00:00:00Z</updated>
			<id>https://irina.codes/naming-your-rest-endpoints/</id>
			<content type="html">&lt;p&gt;How to name your REST endpoint? How does your endpoint look? Are there any guidelines or best practices? How to name your endpoints?&lt;/p&gt;
&lt;h3 id=&quot;a-short-introduction&quot; tabindex=&quot;-1&quot;&gt;A short introduction &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/naming-your-rest-endpoints/#a-short-introduction&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We often use APIs to implement CRUD operations, or most APIs start like this, but all of them evolve over time.&lt;br /&gt;
CRUD is an acronym that stands for Create, Read, Update, Delete. And these are all the operations that you can apply to a specific resource.&lt;/p&gt;
&lt;p&gt;Let&#39;s take childish example, and say you need to manage pets for a veterinary cabinet and you are interested in cats:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/api/getAllCats&lt;/code&gt;- wouldn&#39;t &lt;code&gt;/api/cats&lt;/code&gt; be easier to remember?&lt;br /&gt;
&lt;code&gt;/api/newCat&lt;/code&gt; - how about &lt;code&gt;/api/cats&lt;/code&gt; and use POST as HTTP Verb?&lt;br /&gt;
&lt;code&gt;/api/getBlackCats&lt;/code&gt;- why should you have a different endpoint? &lt;code&gt;/api/cats?color=black&lt;/code&gt; will have the same path, but the parameter value will change an get you a lot of flexibility&lt;br /&gt;
&lt;code&gt;/api/getCatsOwners&lt;/code&gt;- how about &lt;code&gt;/api/cats/{id}/owners&lt;/code&gt;- this way you will know exactly to which cat you are reffering. The one with the {id} in the URL path.&lt;br /&gt;
&lt;code&gt;/api/getHungerLevel&lt;/code&gt;- hunger level of...? cat? which cat? maybe is youru hunger level?&lt;/p&gt;
&lt;p&gt;If you look at the endpoints, are very hard to remember, and once you add more operations, it will be even harder to remember and harder to manage. Plus, you have no reason to include get{something} in the endpoint or have tons of endpoints. The HTTP verb will be the one that tells what operation you are doing. Either is a GET, a POST or something else. KISS, as in code, also applies here. And more than that, we need to keep in mind that we need to leverage the underlying protocol as much as we can.&lt;/p&gt;
&lt;p&gt;In my opinion, an endpoint in a REST API should be self-explanatory. Just by looking at the URL, you( and the server) should be able to tell what is the resource it handles without needing a manual.&lt;/p&gt;
&lt;p&gt;One ot the things REST is trying to do, is to remove the uglyness we all dealt with in SOAP. SOAP exposed a set of operations in a WSDL file, and to &#39;call&#39; those operations we made a single type of call(POST or GET). REST is trying to get rid of that paradigm and get us closer to the underlying protocol.&lt;/p&gt;
&lt;p&gt;Luckily, if you are nostalgic(or not) we have tools like &lt;a href=&quot;https://swagger.io/&quot;&gt;Swagger&lt;/a&gt; and the Open API standard to help us with the documentation, but even so, and URL should express intent in the &#39;eyes&#39; of the consumer.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;COOL URI&#39;s don&#39;t change.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&#39;s look at the 2 endpoints that you will need in a REST API.&lt;/p&gt;
&lt;h3 id=&quot;an-endpoint-for-the-collection&quot; tabindex=&quot;-1&quot;&gt;An endpoint for the collection &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/naming-your-rest-endpoints/#an-endpoint-for-the-collection&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/api/cats&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will allow you 2 operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;retrieve the collection by HTTP GET (a read)&lt;/li&gt;
&lt;li&gt;Create a new resource by using HTTP POST (a create)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;an-endpoint-for-a-specific-item&quot; tabindex=&quot;-1&quot;&gt;An endpoint for a specific item &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/naming-your-rest-endpoints/#an-endpoint-for-a-specific-item&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;/api/pets/{id}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;This will allow you 4 operations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;retrieve the item by using HTTP GET(a read)&lt;/li&gt;
&lt;li&gt;update an existing resource by using HTTP PUT(an update) or HTTP PATCH&lt;/li&gt;
&lt;li&gt;delete and existing resource by using HTTP DELETE(a delete)&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;related-hierarchical-resources&quot; tabindex=&quot;-1&quot;&gt;Related/hierarchical resources &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/naming-your-rest-endpoints/#related-hierarchical-resources&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Any hierarchical resource can be represented further in the URL path, but the convention remains, and applies down in the hierarchy.&lt;/p&gt;
&lt;p&gt;Let&#39;s take the example of wanting to represent the owners of a specific cat:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;/api/cats/{id}/owners&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Looking at this URL we realize that in the cats collection, we are interested in the specific {id} of the cats, and from this specific cat, we want the owners collection.&lt;/p&gt;
&lt;p&gt;When we obtain the list of owners, we might be interest to obtain a specific one, and we can take another step : &lt;code&gt;/api/cats/{id}/owners/{ownerid}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;You are not forced to model your data and store it in the database according to the endpoints, or the other way a round. Remember, in REST, you work with representations of resources, and those representations are handled by you. At the server level, for more decoupling and simplicity, you should use DTOs to transform or aggregate data.&lt;/p&gt;
&lt;h3 id=&quot;takeaways&quot; tabindex=&quot;-1&quot;&gt;Takeaways &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/naming-your-rest-endpoints/#takeaways&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Name your endpoints in such a way to have meaning for the business and your consumers&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You don&#39;t need a different endpoint to represent a new operation. You can give meaning to your request by using the right HTTP verb&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;You can use GET, POST to the same endpoint /api/cats : the end result will be different.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;you need two endpoints to represent major operations on a resource&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Variations of specific resources can be represented as parameters in the query string, not different endpoints.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In conclusion, it is up to you how you shape your endpoints. There is no constraint in this. As long as it makes sense for the business, expresses the intent, and is specific enough.&lt;/p&gt;
&lt;p&gt;The meaning for each request/response should be added by using the right HTTP Verbs, headers and &lt;a href=&quot;https://irina.codes/status-codes-for-rest-apis/&quot;&gt;status codes.&lt;/a&gt;&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>.NET Core with NGINX on Linux</title>
			<link href="https://irina.codes/net-core-with-nginx-on-linux/"/>
			<updated>2020-10-07T00:00:00Z</updated>
			<id>https://irina.codes/net-core-with-nginx-on-linux/</id>
			<content type="html">&lt;p&gt;Having .NET Core with NGINX on Linux is easier that you might imagine. In this article I will talk about my experience related to NGINX and what it takes to configure it for the first time. If you come from an IIS/Windows world like me, where you know everything by heart, the declarative approach in NGINX might be a bit odd.&lt;/p&gt;
&lt;h3 id=&quot;i-already-have-kestrel-why-do-i-need-something-else&quot; tabindex=&quot;-1&quot;&gt;I already have Kestrel, why do I need something else? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/net-core-with-nginx-on-linux/#i-already-have-kestrel-why-do-i-need-something-else&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Kestrel is the default server implementation for ASP.NET Core, and it is super fast, cross-platform, customizable and it can run on its own. It looks like it is the perfect server, but it was lacking a lot of features related to security. Another thing about Kestrel is that &amp;quot;u&lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel?view=aspnetcore-3.1&amp;amp;WT.mc_id=DOP-MVP-5003404&quot;&gt;sed as an edge server without a reverse proxy server doesn&#39;t support sharing the same IP and port among multiple processes&amp;quot;.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Although is not required, it is recommend to have a reverse proxy in front of Kestrel because it will give you an extra layer of configuration and defense and integrates smoothly with the existing infrastructure.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;A proxy server is a go‑between or intermediary server that forwards requests for content from multiple clients to different servers across the Internet. A &lt;strong&gt;reverse proxy server&lt;/strong&gt; is a type of proxy server that typically sits behind the firewall in a private network and directs client requests to the appropriate backend server. A reverse proxy provides an additional level of abstraction and control to ensure the smooth flow of network traffic between clients and servers.&lt;/p&gt;
&lt;p&gt;https://www.nginx.com/resources/glossary/reverse-proxy-server/&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;what-is-nginx&quot; tabindex=&quot;-1&quot;&gt;What is NGINX? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/net-core-with-nginx-on-linux/#what-is-nginx&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://www.nginx.com/resources/glossary/nginx/&quot;&gt;NGINX&lt;/a&gt; is an open source software for web serving, load balancing, media streaming, that also has reverse proxy capabilities. One of the goals behind NGINX was to create the fastest web server out there. Now, is one of the most popular servers &lt;a href=&quot;https://w3techs.com/technologies/cross/web_server/ranking&quot;&gt;out there.&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;installing-nginx-on-linux&quot; tabindex=&quot;-1&quot;&gt;Installing NGINX on Linux &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/net-core-with-nginx-on-linux/#installing-nginx-on-linux&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Depending on your Linux distribution the package manager might differ. I had Red Hat &lt;code&gt;sudo yum install nginx&lt;/code&gt; did the trick. After a successful installation you will find the files and folders for NGINX under &lt;code&gt;/etc/nginx&lt;/code&gt; path on the server looking pretty much like this( without the highlighted folders)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/net-core-with-nginx-on-linux/images/image.png&quot; alt=&quot;nginx structure&quot; /&gt;&lt;/p&gt;
&lt;p&gt;nginx structure&lt;/p&gt;
&lt;p&gt;One of the most important files out there is the nginx.conf file that will contain or reference other configurations for your web apps. Bear in mind that, in order to be able to start NGINX server this configuration will have to be valid.&lt;/p&gt;
&lt;h3 id=&quot;configuring-nginx-for-all-your-apps&quot; tabindex=&quot;-1&quot;&gt;Configuring NGINX for all your apps &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/net-core-with-nginx-on-linux/#configuring-nginx-for-all-your-apps&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now, I’ve cleaned up a bit the default nginx.conf and it looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-nginx&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nginx&quot;&gt;&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;user&lt;/span&gt; nginx&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;worker_processes&lt;/span&gt; auto&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;error_log&lt;/span&gt; /var/log/nginx/error.log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pid&lt;/span&gt; /run/nginx.pid&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.&lt;/span&gt;
&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; /usr/share/nginx/modules/*.conf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;events&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;worker_connections&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1024&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;http&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;log_format&lt;/span&gt; main &lt;span class=&quot;token string&quot;&gt;&#39;&lt;span class=&quot;token variable&quot;&gt;$remote_addr&lt;/span&gt; - &lt;span class=&quot;token variable&quot;&gt;$remote_user&lt;/span&gt; [&lt;span class=&quot;token variable&quot;&gt;$time_local]&lt;/span&gt; &quot;&lt;span class=&quot;token variable&quot;&gt;$request&lt;/span&gt;&quot; &#39;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;&lt;span class=&quot;token variable&quot;&gt;$status&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$body_bytes_sent&lt;/span&gt; &quot;&lt;span class=&quot;token variable&quot;&gt;$http_referer&lt;/span&gt;&quot; &#39;&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&#39;&quot;&lt;span class=&quot;token variable&quot;&gt;$http_user_agent&lt;/span&gt;&quot; &quot;&lt;span class=&quot;token variable&quot;&gt;$http_x_forwarded_for&lt;/span&gt;&quot;&#39;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;access_log&lt;/span&gt; /var/log/nginx/access.log main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;sendfile&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;on&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;tcp_nopush&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;on&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;tcp_nodelay&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;on&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;keepalive_timeout&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;65&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;types_hash_max_size&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2048&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; /etc/nginx/mime.types&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;default_type&lt;/span&gt; application/octet-stream&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# Load modular configuration files from the /etc/nginx/conf.d directory.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# See http://nginx.org/en/docs/ngx_core_module.html#include&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;# for more information.&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; /etc/nginx/conf.d/*.conf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;include&lt;/span&gt; /etc/nginx/sites-enabled/*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For easier formatting, I used Visual Studio Code with 2 extensions: &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=william-voyek.vscode-nginx&quot;&gt;Nginx configuration&lt;/a&gt; and &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=raynigon.nginx-formatter&quot;&gt;Nginx Formatter.&lt;/a&gt;&lt;br /&gt;
At line 28, I’ve added an include statement because I want to place all my configurations for different website in separate files, in sites_enabled folder.&lt;/p&gt;
&lt;p&gt;After that, I’ve created the sites_enabled folder, and the certs folder (that will contain certificates). In the sites_enabled folder, I’ve created a file named myapi.conf and inside it, I’ve added this:&lt;/p&gt;
&lt;pre class=&quot;language-nginx&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-nginx&quot;&gt;&lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;server_name&lt;/span&gt; myserver&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;root&lt;/span&gt; /var/opt/myapps/ui&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;index&lt;/span&gt; index.html index.htm index.nginx-debian.html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;location&lt;/span&gt; /&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try_files&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$uri&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$uri&lt;/span&gt;/ /index.html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_read_timeout&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_connect_timeout&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;75s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;location&lt;/span&gt; /api&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_pass&lt;/span&gt; http://localhost:4906&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_http_version&lt;/span&gt; 1.1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; Upgrade &lt;span class=&quot;token variable&quot;&gt;$http_upgrade&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; Connection keep-alive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; Host &lt;span class=&quot;token variable&quot;&gt;$host&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_cache_bypass&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;$http_upgrade&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; X-Forwarded-For &lt;span class=&quot;token variable&quot;&gt;$proxy_add_x_forwarded_for&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_set_header&lt;/span&gt; X-Forwarded-Proto &lt;span class=&quot;token variable&quot;&gt;$scheme&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_read_timeout&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;300s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token directive&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;proxy_connect_timeout&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;75s&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the configuration file I have 2 &#39;location&#39; sections, one for an UI app, and one for the API. The two apps I want to be served from the same &#39;domain&#39; but on a different path:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;the ui app from the root like /myapp&lt;/li&gt;
&lt;li&gt;the api from something deeper in the hierarchy like: /myapp/api&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once this is done, you can test the nginx configuration by running &lt;code&gt;nginx -t -c /etc/nginx/nginx.conf&lt;/code&gt; in the terminal. If that is successful, you can go ahead, start NGINX server&lt;/p&gt;
&lt;h3 id=&quot;useful-nginx-commands&quot; tabindex=&quot;-1&quot;&gt;Useful NGINX commands &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/net-core-with-nginx-on-linux/#useful-nginx-commands&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;sudo nginx service start&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo nginx service restart&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;sudo nginx service status&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;in-summary&quot; tabindex=&quot;-1&quot;&gt;In summary &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/net-core-with-nginx-on-linux/#in-summary&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We looked at how to configure .NET Core with NGINX on Linux, but the same configs will work on Windows also. The only thing you need to do besides configure NGINX is to &lt;a href=&quot;https://irina.codes/net-api-as-a-linux-service/&quot;&gt;run the API as a Linux Service&lt;/a&gt;, and to make sure is up.&lt;/p&gt;
&lt;p&gt;In a following post we will add https in front of our API, because we need to care about security. :)&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>.NET API as a Linux Service</title>
			<link href="https://irina.codes/net-api-as-a-linux-service/"/>
			<updated>2020-10-06T00:00:00Z</updated>
			<id>https://irina.codes/net-api-as-a-linux-service/</id>
			<content type="html">&lt;p&gt;.NET API as a Linux Service is just one of the many options when it comes to hosting.&lt;/p&gt;
&lt;p&gt;One thing I love about .NET Core is that it runs everywhere. Running .NET APIs as Linux services, allows different organizations to reduce some costs by maybe reusing infrastructure. Before .NET Core, .NET was almost synonymous with Windows. The only server for .NET was IIS.&lt;/p&gt;
&lt;p&gt;Now you have a ton of options when it comes to hosting your .NET applications.&lt;br /&gt;
You want to keep running it on Windows? You can host and install it as a Windows Service with &lt;a href=&quot;https://nssm.cc/&quot;&gt;NSSM&lt;/a&gt;. Do you want Kestrel instead of IIS Express? No worries, you can do that. Nowadays, sky is the limit when it comes to hosting a .NET API.&lt;br /&gt;
One thing you will need to do is to install .NET on the server. You can read &lt;a href=&quot;https://docs.microsoft.com/en-us/dotnet/core/install/linux?WT.mc_id=DOP-MVP-5003404&quot;&gt;here&lt;/a&gt; how to install .NET on different Linux distributions. (If you never did this or you are not an experienced Linux user).&lt;/p&gt;
&lt;h3 id=&quot;what-you-need&quot; tabindex=&quot;-1&quot;&gt;What you need &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/net-api-as-a-linux-service/#what-you-need&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;- Putty or a way to connect to your Linux server&lt;br /&gt;
- sudo rights or execution rights on the server&lt;/p&gt;
&lt;h2 id=&quot;running-an-api-as-a-linux-service&quot; tabindex=&quot;-1&quot;&gt;Running an API as a Linux Service &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/net-api-as-a-linux-service/#running-an-api-as-a-linux-service&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you have your API in good standing, &lt;a href=&quot;https://irina.codes/versioning-rest-apis/&quot;&gt;versioned,&lt;/a&gt; and with t&lt;a href=&quot;https://irina.codes/status-codes-for-rest-apis/&quot;&gt;he right status codes&lt;/a&gt; and you chose Kestrel, the only thing you need to do is to create a .service file and to put it on your Linux server. In my case, the API was hosted on a RedHat distribution.&lt;br /&gt;
The service content will be a file with the .service extension (let&#39;s call the file myapi.service), placed under &lt;code&gt;/etc/systemd/system&lt;/code&gt; path.&lt;/p&gt;
&lt;p&gt;The .service file will contain info related to what it will need to run and from where, and a few flags.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Unit]
Description=Awesome API
[Service]
WorkingDirectory=/var/opt/myapps/myapi
ExecStart=/usr/bin/dotnet /var/opt/myapps/myapi/MyApi.dll
Restart=always
RestartSec=10
SyslogIdentifier=myapi
User=root
Environment=ASPNETCORE_ENVIRONMENT=Development
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
[Install]
WantedBy=multi-user.target
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Assuming you have sudo rights, you will need to start the service by running &lt;code&gt;systemctl start myapi.service&lt;/code&gt;&lt;/p&gt;
&lt;h3 id=&quot;useful-linux-commands&quot; tabindex=&quot;-1&quot;&gt;Useful linux commands &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/net-api-as-a-linux-service/#useful-linux-commands&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;systemctl stop myapi.service&lt;/code&gt; stops your service&lt;/p&gt;
&lt;p&gt;&lt;code&gt;systemctl start myapi.service&lt;/code&gt; starts your service&lt;/p&gt;
&lt;p&gt;&lt;code&gt;systemctl status myapi.service&lt;/code&gt; returns the status of your service. Is useful when you want to make sure the service is running or for debugging purposes. The response will contain some aditional info, depending on what you have configure, but it will look pretty much like this:&lt;img src=&quot;https://irina.codes/net-api-as-a-linux-service/images/thumbnail_image001.png&quot; alt=&quot;status response&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Once you have your NET API as a Linux Service up and running, you might need to make it run on HTTPS.&lt;br /&gt;
You&#39;ll see in another article how you can configure NGINX Reverse proxy to have Https.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Status Codes for REST APIs</title>
			<link href="https://irina.codes/status-codes-for-rest-apis/"/>
			<updated>2020-07-16T00:00:00Z</updated>
			<id>https://irina.codes/status-codes-for-rest-apis/</id>
			<content type="html">&lt;p&gt;When it comes to Status Codes for REST APIs, we are all in a hurry to deliver functionality and we often ignore them. Come as it may. We use what we get. And that&#39;s it.&lt;/p&gt;
&lt;p&gt;REST as a concept should also be about meaning, longevity and empathy towards our API consumers. We should be able to look at a request/response in isolation and understand what is wrong or what happened.&lt;/p&gt;
&lt;p&gt;Choosing Status Codes for REST APIs sometimes is not easy, but at least we should give this a thought.&lt;/p&gt;
&lt;p&gt;In this article, I will talk about Status Codes for REST APIs: the most common and the ignored ones, and a few usage scenarios.&lt;/p&gt;
&lt;p&gt;We all know that we have 5 categories when it comes to status codes. And depending on the response category we should be able to tell what happened with that request. Was the request bad? There is an error processing it?&lt;/p&gt;
&lt;h4 id=&quot;status-codes-categories&quot; tabindex=&quot;-1&quot;&gt;Status Codes - Categories &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/status-codes-for-rest-apis/#status-codes-categories&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;1xx - Informational&lt;/li&gt;
&lt;li&gt;2xx - Success&lt;/li&gt;
&lt;li&gt;3xx - Redirect&lt;/li&gt;
&lt;li&gt;4xx - Client Error&lt;/li&gt;
&lt;li&gt;5xx - Server error&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&#39;ve seen APIs where the status code was 200 OK, but the response body looked like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/status-codes-for-rest-apis/images/image.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;In this case, me or my app, as an API consumer...what should I understand? Was my request successfully processed, or.... am I unauthorized? #confusing. So don&#39;t do this in your APIs.&lt;/p&gt;
&lt;h2 id=&quot;2xx-sucess-status-codes&quot; tabindex=&quot;-1&quot;&gt;2xx - Sucess status codes &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/status-codes-for-rest-apis/#2xx-sucess-status-codes&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;These are success status codes because they start with 2, but in particular, that code should express exactly what happened. You shouldn&#39;t return 200 OK for all the scenarios and then write an error in the body. (if you want to add meaning and have a well-rounded API.)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;200 OK&lt;/strong&gt;&lt;br /&gt;
When the response what processed with success, and usually has a response body.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;201 Created&lt;/strong&gt; - As a response for a POST request that has the effect of creating a new resource. This should be accompanied by a Location header indicating the location for the newly created resource&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;202 Accepted&lt;/strong&gt; - For scenarios where there is no need for a response body. e.g for queuing something for later processing.&lt;br /&gt;
Also, this works very fine with the Location header, for polling scenarios, where you call the endpoint from time to time to see the status of a resource processing&lt;br /&gt;
&lt;strong&gt;204 No Content&lt;/strong&gt; - usually as a response for a DELETE request&lt;/p&gt;
&lt;h2 id=&quot;4xx-client-errors&quot; tabindex=&quot;-1&quot;&gt;4xx - Client errors &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/status-codes-for-rest-apis/#4xx-client-errors&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;400 - Bad Request&lt;/strong&gt;&lt;br /&gt;
Bad Request is in my opinion, one of the most miss-used status codes. I&#39;ve seen it returned when due to some business logic the request couldn&#39;t be processed, but the request was well formed.&lt;br /&gt;
Think about it - when the request is well-formed, it has the parameters, the body and everything all good, but is a business rule on the backend that prevents that request from being processed....is not a Bad Request. Is anything but a Bad Request.&lt;br /&gt;
When you see a Bad Request, the tendency is to look at the content of the request to see what is wrong, fix that, and issue the request again.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Dummy Scenario&lt;/strong&gt;: Online Store. You can&#39;t create 2 products with the same name and brand.&lt;br /&gt;
&lt;strong&gt;401 - Unauthorized&lt;/strong&gt;, &lt;strong&gt;403 - Forbidden&lt;/strong&gt;&lt;br /&gt;
&lt;strong&gt;404 - Not Found&lt;/strong&gt; - resource is not there, or maybe it was deleted.&lt;br /&gt;
&lt;strong&gt;406 - Not acceptable&lt;/strong&gt; - when the server can&#39;t return a response that matches the Accept header of the request. To force .NET to take the Accept header into consideration you will need to set a flag to true:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt; services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;setupAction &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
 &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    setupAction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ReturnHttpNotAcceptable &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;409 - Conflict&lt;/strong&gt; - When there is a business rule that prevents you from creating that resource as a result of a POST. Another scenario can be with using PUT to update a specific resource, and due to its state, you can&#39;t.&lt;br /&gt;
For this, the RFCs mention the fact the PUT requests might have versions and updating one might give you conflict.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;410 - Gone&lt;/strong&gt; - used to mark something as not available anymore; like promotions...or out of stocks..maybe... or simply to show a soft delete&lt;br /&gt;
&lt;strong&gt;415 - Unsupported Media-Type&lt;/strong&gt;; When the server can&#39;t understand the body of the request, due to a wrong Content-Type header, or limited capabilities on the server.&lt;br /&gt;
For example, if you try to send an XML in the request but the server doesn&#39;t know how to read XML, you should get this status code.&lt;br /&gt;
&lt;strong&gt;422 - Unprocessable Entity -&lt;/strong&gt; the server understands the request, the request is well-formed but due to some rules, it can&#39;t be processed. It can be used instead of 409 - Conflict and also covers a lot of ground. You can configure this in &lt;code&gt;Startup.cs&lt;/code&gt; by leveraging &lt;code&gt;ApiBehaviourOptions&lt;/code&gt; like this:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;           services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ConfigureApiBehaviorOptions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;setupAction &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                setupAction&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;InvalidModelStateResponseFactory &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; problemDetails &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;ValidationProblemDetails&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ModelState&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        Type &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://yourApi.com/modelvalidationproblem&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        Title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;One or more model validation errors occurred.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        Status &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; StatusCodes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Status422UnprocessableEntity&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        Detail &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;See the errors property for details.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                        Instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;HttpContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Request&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Path
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

                    problemDetails&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Extensions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;traceId&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; context&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;HttpContext&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;TraceIdentifier&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

                    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;UnprocessableEntityObjectResult&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;problemDetails&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                        ContentTypes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;application/problem+json&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, you can hook yourself to the ModelState and treat validation errors as 422 status codes instead of Bad requests if you need that. You can find more examples &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/web-api/handle-errors?view=aspnetcore-3.1&quot;&gt;in the docs&lt;/a&gt;. As a result, ASP.NET higher than 2.1 generates &lt;a href=&quot;https://tools.ietf.org/html/rfc7807&quot;&gt;RFC-7807 compliant responses&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As a side note, if you configure ApiBehaviour to return BadRequest for model validation, you won&#39;t need to scatter all over your controllers these lines of code:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;if (ModelState.IsValid) 
 {
     return BadRequest(ModelState);
 }
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/status-codes-for-rest-apis/#conclusion&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Even though the most common status codes that might fit a lot of scenarios, we should bear in mind that there are around &lt;strong&gt;63 of them!!&lt;/strong&gt; Why? For us to use!&lt;br /&gt;
If we don&#39;t use &amp;quot;one size fits all&amp;quot; in terms of the technology we choose, why do we apply this on status codes?&lt;br /&gt;
If don&#39;t think &lt;a href=&quot;https://irina.codes/versioning-rest-apis/&quot;&gt;about versioning&lt;/a&gt;, or headers, or HATEOAS in our APIs, at least we should have the right status codes.(and verbs, and naming :) )&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Extending EF Core DbContext</title>
			<link href="https://irina.codes/extending-ef-core-dbcontext/"/>
			<updated>2020-05-29T00:00:00Z</updated>
			<id>https://irina.codes/extending-ef-core-dbcontext/</id>
			<content type="html">&lt;p&gt;Entity Framework allows you to extend the DbContext class to add your own entities, extend existing entities, and customize them at runtime. All of these in a clean and simple manner, if you are willing to do so. It doesn&#39;t even matter if those entities don&#39;t have a table representation. You can do that by leveraging C# partial classes and methods. Extending EF Core DbContext is a common scenario, but I think we&#39;re often not aware of its real power.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/extending-ef-core-dbcontext/images/image-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;The way I like to organize entities in my projects&lt;/p&gt;
&lt;h2 id=&quot;introducing-onmodelcreatingpartial&quot; tabindex=&quot;-1&quot;&gt;Introducing OnModelCreatingPartial &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/extending-ef-core-dbcontext/#introducing-onmodelcreatingpartial&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Commonly your application context will be similar to this: a lot of DbSet&lt;T&gt; that map to the database table object.&lt;/T&gt;&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConferenceContext&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;DbContext&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConferenceContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;DbContextOptions&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;ConferenceContext&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Database&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;EnsureCreated&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;DbSet&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Speaker&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Speakers &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;DbSet&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Talk&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; Talks &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


        &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;OnModelCreating&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModelBuilder&lt;/span&gt; modelBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;OnModelCreatingPartial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;modelBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;OnModelCreatingPartial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModelBuilder&lt;/span&gt; modelBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might notice the OnModelCreatingPartial method in the example above. I&#39;ve noticed this recently after using a database-first approach in a small project. To be honest, it was really an &amp;quot;Aha!&amp;quot; moment, because if you look into DbContext class source, you will not find a reference to it.&lt;/p&gt;
&lt;p&gt;This method gives you a simple and clean way to hook into &lt;code&gt;OnModelCreating&lt;/code&gt; method to add customization for the extra-entities you might need.&lt;br /&gt;
If you are like me you keep an eye on performance or data transfer from the database into the project, you will use raw SQL queries. As a result, you will end up with many classes used only for specific scenarios.&lt;/p&gt;
&lt;p&gt;Before EF Core 3.0 there was another datatype DbQuery that allowed you to specify that the entity T is used only for querying and not for inserts or other operations. &lt;a href=&quot;https://irina.codes/how-to-generate-idempotent-scripts-from-ef-core-migrations/&quot;&gt;Migrations and idempotent scripts&lt;/a&gt; generated are done in the same way, but above EF Core 3.0, everything is a DBSet&lt;T&gt;.&lt;/T&gt;&lt;/p&gt;
&lt;p&gt;It is up to you to tell EF Core that T has no key, like in the example below. You can read more about keyless entity types and the future of this in the &lt;a href=&quot;https://docs.microsoft.com/en-us/ef/core/modeling/keyless-entity-types?tabs=data-annotations&quot;&gt;Microsoft docs.&lt;/a&gt; As you see. extending EF Core DbContext is easy and you can use the approach you are comfortable with either with FluentApi or annotations.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;partial&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConferenceContext&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;DbSet&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TicketSummary&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; TicketSummaries &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;OnModelCreatingPartial&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ModelBuilder&lt;/span&gt; modelBuilder&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            modelBuilder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Entity&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TicketSummary&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;HasNoKey&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
           
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this example, TicketSummary class is used to retrieve results from a raw SQL query. In that query, I make a join and cherry-pick a few columns. That is it. I have no such table called TicketSummary in my database. Instead, I used &lt;a href=&quot;https://docs.microsoft.com/en-us/ef/core/modeling/keyless-entity-types?tabs=fluent-api&quot;&gt;FluentApi&lt;/a&gt; to specify that there is no key for the entity.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Versioning REST APIs</title>
			<link href="https://irina.codes/versioning-rest-apis/"/>
			<updated>2020-04-24T00:00:00Z</updated>
			<id>https://irina.codes/versioning-rest-apis/</id>
			<content type="html">&lt;h2 id=&quot;headers-or-url&quot; tabindex=&quot;-1&quot;&gt;Headers or URL? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/versioning-rest-apis/#headers-or-url&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the context of REST APIs there is a lot of confusion around versioning. Some people recommend versioning REST APIs by adding the version in the URL. Others don’t version at all, and others recommend versioning REST APIs using headers.&lt;br /&gt;
Nothing bad so far, but I’ve personally seen a lot of different APIs, that serve different domains and purposes that have a v1 in the URL. Not that is very bad, but the same APIs reach their end of life, with the same v1 in the URL. It’s hard to believe that was no change there since their inception. If so, why add a version in the first place?&lt;/p&gt;
&lt;p&gt;I think that these APIs end up with having the v1 forever because the consumer apps are also controlled by the same organization or teams.&lt;/p&gt;
&lt;p&gt;This way, the teams just go and change the consumer apps to accommodate the API changes, and that is it. They don’t have to worry about breaking something, somewhere, because they know and have full ownership over those changes.&lt;/p&gt;
&lt;p&gt;But what if you would have and API exposed for consumers and you have no control over the consumer code? Would this change something in terms of having v1 forever embedded in your endpoints? For sure it would! You would be forced somehow not to break your consumers, or constrained by some SLAs or legal stuff to ensure backwards compatibility, and to be very transparent about your API changes.&lt;/p&gt;
&lt;p&gt;More than than, you will need to ensure that nobody gets hurt by calling your API, and you don’t end up answering to support calls from them. This will make you think about designing your API changes carefully.&lt;br /&gt;
Now, let’s see how we can version.&lt;/p&gt;
&lt;h3 id=&quot;the-usual-approach-to-versioning-url-versioning&quot; tabindex=&quot;-1&quot;&gt;The usual approach to versioning( URL versioning) &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/versioning-rest-apis/#the-usual-approach-to-versioning-url-versioning&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The most common approach to versioning is through URLs. You have our endpoints in a form that includes a version like: &lt;code&gt;[https://myapp.com/api/v1/speakers](https://myapp.com/api/v1/resources).&lt;/code&gt;&lt;br /&gt;
In order to call anything from there, you need to add v1 and you are done.&lt;br /&gt;
If you are talking .NET and WEB API, is very easy to do that for every endpoint just by decorating the Controller with that version, and you are all set.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;api/v1/speakers&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The inventor of REST - Roy Fielding said in a tweet that:
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://irina.codes/img/Rf7K8HAPDu-767.avif 767w&quot; /&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://irina.codes/img/Rf7K8HAPDu-767.webp 767w&quot; /&gt;&lt;img alt=&quot;Roy Fielding about v1&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; class=&quot;img-fluid&quot; src=&quot;https://irina.codes/img/Rf7K8HAPDu-767.png&quot; width=&quot;767&quot; height=&quot;265&quot; /&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;!-- ![Roy Fielding about v1](images/v1.png) --&gt;
&lt;p&gt;REST should imply evolvability&lt;/p&gt;
&lt;p&gt;If you correlate this with what Tim Berners-Lee said then you might have no reason to version in URL. Not in the path anyways.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;a href=&quot;https://www.w3.org/Provider/Style/URI.html&quot;&gt;COOL URIs don’t change&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tim Berners-Lee, 1998&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;why-not-change-the-urls&quot; tabindex=&quot;-1&quot;&gt;Why not change the URLS? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/versioning-rest-apis/#why-not-change-the-urls&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Think about it. Sometimes you navigate to websites and you know the URL by heart. URLs matters for SEO, need to be discoverable, express intent and describe the content.&lt;br /&gt;
If you suddenly decide to reorganize a website, and you go around and change everything in terms of URLs there a big chance to lose users. Their bookmarks won’t work anymore, and they won’t be able to find the content they are interested in. That’s when people usually give up and move on and forget about you.&lt;/p&gt;
&lt;p&gt;If you decide(hopefully) to increase the version, you might break all your clients. And remember, you can’t force your clients to move to the new version when you want.&lt;br /&gt;
Surely you can’t say: &lt;strong&gt;“From now on, all my consumers will use v2”&lt;/strong&gt;. It is physically impossible, since you don’t have control over them, and each of them has different priorities.&lt;/p&gt;
&lt;p&gt;Suddenly, taking everyone on board for the new version becomes a long process. You need to ensure backwards compatibility for who knows how long (read it as forever) and documentation.&lt;/p&gt;
&lt;p&gt;More than that, let’s take another scenario. Microservices and data transfer in Microservices with HttpClients. You usually have somewhere the URLs hardcoded, in a JSON or something or even inside libs with that are distributed through some package manager. If you change the URLs, to change the version to v2, you will need to find all those places in your code where you have it ‘hardcoded’. PS: you know you forget all the places.&lt;br /&gt;
Versioning your REST API in URL, to me is not the best idea.&lt;/p&gt;
&lt;h3 id=&quot;versioning-trough-headers&quot; tabindex=&quot;-1&quot;&gt;Versioning trough headers &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/versioning-rest-apis/#versioning-trough-headers&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now I know is not the most common way to do versioning but is my personal preference. Why?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;“Because cool URIs don’t change”&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Adding meaning and some kind of specificity to a request can be done in different ways, why do it through the URL itself. The URL should be self-describing, don’t pollute that with versioning-related pieces.&lt;/p&gt;
&lt;p&gt;In my opinion, as long as that endpoint is very clear in terms of naming – is specific enough, gives a clear understanding of what is the resource it is supposed to handle you are all set.&lt;/p&gt;
&lt;p&gt;You can add meaning to your requests very easily. You can add proper verbs, custom headers, to hydrate the request, to specify intent. Remember that a request should be understood in isolation, and HTTP is stateless.&lt;br /&gt;
This way you help a bit with content negotiation.&lt;br /&gt;
You add &lt;em&gt;Accept&lt;/em&gt; header property to help the server understand what the client needs and expects, &lt;em&gt;Content-Type&lt;/em&gt; to describe the request body, and so on.&lt;/p&gt;
&lt;p&gt;In the same manner, you can add a custom header property that specifies what is the version that the client wants to call.&lt;/p&gt;
&lt;h3 id=&quot;using-a-library&quot; tabindex=&quot;-1&quot;&gt;Using a library &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/versioning-rest-apis/#using-a-library&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Luckily there is an awesome library in .NET called &lt;code&gt;Microsoft.AspNetCore.Mvc.Versioning&lt;/code&gt; that will speed up a lot of manual work. It will give you a lot of flexibility and config options, and your code won&#39;t be a mess.&lt;/p&gt;
&lt;p&gt;You will need to configure it in Startup.cs with the behavior you want.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddApiVersioning&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;o &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; o&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ApiVersionReader &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
               &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;HeaderApiVersionReader&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;api-version&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, you can go ahead and decorate the controllers with the right API version attribute.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;api/speakers&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ApiController&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ApiVersion&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpeakersController&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ControllerBase&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//code removed for brevity&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Using simple attributes, you are now allowed to have 2 controllers side by side, that have the same route. One of them has the previous version, and the second the new version. Using the same URL and without any changes will respond correctly to requests, as long as the correct header is specified in the request.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;Route&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;api/speakers&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ApiController&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ApiVersion&lt;/span&gt;&lt;span class=&quot;token attribute-arguments&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;2.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Speakers2Controller&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ControllerBase&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When you issue a request to the API, you will need to have &lt;strong&gt;api-version&lt;/strong&gt; header with the correct value specified. &lt;code&gt;api-version:1.0 or api-version : 2.0&lt;/code&gt; Based on that, the request will hit a controller on another.&lt;br /&gt;
You are not limited to only this kind of configuration.&lt;br /&gt;
You can assume a default version if this header is not there, or you can enforce a query parameter for the version, configure default behavior and error pages, and so on.&lt;br /&gt;
Everything is configurable inside Startup.cs&lt;/p&gt;
&lt;h3 id=&quot;when-to-increase-the-version&quot; tabindex=&quot;-1&quot;&gt;When to increase the version? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/versioning-rest-apis/#when-to-increase-the-version&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There is no clear answer to this. It depends on the number of changes and their magnitude. Be pragmatic about it, and don&#39;t take it to the extreme. &lt;strong&gt;An API version number is not the build number&lt;/strong&gt;. Don&#39;t do that.&lt;br /&gt;
How often do you increase the version number is a matter of API design first. Ask yourself a few questions:&lt;/p&gt;
&lt;p&gt;How many consumers you have? In what way are these clients affected by your changes? What is the development speed for your consumers? Are there legal/contract matters? (Maybe they don&#39;t have the same agility as you have)&lt;br /&gt;
Is it easy for you to maintain multiple versions of the same API?&lt;/p&gt;
&lt;p&gt;In conclusion&lt;/p&gt;
&lt;p&gt;Versioning REST APIs is not that hard, and REST itself is not bad. There is a certain amount of discipline to do this. You need to design the API with your consumers/clients in mind and take responsibility when you introduce changes.&lt;/p&gt;
&lt;p&gt;If you find this intriguing, ping me on twitter. Let&#39;s chat!&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>View Razor changes without building the project</title>
			<link href="https://irina.codes/view-razor-changes-without-building-the-project/"/>
			<updated>2020-04-05T00:00:00Z</updated>
			<id>https://irina.codes/view-razor-changes-without-building-the-project/</id>
			<content type="html">&lt;p&gt;How to view Razor changes without building the project. There is no need to build the project to see changes in Views.&lt;/p&gt;
&lt;p&gt;When I was a junior developer I remember my team lead telling me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;No, you don&#39;t need to rebuild the solution if you modify something views&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&#39;ve absorbed this phrase and kept telling it to my students. Until recently to realize that is not of actuality anymore. For many years after that, I stopped working in ASP MVC projects, having only APIS, angular, and React client apps.&lt;/p&gt;
&lt;p&gt;Recently I&#39;ve upgraded an MVC project &lt;a href=&quot;https://irina.codes/from-net-2-2-to-3-1-routing-issues/&quot;&gt;from 2.2 to 3.1&lt;/a&gt; and needed to make some changes in Views.&lt;/p&gt;
&lt;p&gt;Nothing bad so far, I still remembered how it&#39;s done. Yey! It is like riding a bike.&lt;br /&gt;
Oddly enough, I had to rebuild the solution to see the changes made in Views and I was really annoyed, I had just static HTML and couldn&#39;t understand why I have to rebuild. Up until this moment, compilation of views was triggered by the editing. I started to dig around and did some research, and here&#39;s what I&#39;ve found out.&lt;/p&gt;
&lt;h2 id=&quot;razor-compilation-is-enabled-by-default-only-for-build-publish&quot; tabindex=&quot;-1&quot;&gt;Razor compilation is enabled by default only for build/publish &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/view-razor-changes-without-building-the-project/#razor-compilation-is-enabled-by-default-only-for-build-publish&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Razor compilation is enabled by default only for the build and publish scenarios, not for runtime aka when you edit the view.&lt;/p&gt;
&lt;p&gt;Now, if you want that functionality back, you need to install a NuGet package: &lt;code&gt;[Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation](https://www.nuget.org/packages/Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation/)&lt;/code&gt;  and add the middleware and boom, it works.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddControllersWithViews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddRazorRuntimeCompilation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;conditional-runtime-compilation&quot; tabindex=&quot;-1&quot;&gt;Conditional runtime compilation &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/view-razor-changes-without-building-the-project/#conditional-runtime-compilation&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another method to obtain the same thing, which I really don&#39;t like is based on directives. This way, you can keep the default mode, but enable what you need only when you are debugging (running locally).&lt;/p&gt;
&lt;p&gt;First, you need to start by editing your csproj file to add a condition on the package.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;PackageReference Include=&amp;quot;Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation&amp;quot;&lt;/code&gt; &lt;br /&gt;
&lt;code&gt;Version=&amp;quot;3.1.3&amp;quot; Condition=&amp;quot;&#39;$(Configuration)&#39; == &#39;Debug&#39;&amp;quot; /&amp;gt;&lt;/code&gt;  &lt;/p&gt;
&lt;p&gt;After that, you need to make some adjustments in your startup file to add the condition. Start by adding a property to hold IWebHostEnvironment, and set the value from the controller.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IConfiguration&lt;/span&gt; configuration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IWebHostEnvironment&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            Configuration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; configuration&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            HostEnvironment &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;IConfiguration&lt;/span&gt; Configuration &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;IWebHostEnvironment&lt;/span&gt; HostEnvironment &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;After you did that you need to add the #if condition to enable the &#39;dynamic&#39; loading for the conditional runtime compilation&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;            &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; builder &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddControllersWithViews&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token preprocessor property&quot;&gt;#&lt;span class=&quot;token directive keyword&quot;&gt;if&lt;/span&gt; DEBUG&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;HostEnvironment&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsDevelopment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                builder&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddRazorRuntimeCompilation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token preprocessor property&quot;&gt;#&lt;span class=&quot;token directive keyword&quot;&gt;endif&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With this in mind, make sure you change the IWebHostEnviroment scattered around your Startup class, and point it to the newly introduced property. Is not the cleanest solution, I do not prefer this, but it does the job. More than that, we all lie to ourselves that, ok, I&#39;m gonna change the default, and just comment the line of code before deploying. Only this will never happen.&lt;/p&gt;
&lt;p&gt;In the official docs, they say that by using the conditional compilation you make sure that the published output uses the compiled views, are smaller in size, and it doesn&#39;t enable file watchers in production. All these might be true, but it doesn&#39;t make my developer experience nicer. What do you think?&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>From .NET 2.2 to 3.1 - Routing issues</title>
			<link href="https://irina.codes/from-net-2-2-to-3-1-routing-issues/"/>
			<updated>2020-03-30T00:00:00Z</updated>
			<id>https://irina.codes/from-net-2-2-to-3-1-routing-issues/</id>
			<content type="html">&lt;p&gt;Having a website on 2.2, I thought that there is about the case to upgrade. Now or never I said. How hard can it be?&lt;/p&gt;
&lt;p&gt;I went pretty smooth, by using the -&amp;gt; migration guide..until.... I&#39;ve bumped into routing. I&#39;ve spent a decent amount of time trying to debug the issue, without visible progress.&lt;/p&gt;
&lt;p&gt;My issue was that my website had a public side and an Admin area where all the administration magic happens after login.&lt;/p&gt;
&lt;p&gt;Changing from &lt;code&gt;app.AddMvc()&lt;/code&gt; to &lt;code&gt;app.UseEndpoints()&lt;/code&gt; is pretty easy just by changing this:&lt;/p&gt;
&lt;p&gt;app.UseMvc(routes =&amp;gt;
     {&lt;/p&gt;
&lt;p&gt;         routes.MapRoute(
             name: &amp;quot;areaRoute year&amp;quot;,
             template: &amp;quot;{area:exists}/{controller}/{action}/{id?}&amp;quot;,
             defaults: new { controller = &amp;quot;Home&amp;quot;, action = &amp;quot;Index&amp;quot; }
             );}
});&lt;/p&gt;
&lt;p&gt;to this:&lt;/p&gt;
&lt;p&gt;app.UseEndpoints(endpoints =&amp;gt;
{
    endpoints.MapControllerRoute(
        name: &amp;quot;areaRoute year&amp;quot;,
        pattern: &amp;quot;{area:exists}/{controller}/{action}/{id?}&amp;quot;,
        defaults: new {controller = &amp;quot;Home&amp;quot;, action = &amp;quot;Index&amp;quot;}
    );
});&lt;/p&gt;
&lt;p&gt;So the real changes in here are the template parameter name to &lt;em&gt;pattern&lt;/em&gt;, and &lt;code&gt;MapRoute&lt;/code&gt; to &lt;code&gt;MapControllerRoute&lt;/code&gt; and the rest is pretty much the same.&lt;/p&gt;
&lt;p&gt;So far so good, but my routes on the Admin area simply didn&#39;t work anymore. Everything on the admin was not accessible, and I got 404 in the browser. This led me to believe that my routes had an issue around authorization. I tried again and failed again, and I gave up.&lt;/p&gt;
&lt;p&gt;Fortunately, we still have the option to ignore for now the UseEndpoints and fallback to the old version by setting a single property to false in ConfigureServices.&lt;br /&gt;
&lt;code&gt;services.AddMvc(options =&amp;gt; options.EnableEndpointRouting = false);&lt;/code&gt;&lt;br /&gt;
Now, by setting this you can use the old approach without any issues, just make sure you don&#39;t add the app.UseRouting() by any means.&lt;/p&gt;
&lt;h2 id=&quot;take-2-try-again&quot; tabindex=&quot;-1&quot;&gt;Take 2 - try again &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/from-net-2-2-to-3-1-routing-issues/#take-2-try-again&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Renamed everything as it should be and looked a bit at the order of Midleware imports.&lt;/p&gt;
&lt;p&gt;I can say that 3.1 middleware order matters even more than before. You must have&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt; app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseRouting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token comment&quot;&gt;//anything in between&lt;/span&gt;
 app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseAuthentication&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseAuthorization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseEndpoints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;endpoints &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;&lt;span class=&quot;token range operator&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My actual issue was that &lt;code&gt;app.UseAuthentication()&lt;/code&gt; and &lt;code&gt;app.UseAuthorization()&lt;/code&gt; order was reversed. I can&#39;t tell you why it worked in 2.2, but for sure doesn&#39;t work anymore in 3.1.&lt;/p&gt;
&lt;p&gt;After this fix, I was able to have a fully working app migrated to 3.1, and now I use endpoints not routes.&lt;/p&gt;
&lt;p&gt;Conclusion&lt;br /&gt;
We knew that order of the middlewares matters, but in this case there is a slight difference. Make sure you have .UseRouting(), any other middleware you need and then UseAuthentication, UseAuthorization, UseEndpoints&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>How to generate idempotent scripts from EF Core migrations</title>
			<link href="https://irina.codes/how-to-generate-idempotent-scripts-from-ef-core-migrations/"/>
			<updated>2020-03-23T00:00:00Z</updated>
			<id>https://irina.codes/how-to-generate-idempotent-scripts-from-ef-core-migrations/</id>
			<content type="html">&lt;p&gt;Is it possible to generate idempotent scripts from EF Core migrations? Automatically? Yes, it is! But first, let&#39;s see what idempotency means.&lt;/p&gt;
&lt;h2 id=&quot;what-is-an-idempotent-script&quot; tabindex=&quot;-1&quot;&gt;What is an idempotent script? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/how-to-generate-idempotent-scripts-from-ef-core-migrations/#what-is-an-idempotent-script&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;A subroutine with side effects is idempotent if the system state remains the same after one or several calls, in other words, if the function from the system state space to itself associated to the subroutine is idempotent in the mathematical sense given in the &lt;a href=&quot;https://en.wikipedia.org/wiki/Idempotence#Definition&quot;&gt;definition&lt;/a&gt;;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Generally speaking, an idempotent script, no matter if it is written in PowerShell, bash or SQL on any other scripting language, will give you the luxury to run it over and over again, without the fear of breaking something. More than that, any script that might fall under unsupervised hands/system should have some &#39;safety&#39; measures built-in.&lt;/p&gt;
&lt;p&gt;Now, we know from HTTP concepts that GET and HEAD requests are idempotent and safe. But POST requests aren&#39;t neither safe nor idempotent.&lt;/p&gt;
&lt;p&gt;The question is why? Well, first of all, by sending a POST request you end up creating new resources, and if you don&#39;t have a business rule to prevent requests with the same request body, you&#39;ll just have a lot of duplicates and nothing stopping you from creating them.&lt;/p&gt;
&lt;p&gt;Why can&#39;t we apply the same rules when writing database scripts?&lt;/p&gt;
&lt;h2 id=&quot;general-idempotency-guidelines-in-database-scripts&quot; tabindex=&quot;-1&quot;&gt;General idempotency guidelines in database scripts &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/how-to-generate-idempotent-scripts-from-ef-core-migrations/#general-idempotency-guidelines-in-database-scripts&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You must write it in such a way that you check at least the most likely scenarios.&lt;br /&gt;
For example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;for a table creation - check first if the table exists, if yes, do nothing&lt;/li&gt;
&lt;li&gt;for deleting a column - check if exists&lt;/li&gt;
&lt;li&gt;for altering a column - check if exists&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So on and so forth, but you get the point. You need to cover all the scenarios that can end up corrupting your database.&lt;/p&gt;
&lt;p&gt;There are several options to manage your database scripts. With so many third-party or out-of-the-box tools, sometimes is difficult to even choose.&lt;br /&gt;
A few years back I remember using PostgreSQL as a database for a project, and I really enjoyed the syntax. We used to manually make idempotent scripts, tested them locally, then added them to a different repository and shipped them to production with &lt;a href=&quot;https://flywaydb.org/&quot;&gt;Flyway&lt;/a&gt;y.&lt;/p&gt;
&lt;h2 id=&quot;generating-idempotent-scripts-from-ef-core-migrations&quot; tabindex=&quot;-1&quot;&gt;Generating idempotent scripts from EF Core migrations &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/how-to-generate-idempotent-scripts-from-ef-core-migrations/#generating-idempotent-scripts-from-ef-core-migrations&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Assuming you already have a &lt;a href=&quot;https://irina.codes/working-with-ef-core-migrations/&quot;&gt;migration&lt;/a&gt; added there are a few commands that are handy and with a few flags&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;generate script in CLI&lt;br /&gt;
&lt;code&gt;dotnet ef migrations script {lastAppliedMigration} --startup-project MyApp --idempotent&lt;/code&gt;&lt;br /&gt;
By running the command you will get as a response the script in the CLI. Make sure you specify the last working migration in the placeholder. Without that, you will obtain the script for all migrations ever applied in the project.&lt;/li&gt;
&lt;li&gt;generate the script in an .sql file under a specific location and in a specific file:&lt;code&gt;dotnet ef migrations script {lastAppliedMigration} --idempotent --output {FolderName}/{sqlscriptname}.sql --startup-project MyApp&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No matter the script you are running you can always add the &lt;code&gt;--verbose&lt;/code&gt; flag to see what is executed with that script.&lt;/p&gt;
&lt;p&gt;Real-life example: generating a script that adds Addresses to Person&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dotnet ef migrations script MadeRequired --idempotent --output Scripts/Addresses.sql --startup-project Persons&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Will generate this:&lt;/p&gt;
&lt;pre class=&quot;language-sql&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-sql&quot;&gt;
&lt;span class=&quot;token keyword&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &#92;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&#92;_&#92;_EFMigrationsHistory&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MigrationId&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; N&lt;span class=&quot;token string&quot;&gt;&#39;20200314111307&#92;_Addresses&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;BEGIN&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;TABLE&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Address&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
         &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Id&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;IDENTITY&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
         &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;StreetName&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; nvarchar&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;max&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
         &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ZipCode&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; nvarchar&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;max&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
         &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;City&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; nvarchar&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;max&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
         &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;PersonId&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;CONSTRAINT&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;PK&#92;_Address&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;PRIMARY&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Id&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;CONSTRAINT&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;FK&#92;_Address&#92;_Persons&#92;_PersonId&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FOREIGN&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;KEY&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;PersonId&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;REFERENCES&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Persons&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Id&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;DELETE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;CASCADE&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 GO
 &lt;span class=&quot;token keyword&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &#92;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&#92;_&#92;_EFMigrationsHistory&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MigrationId&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; N&lt;span class=&quot;token string&quot;&gt;&#39;20200314111307&#92;_Addresses&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;BEGIN&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;CREATE&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INDEX&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;IX&#92;_Address&#92;_PersonId&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ON&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Address&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;PersonId&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 GO
 &lt;span class=&quot;token keyword&quot;&gt;IF&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;NOT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;EXISTS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;SELECT&lt;/span&gt; &#92;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;FROM&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&#92;_&#92;_EFMigrationsHistory&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;WHERE&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MigrationId&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; N&lt;span class=&quot;token string&quot;&gt;&#39;20200314111307&#92;_Addresses&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;BEGIN&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;INSERT&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;INTO&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&#92;_&#92;_EFMigrationsHistory&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;MigrationId&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &#92;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;ProductVersion&#92;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;VALUES&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;N&lt;span class=&quot;token string&quot;&gt;&#39;20200314111307&#92;_Addresses&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; N&lt;span class=&quot;token string&quot;&gt;&#39;3.1.2&#39;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 &lt;span class=&quot;token keyword&quot;&gt;END&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 GO&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, this script relies heavily on the __EFMigrationsHistory table, which is not exactly very...idempotent. So, my recommendation is to have another look at the generated scripts and customize and test them.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/how-to-generate-idempotent-scripts-from-ef-core-migrations/#conclusion&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;By using the right tools and knowing the right flags we can make our lives easier.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Working with EF Core Migrations</title>
			<link href="https://irina.codes/working-with-ef-core-migrations/"/>
			<updated>2020-03-20T00:00:00Z</updated>
			<id>https://irina.codes/working-with-ef-core-migrations/</id>
			<content type="html">&lt;p&gt;How to create migrations in EF Core with SQL Server provider? How to handle migrations with EF Core? Migrations in a different library than your main project? These are questions we will try to respond in this article&lt;/p&gt;
&lt;p&gt;After a few years working with other ORMs it was about time to come back to Entity Framework to see how it evolved.&lt;/p&gt;
&lt;p&gt;The last time I worked with it, it had the .edmx designer. It was fun sometimes, but I remember having projects with huge databases that were a mess when you needed to update something.&lt;/p&gt;
&lt;p&gt;Back then, we didn’t know about tools like &lt;a href=&quot;https://flywaydb.org/&quot;&gt;Flyway&lt;/a&gt; or &lt;a href=&quot;https://dbup.readthedocs.io/en/latest/&quot;&gt;DbUp&lt;/a&gt; . Versioning database scripts or integrating them into the pipelines wasn&#39;t considered. We just used tools to sync everything in place: schema and data.&lt;/p&gt;
&lt;h2 id=&quot;entity-framework-code-first-approach&quot; tabindex=&quot;-1&quot;&gt;Entity Framework Code first approach &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/working-with-ef-core-migrations/#entity-framework-code-first-approach&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The code-first approach is one of the most common scenarios when talking about databases and .NET Core projects. When using this approach, you consider your C# code to be the source of truth. You extend the &lt;code&gt;DbContext&lt;/code&gt; class and after that, you make the changes in the entity classes.&lt;/p&gt;
&lt;p&gt;To be fair, I don&#39;t rely on migrations done from the codebase. I advise you to take control and generate idempotent SQL scripts from these migrations.&lt;/p&gt;
&lt;h3 id=&quot;adding-an-ef-core-migration&quot; tabindex=&quot;-1&quot;&gt;Adding an EF Core migration &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/working-with-ef-core-migrations/#adding-an-ef-core-migration&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We assume we already have a project and we want to add a change in one of the Entities, and we need to add a migration.&lt;br /&gt;
Assuming that you are using the CLI and you have an application with only one project and you are in the root folder hierarchy you will use something like this&lt;/p&gt;
&lt;p&gt;             &lt;code&gt;dotnet ef migrations add {migrationName} --project {projectName}&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Keep in mind that when running a command, you can also use paths. These are relative to the folder you are running them from the CLI. Usually the root of the project, the one with the .sln file.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dotnet ef migrations add {migrationName} --output-dir {../MyApp.Domain/Migrations} --project {projectName}&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&quot;adding-an-ef-core-migration-in-a-different-project-than-your-main-one&quot; tabindex=&quot;-1&quot;&gt; Adding an EF Core migration in a different project than your main one? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/working-with-ef-core-migrations/#adding-an-ef-core-migration-in-a-different-project-than-your-main-one&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Real-life applications are usually split into different projects. We usually want to keep the domain objects separate from the web application. You will have multiple ‘whatever’ layers that will make the separation clearer for the teams.&lt;/p&gt;
&lt;p&gt;Make sure you navigate to your MyApp.Domain CLI and run the commands from there&lt;/p&gt;
&lt;p&gt;&lt;code&gt;dotnet ef migrations add MyMigration --output-dir ../MyApp.Domain/Migrations --startup-project ../MyApp.Web --verbose&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Otherwise, you will end up with a big error like the one below.&lt;/p&gt;
&lt;p&gt;Y_our target project &#39;MyApp.Web&#39; doesn&#39;t match your migrations assembly &#39;MyApp.Domain&#39;. Either change your target project or change your migrations assembly.&lt;br /&gt;
Change your migrations assembly by using DbContextOptionsBuilder. E.g. options.UseSqlServer(connection, b =&amp;gt; b.MigrationsAssembly(&amp;quot;MyApp.Web&amp;quot;)). By default, the migrations assembly is the assembly containing the DbContext.&lt;br /&gt;
Change your target project to the migrations project by using the Package Manager Console&#39;s Default project drop-down list, or by executing &amp;quot;dotnet ef&amp;quot; from the directory containing the migrations project._&lt;/p&gt;
&lt;p&gt;To fix that, you will need to specify in &lt;code&gt;Startup.cs&lt;/code&gt; the assembly for the migrations.&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;AddDbContext&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TechEventContext&#92;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options &#92;&lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      options&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseSqlServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Configuration&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetConnectionString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;DefaultConnection&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          m &#92;&lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MigrationsAssembly&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;MyApp.Domain&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once you did this, you need to run the command again. If successful, you will have the new migration file added to your MyApp.Domain project, under the Migrations folder.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Configuring a custom domain in Azure</title>
			<link href="https://irina.codes/configuring-a-custom-domain-in-azure/"/>
			<updated>2019-11-03T00:00:00Z</updated>
			<id>https://irina.codes/configuring-a-custom-domain-in-azure/</id>
			<content type="html">&lt;p&gt;Configuring a custom domain in Azure very easy, if you know what to configure. There are domain bindings, TXT records, A records, and we don&#39;t deal with them on a daily basis.&lt;/p&gt;
&lt;p&gt;Your AppService plan must be at least B1, to allow custom domains and SSL bindings.&lt;/p&gt;
&lt;p&gt;Once you have your application deployed, you must create a DNS Zone that will allow you to link your existing custom domain to your AppService.&lt;/p&gt;
&lt;p&gt;So in order for your website to work in every situation, with your custom domain, you will need to add 4 record sets:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;an A record type with www as the name and your website assigned IP as a value&lt;/li&gt;
&lt;li&gt;an A record type with @ as name and your website assigned IP as the value(this will be already created, but make sure is there)&lt;/li&gt;
&lt;li&gt;a TXT record type with www as a name and [yourwebsite] .azurewebsites.net as the value&lt;/li&gt;
&lt;li&gt;a TXT record type with @ as a name and [yourwebsite].azurewebsites.net as the value&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;adding-an-a-record&quot; tabindex=&quot;-1&quot;&gt;Adding an A record &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/configuring-a-custom-domain-in-azure/#adding-an-a-record&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/configuring-a-custom-domain-in-azure/images/adding-recordset.png&quot; alt=&quot;adding an A record set&quot; /&gt;&lt;/p&gt;
&lt;h4 id=&quot;adding-a-txt-record-set&quot; tabindex=&quot;-1&quot;&gt;Adding a TXT record set &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/configuring-a-custom-domain-in-azure/#adding-a-txt-record-set&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/configuring-a-custom-domain-in-azure/images/adding-txt-record.png&quot; alt=&quot;adding a TXT record set&quot; /&gt;&lt;/p&gt;
&lt;p&gt;You need to have these 4 records in order to allow your website to be accessible with www, and without it. Like coolwebsite.com vs www.coolwebsite.com.&lt;/p&gt;
&lt;p&gt;In my case, my website couldn&#39;t be accessed from a few corporate networks. Unfortunately, it took a while until I realized what is happening.&lt;/p&gt;
&lt;p&gt;I tend to access websites without using www in front of the domain. It&#39;s easier to write, and Google Chrome has the tendency to suggest pages :)&lt;/p&gt;
&lt;p&gt;There are a few firewalls or corporate networks that will prevent your website from being loaded if the DNS is not resolved for both options, and you might not even know it&lt;/p&gt;
&lt;p&gt;DNS propagation might take a while, but you can use tools like&lt;br /&gt;
&lt;a href=&quot;https://www.whatsmydns.net/&quot;&gt;https://www.whatsmydns.net/&lt;/a&gt; .&lt;/p&gt;
&lt;p&gt;Using this application you can select any record you want to check to make sure it is propagated.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Web API template in Asp Core 3.0</title>
			<link href="https://irina.codes/web-api-template-in-asp-core-3-0/"/>
			<updated>2019-10-01T00:00:00Z</updated>
			<id>https://irina.codes/web-api-template-in-asp-core-3-0/</id>
			<content type="html">&lt;p&gt;Asp Core 3.0 comes with a lot of interesting things in terms of functionality (gRPC, &lt;a href=&quot;https://irinascurtu.net/tech-articles/worker-service-in-net-core-3-0/&quot;&gt;WorkerServices&lt;/a&gt;, Identity, a little Blazor), but also with template changes.&lt;/p&gt;
&lt;p&gt;One of these changes is the template for Web API, which suffered a small and good transformation.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/web-api-template-in-asp-core-3-0/images/image.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;As soon as the project was created…I was surprised to see the WeatherForecast.cs file. I though...”Oh…I might have chosen the wrong project type”.&lt;/p&gt;
&lt;p&gt;But no, .Net Core team decided that there is time to get rid of that ValuesController that had pretty much no significance. Indeed it had ‘REST-ish” structure and it returned some data but that was it.&lt;/p&gt;
&lt;p&gt;The new Web Api project comes with a few changes.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Has a default model named WeatherForecast.cs&lt;/li&gt;
&lt;li&gt;The ValuesController is replaced by WeatherForecastController - to be honest, I never liked the dummy strings returned by GET action, and now at least the values are more human-friendly and in a way, bring you closer to Blazor :)&lt;/li&gt;
&lt;li&gt;A Logger is already injected in the controller, making it more visible at the platform level&lt;/li&gt;
&lt;li&gt;A list of randomized model properties is returned instead of &#39;value1&#39;, &#39;value2&#39; in previous versions&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The new controller has only the action that responds to GET requests and returns an IEnumerable&lt;WeatherForecast&gt;.&lt;/WeatherForecast&gt;&lt;/p&gt;
&lt;p&gt;private static readonly string[] Summaries = new[]
     {
         &amp;quot;Freezing&amp;quot;, &amp;quot;Bracing&amp;quot;, &amp;quot;Chilly&amp;quot;, &amp;quot;Cool&amp;quot;, &amp;quot;Mild&amp;quot;, &amp;quot;Warm&amp;quot;, &amp;quot;Balmy&amp;quot;, &amp;quot;Hot&amp;quot;, &amp;quot;Sweltering&amp;quot;, &amp;quot;Scorching&amp;quot;
     };&lt;/p&gt;
&lt;p&gt;In terms of controller actions:&lt;/p&gt;
&lt;div class=&quot;code-block-with-title&quot;&gt;
  &lt;div class=&quot;code-block-title&quot;&gt;Title&lt;/div&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token attribute&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HttpGet&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
     &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;IEnumerable&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;WeatherForecast&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
     &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
         &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; rng &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Random&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
         &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Enumerable&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Select&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;WeatherForecast&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
             Date &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; DateTime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Now&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddDays&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;index&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
             TemperatureC &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rng&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&#92;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;55&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
             Summary &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Summaries&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;rng&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Next&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Summaries&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Length&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
         &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;In Startup.cs you will see a lot of new things:&lt;/p&gt;
&lt;pre class=&quot;language-csharp&quot; tabindex=&quot;0&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Startup&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Startup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IConfiguration&lt;/span&gt; configuration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          Configuration &#92;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; configuration&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;IConfiguration&lt;/span&gt; Configuration &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// This method gets called by the runtime. Use this method to add services to the container.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ConfigureServices&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IServiceCollection&lt;/span&gt; services&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          services&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;AddControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token comment&quot;&gt;// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Configure&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;IApplicationBuilder&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IWebHostEnvironment&lt;/span&gt; env&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsDevelopment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseDeveloperExceptionPage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

          app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseRouting&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

          app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseAuthorization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

          app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;UseEndpoints&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;endpoints &#92;&lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              endpoints&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;MapControllers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Unlike previous versions, you won&#39;t see the &lt;em&gt;UseMvc()&lt;/em&gt; extension method in&lt;br /&gt;
&lt;em&gt;ConfigureServices&lt;/em&gt; in Startup.cs, which added the conventions necessary for routing( as we were used to from MVC).&lt;br /&gt;
Now this is replaced by a call to &lt;em&gt;AddControllers&lt;/em&gt;(), which as the name implies will add only the controller part needed in a Web Api Project.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are several options in&lt;br /&gt;
MvcServiceCollectionExtensions class: AddMvc(), AddControllersWithViews() or AddRazorPages()&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;em&gt;app.UseEndpoints()&lt;/em&gt; is a new extension of &lt;a href=&quot;https://github.com/aspnet/AspNetCore/blob/master/src/Http/Routing/src/Builder/EndpointRoutingApplicationBuilderExtensions.cs&quot;&gt;IApplicationBuilder&lt;/a&gt; that dispatches requests. More than that, the configuration part of an endpoint has been moved here from &lt;em&gt;app.UseRouting()&lt;/em&gt;.&lt;br /&gt;
The configuration &lt;em&gt;endpoints.MapControllers()&lt;/em&gt;; maps attribute routing controller.&lt;/p&gt;
&lt;p&gt;These are not major changes, and I personally enjoy how the .NET team is doing their best to make everything as configurable and performant as they can.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Worker Service in .Net Core 3.0</title>
			<link href="https://irina.codes/worker-service-in-net-core-3-0/"/>
			<updated>2019-08-01T00:00:00Z</updated>
			<id>https://irina.codes/worker-service-in-net-core-3-0/</id>
			<content type="html">&lt;h4 id=&quot;what-is-a-worker-service-what-is-solves&quot; tabindex=&quot;-1&quot;&gt;What is a Worker Service, what is solves? &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/worker-service-in-net-core-3-0/#what-is-a-worker-service-what-is-solves&quot;&gt;🔗&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Sometimes our business scenarios need long-running tasks that run in the background, to watch something or compute something.&lt;/p&gt;
&lt;p&gt;This kind of scenario was impossible before .Net Core 2.1. A lot of functionalities were &#39;ported&#39; or re-written from the old version, but this wasn&#39;t included.&lt;/p&gt;
&lt;p&gt;In the old .Net full-framework, we use to implement long-running tasks as Windows services. We delivered them in production not without a lot of hassle.&lt;/p&gt;
&lt;p&gt;When .Net Core appeared, it promised to be not only faster, smaller, performant but also cross-platform.&lt;/p&gt;
&lt;p&gt;It opened a whole new world to .Net developers, it allowed us to change hosting environments as we pleased but it didn&#39;t provide us the whole ecosystem that we had in .Net full-framework.&lt;/p&gt;
&lt;p&gt;Building background-services was impossible until a later version of .Net.&lt;/p&gt;
&lt;p&gt;With .Net core 2.1 release, there was an option to have a self-hosting service inside your web app by implementing IHostedService interface. Later on, they introduced the concept of BackgroundService that you could customize.&lt;/p&gt;
&lt;p&gt;Worker Services become a first-class citizen in .Net Core 3.0. Visual Studio 2019 has a template for it. It was bringing back the concept of ‘service’ as we used to know in .Net full-framework.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/worker-service-in-net-core-3-0/images/1.png&quot; alt=&quot;VIsual Studio 2019 Project template&quot; /&gt;&lt;/p&gt;
&lt;p&gt;Visual Studio 2019 Project template&lt;/p&gt;
&lt;p&gt;The generated project is composed of 2 classes and an appsettings.json which is more than enough to get started.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://irina.codes/worker-service-in-net-core-3-0/images/image.png&quot; alt=&quot;Working With Worker Services &quot; /&gt;&lt;/p&gt;
&lt;p&gt;The generated project&lt;/p&gt;
&lt;h3 id=&quot;worker-service-implementation&quot; tabindex=&quot;-1&quot;&gt;Worker Service Implementation &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/worker-service-in-net-core-3-0/#worker-service-implementation&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At its basis, it’s nothing more than a small and smart console app that has a built-in DI container.&lt;/p&gt;
&lt;p&gt;public static IHostBuilder CreateHostBuilder(string[] args) =&amp;gt;
         Host.CreateDefaultBuilder(args)
             .ConfigureServices((hostContext, services) =&amp;gt;
             {
                 services.AddHostedService&amp;lt;Worker&amp;gt;();
             });&lt;/p&gt;
&lt;p&gt;This will allow you to register all the dependencies (repositories, business services, external libs) that will be used in your Worker, just as you do in Web API or MVC.&lt;/p&gt;
&lt;p&gt;The base class &lt;a href=&quot;https://docs.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.2&amp;amp;tabs=visual-studio&quot;&gt;BackgroundService&lt;/a&gt; – was built to provide a way of running long-running tasks in the background under .Net Core. A Worker is exactly that - a long-running, background task.&lt;/p&gt;
&lt;p&gt;The Worker.cs class is everything you need to implement your functionality. You can control what to run and when to run in ExecuteAsync method.&lt;/p&gt;
&lt;p&gt;public class Worker : BackgroundService
    {
        private readonly ILogger&amp;lt;Worker&amp;gt; _logger;&lt;/p&gt;
&lt;p&gt;        public Worker(ILogger&amp;lt;Worker&amp;gt; logger)
        {
            _logger = logger;
        }&lt;/p&gt;
&lt;p&gt;        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            while (!stoppingToken.IsCancellationRequested)
            {
                _logger.LogInformation(&amp;quot;Worker running at: {time}&amp;quot;, DateTimeOffset.Now);
                await Task.Delay(1000, stoppingToken);
            }
        }
    }&lt;/p&gt;
&lt;p&gt;Having the new Worker Service in .Net Core will allow us to cover different business scenarios. Polling a service, running a specific task at a specific time will be very easy.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>GraphQL to the rescue - a short introduction</title>
			<link href="https://irina.codes/graphql-to-the-rescue-a-short-introduction/"/>
			<updated>2019-04-09T00:00:00Z</updated>
			<id>https://irina.codes/graphql-to-the-rescue-a-short-introduction/</id>
			<content type="html">&lt;p&gt;GraphQL is a query language invented by Facebook and exposed to the public in 2015. Is an open specification that allows you to query APIs in a flexible and intuitive way.&lt;/p&gt;
&lt;p&gt;We keep hearing about GraphQL and how it introduces a revolution in the way you are writing apps, and how GraphQL is better than REST or how it is REST 2.0. It’s not about being better, it’s about solving some flaws REST has, and complementing REST.&lt;/p&gt;
&lt;p&gt;Most of us, when we’re talking about APIs, we’re referring to REST APIs, or at least wannabe REST APIs.&lt;/p&gt;
&lt;h2 id=&quot;a-short-summary-of-rest-constraints&quot; tabindex=&quot;-1&quot;&gt;A short summary of REST constraints &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/graphql-to-the-rescue-a-short-introduction/#a-short-summary-of-rest-constraints&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Client – Server&lt;/strong&gt; – it assumes the existence of a client and a server, and the server should not be aware of the client’s existence&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stateless&lt;/strong&gt; – the server keeps no trace, no identifier at all, and if there is a trace, that is contained in the request. The server treats every request as a new one, having no history&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cache&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Caching is one of the most important specification of the &lt;a href=&quot;https://www.ietf.org/rfc/rfc2616.txt&quot;&gt;HTTP RFC&lt;/a&gt;.  With that it can allow a client to make as many requests as needed, but the server will not necessarily make the trip to the database to bring the same data.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Layered system&lt;/strong&gt; – the client doesn’t know if it talks with a proxy or a load balancer, as long as the response is received, and it shouldn’t care&lt;/p&gt;
&lt;p&gt;5. &lt;strong&gt;Uniform resource&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Has 4 sub-constraints&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Identification of resources&lt;/strong&gt; – each resource should be identifiable and addressable&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manipulation of resources through representation&lt;/strong&gt;s – a client will never interact with a database resource, it most likely interact with a representation of a resource. That representation might be a composed representation, or a sub-representation of, for example a database entity&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Self-descriptive messages&lt;/strong&gt; – Each request/response should contain everything necessary so that each party can be able to understand it correctly. It should contain the right HTTP Method (in the intended usage way), the correct Status Code and the right headers (for example Accept, Content-Type, or any custom header)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;HATEOAS&lt;/strong&gt; – hypermedia as engine of application state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Assumes that the next step the clients will be able to perform will be contained in the response as a separate area.&lt;/p&gt;
&lt;p&gt;{
&amp;quot;links&amp;quot;: {
&amp;quot;self&amp;quot;: { &amp;quot;href&amp;quot;: &amp;quot;/speakers&amp;quot; },&lt;br /&gt;
&amp;quot;next&amp;quot;: { &amp;quot;href&amp;quot;: &amp;quot;/speakers?page=2&amp;quot; }&lt;br /&gt;
}
}&lt;/p&gt;
&lt;p&gt;In other words, &lt;a href=&quot;https://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm&quot;&gt;Roy Fielding&lt;/a&gt; argues that a client shouldn’t know anything about the API, except its initial URI and the appropriate media-types and verbs. This way the server will be able to understand what is asked and respond accordingly if the client includes all the necessary info in the request.&lt;/p&gt;
&lt;p&gt;From that point on, all the application state transitions will be driven by the client-selection of the received representations from the server, or from the user’s manipulation of them.&lt;/p&gt;
&lt;p&gt;REST, as an architectural style is very powerful, and it aims to treat everything as a perfectly addressable resource, leveraging the correct response codes, the correct HTTP Verbs and HTTP headers, while maintaining no trace whatsoever about previous request.&lt;/p&gt;
&lt;p&gt;It aims to allow a server and a client to evolve independently and to allow the server to drive the application state by leveraging HATEOAS and hypermedia types.&lt;/p&gt;
&lt;p&gt;Unfortunately, a good number of the APIs out there, completely ignore the HATEOAS part - under the pretext that it adds overhead to each response or that there’s no real benefit in adding it.&lt;/p&gt;
&lt;p&gt;It might be true in some scenarios, but it also brings a lot of power if implemented correctly and it’s up to your design, to do it or not.&lt;/p&gt;
&lt;h3 id=&quot;issues-with-rest-apis&quot; tabindex=&quot;-1&quot;&gt;Issues with REST APIs &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/graphql-to-the-rescue-a-short-introduction/#issues-with-rest-apis&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;REST APIs even with their demonstrated results, tend to have 2 major problems.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Overfetching&lt;/strong&gt; – we struggle to be so compliant to REST and most part of the data that is transferred over the network ends up being ignored and not displayed in the UI, and it gets thrown.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Underfetching&lt;/strong&gt; – comes hand in hand with overfetching, and is happening when the consumer is forced to make another call to an endpoint to bring other data&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some say that overfetching is bad design, or lack of interest, and it can be avoided. It can, but you don’t always have control over the consumer app.&lt;/p&gt;
&lt;p&gt;As soon as you expose your API to the public - to be consumed by third parties, or if the client application is developed somewhere else you pretty much lost the control over your API’s design.&lt;/p&gt;
&lt;p&gt;You won’t be able to tell what data they use from your responses if they use just a field, or two fields, or the entire response.&lt;/p&gt;
&lt;p&gt;While you can do everything to expose a performant API, you can’t customize it for every possible scenario, and for any potential customer or consumer app.&lt;/p&gt;
&lt;h3 id=&quot;graphql-to-the-rescue&quot; tabindex=&quot;-1&quot;&gt;GraphQL to the rescue &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/graphql-to-the-rescue-a-short-introduction/#graphql-to-the-rescue&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;With GraphQL, you’ll be able to model the data you expose in a way that is very similar to a graph. You’ll have nodes of related data, that can be ‘navigated’ by your consumers.&lt;/p&gt;
&lt;p&gt;query	{
talks{
description
title
feedbacks{
comments
delivery
}
speakers{
companyName
position
}
}
}&lt;/p&gt;
&lt;p&gt;This kind of request is very intuitive, and the response to such query will bring over the network only the requested data, therefore giving a lot of flexibility to potential consumers.&lt;/p&gt;
&lt;p&gt;GraphQL allows your consumer to manipulate hierarchical data and to create queries and mutations on the fly, in the way that you define it on the server side.&lt;/p&gt;
&lt;p&gt;One of the scenarios where GraphQL might prove its power is along with a microservices architecture.&lt;/p&gt;
&lt;p&gt;For this, you could create a gateway-like API that will aggregate data from several APIs and, in turn will expose queries and mutations to the client apps.&lt;/p&gt;
&lt;p&gt;This way you can allow your consumers to get only the data they need, but a degree of nesting that you see fit, all with a single call to the server.&lt;/p&gt;
&lt;p&gt;No more subsequent calls to different endpoints, no more endpoint chaining hell, and no more endpoints! You’ll have only one endpoint used by any consumer might be out there.&lt;/p&gt;
&lt;p&gt;Think about this as if you would like to separate the stones from the sand, and you sift the very fine sand.&lt;/p&gt;
&lt;h2 id=&quot;what-can-you-do-with-graphql&quot; tabindex=&quot;-1&quot;&gt;What can you do with GraphQL &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/graphql-to-the-rescue-a-short-introduction/#what-can-you-do-with-graphql&quot;&gt;🔗&lt;/a&gt;&lt;/h2&gt;
&lt;h3 id=&quot;queries-in-graphql&quot; tabindex=&quot;-1&quot;&gt;Queries in GraphQL &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/graphql-to-the-rescue-a-short-introduction/#queries-in-graphql&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A query in GraphQL is pretty much everything that can be ‘questioned’ from the outside, allowing the consumer to specify what data it needs to be returned from the API.&lt;/p&gt;
&lt;p&gt;Queries can be executed in in parallel, on the same call.&lt;/p&gt;
&lt;p&gt;query {
talk(id: 1) {
description
title
speaker{
lastName
}
}&lt;/p&gt;
&lt;h3 id=&quot;mutations-in-graphql&quot; tabindex=&quot;-1&quot;&gt;Mutations in GraphQL &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/graphql-to-the-rescue-a-short-introduction/#mutations-in-graphql&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A mutation in GraphQL is pretty much everything that changes a certain state. A POST, PUT, DELETE request, that modifies something.&lt;/p&gt;
&lt;p&gt;Mutations will be executed sequentially, not in parallel.&lt;/p&gt;
&lt;p&gt;mutation($talk: talkInput!) {
createTalk(talkInput: $talk) {
title
description
speakerId
}
}&lt;/p&gt;
&lt;h3 id=&quot;downsides-of-graphql&quot; tabindex=&quot;-1&quot;&gt;  Downsides of GraphQL &lt;a class=&quot;header-anchor&quot; href=&quot;https://irina.codes/graphql-to-the-rescue-a-short-introduction/#downsides-of-graphql&quot;&gt;🔗&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Although, you can’t say that GraphQL is better than REST - because these are 2 different concepts, for sure you can leverage both to get the most of your APIs, but you need to be aware of the flaws - &lt;strong&gt;Caching&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Caching is one of the most important specification of the HTTP. We use it to improve performance, to save a trip from the web server to the database server, and we use it to trick the user that everything runs very fast.&lt;/p&gt;
&lt;p&gt;When we’re talking about GraphQL, you’ll have a single URL where you get your data from, but that request can be made with different bodies or parameters.&lt;/p&gt;
&lt;p&gt;Facebook started the hype, and now &lt;a href=&quot;https://developer.github.com/v4/&quot;&gt;GitHub announced that the v4 of their API will be fully on GraphQL.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I mean, why would they do that if they can’t cache responses to improved perceived performance?&lt;/p&gt;
&lt;p&gt;Well, they have so many clients, and so many endpoints, that it really makes sense for them to switch to GraphQL and to give their consumers the needed flexibility. In the end, they can’t tell for sure what data they use from the responses.&lt;/p&gt;
&lt;p&gt;Simply because these giant use GraphQL, it doesn’t mean you should use it too.&lt;/p&gt;
&lt;p&gt;You need analyze what are your scenarios, the pro and cons, and to know exactly what’s the end goal for you. You want to just use it, you want the flexibility of it, are you willing to sacrifice caching to flexibility? Don’t just use it without a real reason.&lt;/p&gt;
</content>
		</entry>
		
		<entry>
			<title>Getting started with git</title>
			<link href="https://irina.codes/getting-started-with-git/"/>
			<updated>2019-01-24T00:00:00Z</updated>
			<id>https://irina.codes/getting-started-with-git/</id>
			<content type="html">&lt;p&gt;Sometimes, git usage might be a little bit overwhelming for those that are just starting to learn programming.&lt;/p&gt;
&lt;p&gt;It&#39;s not a bit deal to use git, but I remember that even for me wasn&#39;t so easy. We&#39;re so comfortable with Visual Studio that the first thing that comes in mind is to install a little extension and to use git from there.&lt;/p&gt;
&lt;p&gt;It would surely work, but once we try the command line, we&#39;re not going to use git in visual studio.&lt;/p&gt;
&lt;p&gt;After watching this video you&#39;ll be able to:&lt;br /&gt;
- create a new repository&lt;br /&gt;
- add code&lt;br /&gt;
- commit changes in your repository&lt;br /&gt;
-update existing code&lt;/p&gt;
&lt;p&gt;Here&#39;s a little intro that might prove useful.&lt;/p&gt;
&lt;!-- In any other file (e.g., a blog post): --&gt;
&lt;script src=&quot;https://irina.codes/public/js/lite-yt-embed.js&quot;&gt;&lt;/script&gt;
&lt;link rel=&quot;stylesheet&quot; href=&quot;https://irina.codes/css/lite-yt-embed.css&quot; /&gt;
&lt;lite-youtube videoid=&quot;t_CB1uZtPlA&quot; style=&quot;background-image: url(&#39;https://i.ytimg.com/vi/t_CB1uZtPlA/hqdefault.jpg&#39;);&quot; params=&quot;start=0&quot;&gt;
    &lt;button type=&quot;button&quot; class=&quot;lty-playbtn&quot;&gt;
        &lt;span class=&quot;lyt-visually-hidden&quot;&gt;Play Video: Irina Scurtu - Introduction to Git &lt;/span&gt;
    &lt;/button&gt;
&lt;/lite-youtube&gt;
&lt;p&gt;Enjoy!&lt;/p&gt;
</content>
		</entry>
	
</feed>
