fubits.dev fubits.dev

2024 in Review

A Year at Tagesspiegel Innovation Lab

Published: Last update:
SvelteKitSSRSupabaseCoolifyVPSSelf-hostingHome Assistantn8nAstroDevOpsOpsMonitoringTech StrategyFreelancingSvelte 5HomeLabData Journalism

To me, 2024 has been a bit different than previous years, mainly for two reasons. A lot has happened at a rather high pace and I learned a lot but with little rest and time for recaps. So I’m writing this kind of as a retrospective and as a log of my learnings and milestones. And to update my portfolio.


Introduction

The two main reasons why my 2024 differs from previous years (as a full-time freelance) are:

  • In December 2023 I joined the interactive / visual journalism department (aka “data driven journalism” / DDJ) of a Berlin-based media company (80% role with a Frontend focus). Here is a detailed write-up of what the job can be like by Jonas Oesch for Nightingale
    • I decided to quit after a year and have returned to freelancing in December 2024.
  • Right before starting the new role I had a few weeks off and began experimenting with self-hosting and running a “home lab”.

One thing led to another (work-related events included) and for a period of time I actually ended up becoming the server guy at work. I continue to do that now as a freelance. More about self-hosting below.

The Year at Tagesspiegel Innovation Lab

As you can guess there probably are reasons that made me quit the job towards the end the first year. However, in this retro I want to focus on outcomes / deliverables and only touch upon a few events and skip the rest. Nonetheless, I learned a lot and had a few rather special opportunities to grow: election nights.

tsp-ltw-de.jpeg

I will review my year at the Tagesspiegel along two main tracks:

  • “tangibles”: features / modules / apps
  • “enablers”: migrations / infrastructure / tooling / DevOps
  • the rest: strategy, hiring, onboarding

In short, I spent far less time on tangibles than on the rest, and that’s neither what I applied for nor what I was properly compensated for. I suggested to formalize my Tech Lead role but it never happened. End of story.

Tangibles

A few noteworthy deliverables / things I’m proud of:

News Quiz App

tsp-quiz.jpeg

My first project and one of the few were I worked fully solo - was prototyping a news quiz app and an internal quiz editor for creating new quizzes. Using Svelte 4, SvelteKit, and Supabase, I built the first prototype within a week. We ran 4 quizzes with up to 5K completed submissions. You can see in the screenshot that I’m even calculating results / distribution at individual quiz item level. Thank you, Postgres functions and Postgres views.

Concept:

  • pre-compiled ES6 module to be mounted in a legacy longread template (Jekyll, no CMS)
  • Supabase + Postgres views for automated aggregation of results
  • SvelteKit endpoints (server routes as “API”) as “middleware” between frontend module and Supabase
  • Quiz editor (using SvelteKit and EditorJS; I would use TipTap for a rewrite)

tsp-editor-prototype.png

Survey / User Participation

At some point, we had to run a campaign prompting users to participate with LLM-generated imagery for an event. Building upon some learnings and progress from both the Quiz and the election tooling, the stack remained mostly the same, but the deployment concept improved.

  • Svelte form served as an iframe / partial from a SvelteKit SSR app
    • form actions for submissions
    • image upload via an standalone endpoint (because I couldn’t break through some reverse proxy limitations to multipart/form-data form submission payload size defaults)
  • Supabase + Supabase Storage
  • simple Astro + Svelte + Supabase for a way to review submissions internally

4 Elections

assets/tsp-eu-election-dashboard.jpeg

Election nights are very very special. Imagine you have to

  • serve tens of thousands of visitors
  • with “live” data
  • from a very diverse set of “sources” (external APIs, internal APIs, FTP server, XML (!!!), a Python cron that fetches Excel from a SMTP mailbox…)
  • among other things with maps where counties might get merged during counting (shout out to Turf.js)
  • having to pierce through Cloudflare and Nginx cache layers at least 3 times along the path (even server-to-server)
  • and where any of Murphy’s Laws might apply (faulty data, incomplete data, cache issues, side-effects of emebedding Svelte in a Vue-based CMS etc)

Election nights are more like a DJ set but with code. Live-coding in production…

All in all for each election we had to operate / serve at least a dozen of static and interactive features - but always with live data and server-side rendered where possible:

  • dashboards with different states
  • static, pre-rendered vector maps
  • interactive maps with live + historical results
  • bar charts, pie charts, seats distribution projections etc.
  • all at different levels because in Germany we usually have at least 2 votes for two different levels of counting

We served each single feature as a iFrame route and as a partial / embed route (basically HTML+CSS+JS without a head / body container), sometimes with query parameters (e.g. for dimensions, parties and election levels for the SVG maps).

I will have to dive into far more detail at some point but what we built was a truly impressive “election live-coverage feature factory”!

Elections we covered in 2024 (in order):

  • Berlin (partial Federal re-election)
    • I inherited a legacy Svelte 3 + Rollup dashboard and a vanilla JS interactive map
    • realized we won’t make it through the year if “we” (at this point = me) don’t rewrite / migrate
  • EU - 3 levels to cover: EU & Germany & Berlin
    • here we battle-tested Svelte 5 and SvelteKit SSR for the first time
    • because I started setting up a library-like collection of components in SvelteKit), a freshly hired colleague delivered their first standalone feature within their first week on my team - just in time for election night
    • I went home at 9.30 in the morning
  • East Germany I: Thuringia & Saxony
    • here I introduced an internal dashboard for remote control of all feature and dashboard states (idle, live, various stages of unofficial and official count progress, done, officially done) and labels
  • East Germany II: Brandenburg
    • yeah - the simplest election came last
  • USA (here I was already working on my handover and only contributed a few features)

tsp-ltw-sn-map.jpeg

Core milestones:

  • Svelte 5 + SvelteKit + SSR, eventually with really snappy pre-rendering
  • Supabase (Postgres and storage)
  • remote control with a Astro + Svelte + Supabase app
  • all self-hosted on our new team-owned server via Coolify

tsp-elections-remote.jpeg

Other Noteworthy Features

screenshot

  • TL;DR: Reboot of Election Polling
    • full rewrite of a vanilla JS feature in Svelte 4
    • canvas prepared but not rolled out: write-up
  • responsive SVG with HTML text layer (for responsive multi-language rich text from a CMS)
    • probably simpler with container queries now, given a Polyfill

  • I also worked on porting or fixing many legacy features such as a Svelte 3 + Mapbox map comparison feature (basically 2 maps overlayed with a slider) and ported it to Svelte 5, Maplibre and a proper map sync library.

tsp-map-slider.jpeg

Enablers

  • introduced Coolify to enable CI/CD and deployment of services such as Supabase, incl. uptime monitoring
  • case-by-case migration / rewrite of vanilla JS and Svelte 3 + Rollup to Svelte 4 / 5 and Vite: huge boost in DX - alone for proper HMR
  • for my handover I set up Storybook + Svelte 5 + Svelte native stories (incl. utility JS & CSS) - this alone is worth a talk and a write-up

  • tactical decision that turned out to be strategic: early adoption of Svelte 5 in mixed mode with Svelte 4
  • introduced Notion for Tech Team management and shadow project management
  • introduced shared password management
  • developed a tech team strategy and roadmap for 2024
  • led 5 technical interviews and hired and onboarded 2 people (Frontend, Backend / DevOps)
  • managed technical production during sprints incl. freelance devs
  • had a whole day of fun with regex-patching the Polyfill.io JavaScript supply chain attack

Technically I was a data journalist for 12 months (and paid as such). I didn’t write a single line for an article.

Self-Hosting

section TODO

TL;DR:

  • I bought smart thermostats and want to enable my partner to control them as well
    • solution: home assistant on a spare Raspberry Pi 4 (with a SATA SSD via USB)
    • turns out, that setting up and running that is very similar to self-hosting Coolify
  • untill this moment I had no backend experience
    • at work I explicitly applied for a frontend role
    • but the team member with backend skills quit only a few weeks after I joined
    • critical capacity gap at work → with my new skills I suddenly became the server guy on top of other things
    • a few months later we finally hired a new colleague but we were already deep in election season so there was no time for handover
  • so I introduced Coolify Cloud at work
  • and started self-hosting Coolify at home
    • for my personal projects
    • and eventually for freelancing - mainly because of
      • a) tons of intense experience and learnings from the election nights
      • b) performance gains by using a Pi 5 + NVMe SSD
      • c) and my desire for ownership and sovereignty

End of 2024 I was running:

  • a Pi 5 with Coolify for clients
  • a Peladn N100 (16GB RAM) with Coolify for myself (“homelab)
  • a Pi 4 with Home Assistant (and 20 or so smart devices)
  • a Pi 3 with PiHole
  • a arm64 VPS (12 core, 24 GB RAM)
  • a x86 root server (8 core, 16 GB RAM)

In 2025 this escalated further. As of Juli 2025 I’m running and managing 3 servers for clients and a beefy one for myself.

servers

Spread over these, I’m running:

  • Home Assistant
  • Immich
  • Jellyfin
  • Karakeep (ex Hoarder)
  • Supabase instances
  • Pocketbase
  • CryptGeon
  • n8n
    • first production use was implementing newsletter double-opt-in with Notion
  • Netdata nodes
  • Uptime Kuma
  • changedetection.io
  • Browserless
  • a dozen or so SvelteKit and Astro projects (both static and SSR with Node/Bun)
  • marimo Python apps (with FastAPI) and notebooks

Freelancing

My freelancing activities came to a full halt in 2024. This also drastically impacted my total income and was one of the reasons that made me leave Tagesspiegel.

tsp-2024-finale.png

The end.