Blog

  • Reviewing my use of AI

    Disclosure

    Since this post is related to the usage of AI, it is appropriate to disclose that all the prompts are my own, and that only my locally hosted brain-model has been prompted to generate the text

    Early in 2024 I started testing out AI assistance when coding at work. At first only with local models, but almost a year later my employer required us to only use one specific AI service and no local models. Through this service I have been able to experiment with multiple other models than the local models I have been running since I first started.

    First impression

    Initially I was really impressed, especially when experiencing the speed seemingly well-written text could be generated, and when using the assistant to generate documentation blocks for existing code. It seemed almost magical to get coherent documentation across our under-documented code base, both inline usually following established standards and inside our docs/ directory. Or when getting it to attempt to restructure or simplify entangled code, though only on smaller and usually independent files. Gradually I began to increase the number of files to be edited simultaneously, especially when I was nudged to add files into the AI-client by the editor (open/active files were shown as greyed-out inside the chat interface, one click on + to add). For our under-documented project this worked really well, for a while.

    Agent Mode

    When “agent mode” was introduced, the nudging in the direction to write to larger sets of files really increased, especially since the client automatically added other seemingly related files from the project when iteration over a given task. This seemed very risky, but was somewhat countered by the fact that the agent now could run simple commands and read the output to actually validate the code it introduced. It was then very tempting to just let the agent brute-force its way through the issues until the code worked.

    One of the consequences of this approach was that when the agent started working, the chat very quickly filled up with walls of text describing what it was responding and how it was responding to its own responses, in iterative loops. At the same time, multiple files were edited multiple times, while the agent tried and retried some code until it seemed to work. Reviewing all of this became a real chore, reading multiple pages in the minimal chat interface on what changes the agent was doing and reviewing the files that was edited. But since the code seemed to work, was this a problem? Why not ignore the feeling of needing control and “vibe code” the problems out of existence? We do have tests that will catch any mistakes, right?

    Planning Assistant

    Later, I started working on a migration project. This seemed like something that would be really straight forward for an AI assistant or agent to handle, right? Translating hundreds of files from one language-dialect and framework into another while adhering to our current coding standard.

    I started with the planning phase, trying to analyze the current state and plan the migration effort, using the agent as a planning assistant. The assistant generated an analysis of our code base, a suggested end-state for the migration, and generated a multi-step implementation plan for how to get there. The text it generated was impressive, long, detailed and very persuasive. It even suggested creating scripts and documents to track the migration effort. It seemed too good to reject.

    Was this a good idea? The legacy code was somewhat unfamiliar to me, as it was written long before I joined the company. It was therefore difficult to evaluate the issues that the assistant raised for the legacy code. My initial expectation was that the migration work would be fairly straight forward, since the goal was only to translate from one framework to another, not to fix all issues with the existing structure or logic. Still, the assistant’s suggested plan included fixing outdated structures, to untangle complicated logic, to rearrange, to establish “best practices”, to document inline and in dedicated documentation files, etc. Every single suggestion seemed reasonable at face-value, but I completely overlooked the scope increasing.

    When discussing the plans, I tried to play the devil’s advocate and pushed back on several suggestions, in an iterative feedback loop to gradually improve the initial plan. The assistant provided well-structured and persuasive responses that seemed to take into account all the scenarios I threw at it. I got lists of pros and cons, and a concluding suggestions of hybrid approaches that kept all future options open. But is a hybrid approach always the best approach? And importantly, how much time did I actually spend on this back-and-forth, compared to just planning using my own head directly?

    Progress Tracking

    The assistant was used to create tracking scripts, and it even created a web interface with dashboards to track the migration effort. It suggested ingesting existing files to calculate the dependencies between them, and to track what areas where migrated. It seemed brilliant, flashy, organized. The vibe coding sessions were enjoyable, but time flied by when running multiple rounds with the agent assistant to fix the errors in the code to build the tracking solution. Was the tracking solution perfect yet? No, the assistant kept on suggesting improvements. Why not generate scripts that creates mermaid-diagrams from the dependencies? Why not improve the web interface with traffic lights to visualize the current state? Why not update the classification of the migration-tracking-script to improve the readability for other stakeholders when reading through the enormous block of generated documentation? They do read the work-in-progress-documents, right?

    Captured by the vibes, I forgot to ask myself if this is actually needed. Was the migration project so large and complex that we needed high-level dashboards and automated tracking scripts to track the provenance of each individual file in the repository? What downstream consequences would such a strict approach have on our effort to migrate and the scope of the project? I at least, was mesmerized by the vibe and fell helpless down the rabbit hole until I realized too late that more time was used to fix the reporting dashboard than actually doing the work. Is this simply classic and willful procrastination?

    Git Commit

    Then the actual work started. I took the time to write detailed instruction files for how to migrate each type of file, and strict rules for how to handle references in the new framework. Then I instructed the assistant to follow the migration plan for one domain at a time, including rearranging the file and directory structure, documentation and the whole shebang. In theory this would save a lot of time and manual effort, especially if just vibe coding it.

    During our migration, I frequently ran build commands to test the output, and it frequently failed, requiring me to either ask the assistant to give it another try or fix the files manually. Very often I needed to fix the files manually as well, which over time made it very difficult to distinguish our edits. Reverting just the edits of the assistant using plain git? Good luck with that!

    Because I had let the scope increase beyond minimal migration, the migrated output consisted of multiple types of changes. Since changing framework, most files had changes on almost all lines. Using git diff then became almost useless, as the before-after-diff was so different. Committing this to our git log often became an all or nothing exercise.

    When the migration effort had progressed for a while and pushing the code to our testing server, I realized that there was unexpected discrepancies between the legacy and the migrated output (unexpected database changes). Now I had to go back and debug to identify what changes had been made that actually changed logic and not only the structure. Since I at this point had limited my understanding of the data flow by vibe coding the migration, this was extremely time consuming, and likely alone took more than I saved during all the vibe sessions :-/

    Risk Aversion

    I must admit that I probably should have aborted the vibe coding approach earlier, but I was on a mission to test out the potential benefits of vibe coding and did not care (enough yet). For each domain, I ran through this whole process at least twice, and at least one of those attempts crash landed due to the end result not giving the same output as the legacy, and the git log being impossible to deconstruct in a way that I could debug the origin of the discrepancy without actually rewriting the migration manually. It might also be a symptom of overly complex code to begin with, but this was also the reason why I actually needed the migration to be just that, only migration and nothing else.

    To this day, when I try to discuss everything from architectural changes to minuscule single-file migrations, the AI assistant consistently increases the scope of the assignment. Doing a simple migration using agent mode also typically results in some other issues with the original code and it automatically attempts to fix it resulting in additional code I was not asking for. Discussing larger architectural or strategic changes consistently results in well-structured pros and cons with a summary recommendation of a “hybrid approach” keeping all options open. What is this behavior really?

    I think my primary takeaways from this small adventure are to be critical of suggestions that increase scope, to still hand-code, work incrementally, capture small and distinct changes in git commits, and to write good commit messages.

  • Joining the IndieWeb

    Some history

    I have long been following the IndieWeb movement as well as the works around ActivityPub and the Fediverse. Especially write-ups, podcasts and interviews by or with the brilliant Christine Lemmer-Webber (personal site: https://dustycloud.org/), one of the members of the Social Web Working Group and co-author of the ActityPub protocol.

    Given the number of podcasts and articles I have worked through in the last few years it was probably about time that I actually made an effort to do something with it, right? Well, since we are still in the retrospective part of this post, let’s recap some of the sources that influenced me up until this day. The list is just from the top of my head, so it is incomplete and not at all chronological.

    • The Switching.Software website. I stumbled upon this site when searching for alternatives in frustration over the lock-in in software and the surveillance in social media. This site redirected me to Framasoft and Disroot, which in turn set me on track to discover the ongoing work on ActivityPub. This site also made me aware of other distributed social networks like Friendica and GNU social. I tested Friendica before they supported ActivityPub as it looked similar to Facebook at the time, but never sticked with it.
    • The Libre Lounge Podcast that expanded upon free software, the fediverse and ActivityPub through many episodes. I found this podcast when searching for podcasts and interviews with the authors of ActivityPub. The episodes explaining how ActivityPub works was especially enlightening, but I kept on listening for all the other episodes regarding building distributed communities, hacker culture, free software, using Emacs, and the establishment of Spritely.
    • The Foss & Crafts Podcast that continued where Libre Lounge ended, but included Christine’s partner in discussion crafting more generally. I think this podcast made an interesting bridge between the low-level technicalities of code and protocols, and other types of creative outbursts like textile and knitting. And it was fun to listen ๐Ÿ™‚
    • I stumbled upon the abbreviation POSSE in some way which led me to the wiki page on indieweb.org, which made me discover the IndieWeb movement for the first time, and the concept clicked immediately. POSSE means Publish (on your) Own Site, Syndicate Elsewhere.

    Actually joining the IndieWeb

    Initial reference: https://indiewebify.me/

    Level 1: Become a citizen of the IndieWeb

    • Get a domain (check)
    • Get Web Sign In (check) – I just followed the guide, set up IndieAuth and logged into indieweb.org using my own domain! It was simpler than I thought ๐Ÿ˜€

    Level 2 and beyond: to do in a later post

  • Testing Debian Trixie

    Debian 13 “trixie” is out, so why not refresh my computer and give it a spin? I actually was about to replace my daily driver computer anyway, so the release timing was perfect ๐Ÿ™‚


    Downloaded the release from debian.org and checked the amazing InstallingDebianOn project for my specific computer brand and model. My particular brand and model was not yet added to the wiki for Debian 13, but in practice that meant that all the driver issues in earlier Linux Kernels was fixed before this release.

    After first setup, right-clicking on the trackpad did not work. Looked up the issue on the internet, found an answer on StackExchange. Basically a configuration error that is fixed by changing the click-method to “areas”:

    gsettings set org.gnome.desktop.peripherals.touchpad click-method areas

    GNOME’s Online Accounts is steadily improving, so all my accounts were easily added, including WebDAV which now can be added as a collection instead of adding CalDav, CardDav and WebDav separately.


    I installed Tailscale, which on the website does not state an installation for Debian >12. An issue in their git project states that this is fixed, but the installation script they provide does not use the new deb822-style source format. This program came in handy:

    apt modernize-sources

    And now apt install tailscale works like a charm!


    #happyuser ๐Ÿ˜€

  • How do I… enable GPU passthrough from Proxmox to unprivileged LXC?

    Prerequisites

    1. Downloaded driver and installed the driver with kernel modules on the Proxmox host

    Push the driver run-file into the LXC

    From the Proxmox host shell:

    pct push <lxc id> <path/to/source/file> <path/to/target/file>
    pct push 100 ./NVIDIA-Linux-x86_64-580.65.06.run ./NVIDIA-Linux-x86_64-580.65.06.run

    Enter into the LXC:

    pct enter 100

    Find the file and make the run-file executable

    chmod +x NVIDIA-Linux-x86_64-580.65.06.run

    Uninstall any previous drivers

    If you already have a driver installed, this can be uninstalled using the uninstall script that was included in the installation:

    /usr/bin/nvidia-uninstall

    It will complain about not finding the kernel modules, but since this is an LXC these kernel modules are located on the host (Proxmox) and not in the guest (LXC), so this is fine.

    Install the driver

    Install using the run-file, but skip the kernel modules:

    ./NVIDIA-Linux-x86_64-580.65.06.run --no-kernel-module

    Mount the GPU device into the LXC

    external reference: Using an Nvidia GPU with Proxmox LXC

    On the Proxmox host, identify the device IDs:

    ls -al /dev/nvidia*

    It should return something like this:

    # ls -al /dev/nvidia*
    crw-rw-rw- 1 root root 195,   0 Aug  4 11:19 /dev/nvidia0
    crw-rw-rw- 1 root root 195, 255 Aug  4 11:19 /dev/nvidiactl
    crw-rw-rw- 1 root root 195, 254 Aug  4 11:19 /dev/nvidia-modeset
    crw-rw-rw- 1 root root 234,   0 Aug  5 14:49 /dev/nvidia-uvm
    crw-rw-rw- 1 root root 234,   1 Aug  5 14:49 /dev/nvidia-uvm-tools

    The device Ids are in columns 5, so 195 and 234 for the devices under /dev/. Add or update these ids into the unprivileged LXC config. Using the nano editor here:

    nano /etc/pve/lxc/<lxc id>.conf

    Allow access to the device ids:

    lxc.cgroup2.devices.allow: c 195:* rw
    lxc.cgroup2.devices.allow: c 234:* rw

    Then mount the corresponding device paths into the LXC in the same config file:

    lxc.mount.entry: /dev/nvidia0 dev/nvidia0 none bind,optional,create=file
    lxc.mount.entry: /dev/nvidiactl dev/nvidiactl none bind,optional,create=file
    lxc.mount.entry: /dev/nvidia-modeset dev/nvidia-modeset none bind,optional,create=file
    lxc.mount.entry: /dev/nvidia-uvm dev/nvidia-uvm none bind,optional,create=file
    lxc.mount.entry: /dev/nvidia-uvm-tools dev/nvidia-uvm-tools none bind,optional,create=file
    lxc.mount.entry: /dev/nvram dev/nvram none bind,optional,create=file

    Save the config, reboot the LXC and verify that the GPU is available by running nvidia-smi

  • How do I… install Nvidia Drivers on Debian (or Proxmox)?

    I have installed Nvidia drivers using the official run-file from Nvidia (Unix Driver Archive), and when it is time to update I always forget how to do all the steps. So here is the notes I have taken for my future self:

    Download the run-file

    Go to the Manual Driver Search and find the latest driver for the GPU model. Go to the corresponding download page and copy the URL from the Download Now button

    SSH into the machine that needs an update and download the driver there:

    wget <download-url>

    In this particular case it was this version:

    wget https://us.download.nvidia.com/XFree86/Linux-x86_64/580.65.06/NVIDIA-Linux-x86_64-580.65.06.run

    Make the run-file executable:

    chmod +x <filename>

    In this particular case it was:

    chmod +x NVIDIA-Linux-x86_64-580.65.06.run

    Uninstall the previous driver

    When installing the driver using the run-file, Nvidia will also install the uninstaller script and other scripts here: /usr/bin/nvidia-*. It might not be necessary to uninstall previous versions, perhaps unless you are downgrading. For my case, since installing on a server, I like to clean up any unused packages.

    Run the uninstaller:

    /usr/bin/nvidia-uninstall

    Select “No”

    Installing the new driver

    Run the installer and build the kernel modules. Run with the –dkms flag to automatically rebuild kernel modules when the kernel is updated:

    ./NVIDIA-Linux-x86_64-580.65.06.run --dkms

    Test the installation by running nvidia-smi. The output should be something like this:

    +-----------------------------------------------------------------------------------------+
    | NVIDIA-SMI 580.65.06              Driver Version: 580.65.06      CUDA Version: 13.0     |
    +-----------------------------------------+------------------------+----------------------+
    | GPU  Name                 Persistence-M | Bus-Id          Disp.A | Volatile Uncorr. ECC |
    | Fan  Temp   Perf          Pwr:Usage/Cap |           Memory-Usage | GPU-Util  Compute M. |
    |                                         |                        |               MIG M. |
    |=========================================+========================+======================|
    |   0  NVIDIA GeForce GTX **** Ti     Off |   00000000:01:00.0 Off |                  N/A |
    |  0%   52C    P0             37W /  180W |       0MiB /   8192MiB |      0%      Default |
    |                                         |                        |                  N/A |
    +-----------------------------------------+------------------------+----------------------+
    
    +-----------------------------------------------------------------------------------------+
    | Processes:                                                                              |
    |  GPU   GI   CI              PID   Type   Process name                        GPU Memory |
    |        ID   ID                                                               Usage      |
    |=========================================================================================|
    |  No running processes found                                                             |
    +-----------------------------------------------------------------------------------------

  • Why write?

    I’m usually very private online these days. After working with digital marketing and web analytics for a while, and around the time of Snowden, I started to worry about the use and misuse of my digital footprints. After reading Ten Arguments for Deleting Your Social Media Accounts Right Now by Jaron Lanier I took the leap and went about to deactivate my presence on the social platforms and trying to clean up my online clutter. GDPR was very helpful in this regard, as I had hundreds of emails from different online spaces that requested my consent to keep data about me.

    Well.. then what? I successfully disconnected from everything that attracted me to the web in the first place, as well as the creepy attention economy, data mining social platforms and the rest.

    Ever since 5th grade I had some form of place on the web where I wrote or shared photos. From classic free-hosting-using-FTP-to-upload-static-html-files, to custom pages on My Space and WordPress. Online forums, communities and personal spaces. These were things I actually liked. Then I stumbled upon indieweb.org and was gradually drawn back to the online world.

    Things take time though, I first learned about IndieWeb several years a go, but the family phase of life is really hectic. But here goes, I just need to do something. Now.

    So welcome to my little corner of the web. It is mostly meant to be my personal digital garden, so do not expect regular updates or anything special. Just my own notes on what I am up to.