I wanted to understand what happens when you stop treating an LLM as a chatbot and start treating it as a component in a software system.
So I built a local AI storyteller using a local LLM through an OpenAI-compatible API.
Continue reading →
People need not only to obtain things; they need above all the freedom to make things among which they can live, to give shape to them according to their own tastes.
— Ivan Illich
Tools for Conviviality
Continue reading →
The world is changed.
I feel it in the water.
I feel it in the earth.
I smell it in the air.
Much that once was is lost, for none now live who remember it.
— Galadriel (The Fellowship of the Ring)
Continue reading →
Kotlin adds the substringBefore, substringBeforeLast, substringAfter and substringAfterLast extension functions to the String class. Instead of using indices to get a substring you can use a string or character value. The functions without Last use the first occurrence of the delimiter and the methods with Last use the last occurrence. If the delimiter is not found the original string is returned. You can supply a value that should be returned when the delimiter is not found.
Continue reading →
Kotlin has very useful extensions functions for working with collections. These extension functions make working with collections more easy and fun. One of the extension functions is the random function. When you call random() Kotlin returns a single element from the collection using the default random source. The function also accepts a Random instance as argument. This instance has a seeded value to return repeatable random values.
Continue reading →
Jim is a guy who never stops exploring.
A couple of years ago, he dove into dependency injection.
But now, all of a sudden, something else hit him.
Event systems are pretty common these days.
As a Spring guy, he’s been using events for ages, but now he feels the need to know how they actually work under the hood.
So, how do they actually work?
Continue reading →
Sometimes you want to transform only the keys in a Map, or only transform the values. Kotlin has two useful methods to achieve this: mapKeys and mapValues. You can use mapKeys to transform the keys of the map while keeping the values the same. With mapValues you can transform the values of the map while keeping the keys the same. Both methods accept a lambda function as asrgument of type Map.Entry and must return the new key or value. In order to transform the keys or values and add the result to an existing Map you can use the methods mapKeysTo and mapValuesTo. The first argument is the existing (mutable) Map and the second argument is the lambda function.
Continue reading →
The def keyword in Nushell is used to define a custom command. By adding the --wrapped flag you can tell Nushell to accept unknown flags and arguments and pass them on as strings. In the command parameters you use a "rest" parameter defined by … to capture the remaining arguments. Then in the command body you use the spread operator … to expand them again. This is very useful when you want to create a small wrapper around an external command with lots of options, but without having to define all those options yourself.
Continue reading →
In the previous part, I discussed how AI impacts learning and team effectiveness, highlighting the tension between speed and deeper understanding.
While AI offers clear advantages, it also introduces new risks for long-term team health.
In this final part, I focus on how teams can consciously integrate AI while preserving collaboration, psychological safety, and strong team dynamics.
Continue reading →
In part one, I explored how AI is subtly changing team dynamics through isolation and echo chamber effects.
While AI makes individual developers more productive, it can also reduce the natural collaboration that strengthens teams.
In this part, we take a closer look at what this means for learning, knowledge transfer, and the balance between speed and depth in software development.
Continue reading →