all blog posts

Training with ML.NET and LightGBM on Synology with Docker
It just worked on Windows 😀

dotnet/machinelearning : ML.NET is an open source and cross-platform machine learning framework for .NET.

microsoft/LightGBM : A fast, distributed, high performance gradient boosting (GBT, GBDT, GBRT, GBM or MART) framework based on decision tree algorithms, used for ranking, classification and many other machine learning tasks.

After writing some code to fetch and mold the data, I was ready to train “my” AI, dubbed “PongRank” which was easy enough and all was good until I added a small WebApi to automate future syncing, retraining and predictions.

2 May 2025
war-story net synology

Site Inspector
Check broken links and typos

While moving some todos from one location to another I came across “Check Site Inspector” and thought, why not run it against this website right now – how hard can it be.

siteinspector/siteinspector :

Not that hard it turns out.

20 Apr 2025
tutorial

Bootcamp: Home Automation
Smart Socket soldering and Home Assistant integration

For our bootcamp we try to get everyone out of their comfort zone and, like many of our sessions, try to do something hands-on.

This year the topic of choice was “Home Automation”. The day was divided into two parts:

  • Solder your own “smart socket” (and take it home with you!)
  • Integrate it into one of the “smart houses” with Home Assistant
16 Apr 2025
fun

Newtonsoft.Json vs System.Text.Json
Just what we needed, choices.

JamesNK/Newtonsoft.Json

System.Text.Json (STJ) is about twice as fast as Newtonsoft.Json while only consuming 50% as much memory. A legacy project might still be using Newtonsoft but chances are serialization isn’t a bottleneck so it’s probably not worth making the switch.

For new applications you’ll want to stick with STJ however. Only add the extra nuget if you really need one of the advanced Newtonsoft features (ex: Linq to Json, polymorphic serialization, circular reference handling).

16 Apr 2025
tutorial

UnitTest: Check Security on your Controllers
Make sure your controller action methods are properly secured, automatically, on your CI

Adding the [AllowAnonymous] or [Authorize("policy")] whenever a new Controller Action Method is added, it’s something that is easily forgotten.

… As I noticed when I was looking at some of our controllers 😵

Of course I have written this test many times before. These days it can be quickly re-created with AI but to avoid having to debug its code, here is the copy pasta version, with some possible variations.

14 Apr 2025
testing war-story

ReduxJS/Toolkit: createSelector
Batteries Included: Reselect

reduxjs/reselect : Create memoized ‘selector’ functions

One of the Redux Style Guide “Strongly Recommended” rules is Keep State Minimal and Derive Additional Values:

Keep the actual data in the Redux store as minimal as possible, and derive additional values from that state as needed.

Another strongly recommended rule is Normalize Complex Nested/Relational State

Many applications need to cache complex data in the store. That data is often received in a nested form from an API, or has relations between different entities in the data (such as a blog that contains Users, Posts, and Comments).

Of course your components are interested in those derived values and in the nested data. Keeping those calculations and recombining the data efficient is where createSelector comes into play.

27 Feb 2025
tutorial react

ReduxJS/Toolkit: TypeScript
How to keep all this type-safe

You don’t want to be using useDispatch, useSelector etc directly in your code. Instead they are wrapped so that you can enjoy complete type safety!

17 Feb 2025
tutorial react

ReduxJS/Toolkit: createAsyncThunk
Let's do some API calls shall we

createAsyncThunk generates promise lifecycle action types, which you handle in the extraReducers of your slice.

import { createAsyncThunk } from "@reduxjs/toolkit";

export const fetchStuff = createAsyncThunk(
  'resource/action',
  async (params, thunkApi) => {
    try {
      const response = await fetch(`/api/resource/${params.id}`);
      return await response.json();
    } catch {
      // Also possible to return a rejected Promise
      return thunkApi.rejectWithValue("Well, that failed");
    }
  }
);

This dispatches the following actions (from the first “Type” argument):

  • resource/action/pending: display a spinner or something?
  • resource/action/rejected: display an error message?
  • resource/action/fulfilled: while the previous two could be considered optional, you definitely want to handle this one!
13 Feb 2025
tutorial react