Experimentation, dotnet style

Safe (ish!) experimentation and continuous improvements are key to healthy codebases. We discussed https://github.com/scientistproject/Scientist.net yesterday, so I thought I'd get Cursor using GPT-5 to knock up a swift dotnet8 example.

A few birds with one stone as it were. AI, latest model and cursor version trial, and a dotnet library that I wasn't familiar with. Win win.

 Set up the experiment within the GET method.

app.MapGet("/api/data/randomdata", () =>
{
    var result = Scientist.Science<string>("random-data-experiment", experiment =>
    {

In this case, I chose to return a static json response as my control:

        experiment.Use(() =>
        {
            var data = new
            {
                Id = Random.Shared.Next(1, 1000),
                Name = $"Item{Random.Shared.Next(1, 100)}",
                Timestamp = DateTime.UtcNow
            };
            return JsonSerializer.Serialize(data);
        });

    });

and return some json read from a file as the experiment:

        experiment.Try(() =>
        {
            var filePath = Path.Combine(app.Environment.ContentRootPath, "Data", "sample.json");
            return System.IO.File.ReadAllText(filePath);
        });

Here's the console outputter from earlier.

using System;
using System.Threading.Tasks;
using GitHub;

namespace ScientistDemo.Infrastructure
{
	public class ConsoleResultPublisher : IResultPublisher
	{
		public Task Publish<T, TClean>(Result<T, TClean> result)
		{
			Console.WriteLine($"[Scientist] Experiment: {result.ExperimentName}");
			Console.WriteLine($"[Scientist] Matched: {result.Matched}");
			Console.WriteLine($"[Scientist] Control: value={Format(result.Control.Value)}, duration={result.Control.Duration}, exception={(result.Control.Exception == null ? "none" : result.Control.Exception.GetType().Name)}");
			foreach (var candidate in result.Candidates)
			{
				Console.WriteLine($"[Scientist] Candidate '{candidate.Name}': value={Format(candidate.Value)}, duration={candidate.Duration}, exception={(candidate.Exception == null ? "none" : candidate.Exception.GetType().Name)}");
			}
			if (!result.Matched)
			{
				Console.WriteLine("[Scientist] Mismatch detected!");
			}
			return Task.CompletedTask;
		}

		private static string? Format(object? value)
		{
			return value?.ToString();
		}
	}
}

By adding the following, we can actually get the output logged somewhere we can read it:

// Configure Scientist to publish results to console
Scientist.ResultPublisher = new ConsoleResultPublisher();

Here's the console output.

[Scientist] Experiment: random-data-experiment
[Scientist] Matched: False
[Scientist] Control: value={"Id":754,"Name":"Item50","Timestamp":"2025-09-04T10:59:44.315161Z"}, duration=00:00:00.0074269, exception=none
[Scientist] Candidate 'candidate': value={
    "Id": 1,
    "Name": "SampleItem",
    "Timestamp": "2025-09-04T10:44:17Z"
}

, duration=00:00:00.0008843, exception=none
[Scientist] Mismatch detected!

Full code here: https://github.com/adrenalinehit/dotnet-scientest/tree/main

There's plenty more scientist.net can do. Do check out its documentation on github.

Thanks for reading.

 

Comments