<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Sebastian Nilsson | Tech Leadership Advisor · AI Strategist · Fractional CTO · Renaissance Engineer - Blog</title>
    <link>https://sebnilsson.com</link>
    <description>Tech leadership advisor and AI strategist helping organizations make technology and AI decisions that survive production. Speaker, instructor, and hands-on engineer since 2002.</description>
    <language>en</language>
    <lastBuildDate>Thu, 30 Jul 2026 07:29:00 GMT</lastBuildDate>
    <atom:link href="https://sebnilsson.com/feed.xml" rel="self" type="application/rss+xml"/>
    
    <item>
      <title>C# DateTimeOffset Formats: ISO 8601, RFC 3339, JSON &amp; Unix Time</title>
      <link>https://sebnilsson.com/blog/csharp-datetimeoffset-formats-iso-8601-rfc-3339-json-and-unix-time</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/csharp-datetimeoffset-formats-iso-8601-rfc-3339-json-and-unix-time</guid>
      <description>Format and parse C# DateTimeOffset as ISO 8601, RFC 3339, JSON and Unix time, and see which formats survive the round trip between services.</description>
      <content:encoded><![CDATA[<callout icon="info">
<p><em>This is a modern .NET companion to my original post about <a href="/blog/c-datetime-to-rfc3339-iso-8601">C# DateTime to RFC3339/ISO 8601</a>.</em></p>
</callout>
<p>Date and time become harder to work with as soon as a timestamp leaves your single-language application. In JSON, query strings, headers, logs, and file names, it becomes text or a number that another system has to interpret correctly.</p>
<p>In this article, we'll go through the practical formats for services, TypeScript/JavaScript frontends, logs, and files. For each one, we'll look at how to produce it from a <code>DateTimeOffset</code> and what survives when you parse it back.</p>
<p>In modern .NET code, use <a href="https://learn.microsoft.com/dotnet/api/system.datetimeoffset"><code>DateTimeOffset</code></a> for timestamps. <strong>Unlike <code>DateTime</code>, it stores both the clock time and its <a href="https://en.wikipedia.org/wiki/UTC_offset">UTC offset</a></strong>, which is the information most easily lost at a service boundary.</p>
<p>Every example below formats and parses the same timestamp, a summer afternoon in Central European Summer Time:</p>
<pre><code class="language-csharp">var timestamp = new DateTimeOffset(
    year: 2026, month: 7, day: 14,
    hour: 9, minute: 11, second: 30, millisecond: 123,
    offset: TimeSpan.FromHours(2));
</code></pre>
<h1>DateTimeOffset Formats at a Glance</h1>
<p>This overview shows what each format preserves when parsed back:</p>











































































<table><thead><tr><th>Format</th><th>Example output</th><th>Parses back to <code>DateTimeOffset</code></th></tr></thead><tbody><tr><td>ISO 8601 / RFC 3339 with offset</td><td><code>2026-07-14T09:11:30.123+02:00</code></td><td>Yes, exactly</td></tr><tr><td>RFC 3339 in UTC</td><td><code>2026-07-14T07:11:30.123Z</code></td><td>Yes, as UTC</td></tr><tr><td><code>System.Text.Json</code> default</td><td><code>2026-07-14T09:11:30.123+02:00</code></td><td>Yes, exactly</td></tr><tr><td>.NET round-trip (<code>O</code>)</td><td><code>2026-07-14T09:11:30.1230000+02:00</code></td><td>Yes, exactly</td></tr><tr><td>Unix seconds</td><td><code>1784013090</code></td><td>Yes, as UTC, without ms</td></tr><tr><td>Unix milliseconds</td><td><code>1784013090123</code></td><td>Yes, as UTC</td></tr><tr><td>RFC 1123 (<code>R</code>)</td><td><code>Tue, 14 Jul 2026 07:11:30 GMT</code></td><td>Yes, as UTC, without ms</td></tr><tr><td>Sortable (<code>s</code>)</td><td><code>2026-07-14T09:11:30</code></td><td>No, offset missing</td></tr><tr><td>Universal sortable (<code>u</code>)</td><td><code>2026-07-14 07:11:30Z</code></td><td>Yes, as UTC, without ms</td></tr><tr><td>Compact UTC</td><td><code>20260714T071130.123Z</code></td><td>Yes, as UTC</td></tr><tr><td>Date only</td><td><code>2026-07-14</code></td><td>No, time and offset missing</td></tr><tr><td>Time only</td><td><code>09:11:30.123</code></td><td>No, date and offset missing</td></tr><tr><td>Localized display</td><td><code>Tuesday, July 14, 2026 9:11:30 AM</code></td><td>No, offset and ms missing</td></tr></tbody></table>
<p>One of the first four rows is almost always the right answer for an API. Of the <a href="https://learn.microsoft.com/dotnet/standard/base-types/standard-date-and-time-format-strings">.NET standard specifiers</a>, <code>O</code> preserves the offset and all seven fractional digits, <code>R</code> is useful for HTTP headers, <code>s</code> drops the offset, and <code>u</code> converts to UTC. The remaining formats deliberately discard information, which is useful when you intend it, but it's a bug when you don't.</p>
<p>UTC (Coordinated Universal Time) is the global reference time represented by the zero offset <code>+00:00</code>, often written as <code>Z</code>. <strong>Converting a timestamp to UTC preserves the exact instant and its available fractional precision in one unambiguous value, which makes it a reliable choice for storage and exchange between systems.</strong></p>
<h1>Format a DateTimeOffset as ISO 8601 and RFC 3339</h1>
<p><a href="https://www.iso.org/iso-8601-date-and-time-format.html">ISO 8601</a> is a large standard that covers dates, times, UTC, local time with an offset, durations, intervals, and more. It's broad enough that "we use ISO 8601" isn't really a contract on its own. For timestamps exchanged between applications, <a href="https://www.rfc-editor.org/rfc/rfc3339#section-5.6">RFC 3339</a> defines a much narrower profile of it, and that profile is what most APIs mean when they say ISO 8601.</p>
<p><strong>If you have no other requirement, an RFC 3339 timestamp with an explicit offset is the format to send.</strong> It's unambiguous, and almost every language and framework can parse it. <a href="https://www.rfc-editor.org/rfc/rfc3339#section-5.1">RFC 3339 timestamps also sort chronologically as text</a> when they use the same offset representation and the same number of fractional digits.</p>
<p>Use one format for an explicit offset and another when the contract requires UTC with <code>Z</code>:</p>
<pre><code class="language-csharp">const string offsetFormat = "yyyy-MM-dd'T'HH:mm:ss.fffK";
const string utcFormat = "yyyy-MM-dd'T'HH:mm:ss.fff'Z'";

var withOffset = timestamp.ToString(offsetFormat, CultureInfo.InvariantCulture);
// 2026-07-14T09:11:30.123+02:00

var inUtc = timestamp.ToUniversalTime().ToString(utcFormat, CultureInfo.InvariantCulture);
// 2026-07-14T07:11:30.123Z
</code></pre>
<p>Lowercase <code>fff</code> requires exactly three fractional digits. Use <code>FFFFFFF</code> instead when trailing zeros and the decimal point may be omitted. For a <code>DateTimeOffset</code>, the <a href="https://learn.microsoft.com/dotnet/standard/base-types/custom-date-and-time-format-strings#time-zone-k-format-specifier"><code>K</code> token is equivalent to <code>zzz</code></a>: both write the numeric offset, including <code>+00:00</code> for UTC.</p>
<p><strong>Only append <code>Z</code> after converting the clock time to UTC.</strong> Adding it directly to the example's <code>09:11:30</code> moves the timestamp two hours into the future without raising an error.</p>
<p>The offset format parses directly. Since the <code>Z</code> in <code>utcFormat</code> is a literal, the UTC format needs explicit parsing styles:</p>
<pre><code class="language-csharp">var utcStyles = DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal;

var parsedOffset = DateTimeOffset.ParseExact(
    withOffset, offsetFormat, CultureInfo.InvariantCulture, DateTimeStyles.None);
// 2026-07-14T09:11:30.1230000+02:00

var parsedUtc = DateTimeOffset.ParseExact(
    inUtc, utcFormat, CultureInfo.InvariantCulture, utcStyles);
// 2026-07-14T07:11:30.1230000+00:00
</code></pre>
<p>The offset says how far the clock was from UTC, not which time zone it was in. If the receiver needs the zone itself, send the <a href="https://learn.microsoft.com/dotnet/api/system.timezoneinfo.findsystemtimezonebyid">IANA time zone ID</a> separately. The <a href="https://learn.microsoft.com/dotnet/api/system.globalization.datetimestyles"><code>DateTimeStyles</code> documentation</a> covers the UTC parsing flags used again below.</p>
<h2>In the Basic Format for File Names and Logs</h2>
<p>ISO 8601 also has a basic format without separators, which works where <code>:</code> isn't allowed or is inconvenient, such as file names, log identifiers, cache keys, and exported data. Fixed-width UTC values sort chronologically as plain text, which is often the real reason to use them:</p>
<pre><code class="language-csharp">const string fileFormat = "yyyyMMdd'T'HHmmss.fff'Z'";

var fileTimestamp = timestamp
    .ToUniversalTime()
    .ToString(fileFormat, CultureInfo.InvariantCulture);
// 20260714T071130.123Z

DateTimeOffset.ParseExact(fileTimestamp, fileFormat, CultureInfo.InvariantCulture, utcStyles);
// 2026-07-14T07:11:30.1230000+00:00
</code></pre>
<p><code>DateTimeOffset.Parse</code> doesn't accept this <a href="https://www.iso.org/iso-8601-date-and-time-format.html">basic format</a>, and it has no format parameter. Use <a href="https://learn.microsoft.com/dotnet/api/system.datetimeoffset.parseexact"><code>DateTimeOffset.ParseExact</code></a> with <code>fileFormat</code> and keep the format string next to the code that reads these values back. <strong>Keep the field widths and the UTC suffix consistent if you rely on alphabetical sorting</strong>, since a single variable-width field breaks the ordering for every file in the directory.</p>
<h1>DateTimeOffset in JSON and TypeScript/JavaScript</h1>
<p>When a <code>DateTimeOffset</code> is part of a JSON request or response, you usually shouldn't call <code>ToString</code> at all. <a href="https://learn.microsoft.com/dotnet/standard/datetime/system-text-json-support"><code>System.Text.Json</code></a> reads and writes the extended ISO 8601 profile by default, which is also valid RFC 3339, and it's what ASP.NET Core uses for minimal APIs and controllers out of the box:</p>
<pre><code class="language-csharp">var json = JsonSerializer.Serialize(timestamp);
// "2026-07-14T09:11:30.123+02:00"

var parsed = JsonSerializer.Deserialize&#x3C;DateTimeOffset>(json);
// 2026-07-14T09:11:30.1230000+02:00
</code></pre>
<p>The default output trims trailing zeros, so it writes a timestamp with whole seconds as <code>2026-07-14T09:11:30+02:00</code>. <strong><code>System.Text.Json</code> writes a <code>DateTimeOffset</code> in UTC with <code>+00:00</code>, but a <code>DateTime</code> with <code>DateTimeKind.Utc</code> with <code>Z</code>:</strong></p>
<pre><code class="language-csharp">JsonSerializer.Serialize(timestamp.ToUniversalTime()); // "...T07:11:30.123+00:00"
JsonSerializer.Serialize(timestamp.UtcDateTime);       // "...T07:11:30.123Z"
</code></pre>
<p>Both are correct RFC 3339 and both parse the same way in .NET and in TypeScript/JavaScript. It only becomes a problem when something on the other side compares timestamp strings for equality, or when a test asserts on the exact text.</p>
<p>If the API contract requires UTC with <code>Z</code> and a fixed precision, add a converter once instead of formatting values by hand throughout the application:</p>
<pre><code class="language-csharp">public sealed class UtcDateTimeOffsetJsonConverter : JsonConverter&#x3C;DateTimeOffset>
{
    private const string Format = "yyyy-MM-dd'T'HH:mm:ss.fff'Z'";

    public override DateTimeOffset Read(
        ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        return DateTimeOffset.ParseExact(
            reader.GetString()!,
            Format,
            CultureInfo.InvariantCulture,
            DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal);
    }

    public override void Write(
        Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
    {
        writer.WriteStringValue(
            value.ToUniversalTime().ToString(Format, CultureInfo.InvariantCulture));
    }
}
</code></pre>
<p>Because <code>Read</code> uses <code>ParseExact</code>, this converter accepts only UTC timestamps that end in <code>Z</code> and contain exactly three fractional digits. The default <a href="https://learn.microsoft.com/dotnet/standard/datetime/system-text-json-support"><code>System.Text.Json</code> <code>DateTimeOffset</code> format</a> uses variable fractional precision and writes UTC as <code>+00:00</code>, so every client has to follow this custom contract too.</p>
<p>Register it once with <code>options.Converters.Add(new UtcDateTimeOffsetJsonConverter())</code>. <strong>Let the serializer own the wire format, and let a <a href="https://learn.microsoft.com/dotnet/standard/serialization/system-text-json/converters-how-to">custom converter</a> own any deviation from it.</strong></p>
<h2>Read the Value in TypeScript/JavaScript</h2>
<p>A browser can read either the default JSON string or Unix milliseconds as the same instant:</p>
<pre><code class="language-ts">const fromText = new Date("2026-07-14T09:11:30.123+02:00");
const fromUnix = new Date(1784013090123);

console.log(fromText.toISOString());                    // 2026-07-14T07:11:30.123Z
console.log(fromText.getTime());                        // 1784013090123
console.log(fromText.getTime() === fromUnix.getTime()); // true
</code></pre>
<p>A TypeScript/JavaScript <a href="https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date"><code>Date</code></a> stores milliseconds since the epoch, so it uses <code>+02:00</code> to find the instant and then discards the offset. <code>JSON.stringify</code> writes the result in UTC with <code>Z</code>. <strong>If the frontend needs the sender's original offset, send it separately.</strong></p>
<p>There are three traps worth knowing about at this boundary:</p>
<ul>
<li><strong>A date and time without an offset is read as the browser's local time.</strong> <code>new Date("2026-07-14T09:11:30")</code> means something different for every visitor, which is exactly what the <code>s</code> format produces.</li>
<li><strong>A date-only string is read as UTC instead.</strong> <code>new Date("2026-07-14")</code> is midnight UTC, so the same string is inconsistent with the rule above. This is the classic reason a date shows up as the day before for users west of UTC.</li>
<li><strong>Unix seconds passed straight to <code>new Date</code> land in January 1970.</strong> <code>new Date(1784013090)</code> is <code>1970-01-21T15:33:33.090Z</code>, because the constructor expects milliseconds.</li>
</ul>
<p>The <a href="https://tc39.es/ecma262/#sec-date-time-string-format">ECMAScript Date Time String Format</a> defines exactly three fractional digits. The seven digits produced by <code>O</code> therefore rely on engine-specific fallback parsing. <strong>Keep <code>O</code> for .NET-to-.NET text rather than a browser-facing wire format.</strong></p>
<h1>Convert a DateTimeOffset to and From Unix Time</h1>
<p>Unix time, also called epoch time, counts from <code>1970-01-01T00:00:00Z</code> and is useful when a contract expects a number. <code>DateTimeOffset</code> has <a href="https://learn.microsoft.com/dotnet/api/system.datetimeoffset.tounixtimeseconds">built-in Unix time support</a> in both directions:</p>
<pre><code class="language-csharp">var seconds = timestamp.ToUnixTimeSeconds();           // 1784013090
var milliseconds = timestamp.ToUnixTimeMilliseconds(); // 1784013090123

DateTimeOffset.FromUnixTimeSeconds(seconds);
// 2026-07-14T07:11:30.0000000+00:00

DateTimeOffset.FromUnixTimeMilliseconds(milliseconds);
// 2026-07-14T07:11:30.1230000+00:00
</code></pre>
<p>The original offset is gone when the value comes back as UTC. Seconds discard the fractional second, while milliseconds discard any finer 100-nanosecond ticks. <strong>Agree on the unit, because reading seconds as milliseconds produces a date in January 1970 rather than an error.</strong> JWT claims use <a href="https://www.rfc-editor.org/rfc/rfc7519#section-4.1.4">seconds</a>, while JavaScript <code>Date</code> uses milliseconds.</p>
<h1>Calendar Values and Display</h1>
<p>Sometimes the value is not a timestamp. Use <a href="https://learn.microsoft.com/dotnet/api/system.dateonly"><code>DateOnly</code></a> for birthdays and billing days, and <a href="https://learn.microsoft.com/dotnet/standard/datetime/how-to-use-dateonly-timeonly"><code>TimeOnly</code></a> for opening times and daily alarms:</p>
<pre><code class="language-csharp">var dateAtOffset = DateOnly.FromDateTime(timestamp.DateTime);    // 2026-07-14
var timeAtOffset = TimeOnly.FromDateTime(timestamp.DateTime);    // 09:11:30.123
JsonSerializer.Serialize(dateAtOffset); // "2026-07-14"
JsonSerializer.Serialize(timeAtOffset); // "09:11:30.1230000"
</code></pre>
<p>For display, standard formats such as <code>d</code> and <code>F</code> use the supplied <a href="https://learn.microsoft.com/dotnet/api/system.globalization.cultureinfo"><code>CultureInfo</code></a>:</p>
<pre><code class="language-csharp">var culture = CultureInfo.GetCultureInfo("sv-SE");

timestamp.ToString("d", culture); // 2026-07-14
timestamp.ToString("F", culture); // tisdag 14 juli 2026 09:11:30
</code></pre>
<p><strong>Choose the relevant offset before extracting a calendar value, and keep display text as output only.</strong> A date near midnight may differ between the original offset and UTC. Culture changes the representation, not the time zone, which is a separate conversion using <a href="https://learn.microsoft.com/dotnet/api/system.timezoneinfo"><code>TimeZoneInfo</code></a>. For a browser-side example using older tools, see <a href="/blog/display-local-datetime-with-moment-js-in-asp-net">Display Local DateTime with Moment.js in ASP.NET</a>.</p>
<h1>Parse Input Strings Exactly</h1>
<p>When an API accepts more than one representation, <a href="https://learn.microsoft.com/dotnet/api/system.datetimeoffset.tryparseexact"><code>TryParseExact</code></a> validates the input against an array of formats and rejects everything else:</p>
<pre><code class="language-csharp">string[] acceptedFormats =
{
    "O",
    "yyyy-MM-dd'T'HH:mm:ss.fffK",
    "yyyy-MM-dd'T'HH:mm:ss.FFFFFFFK"
};

if (DateTimeOffset.TryParseExact(
    input, acceptedFormats, CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsed))
{
    // 2026-07-14T09:11:30.1230000+02:00
}
</code></pre>
<p>The more flexible <a href="https://learn.microsoft.com/dotnet/api/system.datetimeoffset.tryparse"><code>DateTimeOffset.TryParse</code></a> handles a much wider range of input, which is convenient until it accepts something you did not intend. <strong>When the input has no offset, <code>TryParse</code> fills in the offset of the machine that runs it</strong>, so the same request parses differently on a developer's laptop and on a server in another region. Pass <code>DateTimeStyles.AssumeUniversal</code> when a missing offset must mean UTC, and use <code>TryParseExact</code> when the accepted formats are part of the contract.</p>
<h1>Summary</h1>
<p>Keep timestamps as <code>DateTimeOffset</code> inside the application and database, and format them only at system boundaries. SQL Server has a native <a href="https://learn.microsoft.com/sql/t-sql/data-types/datetimeoffset-transact-sql"><code>datetimeoffset</code></a> type, while strings lose validation, date arithmetic, and reliable sorting across offsets.</p>
<ul>
<li><strong>Text APIs and frontends</strong>: use the <code>System.Text.Json</code> default, or a documented RFC 3339 contract. Keep <code>O</code> for .NET-to-.NET text that needs all seven fractional digits.</li>
<li><strong>Numeric contracts</strong>: use Unix time and agree on seconds or milliseconds.</li>
<li><strong>Files and display</strong>: use fixed-width UTC for sortable names, and localized text produced from the typed timestamp for people.</li>
</ul>
<p><strong>Pick a format based on the offset and precision it preserves, parse the same format you write, and supply any missing offset explicitly.</strong> A wrong timestamp is still valid, which is why these mistakes rarely raise an error.</p>
<p><em>For the original <code>DateTime</code> and .NET Framework implementation, see the <a href="/blog/c-datetime-to-rfc3339-iso-8601">original article</a>.</em></p>]]></content:encoded>
      <pubDate>Thu, 30 Jul 2026 07:29:00 GMT</pubDate>
      <category>.NET</category>
      <category>C#</category>
      <category>TypeScript</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>Enforce Module Boundaries in TypeScript</title>
      <link>https://sebnilsson.com/blog/enforce-module-boundaries-in-typescript</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/enforce-module-boundaries-in-typescript</guid>
      <description>TypeScript has no way to do internal exports. But there are ways to enforce which exports are part of a module&apos;s public API.</description>
      <content:encoded><![CDATA[<p>When a TypeScript codebase grows, you usually start organizing it into feature folders. Before long, each folder contains both the functions the rest of the application should use and the helpers that only exist to support them.</p>
<p>Clear boundaries help you stay in control, avoid obvious bugs, and make larger changes with more confidence. This matters even more when AI can make broad changes under the hood. You still need to preserve the functionality that the rest of the application relies on.</p>
<p>Although the examples use TypeScript, the same ideas work for JavaScript. Workspaces, package <code>exports</code>, barrel files, and the Biome and ESLint rules are not TypeScript-only. The TypeScript-only parts are project references, declaration output, and type-aware ESLint configuration.</p>
<p>In this article, we'll look at package <code>exports</code> in a monorepo and lint rules for a single codebase.</p>
<h1>Packages And Workspaces</h1>
<p>The usual way to create a stronger boundary is to split a monorepo into packages. Each package has its own <code>package.json</code>, build output, and public entry points. Other packages then depend on it by name instead of reaching into its source tree.</p>
<p><a href="https://pnpm.io/workspaces">pnpm workspaces</a> find and link the packages locally. They do not decide which files are public, though:</p>
<pre><code class="language-yaml"># pnpm-workspace.yaml
packages:
  - apps/*
  - packages/*
</code></pre>
<p>The package's <code>exports</code> map says what other packages are allowed to import:</p>
<pre><code class="language-json">// packages/orders/package.json
{
  "name": "@acme/orders",
  "private": true,
  "types": "./dist/index.d.ts",
  "exports": "./dist/index.js"
}
</code></pre>
<p>The source entry point lists the symbols that belong to that public API:</p>
<pre><code class="language-ts">// packages/orders/src/index.ts
export { createOrder } from "./create-order.js";
export type { Order, OrderTotals } from "./types.js";
</code></pre>
<p>The consuming app can import <code>createOrder</code> from <code>@acme/orders</code>, but this deep import is not part of the public API:</p>
<pre><code class="language-ts">import { calculateTotals } from "@acme/orders/calculate-totals";
</code></pre>
<p>With a module resolution mode that understands package <code>exports</code>, TypeScript rejects the deep import because that subpath was never exported. It's worth noting that a path alias or relative import into another package's <code>src</code> directory can still bypass this, so teams usually lint against cross-package source imports too.</p>
<p>That is the important distinction: <strong>the workspace links packages together, while the <code>exports</code> map controls which package paths consumers can use</strong>. TypeScript project references help with build ordering and keep the projects separate:</p>
<pre><code class="language-json">// packages/orders/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src"]
}
</code></pre>
<p>Project references are TypeScript-specific. You can read more in TypeScript's <a href="https://www.typescriptlang.org/docs/handbook/project-references">project references documentation</a> and Node's <a href="https://nodejs.org/api/packages.html#exports"><code>exports</code> documentation</a>.</p>
<h1>When Everything Is One Codebase</h1>
<p>Packages are useful when a repository already has clear package-sized parts. They are more ceremony than many applications need, though. If everything lives under one <code>src</code> directory, there is no package boundary between <code>orders</code> and <code>checkout</code>:</p>
<pre><code class="language-text">src/
├── features/
│   ├── orders/
│   └── checkout/
└── app/
</code></pre>
<p>An <code>export</code> is still importable from any file that can resolve its path. Path aliases make imports nicer, but they do not make folders private. In a single codebase, you need a convention and a lint rule to stop people bypassing public entry points.</p>
<h1>Barrel Files As A Convention</h1>
<p>A barrel file is usually an <code>index.ts</code> file that re-exports the symbols meant for use outside a folder:</p>
<pre><code class="language-ts">// src/features/orders/index.ts
export { createOrder } from "./create-order";
export { getOrderSummary } from "./order-summary";
</code></pre>
<p>Code elsewhere imports from <code>@/features/orders</code>, while internal helpers stay out of the barrel. Without linting, someone can still import them directly:</p>
<pre><code class="language-ts">import { calculateTotals } from "@/features/orders/calculate-totals";
</code></pre>
<h2>Enforcing Barrel Imports</h2>
<p>ESLint's <a href="https://github.com/import-js/eslint-plugin-import/blob/main/docs/rules/no-internal-modules.md"><code>import/no-internal-modules</code></a> rule can reject submodule imports while still allowing the folder entry point:</p>
<pre><code class="language-json">{
  "rules": {
    "import/no-internal-modules": [
      "error",
      { "forbid": ["@/features/*/*"] }
    ]
  }
}
</code></pre>
<p>The exact pattern depends on your aliases. For larger architectures, <a href="https://github.com/javierbrea/eslint-plugin-boundaries/blob/master/docs/rules/entry-point.md"><code>eslint-plugin-boundaries</code></a> can define which entry points different kinds of folders may use.</p>
<p>Biome does not require barrel imports. Its <a href="https://biomejs.dev/linter/rules/no-barrel-file/"><code>noBarrelFile</code></a> rule does the opposite and reports files that re-export other modules.</p>
<h2>The Limits Of Barrel Files</h2>
<p>Barrel files are useful documentation, but they also have a few problems:</p>
<ul>
<li><strong>They are only a convention without linting.</strong> An <code>index.ts</code> file is not a visibility boundary, so deep imports are still possible.</li>
<li><strong>They can make the dependency graph bigger.</strong> Re-exporting runtime modules can load or analyze more modules than the consumer needs. This is the reason Biome provides <code>noBarrelFile</code>.</li>
<li><strong>They can run top-level side effects.</strong> A runtime re-export adds its target module to the import graph. When the barrel is loaded, that module may be instantiated and evaluated even if the caller needs a different export. Top-level work such as registering a handler, changing global state, reading environment state, or logging can then run unexpectedly. Bundlers may remove unused modules when static analysis and side-effect metadata allow it, but tree-shaking is not a replacement for side-effect-free modules.</li>
<li><strong>They can hide ownership and create cycles.</strong> A large <code>index.ts</code> becomes another API manifest, and importing back through it from an internal file can introduce a cycle.</li>
</ul>
<p>If you want smaller module graphs and clearer ownership, Biome's no-barrel approach is a good alternative.</p>
<h2>No Barrels With Biome</h2>
<p>Enable <code>noBarrelFile</code> together with <code>noPrivateImports</code>:</p>
<pre><code class="language-json">{
  "linter": {
    "rules": {
      "correctness": {
        "noPrivateImports": "error"
      },
      "performance": {
        "noBarrelFile": "error"
      }
    }
  }
}
</code></pre>
<p>Now you can import files directly, while visibility is declared on their exports:</p>
<pre><code class="language-ts">// src/features/orders/create-order.ts

/** @public */
export function createOrder() {
  return calculateTotals();
}

/** @package */
export function calculateTotals() {
  // ...
}
</code></pre>
<p>Biome recognizes <code>@public</code>, <code>@package</code>, and <code>@private</code>, along with the equivalent <code>@access</code> values. Package visibility allows imports from the same folder and its subfolders. Private exports are normally limited to the same module. <code>index.ts</code> and <code>mod.ts</code> files get special handling so their submodules can use private exports.</p>
<p>Code inside <code>src/features/orders</code> can use <code>calculateTotals</code>, while code in <code>src/features/checkout</code> gets a lint error if it imports it.</p>
<p><img src="https://files.sebnilsson.com/web/images/enforce-module-boundaries-in-typescript/concept.png" alt="Enforcing Module Boundaries in TypeScript Concept"></p>
<h1>Flipping The Default Is Not Available</h1>
<p>It would be convenient to make every export package-private and add <code>@public</code> only to the intended API. Biome does not provide that setting. Untagged exports are public, and there is no supported <code>defaultVisibility</code> option.</p>
<p>Mark internal exports with <code>@package</code> or <code>@private</code>, and leave public exports untagged or mark them with <code>@public</code>. <code>noPrivateImports</code> checks static imports, but not dynamic <code>import()</code> or CommonJS <code>require()</code> calls. It also ignores resources and dependencies under <code>node_modules</code>.</p>
<h1>The ESLint Alternative</h1>
<p>With ESLint, <a href="https://github.com/uhyo/eslint-plugin-import-access"><code>eslint-plugin-import-access</code></a> provides a similar JSDoc-based model for typed configurations. It supports <code>@package</code>, <code>@private</code>, and <code>@public</code>, and includes a TypeScript language-service plugin that can keep restricted exports out of auto-completion.</p>
<p>For broader architecture rules, <a href="https://github.com/javierbrea/eslint-plugin-boundaries"><code>eslint-plugin-boundaries</code></a> can classify folders and control which parts of the codebase may import from each other.</p>
<h1>Faster Feedback In VS Code</h1>
<p>The command line and CI remain the final authority, but editor extensions can show boundary violations while you work instead of making you wait for a build or full lint run.</p>
<p>Install the <a href="https://marketplace.visualstudio.com/items?itemName=biomejs.biome">official Biome extension</a> with <code>@biomejs/biome</code>, or the <a href="https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint">Microsoft ESLint extension</a> with the ESLint plugins used by your project:</p>
<pre><code class="language-sh">pnpm add -D @biomejs/biome eslint eslint-plugin-import
</code></pre>
<p>If you use <code>eslint-plugin-import-access</code>, add it with <code>typescript</code> and <code>typescript-eslint</code> as required by your ESLint configuration. The extensions provide inline diagnostics and quick fixes:</p>
<pre><code class="language-jsonc">// .vscode/settings.json
{
  "editor.formatOnSave": true,
  "editor.codeActionsOnSave": {
    "source.fixAll.biome": "explicit",
    "source.fixAll.eslint": "explicit"
  },
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ]
}
</code></pre>
<p>Choose one tool to own formatting and let the other focus on lint diagnostics. <strong>An invalid import shows up in the editor instead of surprising you after the build or CI lint step.</strong></p>
<h1>Summary</h1>
<p>In a monorepo, pnpm workspaces connect packages, project references describe the TypeScript build graph, and package <code>exports</code> define the public paths. In a single codebase, barrels are a convention that ESLint can enforce, while Biome can ban them and enforce visibility on individual exports.</p>
<p><strong>Biome keeps untagged exports public, so internal exports must be annotated explicitly.</strong></p>
<p><em>The same guidance applies to JavaScript. Only the project references, declaration output, and typed-linting parts require TypeScript.</em></p>]]></content:encoded>
      <pubDate>Tue, 14 Jul 2026 07:20:00 GMT</pubDate>
      <category>Developer Tools</category>
      <category>JavaScript</category>
      <category>TypeScript</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>Layers of Instructions for AI Coding Agents</title>
      <link>https://sebnilsson.com/blog/layers-of-instructions-for-ai-coding-agents</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/layers-of-instructions-for-ai-coding-agents</guid>
      <description>AI coding agents receive their instructions through several different layers. Each layer loads at a different time, with a different trigger and a different cost.</description>
      <content:encoded><![CDATA[<p>When you give instructions to an AI coding agent, it can look like one single thing: you write a prompt, maybe add a project instructions file, and the agent goes to work. In practice, the instructions arrive through several different layers, and <strong>the layers behave completely differently from each other</strong>. Some load automatically on every single message, some only load when the agent decides to go looking, and some are never loaded as files at all, but fetched live at the exact moment they're needed.</p>
<p>You want to get the right piece of knowledge into the AI coding agent at the right time. If done incorrectly, you could bring in too many tokens of data for every message, for the rest of the session. Or the agent doesn't see it until it has already gone down the wrong path. Both kinds of mistakes lead to the same results: worse outcomes, higher costs, and more iterations of corrections.</p>
<p>In this article, we'll go through the four layers of instructions, what triggers each of them to load, and how to pick the right layer for the right kind of knowledge. The layers exist in Codex, Claude Code, Cursor, Gemini CLI, GitHub Copilot, and most similar tools, even if the file names differ between them.</p>
<h1>Understanding the Different Layers</h1>
<p>It helps to stop thinking of context as one big bucket, and instead think of it as separate delivery mechanisms:</p>






























<table><thead><tr><th>Layer</th><th>When it loads</th><th>Examples</th></tr></thead><tbody><tr><td>Always-loaded</td><td>At the start of every session, automatically</td><td><code>AGENTS.md</code>, <code>CLAUDE.md</code></td></tr><tr><td>On-demand</td><td>When the agent decides to read it</td><td><code>README.md</code>, source code, tests</td></tr><tr><td>Progressive disclosure</td><td>A short index always, the full details on use</td><td>Skills, plugins, deferred MCP tools</td></tr><tr><td>Live lookups</td><td>Nothing is preloaded, a lookup goes out when needed</td><td>CLI commands, docs lookups, web fetches</td></tr></tbody></table>
<p>Every mechanism you'll recognize from these tools falls into one of these buckets. Once you can place a piece of knowledge in the right bucket, deciding where it belongs stops being guesswork.</p>
<p><img src="https://files.sebnilsson.com/web/images/layers-of-instructions-for-ai-coding-agents/four-layers.png" alt="The four layers of instructions"></p>
<h1>Always-Loaded Instructions</h1>
<p>This is the layer most people already know: <a href="https://agents.md/"><code>AGENTS.md</code></a>, the open format used by Codex, GitHub Copilot, and many other tools, <code>CLAUDE.md</code> in Claude Code, and <a href="https://geminicli.com/docs/cli/gemini-md/"><code>GEMINI.md</code></a> in Gemini CLI. These files are read automatically at the start of every session, before the agent does anything else.</p>
<p><em>If your repository already has an <code>AGENTS.md</code>, the <code>CLAUDE.md</code> file can simply contain the text <code>@AGENTS.md</code> to <a href="https://code.claude.com/docs/en/memory#import-additional-files">automatically import it</a>, so you don't have to maintain two copies.</em></p>
<p>The automatic loading is exactly what makes this layer expensive. A ten-line instruction file costs you ten lines, once. <strong>A three-hundred-line instruction file costs you three hundred lines on every single message of the session</strong>, even when the current task needs none of it. There is no opt-out and no "only when relevant" for this layer.</p>
<p>This is why keeping this layer short matters so much. Put the things here that the agent genuinely can't figure out on its own: the exact test command, the package manager, the files it must never touch, and the safety rules that apply every time. Almost everything else can live in one of the cheaper layers below.</p>
<p>This is also a good layer to keep instructions on where the agent can look for more information when needed, for on-demand reads or progressive disclosure. For example, stating the path where documentation files are located, where to read previous ADRs, code style guidelines or similar, so the agent doesn't have to scan multiple directories for it and waste context.</p>
<h1>On-Demand Reads</h1>
<p><code>README.md</code>, type definitions, existing tests, the component you tell the agent to "follow the pattern of". None of this loads automatically. The agent has to decide to read the file, spend a tool call doing it, and the content then stays in the context for the rest of the session (or until it gets compacted away).</p>
<p>When used well, this is a good deal. You don't pay for anything that isn't relevant to the current task, and the files can carry far more detail than you would ever want in an always-loaded file. The trade-off is that <strong>the agent has to make the right call about what to read</strong>.</p>
<p>Point the agent to the right file, and you get one cheap, targeted read. Leave it vague, and the agent might read five files to find the one that mattered, or miss it completely.</p>
<p>It's worth noting that some tools can scope instructions to a part of the repository, like <a href="https://code.claude.com/docs/en/memory#path-specific-rules"><code>.claude/rules/</code>-files with <code>paths:</code> in their frontmatter</a>, <a href="https://code.visualstudio.com/docs/copilot/customization/custom-instructions">Copilot's <code>.instructions.md</code> files with <code>applyTo</code> globs</a>, or nested <code>AGENTS.md</code> files deeper down in the folder structure. These sit right between always-loaded and on-demand: they don't load for every session, but they load automatically as soon as the agent touches a matching file. This is a useful middle ground for conventions that only matter in one part of a codebase, for example, in a monorepo.</p>
<h1>Progressive Disclosure</h1>
<p>This is the layer that's easiest to underestimate, and the one that makes the other layers scale.</p>
<p>Skills and plugins are the clearest example. The agent doesn't load the full instructions of every skill up front, since that would defeat the whole point. What it always loads is a short index: a name and a one-line description for each available skill. The full content is only pulled into the context when a skill is actually invoked. <strong>The agent knows that the capability exists, without having paid for reading all of it</strong>, until it's actually needed.</p>
<p>Anthropic's post about <a href="https://www.anthropic.com/engineering/equipping-agents-for-the-real-world-with-agent-skills">Agent Skills</a> calls this principle <em>progressive disclosure</em>, and it's a good name for the whole layer.</p>
<p>MCP tools show why this layer matters, because they traditionally haven't used it. <strong>When you connect an MCP server, the client typically loads every tool's name, description, and full parameter schema up front</strong>, straight into the always-loaded layer. This is why adding too many MCP servers is a well-known problem: <a href="https://www.anthropic.com/engineering/advanced-tool-use">Anthropic measured</a> a typical five MCP-server setup at around 55,000 tokens of tool definitions, <strong>before the agent has even done any work</strong>.</p>
<p>The fix is moving the definitions into this layer. With deferred tool definitions, supported by both <a href="https://platform.claude.com/docs/en/agents-and-tools/tool-use/tool-search-tool">Anthropic's</a> and <a href="https://developers.openai.com/api/docs/guides/tools-tool-search">OpenAI's</a> platforms, only an index of names stays loaded, and a full schema only arrives when that specific tool is searched for or used. <a href="https://code.claude.com/docs/en/mcp#scale-with-mcp-tool-search">Claude Code</a> and <a href="https://github.com/openai/codex/releases/tag/rust-v0.142.2">Codex</a> both made this the default in mid-2026, but with fallbacks to up-front loading for older models and providers, so it's still worth checking how your tool of choice handles it.</p>
<p>You can build the same pattern yourself, with nothing more than Markdown files. Keep a short index in an always-loaded file, like <code>AGENTS.md</code>, where each line describes a subject and points to the file holding the details. The pointer instructions mentioned in the always-loaded layer are exactly this: the index costs one line per subject on every message, while a linked file only enters the context when a task actually needs it. These file pointers can be used recursively through folders and files.</p>
<p><a href="https://code.claude.com/docs/en/memory#auto-memory">Claude Code's auto memory</a> is a built-in example of the pattern. The agent writes down its own learnings in topic files and maintains a <code>MEMORY.md</code> file as a concise index of what's stored where. The index is loaded at the start of every session, while the topic files are only read on demand, when something in the index matches the task at hand.</p>
<p>Anything that is occasionally useful, expensive to load, and possible to identify with a short label is a candidate for this layer. This is the argument for moving long, situational workflows out of <code>AGENTS.md</code> and into skills: a one-line pointer costs almost nothing, and the full content only loads when it's actually relevant.</p>
<h1>Live Lookups</h1>
<p>The last layer isn't really context in the usual sense. It's a request-response cycle in the middle of a task: nothing is preloaded, a lookup goes out when it's needed, and only the answer comes back. Anthropic's article about <a href="https://www.anthropic.com/engineering/effective-context-engineering-for-ai-agents">context engineering</a> calls this strategy <em>just-in-time</em> context: the agent holds lightweight references, like file paths and URLs, and fetches the actual data at runtime.</p>
<p>The most common form isn't MCP servers or web fetches, it's plain CLI commands. <code>git log</code> and <code>git diff</code> answer history questions without a single file being read into the context. The <a href="https://cli.github.com/"><code>gh</code> CLI</a> turns pull requests, issues, and CI runs into one-line lookups. A <a href="https://jqlang.org/"><code>jq</code></a> filter returns the one field the agent asked about, instead of the whole JSON file. <strong>The shape of the command decides the shape, and the size, of the answer</strong>, which is what makes the terminal such a strong context tool for agents.</p>
<p>The same mechanism covers MCP calls to a database, documentation lookups, and web fetches. The context is not filled up until the moment of use, and the answer is current, even when the underlying data has changed since yesterday. Asking a documentation tool for the signature of one specific function costs a fraction of pasting in an entire API reference. The catch is that <strong>live lookups only work well when they're specific</strong>. A vague query returns a vague and bloated answer, and you're back to the same problem as pasting in a whole file.</p>
<h1>A Concrete Example</h1>
<p>Let's take one concrete instruction, straight from the <code>AGENTS.md</code> of this website: "run <code>pnpm run format</code> after code changes". Where it belongs depends completely on how often it's needed:</p>
<ul>
<li>If it applies to <strong>every change in the repository</strong>, it belongs in <code>AGENTS.md</code>/<code>CLAUDE.md</code>. It's short, it's universal, and the always-loaded cost is worth it.</li>
<li>If it only applies to <strong>one part of the repository</strong>, with its own formatting rules, it belongs in a path-scoped rule or a nested instruction file, not in the root file.</li>
<li>If it's one step of <strong>a larger, occasional workflow</strong>, like a release checklist, it belongs in a skill that only loads when that workflow runs.</li>
<li>If the right command <strong>depends on what was changed</strong>, it's not really a standing instruction at all. It's something the agent should look up in <code>package.json</code> on demand, instead of something you hardcode anywhere.</li>
</ul>
<p>The same fact, four possible homes, and four very different costs depending on which one you pick.</p>
<p><img src="https://files.sebnilsson.com/web/images/layers-of-instructions-for-ai-coding-agents/pick-the-right-layer.png" alt="How to pick the right layer"></p>
<h1>Picking the Right Layer</h1>
<p>There is no right or wrong when it comes to picking a layer, but asking three questions will settle most cases:</p>
<ul>
<li><strong>How often is it needed?</strong> Knowledge that's needed on every message belongs in the always-loaded layer, if you can keep it short. Knowledge that's needed occasionally belongs in a layer that only loads when it's relevant.</li>
<li><strong>Is it durable, or about the current task?</strong> Durable conventions belong in files. Current-task specifics belong in the prompt, or in a scratch file the agent reads once.</li>
<li><strong>Would a lookup be cheaper than a preload?</strong> If the agent can find the answer itself with one targeted read or one query, that's usually cheaper than a standing instruction, and it stays correct when the underlying thing changes.</li>
</ul>
<p>These questions won't give a universally right answer, but they're a good check of your instincts before you add one more paragraph to a file that will reload on every message for the rest of the project's life.</p>
<h1>More Than a Cost Problem</h1>
<p>It's tempting to treat all of this purely as cost optimization, but the quality effect is at least as important.</p>
<p>A short, focused always-loaded layer keeps the agent's attention on the few rules that apply every time, which makes it follow them more reliably. This effect is real enough that Claude Code's documentation <a href="https://code.claude.com/docs/en/memory#write-effective-instructions">recommends keeping instruction files under 200 lines</a>, since longer files reduce how reliably the instructions get followed.</p>
<p>The other layers contribute in the same direction. Well-placed pointers and indexes let the agent start from the right assumptions and go straight to the relevant files, so its effort goes into the actual task. The first attempt is more often the right one, and the follow-up corrections become fewer. Overloading or starving the layers produces the reverse, in both quality and cost.</p>
<p><strong>Getting the layering right improves quality and cost at the same time</strong>, because both depend on the same thing: the right information arriving at the right time, in the right shape.</p>
<p><img src="https://files.sebnilsson.com/web/images/layers-of-instructions-for-ai-coding-agents/why-layering-matters.png" alt="Why layering matters"></p>
<h1>Summary</h1>
<p>Instructions to an AI coding agent don't travel through one single pipe. They arrive as always-loaded files, on-demand reads, progressively disclosed indexes, and live lookups, where each layer has a different trigger and a different cost.</p>
<p><strong>The skill worth building is not writing a longer <code>AGENTS.md</code>. It's recognizing which layer a piece of knowledge actually belongs in</strong>, and trusting the other layers to do their job. Keep the root file short, move the occasional workflows into skills, and let the agent look up the rest.</p>]]></content:encoded>
      <pubDate>Mon, 06 Jul 2026 08:08:00 GMT</pubDate>
      <category>AI</category>
      <category>AI Coding Agents</category>
      <category>Claude</category>
      <category>Codex</category>
      <category>Copilot</category>
      <category>Gemini</category>
      <category>Productivity</category>
    </item>
    <item>
      <title>Nullable GUID Route Constraints in ASP.NET Core</title>
      <link>https://sebnilsson.com/blog/nullable-guid-route-constraints-in-asp-net-core</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/nullable-guid-route-constraints-in-asp-net-core</guid>
      <description>Handle optional and nullable GUID route parameters with built-in ASP.NET Core route constraints.</description>
      <content:encoded><![CDATA[<callout icon="info">
<p><em>This is the ASP.NET Core companion to my original post on <a href="/blog/nullableguidconstraint-for-asp-net-mvc-webapi">NullableGuidConstraint for ASP.NET MVC &#x26; WebApi</a>.</em></p>
</callout>
<p>The original article implements one constraint for both traditional ASP.NET MVC and Web API. ASP.NET Core unified those programming models and includes a built-in <a href="https://learn.microsoft.com/aspnet/core/fundamentals/routing#route-constraints"><code>guid</code> route constraint</a>, so the custom compatibility constraint is no longer needed in modern applications.</p>
<h1>Optional nullable GUID parameters</h1>
<p>Combine the built-in <code>guid</code> constraint with the <a href="https://learn.microsoft.com/aspnet/core/fundamentals/routing#route-template-reference">optional-route marker</a>:</p>
<pre><code class="language-csharp">[HttpGet("customers/{id:guid?}")]
public IActionResult Get(Guid? id)
{
    return id is null
        ? Ok("Return all customers")
        : Ok($"Return customer {id}");
}
</code></pre>
<p>This route matches both:</p>
<pre><code class="language-text">/customers
/customers/0f8fad5b-d9cb-469f-a165-70867728950e
</code></pre>
<p>It does not match a non-GUID value such as:</p>
<pre><code class="language-text">/customers/not-a-guid
</code></pre>
<h1>Required GUID parameters</h1>
<p>Remove <code>?</code> when the route requires a GUID:</p>
<pre><code class="language-csharp">[HttpGet("customers/{id:guid}")]
public IActionResult Get(Guid id)
{
    return Ok($"Return customer {id}");
}
</code></pre>
<h1>Minimal APIs</h1>
<p>The same constraint syntax works with <a href="https://learn.microsoft.com/aspnet/core/fundamentals/minimal-apis/route-handlers">minimal API route handlers</a>:</p>
<pre><code class="language-csharp">app.MapGet("/customers/{id:guid?}", (Guid? id) =>
{
    return id is null
        ? Results.Ok("Return all customers")
        : Results.Ok($"Return customer {id}");
});
</code></pre>
<h1>Constraint versus validation</h1>
<p>Route constraints help endpoint selection. They are not a replacement for validating business rules.</p>
<p>Use <code>{id:guid}</code> when a non-GUID path should not select the endpoint. Validate rules such as whether the GUID exists, is allowed, or must not be <code>Guid.Empty</code> inside the endpoint or application layer.</p>
<p>For traditional ASP.NET MVC and Web API, including the shared custom <code>NullableGuidConstraint</code>, see the <a href="/blog/nullableguidconstraint-for-asp-net-mvc-webapi">original article</a>.</p>]]></content:encoded>
      <pubDate>Tue, 30 Jun 2026 06:54:00 GMT</pubDate>
      <category>.NET</category>
      <category>ASP.NET</category>
      <category>C#</category>
    </item>
    <item>
      <title>ASP.NET Core Request Paths Reference</title>
      <link>https://sebnilsson.com/blog/asp-net-core-request-paths-reference</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/asp-net-core-request-paths-reference</guid>
      <description>Compare request URL, path, query string, host, scheme, and base-path values in ASP.NET Core.</description>
      <content:encoded><![CDATA[<callout icon="info">
<p><em>This is the modern ASP.NET Core update of my original <a href="/blog/asp-net-request-paths-reference">ASP.NET Request Paths Reference</a>.</em></p>
</callout>
<p>That older reference remains useful for applications built on traditional ASP.NET and <code>System.Web</code>. A separate article is needed because ASP.NET Core replaced those request APIs, separates URL components differently, and commonly runs behind reverse proxies or under a configured base path.</p>
<p>Consider this request:</p>
<pre><code class="language-text">https://www.example.com/MyApplication/MyFolder/MyPage?key=value
</code></pre>
<p>In this example, <code>/MyApplication</code> is the application's configured base path. ASP.NET Core exposes that prefix as <code>Request.PathBase</code>, and exposes the remaining path inside the application as <code>Request.Path</code>.</p>
<p><code>PathBase</code> is not automatically the first URL segment. It is a configured or forwarded path prefix for the application. It is often empty when the app runs at the domain root, such as <code>https://www.example.com/</code>. It can also contain one or more segments, such as <code>/MyApplication</code> or <code>/apps/customers</code>, when the app runs under that URL prefix.</p>
<h1>Request path properties</h1>
<p>In a controller, Razor Page, middleware, or endpoint, use properties from <a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httprequest"><code>HttpRequest</code></a>:</p>





































<table><thead><tr><th>Property</th><th>Example value</th></tr></thead><tbody><tr><td><a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httprequest.scheme"><code>Request.Scheme</code></a></td><td><code>https</code></td></tr><tr><td><a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httprequest.host"><code>Request.Host</code></a></td><td><code>www.example.com</code></td></tr><tr><td><a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httprequest.pathbase"><code>Request.PathBase</code></a></td><td><code>/MyApplication</code></td></tr><tr><td><a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httprequest.path"><code>Request.Path</code></a></td><td><code>/MyFolder/MyPage</code></td></tr><tr><td><a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httprequest.querystring"><code>Request.QueryString</code></a></td><td><code>?key=value</code></td></tr><tr><td><a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httprequest.query"><code>Request.Query</code></a></td><td><code>Request.Query["key"]</code> is <code>value</code></td></tr><tr><td><a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.httprequest.protocol"><code>Request.Protocol</code></a></td><td><code>HTTP/2</code> or the active protocol</td></tr></tbody></table>
<p><code>PathBase</code> contains the path prefix that has been separated from the request path. <code>Path</code> contains the remaining request path used by routing inside the application.</p>
<p>For example, if the incoming URL path is <code>/MyApplication/MyFolder/MyPage</code>:</p>

























<table><thead><tr><th>Configuration</th><th><code>Request.PathBase</code></th><th><code>Request.Path</code></th></tr></thead><tbody><tr><td>No base path</td><td>empty</td><td><code>/MyApplication/MyFolder/MyPage</code></td></tr><tr><td>Base path is <code>/MyApplication</code></td><td><code>/MyApplication</code></td><td><code>/MyFolder/MyPage</code></td></tr><tr><td>Base path is <code>/MyApplication/MyFolder</code></td><td><code>/MyApplication/MyFolder</code></td><td><code>/MyPage</code></td></tr></tbody></table>
<p>The split can come from middleware such as <a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.builder.usepathbaseextensions.usepathbase"><code>UsePathBase</code></a>, hosting under a sub-application, or forwarded proxy configuration such as <code>X-Forwarded-Prefix</code>.</p>
<pre><code class="language-csharp">var request = HttpContext.Request;

var pathWithinApplication = request.Path;
var fullPath = request.PathBase + request.Path + request.QueryString;
</code></pre>
<p>For the example request:</p>
<pre><code class="language-text">pathWithinApplication = /MyFolder/MyPage
fullPath              = /MyApplication/MyFolder/MyPage?key=value
</code></pre>
<h1>Build the absolute request URL</h1>
<p>ASP.NET Core deliberately keeps URL components separate. To create the absolute display URL, use <a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.http.extensions.urihelper.getdisplayurl"><code>GetDisplayUrl()</code></a> from <code>Microsoft.AspNetCore.Http.Extensions</code>:</p>
<pre><code class="language-csharp">using Microsoft.AspNetCore.Http.Extensions;

var absoluteUrl = Request.GetDisplayUrl();
// https://www.example.com/MyApplication/MyFolder/MyPage?key=value
</code></pre>
<p>When the application runs behind a reverse proxy, <a href="https://learn.microsoft.com/aspnet/core/host-and-deploy/proxy-load-balancer">configure forwarded headers</a> so <code>Scheme</code>, <code>Host</code>, and the generated absolute URL reflect the original client request.</p>
<h1>Generate application links</h1>
<p>Do not manually concatenate paths when generating links to endpoints. Use ASP.NET Core link generation with <a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.iurlhelper.action"><code>Url.Action</code></a>:</p>
<pre><code class="language-csharp">var path = Url.Action("Details", "Customers", new { id = 42 });
var absolute = Url.Action(
    "Details",
    "Customers",
    new { id = 42 },
    protocol: Request.Scheme);
</code></pre>
<p>For endpoint routing outside a controller, inject <a href="https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.routing.linkgenerator"><code>LinkGenerator</code></a>.</p>
<h1>Which property should you use?</h1>
<ul>
<li>Use <code>Request.Path</code> to inspect the route path inside the application.</li>
<li>Use <code>Request.PathBase + Request.Path</code> when the mounted base path matters.</li>
<li>Use <code>Request.Query</code> to read parsed query-string values.</li>
<li>Use <code>GetDisplayUrl()</code> when you need the complete incoming URL for diagnostics or display.</li>
<li>Use <code>Url</code>, <code>LinkGenerator</code>, or endpoint names when generating application links.</li>
</ul>
<p>For traditional ASP.NET APIs such as <code>Request.RawUrl</code>, <code>Request.ApplicationPath</code>, and <code>Request.Url</code>, see the <a href="/blog/asp-net-request-paths-reference">original ASP.NET Request Paths Reference</a>.</p>]]></content:encoded>
      <pubDate>Fri, 26 Jun 2026 06:47:00 GMT</pubDate>
      <category>.NET</category>
      <category>ASP.NET</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>AI Driven Migration of New Website</title>
      <link>https://sebnilsson.com/blog/ai-driven-migration-of-new-website</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/ai-driven-migration-of-new-website</guid>
      <description>Experiences from migrating sebnilsson.com from ASP.NET to Next.js using AI coding agents.</description>
      <content:encoded><![CDATA[<p>My personal website has existed in different forms since 2012. The previous version was built with the original <a href="https://dotnet.microsoft.com/apps/aspnet/mvc">ASP.NET MVC</a> and later upgraded to <a href="https://docs.microsoft.com/aspnet/core/">ASP.NET Core</a>. Over the years, it accumulated controllers, Razor views, view components, custom routing, URL rewrite rules, RavenDB integrations, Gulp scripts, SCSS styles, frontend build scripts, and several supporting libraries.</p>
<p>It worked, but maintaining it had become unnecessarily complicated for what the website needed to be. For example, setting up the frontend pipeline for a small tweak, sometimes several years and major npm package versions later, was an unbelievable waste of time.</p>
<p>Most of the website consists of content: blog posts, conference talks, articles, podcast appearances, and information about me. It did not need a database-backed application with over-complicated architecture and exaggerated technical solutions. I wanted a modern website that was easier to change, faster to experiment with, and better suited for working on together with AI coding agents.</p>
<h1>Why Next.js</h1>
<p>It's quite clear that <a href="https://2025.stateofjs.com/en-US/libraries/front-end-frameworks">React has sort of won the frontend race</a>. I used to think the React model was too simple and stupid, but after using it for a while, I realized that this was actually the strength of the framework. It ensured a data flow which was easy to follow and troubleshoot.</p>
<p>Also, I wanted a website which was rendered on the server, for SEO and predictability, but still allowed the full frontend expressiveness of TypeScript/JavaScript. React has, by far, the largest ecosystem for components and functionality out there. AI coding agents are extremely competent in React and very good at Next.js. Personally, I'm also quite comfortable in Next.js, all the way from Next.js 13 and forward. All that put together made the decision easy for me.</p>
<h1>AI-Powered Migration</h1>
<p>The migration was done in mid-2026, so it was of course done in collaboration with AI-powered tools, such as AI coding agents.</p>
<p>Deleting the source code for the previous ASP.NET solution in the repository deleted more than 9 000 lines of code across 118 files. That included various code, templates, scripts, libraries, configuration, and more, accumulated during more than a decade.</p>
<p>Deleting the application was the easy part. The important part was preserving the content. I had some foresight here, and all my blog posts were already in <a href="https://daringfireball.net/projects/markdown/">Markdown-formatted</a> files.</p>
<h2>Migrating to Next.js</h2>
<p>I then used <a href="https://v0.dev/">Vercel's v0</a> tool to create the first Next.js version of the website. It provided a useful visual starting point with an App Router application, pages, components, <a href="https://tailwindcss.com/">Tailwind CSS</a>, and <a href="https://ui.shadcn.com/">shadcn/ui</a>. It produced an impressive result quickly, but it was still just a starting point.</p>
<p>The initial version contained more than one hundred new files, including dozens of UI components the website did not use. Some parts looked finished while the underlying structure still needed work. The application could render pages, but it was not yet the website I had envisioned.</p>
<h2>Choosing an AI Coding Agent</h2>
<callout icon="warning">
<p><strong>Warning:</strong> This section is subjective, partly based on my own tooling habits, and likely to age quickly. It could be outdated days after the post is published.</p>
</callout>
<p>From this point, I needed a strong AI coding agent to work with, to build the technical solution I wanted, in the way and with the structure I wanted. The main agents I used were <a href="https://code.claude.com/">Claude Code</a> CLI, <a href="https://developers.openai.com/codex/cli">Codex CLI</a>, and <a href="https://developers.openai.com/codex/app">Codex App</a>.</p>
<p>The <a href="https://martinfowler.com/articles/harness-engineering.html">harnesses</a> and the <a href="https://openrouter.ai/models">AI models</a> are changing constantly, with us being in the midst of a fierce race to the top between the frontier models. Despite benchmarks, published features, and measured differences, <strong>people easily base their preferences on feelings</strong> and how well a coding agent vibes with them (pun intended). I know that I did.</p>
<p>My experience with Claude vs. Codex was roughly this: Codex reads between the lines of <strong>my prompts</strong> extremely well and gets very close to what I intended to ask for. I've also found a good flow working with it. Claude produces great results, but burns tokens fast, sometimes does work I did not request, and bombards me with approval prompts for every little change.</p>
<p>At the time of writing this article, I found specifically that the <a href="https://developers.openai.com/codex/app">Codex App</a> had the UX that worked best for me personally. So, that was my primary tool, while complementing it with Claude Code for second opinions or when I ran out of token usage.</p>
<h2>Token Optimization</h2>
<p>I intentionally used the lowest tier subscriptions for both Codex and Claude, to see how far it could take me. This meant that I really needed to mind my token burn. This drove me to adopt some key patterns, to get <strong>the most output and best results for the least amount of tokens</strong>. I shortly considered installing the <a href="https://www.skills.sh/juliusbrussee/caveman/caveman">Caveman-skill</a> to really minimize token usage but chose not to, as I wanted a realistic experience and to optimize in more conventional ways.</p>
<p>Another early task was updating the shadcn/ui setup and removing all components not used. That change alone removed thousands of lines from the repository. A <strong>key to saving tokens</strong> here was to tell the agent to use the <a href="https://ui.shadcn.com/docs/cli">shadcn CLI</a> instead of reading and writing large source files directly from GitHub (which I once had to stop an agent from doing, mid-token apocalypse).</p>
<h2>Better AI Agent Instructions</h2>
<p>First, I ensured the project had a solid <code>AGENTS.md</code> to give the AI coding agent basic information about the project. The key here was to <strong>avoid including information which easily starts to drift</strong> with changes to the website.</p>
<p>Another important instruction was to tell the agent to read the latest Next.js documentation, included in the installed npm package, before making relevant changes. Instructions on formatting, linting, and building were also included.</p>
<p>I also added specialized agent skills for areas such as Next.js best practices, React best practices, shadcn/ui, and SEO. To ensure access across both Codex and Claude, I used <a href="https://skills.sh/">Vercel's skills.sh</a>.</p>
<h2>AI Agent Iterative Development</h2>
<p>The work was not a single conversion prompt followed by a finished website. It was an iterative development process where I remained responsible for the direction, while Codex handled most of the implementation.</p>
<p>First, I took the opportunity, <strong>while the codebase was still relatively small</strong>, to do larger sweeping operations with agents and set codebase-wide baselines. I installed <a href="https://www.skills.sh/vercel/nextjs-skills/next-best-practices">Vercel's own skills for Next.js best practices</a> and asked the agent to improve everything according to the best practices. It improved the codebase to better match Next.js conventions, reorganized code, improved SEO, implemented server actions, and simplified where possible.</p>
<p>The <strong>key workflow</strong> I used to ensure the <strong>best outcome and surprising effectiveness in token usage</strong> was the following points:</p>
<ul>
<li>Point out the exact entry point file for the change.
<ul>
<li>If changing the functionality or look of a page on the site, I pointed to the specific <code>page.tsx</code> file.</li>
<li>If the change was pure functionality, I pointed to the relevant file or files.</li>
</ul>
</li>
<li>I described the expected outcome of the functionality I wanted added.
<ul>
<li>If I knew of specific functional or technical limitations, I explicitly mentioned these.</li>
</ul>
</li>
<li>Default to medium effort on models. Use low or medium effort for very straight forward tweaks.</li>
<li>For tweaks to implementations which could be improved, I pointed to the exact file and named function and variable names to focus on.</li>
<li>Keep conversation threads short. Only stay in the same thread of changes as long as most of the context is relevant.</li>
</ul>
<p>Explicitly pointing out where to look seemed to keep token usage down significantly. I very rarely hit the limits on the lowest tier subscriptions to Codex and Claude. That makes sense, since the AI probably doesn't have to scan as much of the file, or multiple files, to figure out where to start and where to make the changes.</p>
<p><em>It's important to note that this was highly efficient at the time of posting this blog. The harnesses and/or models might change/improve significantly over time.</em></p>
<h1>Stabilize and Improve Content</h1>
<p>The blog was one of the most important parts of the migration. Some posts have existed for more than fifteen years, and their URLs still receive traffic. Preserving those posts and their URLs mattered more than reproducing every detail of the old application.</p>
<p>The first Next.js version loaded the Markdown files using custom code. With Codex, I later introduced Velite to provide a clearer content model and build process.</p>
<p>After deliberating with both Codex and Claude, we decided to use <a href="https://velite.js.org/">Velite</a> to structure the existing blog post content in Markdown format. This means each blog post now has validated frontmatter containing fields such as its title, publication date, summary, tags, image, series, and draft status. Velite processes the Markdown and produces structured data for Next.js.</p>
<p>This made several later improvements easier:</p>
<ul>
<li>Posts can be grouped into a series</li>
<li>Tags can be sorted and filtered consistently</li>
<li>Blog search can operate on structured content</li>
<li>Draft posts are supported outside production</li>
<li>RSS feeds and sitemaps can use the same content source</li>
<li>Syntax highlighting can be applied during content processing</li>
</ul>
<p>Codex helped implement these features incrementally. Blog search, for example, was added as a focused feature rather than as part of the original migration. The same was true for series navigation, improved code highlighting, responsive images, and fallback images.</p>
<p>Each change was small enough to understand and review, but together they preserved the previous blog experience and, in many ways, improved on it.</p>
<h1>Moving Beyond a Direct Replacement</h1>
<p>At first, the goal was to replace the old ASP.NET website. Once the new foundation was stable, the work became more interesting: I could start improving what the website actually communicated.</p>
<p>The old site had grown around sections created at different times. During the migration, I reconsidered whether those sections still represented me and my work. The start page was redesigned several times and my portfolio content was restructured quite a lot. Articles I've written and podcast appearances are now shown on the website. Conference talks and speaking information became more prominent.</p>
<p>Some of these changes began as broad requests such as improving the start page. Others were extremely specific, such as adjusting the behavior of a navigation button or changing the layout of a single conference talk.</p>
<h1>Using Codex as a Reviewer</h1>
<p>Codex was also useful when I did not have a specific implementation in mind.</p>
<p>I could ask it to review the website for unnecessary complexity, Next.js issues, SEO opportunities, or performance problems. It would inspect the current repository, identify concrete improvements, and implement the ones that made sense.</p>
<p>The important part was still reviewing the result. Codex can inspect a large amount of code quickly, but it does not own the product decisions. Some technically valid suggestions did not match the website I wanted. Others needed another iteration after I saw them in the browser.</p>
<p>The best results came from treating Codex as an engineering collaborator rather than as an automatic code generator.</p>
<h1>Summary</h1>
<p><strong>This migration couldn't have been done without using modern AI coding agents.</strong> At least not within any reasonable time.</p>
<p>The AI agents played an important role, but not by performing one automatic conversion. Their role was closer to that of engineering collaborators: inspecting the existing work, implementing focused improvements, cleaning up generated code, following project-specific instructions, and helping me improve the process continuously.</p>
<p>The website moved from a server-side ASP.NET application with more than a decade of accumulated architecture to a content-focused Next.js application built around Markdown and reusable React components.</p>
<p><strong>.NET, C#, and ASP.NET are still my main tools of choice</strong> when building high-performant, robust, testable, and long-lived solutions for serious organizations. ASP.NET for building APIs is still unmatched and C# is by far my favorite language. That said, Next.js and React are my favorites for server-side and client-side hybrid solutions for stable, dynamic, and expressive websites and web applications.</p>
<p>The migration gave me a modern website. More importantly, it <strong>solidified a collaboration process with AI coding agents</strong> which lets me make improvements, add features, or just do small tweaks extremely quickly and get them out to production within minutes. We really live in amazing times, and hopefully it'll only get better.</p>]]></content:encoded>
      <pubDate>Mon, 22 Jun 2026 07:33:00 GMT</pubDate>
      <category>AI</category>
      <category>AI Coding Agents</category>
      <category>Claude</category>
      <category>Codex</category>
      <category>Next.js</category>
      <category>Productivity</category>
      <category>Projects</category>
      <category>TypeScript</category>
    </item>
    <item>
      <title>dotnet cleanup v2: Quickly Clean Up bin, obj &amp; node_modules Folders</title>
      <link>https://sebnilsson.com/blog/dotnet-cleanup-v2-quickly-clean-up-bin-obj-node-modules-folders</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/dotnet-cleanup-v2-quickly-clean-up-bin-obj-node-modules-folders</guid>
      <description>Use the rebuilt dotnet-cleanup .NET tool to quickly remove generated directories, using glob patterns.</description>
      <content:encoded><![CDATA[<callout icon="info">
<p><em>This blog post is about version 2 of my .NET Tool <code>dotnet-cleanup</code>. You can read the post about version 1 here: <a href="/blog/dotnet-cleanup-clean-up-solution-project-folder"><code>dotnet-cleanup</code>: Clean Up Solution, Project &#x26; Folder</a>.</em></p>
</callout>
<p>The v2 of the tool has been rebuilt around directory-tree cleanup and glob patterns. Compared with the original solution-and-project-oriented version, the current version can be used on any directory with sub-directories including .NET projects, web-applications, or any combination of these. It can also be used on other types of directories by specifying which folders and files, with glob patterns, to clean up.</p>
<p>The tool command name remains <a href="https://www.nuget.org/packages/DotnetCleanup"><code>dotnet-cleanup</code></a>, with a hyphen.</p>
<p>Like with the previous version, one big USP (unique selling point) for this tool is that it removes the cleanup folders extremely fast, by first moving them to a temporary folder, before actually deleting them off disk.</p>
<h1>Install the tool</h1>
<p>Install <a href="https://www.nuget.org/packages/DotnetCleanup"><code>dotnet-cleanup</code></a> as a global .NET tool:</p>
<pre><code class="language-shell">dotnet tool install --global dotnet-cleanup
</code></pre>
<p>Update an existing installation:</p>
<pre><code class="language-shell">dotnet tool update --global dotnet-cleanup
</code></pre>
<h1>Include and exclude directories</h1>
<p>Add include patterns with <code>--path</code> or <code>-p</code> and exclude patterns with <code>--exclude</code> or <code>-x</code>:</p>
<pre><code class="language-shell">dotnet-cleanup \
  -p "**/bin" \
  -p "**/obj" \
  -p "**/node_modules" \
  -x "**/samples/**"
</code></pre>
<p>This glob-based model is the biggest change from the original tool. It can clean generated directories across mixed .NET and web repositories without first resolving a solution or project file.</p>
<p>By default, the tool includes the following paths:</p>
<pre><code class="language-text">**/bin
**/obj
**/node_modules
</code></pre>
<h1>Cleanup preview</h1>
<p>The tool will first give you a list of matching files and directories, which will be deleted. You'll then have to actively confirm.</p>
<pre><code class="language-shell">dotnet-cleanup
</code></pre>
<p>If you want to be totally sure that no files will actually be cleaned, add the <code>--noop</code> option:</p>
<pre><code class="language-shell">dotnet-cleanup --noop
</code></pre>
<p><code>--noop</code> lists matching directories without moving or deleting them. Use it before every new automated cleanup configuration.</p>
<h1>CI/CD scenario: Auto confirm</h1>
<p>To skip the confirmation prompt, for example when used inside a CI/CD solution, add <code>--yes</code> to automatically confirm the deletions:</p>
<pre><code class="language-shell">dotnet-cleanup --yes
</code></pre>
<h1>Customization</h1>
<p>Matched directories are moved to a temporary directory before deletion by default. There are options provided for controlling that process, if needed:</p>
<pre><code class="language-shell">dotnet-cleanup --no-move
dotnet-cleanup --no-delete
dotnet-cleanup --temp-path "D:\cleanup-temp"
</code></pre>
<p>Use <code>--no-delete</code> when you want the moved directories to remain available for inspection. Use <code>--no-move</code> only when direct deletion is acceptable.</p>
<h1>Review the current options</h1>
<p>The README and command-line help are the source of truth for current behavior:</p>
<pre><code class="language-shell">dotnet-cleanup --help
</code></pre>
<p>Cleanup is destructive. Commit or stash work, run <code>--noop</code>, and review include and exclude patterns before confirming.</p>
<p>See the <a href="https://github.com/sebnilsson/DotnetCleanup">DotnetCleanup repository</a> for the current README and source.</p>
<p>The <a href="/blog/dotnet-cleanup-clean-up-solution-project-folder">original article</a> documents the earlier solution-and-project-oriented implementation.</p>]]></content:encoded>
      <pubDate>Wed, 17 Jun 2026 06:46:00 GMT</pubDate>
      <category>.NET</category>
      <category>.NET Tools</category>
      <category>Developer Tools</category>
      <category>Productivity</category>
      <category>Projects</category>
    </item>
    <item>
      <title>dotnet-ping: Ping URLs in the Terminal</title>
      <link>https://sebnilsson.com/blog/dotnet-ping-ping-urls-in-the-terminal</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/dotnet-ping-ping-urls-in-the-terminal</guid>
      <description>When working with the web development at a rapid pace, you need to constantly ensure that key parts of your website always work</description>
      <content:encoded><![CDATA[<p>When working with the web development at a rapid pace, you need to constantly ensure that key parts of your website always work.
There are tools out there that offers advanced monitoring,
but what about when you just want to fire off quick ensuring tests on your local machine?</p>
<p>This is the background to why I built <code>dotnet-ping</code>,
which is easily installed as a <a href="https://learn.microsoft.com/dotnet/core/tools/global-tools">.NET Global Tool</a>
and makes pinging URLs via the terminal really easy.</p>
<h1>Installation</h1>
<p>Download <a href="https://dotnet.microsoft.com/download">the latest version of .NET</a>. Then install the <a href="https://www.nuget.org/packages/dotnet-ping"><code>dotnet-ping</code></a> .NET Tool, using the terminal:</p>
<pre><code class="language-sh">dotnet tool install -g dotnet-ping
</code></pre>
<p>Once installed, you can verify the tool is available by running <code>dotnet tool list -g</code>.</p>
<h1>Basic scenarios</h1>
<p>To ping a single URL just supply the URL as the command argument:</p>
<pre><code class="language-sh">dotnet ping https://example.com/section/page
</code></pre>
<p>If you want to ping multiple URLs, you can supply multiple command arguments.</p>
<pre><code class="language-sh">dotnet ping example.com/page other.com/other-page
</code></pre>
<p>If a URL is missing the protocol (e.g., <code>http://</code> or <code>https://</code>), the tool will automatically assume <code>https://</code>.</p>
<p>You can use the <code>-s</code> option to set the sleep time between requests
and the <code>-t</code> option to set the timeout limit for a response from the pinged URL.</p>
<p>If you want to ping multiple pages on the same website, you can supply a base-URL, using the <code>-b</code> option:</p>
<pre><code class="language-sh">dotnet ping -b example.com /home /about /contact-us /products
</code></pre>
<p>Pings expect, by default, a <code>200</code> response as HTTP status. This can be modified with the <code>-e</code> option.
The full list of documentation can be found by calling the tool with the <code>-h</code> option or looking at the <code>README</code> on
<a href="https://github.com/sebnilsson/DotnetPing">GitHub</a> or on <a href="https://www.nuget.org/packages/dotnet-ping/">NuGet.org</a>.</p>
<h1>Advanced scenarios</h1>
<p>For more advanced scenarios, where you want to have a better overview and more complex configuration for multiple URLs,
the tool supports configuration via a <code>.json</code> file. If there is a <code>ping.json</code> file in the directory that the tool is executed from,
and no URL is passed as a command argument, this file will be used.</p>
<p>The basic structure of the file allows an array of URLs (<code>urls</code>) and their configuration,
as well as an array of configuration group (<code>groups</code>), which are applied to one or multiple URLs.</p>
<pre><code class="language-json">{
    // Single URLs, with Single configurations
    "urls": [
        {
            "url": "test.com", // Required
            "method": "GET", // Default: GET
            "timeout": 15000, // Default: 5000ms
            "sleep": 100, // Default: 500ms
            "expect": [ 200, 201, 403 ] // Default: 200
        },
        {
            "url": "http://test2.com",
            "method": "DELETE",
            "timeout": 10000,
            "sleep": 200,
            "expect": [ 201, 202, 204 ]
        }
    ],
    // Groups of configurations, with single or multiple URLs
    "groups": [
        {
            "timeout": 20000,
            "sleep": 250,
            "expect": [ 301, 302 ],
            "urls": [ "test3.com/redirect", "test4.com/redirect" ]
        },
        {
            "timeout": 5000,
            "sleep": 550,
            "baseUrl": "https://test5.com/",
            "expect": [ 200, 201 ],
            "urls": [ "/", "/about", "/contact", "products" ]
        }
    ]
}
</code></pre>
<h1>Usage Documentation</h1>
<p>See the usage documentation of the tool by running <code>dotnet ping -h</code>:</p>
<pre><code class="language-sh">USAGE:
    dotnet-ping [urls] [OPTIONS]

EXAMPLES:
    dotnet-ping https://example.com
    dotnet-ping https://example.com other.com -s 1000
    dotnet-ping https://example.com other.com -s 1000 -s 2000 -t 5000
    dotnet-ping /about /contact -b https://example.com
    dotnet-ping -c ping.json

ARGUMENTS:
    [urls]    The URLs to ping. If not specified, the URLs are read from the JSON config file

OPTIONS:
    -h, --help        Prints help information
    -b, --base-url    Sets the base-URL to use for requests
    -c, --config      The path to the JSON config file
    -d, --debug       Use debug console messaging
    -e, --expect      Sets the expected status code of requests. Default: 200
    -X, --request     Sets the request method. Default: GET
    -m, --minimal     Use minimal console messaging
    -s, --sleep       Sets the sleep wait time between requests in milliseconds. Default: 500ms
    -t, --timeout     Sets the timeout for requests in milliseconds. Default: 5000ms. If two values are provided, a
                      random number between the two numbers will be generated for each request
</code></pre>
<h1>Outro</h1>
<p>Take it for a spin and feel free to give feedback on the tool.
If you find bugs or have suggestions,
feel free to <a href="https://github.com/sebnilsson/DotnetPing">open an issue on GitHub or submit a pull request</a>.</p>
<p>You can find the <a href="https://github.com/sebnilsson/DotnetPing">source-code on GitHub</a>
and the <a href="https://www.nuget.org/packages/dotnet-ping/">package on Nuget</a>.</p>]]></content:encoded>
      <pubDate>Mon, 14 Oct 2024 06:44:00 GMT</pubDate>
      <category>.NET</category>
      <category>.NET Tools</category>
      <category>Developer Tools</category>
      <category>Productivity</category>
      <category>Projects</category>
    </item>
    <item>
      <title>How to Understand Log Levels</title>
      <link>https://sebnilsson.com/blog/how-to-understand-log-levels</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/how-to-understand-log-levels</guid>
      <description>Log levels are more than just black or red rows in your logging system</description>
      <content:encoded><![CDATA[<p><em><strong>Originally written and published for <a href="https://lightrun.com/best-practices/how-to-understand-log-levels/">Lightrun's best practices</a></strong></em></p>
<hr>
<p>More than once, I've heard experienced software developers say that there are only two reasons to log: either you log <code>Information</code> or you log an <code>Error</code>. The implication here is that either you want to <strong>record something that happened</strong> or you want to be able to <strong>react to something that went wrong</strong>.</p>
<p>In this article, we'll take a closer look at logging and explore the fact that <strong>log levels are more than just black or red rows</strong> in your main logging system. When used correctly, they let you filter certain types of data to be logged to different destinations, have different retention times, and can be searched more easily in different structures of the data.</p>
<h1>Why Logging?</h1>
<p>While developing software, hopefully you write unit tests to validate isolated functionality in your code, add some integration tests, and maybe do some manual testing on top of that. But when your code is running in production, you need a way to get information about what code is being executed inside your software and have that accessible from the outside.</p>
<p>The most common way to do this is to add logging to your code. Usually, this code will then write to a destination, which can be a local file, a database, or a centrally controlled more advanced log solution. You can then access this information to get technical insights into what's going on in the running code.</p>
<p>Log entries, depending on configuration, usually contain a time stamp, a severity level, a message entered in the code, sometimes which class/file is doing the logging, sometimes a stack trace from an error in the code, and other metadata about the execution context. When done right, this can help developers follow the flow of logic through the software.</p>
<p>Many use logging to log key events in the system, like orders completed, users registered, reviews written, and so on. <strong>With correctly structured data, you can pipe this data into different analysis tools to get more business insights.</strong> For example, usually, you turn to your logs when something goes wrong in the software. If logging is set up correctly, you'll be able to figure out where the problem comes from and from which line of code.</p>
<p>If there's not enough information in your logs, you might want to ensure that a lower level of logging is written to your logs, so you can get more details about what's going on in your software. You can usually achieve this by using different log levels.</p>
<h1>Understanding The Different Log Levels</h1>
<p><strong>Log levels convey the intention of the specific content of what's being logged. This can help you filter different levels of log data into different destinations, enabling you to focus on the log level relevant to the current issue you're facing.</strong> Logging to different destinations also allows you to have different retention levels for different logged content.</p>
<p>These are the most common log levels for logging libraries:</p>
<ul>
<li><code>Critical</code><em>*</em></li>
<li><code>Error</code></li>
<li><code>Warn</code></li>
<li><code>Info</code></li>
<li><code>Debug</code></li>
<li><code>Trace</code></li>
</ul>
<p><em>* Sometimes under other names, such as <strong>Fatal</strong> or <strong>Fault</strong>.</em></p>
<p>Different languages and platforms, and their most popular logging libraries, actually support different log levels, even if the core functionality is there across them all.</p>




































































<table><thead><tr><th></th><th><code>Trace</code></th><th><code>Debug</code></th><th><code>Info</code></th><th><code>Warn</code></th><th><code>Error</code></th><th><code>Critical</code></th></tr></thead><tbody><tr><td><a href="https://docs.microsoft.com/dotnet/api/microsoft.extensions.logging.loglevel">C#/.NET</a></td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td></tr><tr><td><a href="https://logging.apache.org/log4j/2.x/manual/customloglevels.html">Java</a></td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️ (as <code>Fatal</code>)</td></tr><tr><td><a href="https://docs.python.org/3/library/logging.html#levels">Python</a></td><td></td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td></tr><tr><td><a href="https://docs.rs/log/0.4.14/log/">Rust</a></td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td></td></tr><tr><td><a href="https://developer.apple.com/documentation/os/oslogtype">Swift</a></td><td></td><td>✔️</td><td>✔️</td><td></td><td>✔️</td><td>✔️ (as <code>Fault</code>)</td></tr><tr><td><a href="https://github.com/MicroUtils/kotlin-logging/blob/master/src/jvmMain/kotlin/mu/KLogger.kt">Kotlin</a></td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td>✔️</td><td></td></tr></tbody></table>
<p>Many logging libraries allow you to also handle an <code>All</code> scenario, where all the levels are written to the configured destinations. The <code>None</code> scenario, then, turns off all the logging. Some libraries also support a <code>Custom</code> scenario, where you can specify exactly which log levels to include.</p>
<h1>Picking the Right Log Level</h1>
<p><strong>There is no right or wrong when it comes to picking a level of logging, but it's important to set the same expectations throughout a project and maybe throughout an entire organization.</strong></p>
<p>Here are a few best-practice suggestions as a starting point that any team should be able to build upon:</p>
<ul>
<li><code>Critical</code>: Used when your application is in an unrecoverable state. It crashes during startup configuration, or a framework-level component is not working as expected.<br>
<em>Example: Your app cannot start up correctly or your DI setup fails.</em></li>
<li><code>Error</code>: Used for errors that stop the flow of the execution. Usually used for unknown and unexpected errors in a central catch-all scenario for an application.<br>
<em>Example: Routing in your web app fails, or code in an MVC controller throws an exception.</em></li>
<li><code>Warn</code>: Used for errors that don't stop the flow of execution. Usually, these errors are somewhat expected, since the logging code is added manually to these points in the code.<br>
<em>Example: Calling an external service, which you know can fail and you can silently recover from.</em></li>
<li><code>Info</code>: Used to log events in the normal flow of the code. Can be used for only system key events, to keep the volume of log data down.<br>
<em>Example: User clicked a call-to-action button or new user registered.</em></li>
<li><code>Debug</code>: Used for scenarios where the information in higher levels is not enough to <a href="https://lightrun.com/debugging/debugging-microservices-the-ultimate-guide/">debug a problem</a>. Should record the flow through the system.<br>
<em>Example: Method X was called in class/object Y, to track different flows.</em></li>
<li><code>Trace</code>: Used to get as much detail as possible, tracking the flow of data through the system. Can contain sensitive data. Should probably never be activated in production.<br>
<em>Example: Method X was called with parameter values Y and Z.</em></li>
</ul>
<p>Usually, <code>Critical</code>, <code>Error</code>, and sometimes <code>Warn</code> levels contain a <strong>full stack trace</strong> for full analysis of the technical context of the problem.</p>
<p>You will probably want different configurations for your logging between your local development environment, your testing environment, and your production environment. It's worth noting that, with most logging libraries, configuring for one level includes the levels above it. For example, if you configure for the <code>Info</code> level, it'll include all levels above it, too, in the logging. So you'll also get <code>Info</code>, <code>Warn</code> (where available), <code>Error</code>, and <code>Critical</code>.</p>
<p>Some libraries allow you to set an upper limit as well, but this is for very specific scenarios, since you would rarely want to see <code>Error</code> logs but not <code>Critical</code> logs, for example.</p>
<p>However, all solutions have different needs, and these needs can change depending on what problem occurs when. Let's glance through some examples of various setups.</p>
<h2>Local Development Environment</h2>
<p><strong>In a development environment on your local machine, you probably want to start off with the <code>Debug</code> level of logging.</strong> You can lower it to <code>Trace</code> if more detailed issues need diagnosed, or increase it to <code>Info</code> if it gets too noisy.</p>
<p>You'll probably always want to write to local files on your file system for quick access, and avoid writing to any central logging destination, unless you're explicitly testing the integration itself. This is because it can generate a lot of data over time, even with a small team, which usually increases cost quite a lot.</p>
<h2>Testing Environment</h2>
<p><strong>In a testing environment, which is not on your local machine, you probably want to set the logging level to <code>Info</code> and then temporarily turn the level down to <code>Debug</code> and <code>Trace</code> as needed for the moment.</strong> If you're not interested in the normal flow of the app and only want to catch errors, you can actually set the logging level to <code>Error</code>.</p>
<p>As it's a test environment, you should only have test data logging here, so lower logging levels should not be logging sensitive production data. You don't have to worry about that aspect here, even if you should always be vigilant of it.</p>
<p>Since this is not a local machine, you probably don't have (and maybe shouldn't have) easy access to the machine's local disk. This is a good opportunity to use a central logging solution (there are plenty of those on the market at the moment at various pricing levels).</p>
<h2>Production Environment</h2>
<p><strong>In a production environment, you could start out by configuring your logging to write <code>Info</code> and higher to a centralized logging service, such as a SaaS solution in the cloud. You should consider writing the <code>Error</code> level and higher to local disk files, in case the SaaS has any technical issues.</strong> Remember to manage the retention of these local logs to avoid being incompliant with data privacy rules and to keep costs down.</p>
<p>For the very complicated and hard-to-solve bugs, you need to be able to activate <code>Debug</code> and <code>Trace</code> level logging. To comply with data privacy rules without too much hassle, consider writing only these two low levels to local files, which can easily be deleted, and set a very short retention time for them. <strong>Always consider using a <em>staging environment</em> first, when using the <code>Trace</code> level</strong> and only use the production environment as the last way out.</p>
<p>You can also consider forwarding just the <code>Info</code> entries to an analysis tool of choice, to analyze key business events being triggered in your system. Some logging libraries support sending additional data in their entries, which can make this scenario more powerful.</p>
<p>If needed, you can also configure <code>Error</code> and <code>Critical</code> to log to another external system, maybe another SaaS, that sends out alerts for these log entries. Many existing SaaSes for logging have this functionality built-in, so there's usually not a need for this configuration, but it's good to know it's an option.</p>
<p>This would then mean that you're double-logging between two systems, but since you hopefully won't have a large volume of this kind of log entry, you don't have to worry too much about cost and you're only using the right tool for the right job.</p>
<h1>The Caveats of Logging</h1>
<p>Logging a lot of data might be the only way to solve some of the craziest bugs created out there, but there are some downsides of logging too much.</p>
<p><strong>Depending on the solution you choose for logging data, too much data could make it very hard to easily search through your logs.</strong> If the logging is split into different files for different days (or hours), it's hard to analyze a trend over time.</p>
<p>Normally, no matter what solution you have in place for logging, <strong>the more data you log, the higher the cost will be</strong>. Even if you're logging to the local disk of the server, space can get used up quickly over time.</p>
<p>There is also a major security risk of leaking sensitive data if you store too much data, with too detailed information, for too long. <a href="https://en.wikipedia.org/wiki/Internet_privacy#Global_privacy_policies">With all the new privacy rules out there</a>, you also risk being noncompliant for things like <a href="https://en.wikipedia.org/wiki/General_Data_Protection_Regulation">GDPR</a>, if you store personal information too long, even in local files.</p>
<h1>Conclusion</h1>
<p><strong>Logging is something you should include in your workflow right from the beginning when writing your software. Setting it up, and with the right strategy, will enable you to produce well-running software, <a href="https://lightrun.com/debugging/complete-agility-extend-your-ci-cd-pipelines-with-continuous-debugging-and-continuous-observability/">monitor it in a robust way</a>, and quickly access details about any problems that occur. Just keep the potential pitfalls in mind, such as cost, searchability, and security.</strong></p>
<p>Having a solid strategy for which log level to use for what scenario is crucial for a long-term path growth path for your software. This will allow you to extend your logging in the future, making use of more refined and mature logging tools, as your software grows and the need inevitably shows itself.</p>]]></content:encoded>
      <pubDate>Sun, 05 Sep 2021 12:27:00 GMT</pubDate>
      <category>.NET</category>
      <category>C#</category>
    </item>
    <item>
      <title>Best 20 C# &amp; .NET Blogs</title>
      <link>https://sebnilsson.com/blog/best-csharp-dotnet-blogs</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/best-csharp-dotnet-blogs</guid>
      <description>Whether you&apos;re getting into C# with the launch of .NET 5 or if you&apos;re a long-time C# developer, there are a lot of great blogs out there</description>
      <content:encoded><![CDATA[<p><em><strong>Originally written for and published on <a href="https://draft.dev/learn/technical-blogs/c-sharp-blogs">the Draft.dev blog</a></strong></em></p>
<hr>
<p>C# is a language loved by its users for being versatile, productive, and keeping up with
the current problems programmers need to solve. For example, maintainers have added
functional programming concepts to the primarily imperative C# language, adding the powerful
query-functionality in LINQ, and smoother handling of asynchronous programming with the <code>async</code> / <code>await</code>-syntax.</p>
<p>The unification of all versions of .NET into .NET 5 has given C# and .NET future-safety,
no matter which platform you use to develop or which platform you develop for: desktop, web, cloud, mobile, or other.</p>
<p>Whether you're getting into C# with the launch of .NET 5 or if you're a long-time C# developer,
there are a lot of great blogs out there. Based on writing quality, consistency, longevity,
technical depth, and usefulness, I've put together <strong>this comprehensive list of the 20 best C# blogs publishing today</strong>.</p>
<h1><a href="https://devblogs.microsoft.com/dotnet/">.NET Blog</a></h1>
<p><img src="https://files.sebnilsson.com/web/images/top-csharp-blogs/dotnet-blog.jpg" alt="Screenshot of the .NET Blog"></p>
<p>Maybe a bit obvious, but the .NET blog from Microsoft should probably your first place to check
for new updates on C# and .NET. They consistently publish updates about everything related to C# and .NET
but also dives deep into different subjects, such as functionality deep within .NET or the details
about the performance improvements being made there.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 5</li>
<li>Longevity: 5</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 5</li>
</ul>
<h1><a href="https://www.hanselman.com/blog">Scott Hanselman's Blog</a></h1>
<p><img src="https://files.sebnilsson.com/web/images/top-csharp-blogs/hanselman.jpg" alt="Screenshot of Scott Hanselman&#x27;s blog"></p>
<p>Sometimes, I'm unsure if you're allowed to use .NET if you're not reading Scott Hanselman's blog.
There might be something in EULA about that. 😉</p>
<p>Jokes aside, Hanselman has been blogging about C# and .NET from the very start and has inspired
many people with the content on his blog, his conference-talks, his podcast, his YouTube-channel, and more.
He mixes high-level articles with some technical deep-dives, and his posts seem to be driven by his passion
for learning and solving problems.</p>
<p>Hanselman has become quite a household name in the .NET-world and is often featured in Microsoft's keynotes.
This is well deserved, given his unparalleled professional approach to quality content, interesting topics,
and entertaining delivery.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 5</li>
<li>Longevity: 5</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 5</li>
</ul>
<h1><a href="https://ardalis.com/blog">Ardalis: Steve Smith's Blog</a></h1>
<p><img src="https://files.sebnilsson.com/web/images/top-csharp-blogs/ardalis.jpg" alt="Screenshot of Ardalis: Steve Smith&#x27;s Blog"></p>
<p>Steve Smith (aka Ardalis) is a strong proponent of producing content instead of consuming it,
which is quite obviously something he lives by when you look at his output. If you are a person
wanting to build your own brand, Steve is a role-model. He continually puts out qualitative information
on his blog, on Twitter, on Twitch, his newsletter, his Pluralsight-courses, and he now also is
building his own developer coaching-community.</p>
<p>Lately, Steve has been focusing on a lot of content around Domain-Driven Design (DDD) and the SOLID-principles.
Much of this can be found on his blog and on some great Pluralsight-courses.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 5</li>
<li>Longevity: 5</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 4</li>
</ul>
<h1><a href="https://www.alvinashcraft.com/">Alvin Ashcraft's Morning Dew</a></h1>
<p><img src="https://files.sebnilsson.com/web/images/top-csharp-blogs/morning-dew.jpg" alt="Screenshot of Alvin Ashcraft&#x27;s Morning Dew"></p>
<p>Want to see basically every new article the community puts out every single day? Well, then Alvin Ashcraft's
<em>Morning Dew</em> has you covered when it comes to the subjects of C#, .NET, and the Microsoft-ecosystem.</p>
<p>Alvin Ashcraft is a longtime Microsoft MVP. From what you can tell from comments on his website,
Morning Dew is the result of Alvin reading through over 1,600 feeds daily. Any interesting articles found
are categorized and published on the blog.</p>
<ul>
<li>Writing Quality: N/A</li>
<li>Consistency: 5</li>
<li>Longevity: 5</li>
<li>Technical Depth: N/A</li>
<li>Broad Usefulness: 5</li>
</ul>
<h1><a href="https://www.troyhunt.com/">Troy Hunt</a></h1>
<p><img src="https://files.sebnilsson.com/web/images/top-csharp-blogs/troy-hunt.jpg" alt="Screenshot of Troy Hunt&#x27;s blog"></p>
<p>One of the most influential voices on the topic of security within the Microsoft-ecosystem is Troy Hunt.
He is the creator of the service <a href="https://haveibeenpwned.com/">Have I Been Pwned (HIBP)</a>, which helps users
identify if and how they've been affected by data breaches and other malicious activity on the internet.</p>
<p>Since Troy has Microsoft MVP for developer-security for a longer time, this is a subject that is often covered on his blog.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 5</li>
<li>Longevity: 5</li>
<li>Technical Depth: 4</li>
<li>Broad Usefulness: 3</li>
</ul>
<h1><a href="https://timheuer.com/blog">Tim Heuer</a></h1>
<p>Tim Heuer has worked at Microsoft since 2005, currently with a focus on .NET, Visual Studio, and Azure.
This gives him a lot of insight, which you can tell by the in-depth content and a great variety of technologies covered on his blog.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 4</li>
<li>Longevity: 5</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 4</li>
</ul>
<h1><a href="https://thedatafarm.com/blog/">The Data Farm: Julie Lerman's World of Data</a></h1>
<p>Julie Lerman is the absolute authority on <a href="https://docs.microsoft.com/en-us/ef/">Entity Framework</a>, including EF Core.
No matter if you're looking for a detailed, low-level problem within EF, or if you're looking for
a solid guide to walk you through various aspects of Entity Framework, there is a real chance that you'll find
material around it produced by Julie Lerman.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 4</li>
<li>Longevity: 5</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 4</li>
</ul>
<h1><a href="https://haacked.com/">You've Been Haacked</a></h1>
<p>Phil Haack was an integral part of helping Microsoft make many of its development-software open-source,
along with Scott Hanselman, Scott Guthrie, and Rob Connery. Since then, Phil has moved on from Microsoft
but still consistently blogs about technologies in the Microsoft-ecosystem.</p>
<p>With his experience inside Microsoft during their exciting transformation, there is usually something interesting
for developers to learn in his posts.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 4</li>
<li>Longevity: 5</li>
<li>Technical Depth: 4</li>
<li>Broad Usefulness: 4</li>
</ul>
<h1><a href="https://blog.ploeh.dk/">ploeh: Mark Seemann</a></h1>
<p>If you feel completely bulletproof in your coding-skills, go check out Mark Seemann's blog and
get a glimpse of the endless things you could be doing better.</p>
<p>Writing good software is an art, so you can have endless opinions on other peoples' opinions,
but there is almost always something new to learn from Mark's deep-diving and well-thought-through posts.
Each article walks you through a complicated concept and helps you understand it and become a better developer
who writes better, more maintainable code.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 5</li>
<li>Longevity: 5</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 3</li>
</ul>
<h1><a href="https://www.cazzulino.com/">Daniel Cazzulino's Blog</a></h1>
<p>Daniel Cazzulino (aka 'kzu') is the creator of the most popular mocking framework for C# and
.NET, called <a href="https://github.com/moq/moq4">Moq</a>, which is used for testing the source-code behind .NET.</p>
<p>Kzu writes about all different kinds of technologies related to the Microsoft-ecosystem and mixes
high-level guides with technical deep dives into non-mainstream topics.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 4</li>
<li>Longevity: 4</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 4</li>
</ul>
<h1><a href="https://devblogs.microsoft.com/visualstudio/">Visual Studio Blog</a></h1>
<p>The blog for the Visual Studio Engineering Team, of course, covers everything related to Visual Studio,
but it also includes adjacent technologies, which can contribute to great inspiration. They cover topics
like Unity, Visual Studio for Mac, GitHub-integrations, debugging with memory-dumping, and much more.</p>
<p>For anyone interested in productivity in your Visual Studio-editor, it's worth noting that
<a href="https://www.madskristensen.net/">Mads Kristensen</a> writes posts on the subject on the Visual Studio Blog.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 5</li>
<li>Longevity: 4</li>
<li>Technical Depth: 4</li>
<li>Broad Usefulness: 4</li>
</ul>
<h1><a href="https://blog.maartenballiauw.be/">Maarten Balliauw</a></h1>
<p>Maarten has a focus on web and cloud-apps, but he was also the founder of <a href="https://www.myget.org/">MyGet</a> and
is a frequent speaker at conferences. He brings a unique mix of experiences and knowledge to his blog,
which covers many different technologies within the Microsoft-ecosystem.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 4</li>
<li>Longevity: 5</li>
<li>Technical Depth: 4</li>
<li>Broad Usefulness: 4</li>
</ul>
<h1><a href="https://codeblog.jonskeet.uk/">Jon Skeet</a></h1>
<p>If you have ever searched for a C# question, there's a 99% chance that you have, at least once,
landed on an answer on Stack Overflow by Jon Skeet.</p>
<p>He's been <strong>the number one contributor to Stack Overflow</strong> for many years and usually with a focus
on .NET and C#. It is said that he's read the whole C# specification (maybe more than once),
and he regularly blogs about some of the deepest subjects around C#.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 4</li>
<li>Longevity: 4</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 3</li>
</ul>
<h1><a href="https://jimmybogard.com/">Jimmy Bogard</a></h1>
<p>Jimmy is the man behind some of the most productivity-boosting frameworks for C# and .NET developers:
AutoMapper and MediatR. These frameworks help you write isolated and more easily maintainable code and
his blog-posts revolve around these frameworks, but also these topics in general.</p>
<ul>
<li>Writing Quality: 4</li>
<li>Consistency: 4</li>
<li>Longevity: 4</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 4</li>
</ul>
<h1><a href="https://weblog.west-wind.com/">Rick Strahl's Web Log</a></h1>
<p>If you've worked with C# and .NET since its early days, especially with a focus on ASP.NET and the web,
you will most probably have run into more than one of Rick Strahl's blog-posts.</p>
<p>In the course of working hands-on solving problems for clients, Rick has documented many solutions to
real-world problems on his blog. The topics often coincide with what other people are looking to answer,
so you can often find one of Rick's articles linked in answers on Stack Overflow.</p>
<ul>
<li>Writing Quality: 4</li>
<li>Consistency: 4</li>
<li>Longevity: 5</li>
<li>Technical Depth: 4</li>
<li>Broad Usefulness: 5</li>
</ul>
<h1><a href="https://blog.jetbrains.com/dotnet/">.NET Tools Blog - JetBrains</a></h1>
<p>JetBrain's blog for .NET covers very useful topics, whether you use their tools or not. They have specific posts
about the popular tools ReSharper and Rider, but also great articles about a broad range of topics related to C# and .NET.</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 5</li>
<li>Longevity: N/A</li>
<li>Technical Depth: 4</li>
<li>Broad Usefulness: 4</li>
</ul>
<h1><a href="http://wildermuth.com/">Shawn Wildermuth</a></h1>
<p>Shawn has been a prolific creator of content around C#, .NET, and the Microsoft-ecosystem since the
earliest days of .NET. Focusing mainly on the web-aspect of .NET, and recently, with a lot of Vue.js content,
he now describes himself as an author, teacher, and filmmaker.</p>
<ul>
<li>Writing Quality: 4</li>
<li>Consistency: 4</li>
<li>Longevity: 5</li>
<li>Technical Depth: 4</li>
<li>Broad Usefulness: 3</li>
</ul>
<h1><a href="https://ericlippert.com/">Fabulous Adventures in Coding - Eric Lippert's Blog</a></h1>
<p>Eric Lippert used to work on the C# language design team, which is clear based on his superhuman
rate of answering questions on Stack Overflow. On his blog, he deep-dives into language-design
with a focus on "making developers' lives better and having fun doing it".</p>
<ul>
<li>Writing Quality: 5</li>
<li>Consistency: 3</li>
<li>Longevity: 5</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 2</li>
</ul>
<h1><a href="https://www.strathweb.com/">StrathWeb - Filip W.</a></h1>
<p>When searching for various intricate technical issues related to the Microsoft-ecosystem,
it is not uncommon to end up on an in-depth article written by Filip. Recently, the topic of
quantum computing has been a big focus, to add to the long archive of articles focused on C# and .NET.</p>
<ul>
<li>Writing Quality: 4</li>
<li>Consistency: 4</li>
<li>Longevity: 4</li>
<li>Technical Depth: 5</li>
<li>Broad Usefulness: 3</li>
</ul>
<h1><a href="http://dontcodetired.com/blog">Don't Code Tired - Jason Roberts</a></h1>
<p>Jason Roberts is a former Microsoft MVP who blogs on different topics around the Microsoft-ecosystem.
Lately, he's been focused on testing with posts about approval tests, feature-flags, and optimizing various testing frameworks.</p>
<ul>
<li>Writing Quality: 4</li>
<li>Consistency: 4</li>
<li>Longevity: 5</li>
<li>Technical Depth: 4</li>
<li>Broad Usefulness: 2</li>
</ul>
<h1>Final Words</h1>
<p>While researching this post, I went through every single account I subscribe to on Twitter to pick out
the best blogs from the best people I've been following for many years - often over a decade.
I was surprised to recognize so many names I had associated with quality content and learned so much from,
but what surprised me even more was how many of these people had stopped blogging or switched focus away from C# and .NET.</p>
<p>Scott Guthrie is probably a perfect example. He used to be one of the top bloggers within our field before he
was promoted to the fancy title of <em>Executive Vice President</em> at Microsoft, where the blog no longer seemed to be a priority.
So subscribe to these blogs while you can. You never know when things will change.</p>]]></content:encoded>
      <pubDate>Mon, 21 Dec 2020 14:44:00 GMT</pubDate>
      <category>.NET</category>
      <category>C#</category>
    </item>
    <item>
      <title>From C# to Rust: Code Basics</title>
      <link>https://sebnilsson.com/blog/from-csharp-to-rust-code-basics</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/from-csharp-to-rust-code-basics</guid>
      <description>Understanding Rust&apos;s code-basics could possibly be fast-tracked by reating these concepts to the equivalent in C# and .NET.</description>
      <content:encoded><![CDATA[<p>Now that we've gotten the <a href="/blog/from-csharp-to-rust-fundamentals/">fundamentals</a> out of the way, we can finally get into the basics of Rust-code.</p>
<p><a href="/blog/from-csharp-to-rust-introduction/">This series</a> is written from the perspective of getting to know Rust from knowing C#. There are already many good <em>getting-started-guides</em> out there for Rust, but <strong>this series tries to leverage your existing .NET/C#-knowledge</strong> to fast-track you into understanding Rust, by relating the concepts to what you already know.</p>
<p><em><strong>Disclaimer</strong>: This series is being written while I'm learning Rust, so I'm by no means an expert and maybe this might be reflected in some of the example code. If you find any blatant errors, feel free to add some feedback in the comments.</em></p>
<h1>Setup</h1>
<p>In the post about <a href="/blog/from-csharp-to-rust-fundamentals/">Rust fundamentals</a>, we could read about how <strong>Rust provides a complete toolchain</strong> for development, which is made available by installing <a href="https://www.rust-lang.org/tools/install">Rustup, the recommended way of installing Rust</a>.</p>
<p>Create a new project in a command-line by running: <code>cargo new example_project</code></p>
<p>The file <strong><code>src/main.rs</code> is your application's entry point</strong>, which will be run when you execute <code>cargo run</code>. So this is a good file to start with, to <strong>follow along in your code-editor</strong>. This file will only contain a function called <code>main</code>, with a simple <em>Hello World</em>, when generated by Cargo:</p>
<pre><code class="language-rust">fn main() {
    println!("Hello, world!");
}
</code></pre>
<h1>Code Basics</h1>
<p>Let's start looking at some basic example-code, to go through some basic concepts in Rust. Since Rust is a C-style language, <strong>you will probably recognize much of the syntax</strong> you see in this code-snippet, as a C#-developer.</p>
<p>You can always find the full code for this series in the <a href="https://github.com/sebnilsson/from-csharp-to-rust"><code>from-csharp-to-rust</code>-project on GitHub</a>.</p>
<h1>Variables</h1>
<p><a href="https://doc.rust-lang.org/reference/variables.html">Variables in Rust</a> are created by using the <code>let</code>-keyword, in a very similar way to the use of the <code>var</code>-keyword in C#.</p>
<pre><code class="language-rust">let title = "Ghost Buster";
let year: u16 = 1984;
</code></pre>
<p><a href="https://doc.rust-lang.org/stable/rust-by-example/types/inference.html">Types are inferred</a> in Rust, as you can see with the <code>title</code>-variable. You can also specify the type, as we do with the <code>year</code>-variable, where we want to use a smaller number-type than what is inferred by default.</p>
<p>One important difference between Rust and C# is that <strong>variables are immutable by default in Rust</strong>. If you want a variable to be mutable, you have to add <code>mut</code> to the variable declaration:</p>
<pre><code class="language-rust">let mut rating_avg = 7.8;

// Someone rated the movie
rating_avg = 7.9;
</code></pre>
<p>Variables can be created as <a href="https://doc.rust-lang.org/std/keyword.const.html">constants in Rust</a>, by using the <code>const</code>-keyword, just like in C#. These variables <strong>have to have their types specified</strong>:</p>
<pre><code class="language-rust">const GOOD_MOVIE_LIMIT: f32 = 7.5;
</code></pre>
<h2>Naming Conventions</h2>
<p>As you may have noticed, <a href="https://doc.rust-lang.org/1.0.0/style/style/naming/README.html">Rust naming conventions</a> use <a href="https://en.wikipedia.org/wiki/Snake_case"><em>Snake case</em></a> (<code>example_variable_name</code>) for its variables. Unlike C# which usually uses <a href="https://en.wikipedia.org/wiki/Camel_case">Camel case</a> (<code>exampleVariableName</code>) or Pascal case (<code>ExampleVariableName</code>) for most of its variables, fields, and properties.</p>
<p>The Rust-compiler will even warn you about using the wrong conventions for different namings.</p>
<p><em>At the moment of writing, Rust-developers also seems to prefer shorter names for variables and functions, than we usually see in C# and .NET.</em></p>
<h1>Strings</h1>
<p>In C#, we only have one string-type for all types of strings. <strong>In Rust, there are two different string-types</strong> you learn about at first. The primitive <a href="https://doc.rust-lang.org/std/str/"><code>str</code>-type</a>, which is immutable and fixed length, as well as the <a href="https://doc.rust-lang.org/std/string/struct.String.html"><code>String</code>-type</a>, which can be modified and grow in size.</p>
<pre><code class="language-rust">let mut value = String::default();
for x in 65..68 {
    value.push(x as u8 as char);
}
// value: "ABC"
</code></pre>
<p>On Stack Overflow, a question called <a href="https://stackoverflow.com/questions/24158114/what-are-the-differences-between-rusts-string-and-str/24159933#24159933"><em>What are the differences between Rust's <code>String</code> and <code>str</code>?</em></a> the most popular answer, by far, summarizes:</p>
<blockquote>
<p>Use <code>String</code> if you need owned string data (like passing strings to other threads, or building them at runtime), and use <code>str</code> if you only need a view of a string.</p>
</blockquote>
<p>Conversion between the two types is quite straight forward:</p>
<pre><code class="language-rust">let title = "Ghostbusters";
// From str to String
let title_string = String::from(title);
// From String to str
let title_str = title_string.as_str();
</code></pre>
<p>In practical use, you can see Rust's <code>String</code>-type as the equivalent of <code>StringBuilder</code> in C#, while Rust's <code>str</code> is the equivalent of <code>ReadOnlySpan&#x3C;char></code>.</p>
<h1>Collections</h1>
<p>In Rust, the two most common types used to create collections are <a href="https://doc.rust-lang.org/std/primitive.array.html"><code>array</code></a> and <a href="https://doc.rust-lang.org/std/vec/struct.Vec.html"><code>Vec</code> (vector)</a>.</p>
<p>The <code>array</code>-type is a fixed-size collection and is compared to an array of a type in C# and .NET, like <code>string[]</code>, <code>int[]</code>, <code>char[]</code>, and so on. The <code>Vec</code>-type can change in size and can be compared to <code>List&#x3C;T></code> in C# and .NET.</p>
<p><strong>Both types can be mutated</strong> if the variable has the <code>mut</code>-keyword. They can also easily be converted between each other.</p>
<pre><code class="language-rust">let mut arr = [1, 2, 3];
arr[0] = 0;

let mut vec = vec![1, 2];
vec.push(3);
vec[0] = 0;

let arr_from_vec = &#x26;vec[0.. 2];
let vec_from_arr = arr.to_vec();
</code></pre>
<p>Rust also has the <a href="https://doc.rust-lang.org/std/collections/struct.HashMap.html"><code>HashMap</code>-type</a>, which is the equivalent of <code>Dictionary&#x3C;TKey, TValue></code> in C# and .NET.</p>
<pre><code class="language-rust">let mut map = HashMap::new();
map.insert("key", 123);
</code></pre>
<h2>Generics</h2>
<p>Rust has <a href="https://doc.rust-lang.org/book/ch10-00-generics.html">support for generics</a>, just like C# does, but with a little bit more smart functionality built into the compiler. In C#, you don't always have to specify the exact generics used in some contexts, since it can be inferred. Rust has some additional functionality for this.</p>
<p>For both <code>Vec</code> and <code>HashMap</code>, <strong>you don't have to specify the type explicitly</strong> when creating the object. It will be <strong>inferred by the first items added</strong> and enforced to the following added items.</p>
<h1>Logic Flow</h1>
<p>The <code>if</code>-statements in Rust are very similar to the ones in C#, except <strong>you don't need parenthesis around the condition</strong>, but it's possible to use it if you want to.</p>
<pre><code class="language-rust">let mut info = String::from(title);

if rating_avg >= GOOD_MOVIE_LIMIT {
    info += " - You're in for a good movie";
} else if rating_avg &#x3C; GOOD_MOVIE_LIMIT &#x26;&#x26; year &#x3C;= 1995 {
    info += "- Could be a classic...";
} else {
    info += "... Do you want to look for another movie?";
}
</code></pre>
<p>The <code>if</code>-statement is in itself an expression, from which you can catch the result. This means that you can <strong>use an <code>if</code>-statement to assign a variable</strong>. You can catch the value resulting from the <code>if</code>, the <code>if else</code>, or the <code>else</code>.</p>
<p>This can be used to create a <em>shorthand if</em>. This is a good replacement for the <em>ternary conditional operator</em> (In C#: <code>var a = true ? b : c</code>), since it's not supported in Rust.</p>
<pre><code class="language-rust">let classic = year &#x3C;= 1995 &#x26;&#x26; !title.ends_with(" II");
let text = if classic { "Potential classic" } else { "We'll see..." };
</code></pre>
<h2>Pattern Matching</h2>
<p>Rust also has very appreciated <a href="https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html">pattern-matching-functionality</a>. This is something that <a href="https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching">C# has implemented</a> and seems to keep on improving it with every version.</p>
<p>Pattern-matching in Rust can do a lot more than just replace simple <code>if</code>-statements. It can also, among other things, be used to assign the result to variables. More sophisticated usages will be shown as this series goes on.</p>
<pre><code class="language-rust">let info = match rating_avg {
    x if x > GOOD_MOVIE_LIMIT =>
        format!("{} - You're in for a good movie", x),
    x if x &#x3C; GOOD_MOVIE_LIMIT &#x26;&#x26; year &#x3C;= 1995 =>
        format!("{} - Could be a classic...", x),
    _ => String::from("... Do you want to look for another movie?"),
};
</code></pre>
<h1>Standard Output</h1>
<p>To write to the standard output, in the equivalent way of <code>Console.WriteLine</code> in C# and .NET, you use <a href="https://doc.rust-lang.org/std/macro.println.html">the function <code>println!</code></a> in Rust.</p>
<pre><code class="language-rust">let hello = "Hello";
let world = "World";
println!("{} {}!", hello, world);
</code></pre>
<p>In .NET, we specify the indexes inside the placeholder-curly brackes like this: <code>Console.WriteLine("{1} {0}!", world, hello)</code>. These explicit indexes can also be used in Rust like this <code>println!("{1} {0}!", world, hello)</code>.</p>
<p>If you don't specify the index In Rust and just use the empty placeholder <code>{}</code>, as in the above example, <strong>the index will be inferred</strong>.</p>
<p>Just like in C# and .NET, you can <a href="https://doc.rust-lang.org/stable/rust-by-example/hello/print/fmt.html">add formatting</a> to a parameter by adding a colon-character (<code>:</code>) after it and a specific flag. For debugging, it's very useful to use the <a href="https://doc.rust-lang.org/rust-by-example/hello/print/print_debug.html">debug-formatting</a>, which <strong>shows the whole content of the object</strong> in the console.</p>
<pre><code class="language-rust">let obj = complex_obj();
println!("Debug: {:?}", obj);
println!("Debug pretty: {:#?}", obj);
</code></pre>
<h2>Command-Line Arguments</h2>
<p><em>Standard input</em> is not as straight forward as <em>Standard output</em> and not a one-liner like in C# and .NET with <code>Console.ReadLine</code>. So we will get more into that in future articles.</p>
<p>Getting access to the <strong>command-line arguments passed to the application</strong> is a lot easier. This can be used to quickly build a CLI-type application.</p>
<pre><code class="language-rust">// Execute: cargo run "The Shawshank Redemption"

let args: Vec&#x3C;String> = env::args().collect();
let first_arg =
    if args.len() > 1 { args[1].clone() } else { String::default() };
// first_arg: "The Shawshank Redemption"
</code></pre>
<h1>Functions</h1>
<p><a href="https://doc.rust-lang.org/book/ch03-03-how-functions-work.html">Functions in Rust</a> work just like in C#, except you declare it using the <code>fn</code>-keyword. <strong>You can specify a return-type</strong>, but if you don't the function will just act like a <code>void</code> in C#.</p>
<pre><code class="language-rust">fn is_sequel(title: &#x26;str) -> bool {
    if title.is_empty() {
        return false;
    }
    let sequel_title = title.ends_with("II");

    sequel_title
}
</code></pre>
<p>Notice the <strong>lack of a trailing semicolon</strong> (<code>;</code>) behind the <code>sequel_title</code>-variable at the end of the method. This makes the statement into an "expression" and Rust will <strong>implicitly accept this as a return statement</strong>, as long as it's the <strong>final expression of the method</strong>. This is why a <code>return</code> is needed in the body of the early returning <code>if</code>-statement.</p>
<p>This is a <strong>feature that might look weird</strong> when you're coming from another C-style language, but that you will see all over the place in Rust-code.</p>
<p>Functions are then called with their names and parameters, just like in C#:</p>
<pre><code class="language-rust">fn main() {
    let sequel = is_sequel("Ghostbusters");
}
</code></pre>
<h2>Macros</h2>
<p>In this article, you've seen the use of the <code>println!</code>-function, with the unusually looking exclamation-point (<code>!</code>) at the end. This annotation makes this a <em>macro</em>. This is a too advanced of a topic to go into in this part about basic code, so I'll just point to official <a href="https://doc.rust-lang.org/book/ch19-06-macros.html">Rust Programming Language Book's section on macros</a>, which summarizes:</p>
<blockquote>
<p>Macros are a way of writing code that writes other code, which is known as <em>metaprogramming</em>.</p>
</blockquote>
<h1>Null-Values</h1>
<p>The notorious <a href="https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions"><em>Billion dollar mistake</em></a> of allowing <code>null</code>-values into programming languages seems to be a <strong>problem tackled only recently</strong>. C# got <em>Nullable reference types</em> with C# 8.0 in late 2019 and F# doesn't have nulls built-in at all (even if there are ways to get around it).</p>
<p><strong>Rust does not support null values.</strong> Instead, it uses patterns to wrap around types, to help indicate if there was a value or not. The most commonly used such type is the <a href="https://doc.rust-lang.org/std/option/"><code>Option</code>-type</a>. It will return either a <code>Some</code>, carrying the value, or a <code>None</code>, indicating there was no object.</p>
<pre><code class="language-rust">fn main() {
    let quotient = divide(123, divisor);
    match quotient {
        Some(x) if x > 10 => println!("Big division result: {}", x),
        Some(x) => println!("Division result: {}", x),
        None => println!("Division failed")
    }
}

fn divide(dividend: i32, divisor: i32) -> Option&#x3C;i32> {
    if divisor == 0 {
        return None;
    }
    Some(dividend / divisor)
}
</code></pre>
<h1>Object-Orientation</h1>
<p><a href="https://doc.rust-lang.org/book/ch17-00-oop.html">Object-orientation in Rust</a> is a point in the code-basics where <strong>things get a bit strange</strong> for the regular C#-developer. In C#, we are used to having a <code>class</code> that owns its fields, properties, and methods. The class is then a template for creating an <em>instance</em> of an object.</p>
<p>Rust uses what they call a <code>struct</code> instead of classes, which is just a "dumb" data-structure, which contains fields, but <strong>they do not (directly) contain properties or methods</strong>. Instead, Rust provides the possibility to add functions to a <code>struct</code> through the <code>impl</code>-keyword.</p>
<pre><code class="language-rust">pub struct Movie {
    pub title: String,
    release_year: u16,
}

impl Movie {
    // Constructor - Associated method
    pub fn new(title: &#x26;str, release_year: u16) -> Self {
        Self { title: String::from(title), release_year }
    }
    // Method
    pub fn display_title(&#x26;self) -> String {
        format!("{} ({})", self.title, self.release_year)
    }
    // Method with mutability
    pub fn update_release_year(&#x26;mut self, year: u16) {
        self.release_year = year;
    }
}

fn main() {
    let movie = Movie::new("Ghostbusters", 1984);
    println!("Movie: {}", movie.display_title());
}
</code></pre>
<p>First, we define the <code>struct</code> and its fields, where we add the <code>pub</code>-keyword to the <code>struct</code> itself and its field <code>title</code>. <strong>All public fields are mutable</strong> in Rust if used with the <code>mut</code>-keyword by other code. If you want to protect a field, use methods, like the <code>update_release_year</code>-function in this example.</p>
<p>In the <code>impl</code>-block, we have the <code>new</code>-function, which acts as a constructor. It's an associated method to the type, which is the equivalent of a <code>static</code> method in C#. It returns a new instance of the implementing type, which in this case is a <code>Movie</code>.</p>
<p>The function <code>display_title</code> is a <strong>method on the instance of the object</strong> and through the <code>self</code>-keyword in the first parameter, it can access fields and methods on the object, even non-public ones. It's the equivalent to the <code>this</code>-keyword in C#.</p>
<h2>Inheritance</h2>
<p><strong>Rust doesn't have inheritance</strong>. At least not in the way a C#-developer is used to it. This seems to be a very <a href="https://doc.rust-lang.org/book/ch17-01-what-is-oo.html">conscious decision</a> by the Rust-team.</p>
<p>Instead of inheritance, Rust provides the very powerful <a href="https://doc.rust-lang.org/1.9.0/book/traits.html">concept of traits</a>, which allows you to extend any type, no matter if you've created the type or not. As a C#-developer it acts like of mixture of interfaces and extension-methods. We will look closer at traits in upcoming articles in the series.</p>
<h1>Ownership &#x26; Borrowing</h1>
<p>You've seen the use of the ampersand (<code>&#x26;</code>) character in some places of the example-code. These indicate that the use of an object is done by reference. This is something you normally very rarely need to deal with in C# and .NET.</p>
<p>This relates to the unique concept in Rust of <a href="https://doc.rust-lang.org/1.8.0/book/references-and-borrowing.html">References and Borrowing</a>. This is <strong>the foundational concept which enables the language's memory-safety, performance, and "<a href="https://doc.rust-lang.org/book/ch16-00-concurrency.html">fearless concurrency</a>"</strong>.</p>
<p>This is also one of <strong>the hardest parts of Rust to master</strong>. It's usually the number one complaint about the language from newcomers and a very frequent topic on Stack Overflow and other Q&#x26;A-forums.</p>
<h1>Primitives Comparison Table</h1>
<p>Rust has a set of primitives that overlap quite well with the ones we're used to in C# and .NET. They are listed for reference:</p>

































































<table><thead><tr><th>Rust</th><th>C#</th><th>Comments</th></tr></thead><tbody><tr><td>bool</td><td>bool</td><td></td></tr><tr><td>char</td><td>char</td><td></td></tr><tr><td>i8, i16, i32, i64, i128</td><td>sbyte, short, int, long, (N/A)</td><td>Signed 128-bit integer not available in .NET</td></tr><tr><td>u8, u16, u32, u64, u128</td><td>byte, ushort, uint, ulong, (N/A)</td><td>Unsigned 128-bit integer not available in .NET</td></tr><tr><td>isize, usize</td><td>(N/A)</td><td></td></tr><tr><td>f32, f64</td><td>float, double</td><td>.NET has the 128-bit floating-point number <code>decimal</code>, which is not available in Rust out-of-the-box</td></tr><tr><td>Array</td><td>Array</td><td>Not a primitive type in .NET. Fixed size.</td></tr><tr><td>Tuple</td><td>System.ValueTuple / System.Tuple</td><td>Not a primitive type in .NET</td></tr><tr><td>Slice</td><td>Span&#x3C;T></td><td>Not a primitive type in .NET</td></tr><tr><td>str</td><td>string</td><td><code>str</code> is immutable and fixed length in Rust, but not in .NET</td></tr><tr><td>Function</td><td>Func&#x3C;T></td><td>Not a primitive type in .NET</td></tr></tbody></table>
<h1>Learn More</h1>
<p>If you want to learn more about Rust, unrelated to its equivalent functionality in C# and .NET, you can check out the <a href="https://www.rust-lang.org/learn"><em>Learn Rust</em>-section</a> of the official website, which points to multiple resources, including the <a href="https://doc.rust-lang.org/book/"><em>Rust Programming Language</em>-book</a> and the <a href="https://doc.rust-lang.org/stable/rust-by-example/"><em>Rust by Example</em>-book</a>.</p>
<p>I also found the guide <a href="https://github.com/Dhghomon/easy_rust"><em>Writing Easy Rust</em></a> very helpful, because it helps to explain the more complicated Rust-concepts in easier terms.</p>
<h1>Summary</h1>
<p>This article tried to lead you into the Rust-language by <strong>relating concepts to the ones you already know in C#</strong> and .NET.</p>
<p>Rust is very <strong>similar syntactically</strong> to other C-style languages, including C#. There are some concepts in Rust that are relatively different from what you're used to, but this can be a great way to be <strong>inspired to solve things in new ways</strong>. I've actually heard multiple people say that they've <strong>rediscovered their love for programming by learning Rust</strong>.</p>]]></content:encoded>
      <pubDate>Tue, 28 Jul 2020 13:47:00 GMT</pubDate>
      <category>.NET</category>
      <category>C#</category>
      <category>Rust</category>
    </item>
    <item>
      <title>From C# to Rust: Fundamentals</title>
      <link>https://sebnilsson.com/blog/from-csharp-to-rust-fundamentals</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/from-csharp-to-rust-fundamentals</guid>
      <description>Rust has some fundamental tools and concepts we need to understand before we get into the code.</description>
      <content:encoded><![CDATA[<p>There are some <strong>fundamental tools and concepts which we should understand</strong> before we get into actually coding Rust.</p>
<p>Since this series focuses on Rust from <strong>the perspective of a C#-developer</strong>, we will try to <strong>relate the Rust-concepts to the equivalent concepts in C#</strong> and .NET.</p>
<h1>Modern Toolchain</h1>
<p>Rust comes with a complete toolchain of <strong>everything you need to compile and run your Rust-code</strong>. All the way from the compiler, to package-management, testing, code-formatting, linting, documentation-generation, and more.</p>
<h1>The Rust Compiler</h1>
<p>The compiler for Rust <strong>can</strong> be executed by using the command <a href="https://doc.rust-lang.org/rustc/what-is-rustc.html"><code>rustc</code></a>. <strong>This is usually not done</strong> by Rust-developers, but instead, it's done through the Cargo-tool.</p>
<p><strong>.NET equivalent</strong>: <a href="https://docs.microsoft.com/visualstudio/msbuild/msbuild">MSBuild</a> / <a href="https://docs.microsoft.com/dotnet/csharp/language-reference/compiler-options/command-line-building-with-csc-exe">The C#-compiler (csc.exe)</a></p>
<h1>Cargo: Build System &#x26; Package Manager</h1>
<p><a href="https://doc.rust-lang.org/cargo/">Cargo</a> is used by Rust-developers to manage, build, test, and document their projects. This is done by using various <a href="https://doc.rust-lang.org/stable/cargo/commands/">cargo-commands</a>.</p>
<p><strong>.NET equivalent</strong>: <a href="https://docs.microsoft.com/dotnet/core/tools/">.NET CLI</a> and <a href="https://docs.microsoft.com/nuget/">NuGet</a></p>
<h2>Cargo-Commands</h2>
<p>Cargo can be used to <a href="https://doc.rust-lang.org/cargo/guide/creating-a-new-project.html">create a new project</a> by running, for example, <code>cargo new example_project</code>.</p>
<p><strong>.NET equivalent</strong>: <code>dotnet new</code></p>
<p>As mentioned above, Cargo <a href="https://doc.rust-lang.org/cargo/guide/working-on-an-existing-project.html">builds the project</a> and this is done by running the command <code>cargo build</code>.</p>
<p><strong>.NET equivalent</strong>: <code>dotnet build</code></p>
<p>Cargo is the Rust-equivalent of .NET's NuGet, so it's also used to <a href="https://doc.rust-lang.org/cargo/guide/dependencies.html">resolve and download libraries</a>, which it does automatically when you run the <code>cargo build</code>-command.</p>
<p>Now that your code has been built, you can run the code, given that it's a binary. This is done by using <code>cargo run</code>.</p>
<p><strong>.NET equivalent</strong>: <code>dotnet run</code></p>
<p>To just <a href="https://doc.rust-lang.org/edition-guide/rust-2018/cargo-and-crates-io/cargo-check-for-faster-checking.html">check that the code is valid</a> and if that it can build, but not actually do a full build, there is a more lightweight command in Rust, which is <code>cargo check</code>.</p>
<h1>Rustup: Rust Toolchain Installer</h1>
<p>To easily get access to the <a href="https://stackoverflow.com/a/62419829/2429">complete toolchain</a>, we use <a href="https://rustup.rs/"><code>rustup</code>, the Rust toolchain installer</a>, which installs <code>rustc</code>, <code>cargo</code>, the <a href="https://github.com/rust-lang/rustup"><code>rustup</code>-CLI</a>, and other Rust-tools.</p>
<p><strong>.NET equivalent</strong>: .NET Core SDK</p>
<p><a href="https://doc.rust-lang.org/edition-guide/rust-2018/rustup-for-managing-rust-versions.html">With Rustup</a>, you can change which version of Rust is used, install different platforms for cross-compiling, and installing various other components.</p>
<h2>Rust on Windows</h2>
<p><strong>For Rust and Rustup to work on Windows</strong>, you need to <a href="https://visualstudio.microsoft.com/downloads/">install Visual Studio</a>, or the <a href="https://visualstudio.microsoft.com/visual-cpp-build-tools/">C++ Build Tools</a> and the <a href="https://developer.microsoft.com/windows/downloads/windows-10-sdk">Windows 10 SDK</a> separately. Details on this can be found on <a href="https://github.com/rust-lang/rustup#working-with-rust-on-windows">Rustup's GitHub-page</a>.</p>
<h1>Package Manifest</h1>
<p>When you run <code>cargo new</code>, your new project will get a <a href="https://doc.rust-lang.org/cargo/reference/manifest.html"><code>Cargo.toml</code>-file</a>, which is the manifest that contains the meta-data about your project.</p>
<p>An example-version of the file, with the <strong>added dependency to the <em>regex</em>-package</strong>, looks like this:</p>
<pre><code class="language-toml">[package]
name = "example_project"
version = "0.1.0"
authors = ["Seb Nilsson"]
edition = "2018"

[dependencies]
regex = "1.3.9"
</code></pre>
<p><strong>.NET equivalent</strong>: The <code>.csproj</code>-file (or the formerly used <code>packages.config</code>-file).</p>
<p><strong>Node.js equivalent</strong>: The <code>package.json</code>-file.</p>
<h1>Crates</h1>
<p>In Rust, the term <a href="https://doc.rust-lang.org/book/ch07-01-packages-and-crates.html"><em>crate</em></a> is used for a package. It can be either an executable binary or a library.</p>
<p><strong>.NET equivalent</strong>: NuGet-packages</p>
<h2>Crates.io</h2>
<p>To find useful packages/crates to include in your Rust-projects, the registry-site <a href="https://crates.io/">Crates.io</a> is used.</p>
<p><strong>.NET equivalent</strong>: <a href="http://nuget.org/">NuGet.org</a></p>
<h1>Formatting Rust-code</h1>
<p>Cargo contains a command which can automatically format all the Rust-code in your project. It's called <a href="https://github.com/rust-lang/rustfmt"><em>rustfmt</em></a> and can be executed by running <code>cargo fmt</code>.</p>
<p><a href="https://rust-lang.github.io/rustfmt/">Customizing the rules applied when formatting</a> is done by adding a <code>rustfmt.toml</code>-file to the project, which <em>rustfmt</em> then will follow.</p>
<p><strong>.NET equivalent</strong>: There is no built-in way to do this in the .NET CLI, but there is .NET Global Tool from the .NET-team called <a href="https://github.com/dotnet/format"><em>dotnet-format</em></a>, which allows you to run <code>dotnet format</code> on your project.</p>
<h1>Testing</h1>
<p><a href="https://doc.rust-lang.org/cargo/guide/tests.html">Running the tests in a project</a> is also done through Cargo, by running <code>cargo test</code>.</p>
<p><strong>.NET equivalent</strong>: <code>dotnet test</code></p>
<p><a href="https://doc.rust-lang.org/book/ch11-00-testing.html">Testing is built into Rust</a>, so you do not need to add any external testing-package.</p>
<h1>VS Code Setup</h1>
<p>First, make sure you've <a href="https://rustup.rs/">installed Rustup</a>. You can verify that you have it installed by running the command <code>rustup --version</code>, which should show you a version-number.</p>
<p>To get started developing in Rust, with features such as syntax highlighting, auto-complete, code-formatting, refactoring, debugging, and much more, you can install <a href="https://code.visualstudio.com/">Visual Studio Code</a>, with the <a href="https://marketplace.visualstudio.com/items?itemName=rust-lang.rust"><em>Rust</em></a>-extension and the <a href="https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb"><em>CodeLLDB</em></a>-extension.</p>
<p>Consider evaluating the extension <a href="https://marketplace.visualstudio.com/items?itemName=matklad.rust-analyzer"><em>rust-analyzer</em></a> as an alternative to the <em>Rust</em>-extension. It's not as well <a href="https://rust-analyzer.github.io/">documented</a>, but it seems to have more features, including the showing of type inference.</p>
<p>Next, create a new Rust-project by running <code>cargo new</code>. Now, all you have to do now is press <kbd>F5</kbd> in VS Code and it will automatically create a <code>launch.json</code> for you, which allows you to debug your Rust-code.</p>
<h1>Summary</h1>
<p>By just installing <a href="https://rustup.rs/"><code>rustup</code></a>, we get access to the complete Rust-toolchain. This includes, among other things, <a href="https://doc.rust-lang.org/cargo/">Cargo</a>, which lets us <strong>work through the full development-cycle</strong> with our projects, like: <strong>create new</strong> projects, <strong>compile</strong> the code, <strong>run the code</strong>, <strong>format</strong> the code, and <strong>run the tests</strong> in our projects.</p>
<p>Add VS Code with a few Rust-extensions, and you'll have a great developer-experience to get started with.</p>]]></content:encoded>
      <pubDate>Thu, 16 Jul 2020 13:43:00 GMT</pubDate>
      <category>.NET</category>
      <category>C#</category>
      <category>Rust</category>
    </item>
    <item>
      <title>From C# to Rust: Introduction</title>
      <link>https://sebnilsson.com/blog/from-csharp-to-rust-introduction</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/from-csharp-to-rust-introduction</guid>
      <description>Rust has been the most loved programming language for the last 5 years. For a C#-developer, it could be a powerful addition to the tool-belt.</description>
      <content:encoded><![CDATA[<p><a href="https://www.rust-lang.org/">Rust</a> has been the <strong><a href="https://stackoverflow.blog/2020/01/20/what-is-rust-and-why-is-it-so-popular/">most loved programming language</a> for the last 5 years</strong>. This, and many other factors, made me interested in learning more about Rust, especially <strong>from the perspective of a C#-developer</strong>.</p>
<h1>Why Rust?</h1>
<p>In early 2019, a Microsoft engineer revealed that about <a href="https://www.zdnet.com/article/microsoft-70-percent-of-all-security-bugs-are-memory-safety-issues/">70% of all vulnerabilities in their products are due to memory safety issues</a>. Memory <strong>safety is something you get for free with C# and .NET</strong>. But sometimes, you need more tools to squeeze out every last drop of performance.</p>
<p>It's also generally very beneficial to <strong>learn new languages</strong>, which could inspire us to find <strong>new ways of solving problems</strong>. To specifically do so with a modern language, which could replace C/C++ over time and has a <strong>focus on both performance and productivity</strong>, could bring a very <strong>valuable tool</strong> to any developer's toolbox.</p>
<p>Rust has actually been the most loved language every single year, from the <a href="https://insights.stackoverflow.com/survey/2016#technology-most-loved-dreaded-and-wanted">2016 survey</a>, all the way to this year's <a href="https://insights.stackoverflow.com/survey/2020#technology-most-loved-dreaded-and-wanted-languages-loved">2020 survey</a>.</p>
<h1>Rust Overview</h1>
<p>Rust is a C-style language, similar to C# or C++, and even has <a href="https://doc.rust-lang.org/reference/influences.html">influences from C#</a> (among others), with a focus on <strong>memory safety and performance</strong>.</p>
<p>The slogan on <a href="https://www.rust-lang.org/">Rust's official website</a> is:</p>
<blockquote>
<p>A language empowering everyone to build reliable and efficient software.</p>
</blockquote>
<p>The language is <a href="https://en.wikipedia.org/wiki/Static_typing">statically typed</a> and is primarily <a href="https://en.wikipedia.org/wiki/Imperative_programming">imperative</a> but also has <a href="https://en.wikipedia.org/wiki/Functional_programming">functional</a> aspects, <strong>similar to C#</strong>. It's an open-source language <a href="https://research.mozilla.org/rust/">initially developed by Mozilla</a>.</p>
<p>Wikipedia describes the <a href="https://en.wikipedia.org/wiki/Rust_(programming_language)">Rust programming language</a> in the following way:</p>
<blockquote>
<p>Rust is a multi-paradigm programming language focused on performance and safety, especially safe concurrency. Rust is syntactically similar to C++ but provides memory safety without using garbage collection.</p>
</blockquote>
<p>As a C#-developer, you'll find the syntax familiar, with the <em>"Hello World!"</em> for Rust looking like this:</p>
<pre><code class="language-rust">fn main() {
    println!("Hello World!");
}
</code></pre>
<p>Rust was announced in 2010, with version 1.0 released in 2015. In comparison, C# was released in the year 2000, Java in 1995, C++ in 1985, and C in 1972. So it's quite a modern language, which has been <a href="https://doc.rust-lang.org/reference/influences.html">influenced by many other languages</a> and has hopefully used many lessons learned from these.</p>
<h2>Performance</h2>
<p><strong>Rust can be as fast as C and C++</strong> and in some cases, it might be even faster. The code <a href="https://rustc-dev-guide.rust-lang.org/overview.html">compiles to machine code</a>, instead of compiling to an <a href="https://docs.microsoft.com/en-us/dotnet/standard/managed-code">intermediate language (IL), like with C#</a> or being <a href="https://en.wikipedia.org/wiki/Interpreted_language">interpreted on the fly</a>, like with JavaScript or Ruby.</p>
<p>The memory management is not provided by using automated garbage collection (like in .NET/Java) or automatic reference counting (like in Swift/Objective C). The <strong>memory safety-guarantees</strong> are provided by Rust's <strong>key concept called <em><a href="https://doc.rust-lang.org/1.8.0/book/ownership.html">Ownership</a></em></strong>. By enforcing <a href="https://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization">Resource Acquisition Is Initialization (RAII)</a>, Rust makes sure that any objects which go out of scope get deconstructed and its resources are freed up.</p>
<h1>Systems &#x26; Application Programming Language</h1>
<p><em>Systems programming languages</em> typically are more <em>close to the metal</em> and have more direct access to the physical hardware of the machine it is running on, which <strong>enables writing performant software</strong>, such as operating systems, drivers, embedded systems, game-engines, network services, and more. Assembly language, C, and C++ are examples of traditional systems programming languages.</p>
<p>C# is considered as an <em>application programming language</em>, which usually prioritizes <strong>productive ways of building software</strong> which gives functionality to end-users. They are usually <strong>not intended to compete on performance</strong> with systems programming languages.</p>
<p>Rust is a language that tries to <strong><a href="https://doc.rust-lang.org/book/foreword.html">straddle both</a> systems and application programming</strong>.</p>
<h1>Industry Adoption</h1>
<p>The 2020 <em><a href="https://insights.stackoverflow.com/survey/2020">Stack Overflow Developer Survey</a></em> shows that when it comes to <strong>the most professionally used programming language</strong>, <a href="https://insights.stackoverflow.com/survey/2020#most-popular-technologies">C# has a 32,3% popularity, while Rust only has 4,8%</a>. Comparing these numbers to JavaScript's 69,7% popularity might indicate that <strong>Rust is in an early phase of its widespread professional adoption</strong>.</p>
<p>The adoption of Rust does seem to be ramping up though, for good reasons. In a 2020 virtual talk about <a href="https://youtu.be/NQBVUjdkLAA?t=580">Rust at Microsoft</a>, one of their <em>cloud developer advocates</em> said:</p>
<blockquote>
<p>Rust is the industry's <strong>best chance</strong> at safe systems programming</p>
</blockquote>
<p>It does seem like <a href="https://thenewstack.io/microsoft-rust-is-the-industrys-best-chance-at-safe-systems-programming/">Microsoft is exploring gradually switching to Rust</a> from C and C++. These languages are not memory safe, which has <strong>caused a lot of costly errors</strong>, as mentioned before, with Microsoft's having around 70% of all their security bugs related to memory safety issues.</p>
<p>In 2019, Microsoft experimented with rewriting a <a href="https://www.zdnet.com/article/microsofts-rust-experiments-are-going-well-but-some-features-are-missing/">low-level component in Windows with Rust</a>, with a reported generally positive experience.</p>
<p>It's said that Amazon Web Services (AWS) uses Rust to power the deployment of the Lambda serverless runtime and some parts of EC2.</p>
<p><a href="https://www.rust-lang.org/production/users">Many other companies are adopting Rust</a>, including big players like <a href="https://www.youtube.com/watch?v=kylqq8pEgRs">Facebook</a> (also with <a href="https://developers.libra.org/docs/community/coding-guidelines">Libra</a>), <a href="https://twitter.com/benwilliamson/status/1240113606374686721">Apple</a>, Google (with <a href="https://en.wikipedia.org/wiki/Google_Fuchsia">Fuchsia</a>), and <a href="https://blog.cloudflare.com/tag/rust/">Cloudflare</a>.</p>
<h1><em>"From C# to Rust"</em>-Series</h1>
<p>Given that I'm learning Rust as I'm going, the goals of this series might change over time. But at this time, some topics I aim to cover are:</p>
<ul>
<li><a href="/blog/from-csharp-to-rust-fundamentals/"><strong>Fundamentals</strong></a>: Tools and concepts we need to understand</li>
<li><strong>Code basics</strong>: Learning the basics of Rust-code and how the concepts relate to C#</li>
<li><strong>Advanced code</strong>: Diving deeper into more advanced code and concepts in Rust</li>
<li><strong>Rust specific functionality</strong>: Functionality and concepts which exists in Rust, but not in C#</li>
<li><strong>Rust's drawbacks</strong>: Exploring what the reasons are behind the perceived steeper learning curve of Rust</li>
</ul>
<p>There will probably be more subjects discovered along the way. Feel free to <strong>write in the comments</strong> if there are any specific subjects you're interested in, especially coming in as a C#-developer.</p>
<h1>Summary</h1>
<p>Some points that <strong>make me quite excited about Rust</strong> and motivated me to start this exploration are:</p>
<ul>
<li>Modern C-style language, like C#, should give familiarity</li>
<li>The most loved language for half a decade</li>
<li>Enables both high-level productivity and low-level performance</li>
<li>Growing industry adoption, even by many of the giants</li>
</ul>]]></content:encoded>
      <pubDate>Mon, 06 Jul 2020 13:49:00 GMT</pubDate>
      <category>.NET</category>
      <category>C#</category>
      <category>Rust</category>
    </item>
    <item>
      <title>Deno: The Official Node.js-Successor?</title>
      <link>https://sebnilsson.com/blog/deno-the-official-nodejs-successor</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/deno-the-official-nodejs-successor</guid>
      <description>Explore Deno&apos;s design, security model, TypeScript support, and potential as a modern successor to Node.js.</description>
      <content:encoded><![CDATA[<p>Why is <a href="https://deno.land/v1">the announcement of Deno 1.0</a> possibly <strong>very exciting</strong>? Are there <strong>enough upsides to warrant a switch</strong> from Node.js to <a href="https://github.com/denoland/deno">Deno</a>?</p>
<p>The announcement of 1.0 was done middle of May 2020, but the initial announcement came in a presentation named <a href="https://youtu.be/M3BM9TB-8yA"><em>10 Things I Regret About Node.js</em> by Ryan Dahl</a> in mid-2018.</p>
<p>Deno is not just a rearrangement of the first two and the last two letters of "Node". It's built on top of more than <strong>11 years of experience from Node.js</strong> running in production all over the world, <strong>by the original creator of Node.js</strong>. So this does not seem like another case of <em>"I don't like how this project is handled, <a href="https://www.infoworld.com/article/2855057/why-iojs-decided-to-fork-nodejs.html">so I'm making my own fork</a>"</em>, it's a completely new implementation.</p>
<h1>10 Regrets about Node.js</h1>
<p>The 10 things Ryan Dahl regretted about Node.js, which he acknowledges are <strong>impossible to change now</strong>, seem to be <strong>large motivators for the creation of Deno</strong>. It's worth noting that JavaScript has changed a lot during its 11 years of existence and Node has driven a lot of those changes.</p>
<p>The numbered <a href="https://youtu.be/M3BM9TB-8yA">regrets brought up in the talk</a> were:</p>
<ol>
<li><strong>Not sticking with promises</strong>: Promises allow the usage of <code>async</code>/<code>await</code> and avoids "<a href="http://callbackhell.com/">Callback Hell</a>".</li>
<li><strong>Security</strong>: Your linter shouldn't get complete access to your computer and network.</li>
<li><strong>The Build System (GYP)</strong>: Awful experience for users. It's a non-JSON, Python adaptation of JSON.</li>
<li><strong><code>package.json</code></strong>: Not a strictly necessary abstraction and doesn't exist on the web. Includes all sorts of unnecessary information.</li>
<li><strong><code>node_modules</code></strong>: Massively complicates the module resolution. Deviates greatly from browser semantics.</li>
<li><strong><code>require("module")</code> without the extension "<code>.js</code>"</strong>: Needlessly less explicit. Module loader has to query the file system at multiple locations.</li>
<li><strong><code>index.js</code></strong>: Needlessly complicated the module loading system.</li>
</ol>
<p>It was also mentioned that Deno supports the following things:</p>
<ul>
<li>Unhandled promises should die immediately</li>
<li>Support top-level <code>await</code></li>
<li>Browser compatible where functionality overlaps</li>
</ul>
<h1>Introducing Deno</h1>
<p><a href="https://deno.land/">Deno</a> is a runtime for both JavaScript and <a href="https://www.typescriptlang.org/">TypeScript</a>, built on the <a href="https://v8.dev/">V8 JavaScript engine</a> and <a href="https://www.rust-lang.org/">Rust</a>, with the asynchronous runtime <a href="https://tokio.rs/">Tokio</a>.</p>
<p>The <a href="https://deno.land/manual#feature-highlights">feature highlights</a>, as of version 1.0, are:</p>
<ul>
<li><strong>Secure by default</strong>: Access to files, network or environment must be <strong><a href="https://deno.land/manual/getting_started/permissions">explicitly enabled</a></strong></li>
<li><strong>Supports TypeScript out of the box</strong></li>
<li><strong>Ships a single executable</strong>: No separate package-manager, like <em>npm</em></li>
<li><strong>Built-in utilities</strong>: <a href="https://deno.land/manual/testing">test runner</a>, <a href="https://deno.land/manual/tools/formatter">code formatter</a>, built in <a href="https://deno.land/manual/tools/debugger">debugger</a>, dependency inspector and <a href="https://deno.land/manual/tools">more</a></li>
<li><strong>Bundling</strong>: Scripts can be <a href="https://deno.land/manual/tools/bundler">bundled</a> into a single JavaScript file</li>
<li><strong>Standard modules</strong>: Audited and <a href="https://github.com/denoland/deno/tree/master/std">guaranteed to work with Deno</a></li>
</ul>
<h1>Executing JavaScript/TypeScript</h1>
<p>Deno being a single executable file, not needing a separate package-manager or <code>package.json</code>-file, an example of a <strong>working HTTP server application</strong> looks like this:</p>
<pre><code class="language-js">import { serve } from "https://deno.land/std@0.50.0/http/server.ts";
for await (const req of serve({ port: 8000 })) {
  req.respond({ body: "Hello World\n" });
}
</code></pre>
<p>There is <strong>no need to install anything</strong> beforehand or add any configuration-files. <strong>All you need to run is</strong>:</p>
<pre><code class="language-sh">deno run example.js
</code></pre>
<p>Since the code is executed in a sandbox, which is secure by default, the <strong>explicit access must be granted</strong> for the fetching the remote dependency, by adding the flag <code>--allow-net</code> to the command-line.</p>
<p>The remote dependency is <strong>cached locally</strong> and only reloaded if the script is executed with the flag <code>--reload</code>.</p>
<h1>Limitations</h1>
<p>Deno 1.0 <a href="https://deno.land/v1#limitations">has some known limitations</a>, which include things like:</p>
<ul>
<li>No automatic compatibility with existing npm-packages</li>
<li>HTTP server performance is not as good as Node.js, even if it's not too far away</li>
<li>TypeScript performance issues</li>
</ul>
<p>Since Deno uses ECMAScript modules, which uses <code>import</code> instead of <code>require</code>, any module using <code>require</code> has to be converted.</p>
<h1>Summary</h1>
<p><strong>I'm surprised at how excited I am about the potential of Deno.</strong> For me, to have the creator of Node.js use a decade of learning to start over with a blank slate is what makes this a rare situation of high potential.</p>
<p>Some things I look forward to from Deno are:</p>
<ul>
<li>A fresh take on a JavaScript/TypeScript runtime</li>
<li>First Class TypeScript Support</li>
<li>Skipping <code>package.json</code> and <code>npm install</code> when you just want to get started fast</li>
<li>Promises as default: Avoiding callback hell and mixed concepts between different parts of the code</li>
<li>Not having to deal with the <code>node_modules</code>-folder</li>
</ul>]]></content:encoded>
      <pubDate>Mon, 18 May 2020 12:38:00 GMT</pubDate>
      <category>Deno</category>
      <category>JavaScript</category>
    </item>
    <item>
      <title>C# Nullable Reference Types: IntelliSense Confusion</title>
      <link>https://sebnilsson.com/blog/c-nullable-reference-types-intellisense-confusion</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/c-nullable-reference-types-intellisense-confusion</guid>
      <description>Understand confusing IntelliSense behavior when C# projects and libraries use different nullable reference type settings.</description>
      <content:encoded><![CDATA[<p>The feature and concept of <em><a href="https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references">Nullable reference types</a></em> were introduced in C# 8.0 and it basically <strong>made all types non-nullable by default</strong> and ensured that these types could never be assigned the value <code>null</code>. This is one of my favorite features in C# recently, but there are scenarios where <strong>a mixed nullable environment could cause confusion</strong>.</p>
<p><img src="https://files.sebnilsson.com/web/images/nullable-confusion/confusion2.jpg" alt="Confusion"></p>
<p>To enable the assignment of the value <code>null</code> to a type, <strong>you have to explicitly mark that type</strong>. This uses the same concept of nullable as introduced in C# 2.0, where you, for example, make an <code>int</code> nullable by adding a question mark after it: <code>int?</code>.</p>
<p>When we look at a regular example service-class, we can see which benefits can be had from <em>Nullable reference types</em>:</p>
<pre><code class="language-csharp">public class ProductService
{
    // This method accepts a non-null string for 'productId'
    // and always returns a string
    public string FormatProductId(string productId)
    {
        // ...
    }

    // This method accepts a nullable 'formattedProductId'
    // and returns a string or null
    public string? TryGetProductName(string? formattedProductId)
    {
        // ...
    }
}
</code></pre>
<p>This makes things all fine and clear. We know that the method <code>FormatProductId</code> never returns <code>null</code> and that it doesn't accept <code>null</code> in its parameter. We also know that the method <code>TryGetProductName</code> returns a <code>string</code> which could be <code>null</code> and that the parameter accepts a <code>string</code> which could be <code>null</code>.</p>
<p>This is great, this means that <strong>we don't have to perform a <code>null</code>-check</strong> on <code>productId</code>-parameter of the <code>FormatProductId</code>-method, right? <strong>Well, not exactly</strong>...</p>
<h1>Confusion: Mixed nullable environments</h1>
<p>In an environment where all your code has <em>Nullable reference types</em> enabled, you can trust the output of a method and the input to its parameters. <strong>In a mixed nullable environment, things are not as straight forward</strong>, especially when you look at how IntelliSense in Visual Studio signals <strong>what to expect from the code</strong>.</p>
<h2>Scenario 1: Modern app &#x26; legacy library</h2>
<p>Imagine that your new modern app has <em>Nullable reference types</em> enabled, but you're using an external library that is legacy and does not have this enabled. This external library can be your own old library or something you've included from NuGet.</p>
<p>The problem now becomes that the external library is signaling, for example, that it has a method that returns a <code>string</code> and not a <code>string?</code>, so you should be able to trust that it is not <code>null</code>, right? <strong>Unfortunately not</strong>. Even with a local non-nullable project, <strong>IntelliSense tells me that the returned <code>string</code> is not <code>null</code>, even when it is</strong>.</p>
<p><img src="https://files.sebnilsson.com/web/images/nullable-confusion/value-is-not-null.png" alt="Value is not null"></p>
<h2>Scenario 2: Legacy app &#x26; modern library</h2>
<p>Imagine that you have just put together a nice library that you want others to use, either in your project, organization or publicly through NuGet. One of the best parts about using <em>Nullable reference types</em> is that <strong>the compiler will warn you if you try to send in a <code>null</code> value as a parameter</strong> to a method that explicitly states that it doesn't support <code>null</code>.</p>
<p>Nice, now you can clean out all those noisy <code>null</code>-checks at the top of all the methods, right? <strong>Unfortunately not</strong>. Your code might be used by another assembly (or an older version of Visual Studio), which <strong>doesn't detect the non-nullability</strong>.</p>
<p>In a way, this means you have to reverse the way you do <code>null</code>-checks in your code.</p>
<pre><code class="language-csharp">public class ProductService
{
    // This method does not accept a null-value
    // and if it does, it should throw an exception
    public string FormatProductId(string productId)
    {
        if (productId == null)
            throw new ArgumentNullException(productId);
        // ...
    }

    // This method accepts null-values
    // and should adjust its logic accordingly
    public string? TryGetProductName(string? formattedProductId)
    {
        return
            formattedProductId != null
            ? GetProductName(formattedProductId)
            : null;
    }
}
</code></pre>
<h1>Key takeaways</h1>
<p>My own take-aways from exploring this aspect of <em>Nullable reference types</em> are:</p>
<ul>
<li>When building a library, always check for <code>null</code> in incoming method-arguments, even when <em>Nullable reference types</em> is enabled</li>
<li>When consuming an external legacy library, don't trust the return-type to not be <code>null</code> (even if it says it's not)</li>
<li>In a mixed nullable environment, the feature to guard us against <code>NullReferenceException</code>s is likely to mistakenly cause some more of them</li>
<li>When this feature is fully adopted, there will be a reduction in a lot of the overhead in <code>null</code>-handling code</li>
</ul>
<h1>Thoughts</h1>
<p>Hopefully, in .NET 5, this feature is enabled by default and these kinds of confusions, and described associated errors can be avoided.</p>
<p>One idea for an improvement to the IntelliSense-behavior around assemblies that are not known to have <em>Nullable reference types</em> enabled could be to <strong>show all these types as nullable</strong>. Both because it makes things super-clear, but also because of the fact that they actually are nullable.</p>
<p>This change would make <strong>everything in the whole .NET Core CLR light up as nullable</strong>, but as of .NET Core 3.1, it all is nullable, by definition.</p>]]></content:encoded>
      <pubDate>Thu, 13 Feb 2020 16:49:00 GMT</pubDate>
      <category>.NET</category>
      <category>C#</category>
      <category>Visual Studio</category>
    </item>
    <item>
      <title>PowerShell LINQ with Short Aliases</title>
      <link>https://sebnilsson.com/blog/powershell-linq-with-short-aliases</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/powershell-linq-with-short-aliases</guid>
      <description>Create short PowerShell aliases for common LINQ methods to make complex filtering, ordering, and projection pipelines easier to write.</description>
      <content:encoded><![CDATA[<p>Most modern applications or code today deal with some kind of filtering or querying. In C# and .NET, we have <a href="/blog/tags/linq">Language Integrated Query (LINQ)</a>, which we also have access to in PowerShell, because it's built on .NET.</p>
<p>To list the top 10 largest files in the Windows temporary folder, which is larger than 1 Mb and starts with the letter W, skipping the first 5, ordering by size, the C#-code with LINQ would look somewhat like this:</p>
<pre><code class="language-csharp">new System.IO.DirectoryInfo(@"C:\Windows\Temp")
    .GetFiles()
    .Where(x => x.Length > 1024 &#x26;&#x26; x.Name.StartsWith("W"))
    .OrderByDescending(x => x.Length)
    .Select(x => new { x.Name, x.Length })
    .Skip(5)
    .Take(10)
    .ToList()
    .ForEach(x => Console.WriteLine($"{x.Name} ({x.Length})"));
</code></pre>
<p>The equivalent logic in PowerShell has a bit of a more daunting syntax, especially if you're not used to it:</p>
<pre><code class="language-powershell">Get-ChildItem "C:\Windows\Temp" `
| Where-Object {$_.Length -gt 1024 -and $_.Name.StartsWith("W")} `
| Sort-Object {$_.Length} -Descending `
| Select-Object -Property Name, Length -First 10 -Skip 5 `
| ForEach-Object {Write-Host "$($_.Name) ($($_.Length))"}
</code></pre>
<p>That's <strong>a bit explicit and verbose</strong>, but if you use the command <a href="https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.utility/get-alias"><code>Get-Alias</code></a> in PowerShell, you will see a lot of useful aliases, which make the syntax a bit terser and easier to get an overview of:</p>
<pre><code class="language-powershell">gci "C:\Windows\Temp" `
| ?{$_.Length -gt 1024 -and $_.Name.StartsWith("W")} `
| sort{$_.Length} -Descending `
| select Name, Length -First 10 -Skip 5 `
| %{write "$($_.Name) ($($_.Length))"}
</code></pre>
<p>In a real scenario, you probably wouldn't write each result to the console, but let PowerShell present the result in its default grid format.</p>]]></content:encoded>
      <pubDate>Mon, 15 Jul 2019 17:04:00 GMT</pubDate>
      <category>.NET</category>
      <category>LINQ</category>
      <category>PowerShell</category>
    </item>
    <item>
      <title>HTML Encode TagHelper in ASP.NET Core</title>
      <link>https://sebnilsson.com/blog/html-encode-taghelper-in-asp-net-core</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/html-encode-taghelper-in-asp-net-core</guid>
      <description>Build an ASP.NET Core Tag Helper that renders nested HTML, Razor expressions, and Tag Helpers as encoded source code.</description>
      <content:encoded><![CDATA[<p>For a specific scenario recently, I wanted to display the HTML-encoded output of a <a href="https://docs.microsoft.com/en-us/aspnet/core/mvc/views/tag-helpers/intro">TagHelper</a> in ASP.NET Core. So I wanted to use the TagHelper, but not output its actual result, but see the <strong>raw HTML</strong> which would have been included in my template.</p>
<p><img src="https://files.sebnilsson.com/web/images/html-encode-aspnet-core/html.jpg" alt="HTML"></p>
<p>So I created another TagHelper, which allows me to <strong>wrap any HTML, inline code in ASP.NET Core and other TagHelpers</strong>, and get all the content inside the TagHelper's tag to be HTML-encoded, like this:</p>
<pre><code class="language-html">&#x3C;html-encode>
    &#x3C;a href="@Url.Action("Index")">Read More&#x3C;/a>
    @Html.TextBox("No_Longer_Recommended-TagHelpers_Preferred")
    &#x3C;my-other-tag-helper />
&#x3C;/html-encode>
</code></pre>
<p>From this, I will get the raw HTML of the link with an UrlHelper-result, the result of the HTML-helper and the result of my other TagHelper.</p>
<p>The source-code for the <code>html-encode</code>-TagHelper is as follows:</p>
<pre><code class="language-csharp">[HtmlTargetElement("html-encode", TagStructure = TagStructure.NormalOrSelfClosing)]
public class HtmlEncodeTagHelper : TagHelper
{
    public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
    {
        var childContent = output.Content.IsModified
            ? output.Content.GetContent()
            : (await output.GetChildContentAsync()).GetContent();

        string encodedChildContent = WebUtility.HtmlEncode(childContent ?? string.Empty);

        output.TagName = null;
        output.Content.SetHtmlContent(encodedChildContent);
    }
}
</code></pre>]]></content:encoded>
      <pubDate>Thu, 27 Jun 2019 14:31:00 GMT</pubDate>
      <category>.NET</category>
      <category>ASP.NET</category>
      <category>Razor</category>
    </item>
    <item>
      <title>API Rate Limit HTTP Handler with HttpClientFactory</title>
      <link>https://sebnilsson.com/blog/api-rate-limit-http-handler-with-httpclientfactory</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/api-rate-limit-http-handler-with-httpclientfactory</guid>
      <description>Centralize per-second API rate limiting in .NET with a delegating HTTP handler configured through HttpClientFactory.</description>
      <content:encoded><![CDATA[<p>Most APIs have a <strong>Rate Limit</strong> of some sort. For example, <a href="https://developer.github.com/v3/#rate-limiting">GitHub has a limit</a> of 5000 requests per hour. This can be handled, as a consumer of the API, by limiting your use by timing your requests to the API or through caching of the results.</p>
<p>What about when an API <strong>limits your requests per second</strong>? This is probably something you would want to <strong>handle somewhere central in your code</strong> and not spread out everywhere where you make an HTTP call to the API.</p>
<p><img src="https://files.sebnilsson.com/web/images/ratelimithttpmessagehandler/water-funnel.jpg" alt="Funnel"></p>
<p>For me, the solution was to add a <a href="https://docs.microsoft.com/en-us/aspnet/core/fundamentals/http-requests?view=aspnetcore-2.2#outgoing-request-middleware">Outgoing request middleware</a> to the setup of the <a href="https://docs.microsoft.com/en-us/dotnet/standard/microservices-architecture/implement-resilient-applications/use-httpclientfactory-to-implement-resilient-http-requests"><code>HttpClientFactory</code></a>.</p>
<p>With this, I can just configure the startup services to use this <code>RateLimitHttpMessageHandler</code>-class with the <code>HttpClientFactory</code>:</p>
<pre><code class="language-csharp">services
    .AddHttpClient&#x3C;IApi, Api>()
    .AddHttpMessageHandler(() =>
        new RateLimitHttpMessageHandler(
            limitCount: 5,
            limitTime: TimeSpan.FromSeconds(1)))
    .SetHandlerLifetime(Timeout.InfiniteTimeSpan);
</code></pre>
<p>This ensures that wherever I use the class <code>IApi</code>, through dependency injection, it will limit the calls to the API to only 5 calls per second.</p>
<p>The simplified version of code for the <code>RateLimitHttpMessageHandler</code>:</p>
<pre><code class="language-csharp">public class RateLimitHttpMessageHandler : DelegatingHandler
{
    private readonly List&#x3C;DateTimeOffset> _callLog =
        new List&#x3C;DateTimeOffset>();
    private readonly TimeSpan _limitTime;
    private readonly int _limitCount;

    public RateLimitHttpMessageHandler(int limitCount, TimeSpan limitTime)
    {
        _limitCount = limitCount;
        _limitTime = limitTime;
    }

    protected override async Task&#x3C;HttpResponseMessage> SendAsync(
        HttpRequestMessage request,
        CancellationToken cancellationToken)
    {
        var now = DateTimeOffset.UtcNow;

        lock (_callLog)
        {
            _callLog.Add(now);

            while (_callLog.Count > _limitCount)
                _callLog.RemoveAt(0);
        }

        await LimitDelay(now);

        return await base.SendAsync(request, cancellationToken);
    }

    private async Task LimitDelay(DateTimeOffset now)
    {
        if (_callLog.Count &#x3C; _limitCount)
            return;

        var limit = now.Add(-_limitTime);

        var lastCall = DateTimeOffset.MinValue;
        var shouldLock = false;

        lock (_callLog)
        {
            lastCall = _callLog.FirstOrDefault();
            shouldLock = _callLog.Count(x => x >= limit) >= _limitCount;
        }

        var delayTime = shouldLock &#x26;&#x26; (lastCall > DateTimeOffset.MinValue)
            ? (limit - lastCall)
            : TimeSpan.Zero;

        if (delayTime > TimeSpan.Zero)
            await Task.Delay(delayTime);
    }
}
</code></pre>]]></content:encoded>
      <pubDate>Mon, 18 Feb 2019 16:01:00 GMT</pubDate>
      <category>.NET</category>
      <category>C#</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>Azure Storage Easy Web File-Hosting</title>
      <link>https://sebnilsson.com/blog/azure-storage-easy-web-file-hosting</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/azure-storage-easy-web-file-hosting</guid>
      <description>Set up Azure Blob Storage as affordable web file hosting with public access, a custom domain, and Azure CDN.</description>
      <content:encoded><![CDATA[<p>In an ambition to improve my blog a little bit, I wanted to include more images in the posts but felt a <strong>lack of a good solution for web file-hosting</strong>. To find the best fit, I put down a check-list of features and criteria. The solution I was looking for should or must check the following:</p>
<ul>
<li><strong>Ownership of the files:</strong> If the solution used disappears tomorrow, you can still access my files.</li>
<li><strong>Predictable URLs:</strong> The URL to the resources should never change. You don't want to have to update all your blog-posts or other external links floating around the internet.</li>
<li><strong>Good tooling:</strong> Avoiding slow web-upload, when uploading multiple and/or large files, but also easily getting an overview of your files.</li>
<li><strong><em>(Bonus)</em> Pretty URLs:</strong> "Pretty" looking URLs are easier to check for copy-paste errors, but could also potentially benefit <a href="https://en.wikipedia.org/wiki/Search_engine_optimization">SEO</a>.</li>
<li><strong><em>(Bonus)</em> Low or no cost:</strong> Since there are free services out there, paying for file-hosting must be worth it.</li>
</ul>
<p><img src="https://files.sebnilsson.com/web/images/azure-storage-file-hosting/lead.jpg" alt="Images &#x26; Tech"></p>
<h1>Non-fitting Alternatives</h1>
<p>When I started evaluating alternatives, a while back, <strong>Flickr</strong> was still a thing. The problem was that I couldn't predictable directly link to an image I've uploaded. After feeling all the obstacles, which indicated that using Flickr for image-hosting was being actively blocked, I understood it was too much of a hack.</p>
<p>Historically, I've been using <strong>Google's image hosting through Blogger</strong>, which is where this blog started out. The problem was that this also felt like I hack and I was always worried that the URLs would change and I'd have to go through every single blog-posts I've ever made and update all images.</p>
<p>Services like <strong>Dropbox</strong> and <strong>Google Drive</strong> seem to actively try to block the use of their services for this, even if they are accessible through the web.</p>
<h1>Azure Storage for easy web file-hosting</h1>
<p>Enter <a href="https://azure.microsoft.com/services/storage/">Azure Storage</a>, with its widespread adoption, familiar interface and <a href="https://azure.microsoft.com/en-us/pricing/details/storage/blobs/">extremely affordably priced Blob Storage</a>. It checks off all my points in the checklist and more. Giving it's backing, it's fair to assume more functionality will be added ongoingly.</p>
<p><a href="https://azure.microsoft.com/services/storage/blobs/">Azure Blob Storage</a> can be used to store data-blobs of almost any size in the Azure-cloud. By providing a path/key, you can read or write to that "file" at that "path". The overall <a href="https://docs.microsoft.com/en-us/azure/storage/common/storage-scalability-targets">performance of Azure Storage</a> is great and also an important feature of the service, but the simple mechanisms of Azure Blob Storage make it very fast.</p>
<h2>Expose blob-content to the internet</h2>
<p>So you could build a web-app which accesses your files on Azure Blob Storage and expose them through URLs in your API, but you can also <strong>let Azure handle that for you</strong>, by activating <a href="https://docs.microsoft.com/azure/storage/blobs/storage-manage-access-to-resources">anonymous read access to your blobs</a>.</p>
<p>You can do this on blob-level, so <strong>you can have a dedicated blob for public files</strong>, separately from your other blobs in the same Storage-account. These files will be read-only when you use the option <code>Blob (anonymous read access for blobs only)</code>, found under the <em>Access policy</em>-section of the selected blob.</p>
<h2>Upload files</h2>
<p>Then you can use the Azure Portal, programmatically use the <a href="https://docs.microsoft.com/azure/storage/">Azure Blob Storage API</a> to upload files, or you can use application <a href="https://azure.microsoft.com/features/storage-explorer/">Azure Storage Explorer</a>, for a friendly GUI-experience to get started.</p>
<p><img src="https://files.sebnilsson.com/web/images/azure-storage-file-hosting/storage-explorer.png" alt="Azure Storage Explorer"></p>
<p>Now you can share your files anywhere you want, via the provided URL from Azure or through a more pretty URL, using a custom domain.</p>
<h2>Add a custom domain</h2>
<p>To fulfill the criteria of pretty URLs, you can <a href="https://docs.microsoft.com/en-us/azure/storage/blobs/storage-custom-domain-name">set your own custom domain for an Azure Storage-account</a>. If you do not do this, the default URL for Azure Blob storage is <code>https://{storage-account-name}.blob.core.windows.net/{file-path}</code>.</p>
<h2>Activate Azure CDN</h2>
<p>This is a great start for your small project to start out with, but you can in the future easily transition into using the full power of <a href="https://azure.microsoft.com/services/cdn/">Azure Content Delivery Network (Azure CDN)</a> on your existing Azure Storage-account, simply by <a href="https://docs.microsoft.com/azure/cdn/cdn-overview">activating it from the <em>Azure CDN</em>-section</a> in your Storage-account.</p>]]></content:encoded>
      <pubDate>Thu, 10 Jan 2019 15:10:00 GMT</pubDate>
      <category>Azure</category>
      <category>Web Development</category>
    </item>
    <item>
      <title>dotnet-guid: Generate GUIDs/UUIDs with the Command Line</title>
      <link>https://sebnilsson.com/blog/dotnet-guid-generate-guids-uuids-with-the-command-line</link>
      <guid isPermaLink="true">https://sebnilsson.com/blog/dotnet-guid-generate-guids-uuids-with-the-command-line</guid>
      <description>Install and use the dotnet-guid global tool to generate one or more GUIDs and UUIDs from the command line.</description>
      <content:encoded><![CDATA[<p>There have been a few projects, although not many, which I've been involved in, where generating <a href="https://en.wikipedia.org/wiki/Universally_unique_identifier">GUIDs/UUIDs</a> has been important. For that, I used to use online-tools like <a href="https://guidgenerator.com">https://guidgenerator.com</a>, but when I switched machine, the autocomplete in my browser lost that link.</p>
<p>Minimally for this reason, and mostly for fun, I decided to write a <a href="https://docs.microsoft.com/en-us/dotnet/core/tools/global-tools">.NET Core Global Tool</a> which quickly generates one or multiple GUIDs/UUIDs, in whatever format could be needed.</p>
<h1>Installation</h1>
<p>Download the <a href="https://aka.ms/DotNetCore21">.NET Core SDK 2.1</a> or later. Then install the <a href="https://www.nuget.org/packages/dotnet-guid"><code>dotnet-guid</code></a> .NET Global Tool, using the command-line:</p>
<pre><code class="language-sh">dotnet tool install -g dotnet-guid
</code></pre>
<h1>Usage</h1>
<pre><code class="language-sh">Usage: guid [arguments] [options]

Arguments:
  Count         Defines how may GUIDs/UUIDs to generate. Defaults to 1.

Options:
  -?|-h|--help  Show help information
  -n            Formatted as 32 digits:
                00000000000000000000000000000000
  -d            Formatted as 32 digits separated by hyphens:
                00000000-0000-0000-0000-000000000000
  -b            Formatted as 32 digits separated by hyphens, enclosed in braces:
                {00000000-0000-0000-0000-000000000000}
  -p            Formatted as 32 digits separated by hyphens, enclosed in parentheses:
                (00000000-0000-0000-0000-000000000000)
  -x            Formatted as four hexadecimal values enclosed in braces,
                where the fourth value is a subset of eight hexadecimal values that is also enclosed in braces:
                {0x00000000,0x0000,0x0000,{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}}
  -e            Defines if the GUIDs/UUIDs should be empty, using zero-values only.
  -u            Defines if the GUIDs/UUIDs generated should be upper-cased letters.
</code></pre>
<h1>Examples</h1>
<p>To get a single GUID/UUID, simply type:</p>
<pre><code class="language-sh">guid
</code></pre>
<p>To get 3 random GUIDs/UUIDs, with letters in upper-case, formatted with brackets:</p>
<pre><code class="language-sh">guid 3 -b -u
</code></pre>
<p>You can find the <a href="https://github.com/sebnilsson/DotnetGuid">source-code on GitHub</a> and the <a href="https://www.nuget.org/packages/dotnet-guid/">package on Nuget</a>.</p>
<p>You can find <a href="https://github.com/natemcmaster/dotnet-tools">a great list of more .NET Core Global Tools on GitHub</a>, maintained by Nate McMaster.</p>]]></content:encoded>
      <pubDate>Sat, 29 Dec 2018 10:58:00 GMT</pubDate>
      <category>.NET</category>
      <category>.NET Tools</category>
      <category>Developer Tools</category>
      <category>Projects</category>
    </item>
  </channel>
</rss>