I handed in my notice; my last day at work is next Wednesday. My employer for the last 2 years has been excellent to me, but I’ve been itching to try something new.

I don’t have a new job lined up yet; I’m planning to use this time partially as a sabbatical, and partially to see what’s out there on the job market. A “serendipity break” during which I will actively explore different possible paths.

One of my plans is to explore the intersection of native apps and web UI; I’ve done some promising experiments with that and it would be a good excuse to overhaul ReiTunes. I’d also like to build some new tools for working with the best database. Here we go!

.NET has been taking huge leaps and bounds in the last few years, and not everyone is aware of it! We’re now able to build small fast single-file .NET applications; this is arguably Go’s biggest strength, and now you can do it in C# and F# too.

Meanwhile, the .NET community has been making excellent libraries to make console applications slick, polished, and easy to write.

The conditions seem right for a .NET renaissance of sorts; all the pieces are in place for us to build great CLI-first software. Here are some useful+easy things you can do in .NET today:

Publish Small Zero-Dependency Executables

In .NET 6 it’s easy to build your application as a single file with no external dependencies (even the .NET runtime!). Applications that include their own runtime are called self-contained. Self-contained apps can be trimmed to remove unused code; trimming reduces a Hello World application from about 60MB to 12MB.

Trimming is well-supported by nearly the entire standard library. Ecosystem support varies; if a library makes heavy use of reflection, C++/CLI, or COM interop then it might not work with trimming yet.

The easiest way to build a trimmed self-contained single-file executable is dotnet publish with the args --self-contained=true -p:PublishSingleFile=true -p:PublishTrimmed=true.

dotnet-releaser makes publishing your app a breeze; it can build for multiple platforms+architectures, then publish the resulting artifacts in a GitHub release.

Console UI

Spectre.Console is a great replacement for Console.WriteLine() and friends. Coloring text with Spectre is as simple as:

AnsiConsole.MarkupLine("[blue]Hello[/] [yellow]World![/]");

It can also do much more; if you need to print tables or prompt users for input, Spectre.Console has you covered. But if you need to build a full-blown terminal UI, check out gui.cs.

Running External Processes

The System.Diagnostics.Process APIs built into .NET are clumsy, to say the least. Thankfully we have much better options these days! Here’s how to run git status with the excellent SimpleExec:

var (stdout, stderr) = await RunAsync("git", "status");

For a more powerful solution, check out CliWrap; anything you can do in a Bash script, you can do in CliWrap.

I recently spent a bit of time messing around with the Postgres wire protocol. I’d like to build a library that lets any application accept connections as if it were a Postgres database; many database clients and databases speak the Postgres wire protocol, and it seems like a practical way to expose tabular data over the network.

Implementing the protocol isn’t too bad; the docs aren’t great but the protocol itself is relatively straightforward. Unfortunately, client behaviour seems harder to accommodate. Clients can issue arbitrarily complex SQL queries to the database to inspect its schema, and sometimes they expect accurate answers. For example, Npgsql issues this beast and then disconnects if it gets an invalid response.

So I’m stuck with a question: how far should I go to support legitimate client behaviour outside of the protocol? The easiest approach for now is to test with a few popular clients and hardcode responses for any queries that they need implemented, but that will leave a long tail of unsupported clients. On the other end of the complexity scale, I could try running schema queries on an in-memory SQLite instance with database objects that mimic Postgres (certainly a massive yak shave, but tempting nonetheless).

I think my next step will be to dig through the source of a few Postgres-compatible databases to see how they solve this.

I recently assembled a tool:

ScriptCompiler screenshot

It’s a helper for scripting in C#. It watches a directory with C# files for changes, and whenever a file changes it gets compiled into its own executable. You can install it as an always-on service, and it comes with an opinionated set of utilities to make scripting easier.

Why?

C# the language has recently become much more succinct (top-level statements, global usings); seems like it should be good for writing fast, maintainable scripts! But the tooling around the language still isn’t quite there yet; you need a .csproj project file for every program, and dotnet run often takes a second or 2 to start up, it’s not (yet) optimized for scripting use. This proposal should help in many ways (upvote it!) but who knows if or when it will be done.

Also, this has been stuck in my head for a while:

My OS has no notion of build system. It has all the source code on it and I can edit anything and run the command again with the change immediately applied. Interpreter or compiler, I don’t know, that’s an implementation detail. Then I wake up.

Give it a spin

Instructions, source and pre-built executables for Linux+macOS+Windows are available on GitHub.

headshot

Cities & Code

Things that don't quite fit in 280 characters.

Top Categories

View all categories

About

I'm a programmer in Vancouver, Canada. I'm interested in databases, urban planning, computing history, and whatever else catches my fancy.

Learn More / Contact me