Freek Van der Herten's Avatar

Freek Van der Herten

@freek.bsky.social

PHP developer at Spatie, built Mailcoach, ‪myray.app‬, ohdear.app and flareapp.io, blogging at ‪freek.dev‬, organising fullstackeurope.com

2,936 Followers  |  115 Following  |  239 Posts  |  Joined: 25.05.2023  |  1.6919

Latest posts by freek.bsky.social on Bluesky

Making Software Dan Hollick created a beautifully illustrated reference manual that explains how the technology behind software actually works.

🔗 Making Software
#learning #software #reference #guide #education

16.02.2026 13:27 — 👍 0    🔁 0    💬 0    📌 0
AI Horseless Carriages Pete Koomen argues that most AI apps are "horseless carriages," bolting AI onto old paradigms instead of letting users shape the prompts that drive them. He makes the case that the best AI apps should be agent builders, not just agents.

🔗 AI Horseless Carriages

13.02.2026 13:13 — 👍 1    🔁 0    💬 0    📌 0
How to create an awesome product launch video Aaron Francis shares how he used Claude to create an entire launch video for his new app, without using any traditional motion graphics software. No Remotion, no MCPs — just clever prompting and iteration.

🔗 How to create an awesome product launch video

12.02.2026 13:44 — 👍 0    🔁 0    💬 0    📌 0
Laravel Fuse: Circuit breaker for queue jobs [Laravel Fuse](https://github.com/harris21/laravel-fuse) is a circuit breaker package for Laravel queue jobs. When an external service like Stripe or Mailgun goes down, instead of letting thousands of jobs timeout (30s each), the circuit opens after a configurable failure threshold and jobs fail instantly. It supports three-state circuit breaking (closed/open/half-open), intelligent failure classification (429s and auth errors do not trip the circuit), peak hours config, and a built-in status page.

🔗 Laravel Fuse: Circuit breaker for queue jobs

11.02.2026 13:46 — 👍 4    🔁 0    💬 0    📌 1
Real-world examples of using Laravel AI SDK Amit Merchant walks through practical use cases for the [Laravel AI SDK](https://github.com/laravel/ai): mining user data with Eloquent models as context, building a code review bot that comments on PRs, and creating adaptive quiz systems for EdTech apps. Nice examples of agents, structured output, and conversation memory beyond the typical chatbot demo.

🔗 Real-world examples of using Laravel AI SDK

10.02.2026 13:39 — 👍 0    🔁 0    💬 0    📌 0
The Origin of Laravel - a look at v1 Beta 1 A fascinating deep dive into the very first commit of Laravel, made by Taylor Otwell on June 9, 2011. The article explores the original directory structure, the early Eloquent ORM, the session system with its clever flash data prefixing, and the authentication basics that are still recognizable in today's framework.

🔗 The Origin of Laravel - a look at v1 Beta 1

09.02.2026 13:18 — 👍 2    🔁 0    💬 0    📌 0
Excessive Bold Martin Fowler on the overuse of bold in technical writing — and how LLMs have picked up and spread this practice. The more you emphasize, the less power it has.

🔗 Excessive Bold

06.02.2026 13:01 — 👍 3    🔁 0    💬 0    📌 0
Introducing Ray 3.0 Ray 3.0 is here! Completely rebuilt for better performance (60% less memory), a fresh new look, message archiving, and MCP support so AI agents can interact with Ray directly.

🌟 Introducing Ray 3.0

05.02.2026 13:57 — 👍 2    🔁 1    💬 1    📌 0
Personal AI Assistants Changed Everything For Me Christoph reflects on how personal AI assistants have changed his daily workflow — from calendar updates to searching the web less. An interesting take on where this technology is heading.

🔗 Personal AI Assistants Changed Everything For Me

04.02.2026 13:51 — 👍 1    🔁 0    💬 0    📌 0
Partial function application in PHP 8.6 PHP 8.6 will introduce partial function application, allowing you to pre-fill some arguments while leaving others for later. Brent explains this new language feature with practical examples.

🔗 Partial function application in PHP 8.6

03.02.2026 18:37 — 👍 1    🔁 2    💬 0    📌 0
I built a native mobile word game in two weeks At Laracon India, I launched [a major update of Ray](https://myray.app). For that talk, I needed a little demo project to showcase Ray. I built a simple website about a then-fictional mobile app to play a Scrabble-like word game called [WordStockt](https://wordstockt.com). But then I got curious: how far could I push AI-assisted development? Could I actually just create the whole game? After about 10 days, WordStockt is a fully functional word game that's 98% vibe-coded. It's available [for iOS](https://apps.apple.com/app/wordstockt/id6757525145) and [Android](https://play.google.com/store/apps/details?id=com.wordstockt.app). In this post, I'd like to tell you more about it. ![](https://freek.dev/admin-uploads/XRbFtLQeyuQQESw2uIemo4E7LlWAIVexIAeniV24.jpg) ## From Demo to Real App WordStockt is probably the most elaborate demo I've ever built for a talk. It's a classic word game (think Scrabble) where you can challenge friends to asynchronous matches, track your stats, and climb the leaderboards. You can play at your own pace: make a move, close the app, and come back when your opponent has played. Push notifications let you know when it's your turn. Before writing any code, I started describing the main functionalities of the app in a markdown file. First, I let the AI interview me about the requirements I had written to clarify some details. Next, I instructed the AI to split up the work into multiple phases, each with its own detailed markdown file. After I refined those markdowns further, I instructed the AI to start building it phase by phase. Since I know Laravel quite well, I was reasonably sure I could guide the AI well enough to produce good results for the backend. I reviewed the code that it produced, and pushed it to use the patterns and code organization that I like. For the front-end, I was a little bit more nervous. According to my front-end colleagues at Spatie, the best way of building a native app from a single codebase was Expo / React Native. I didn't have experience with either of those (I do know a bit of regular React), so I couldn't always tell if the AI produced quality results. The code it produced always did work however. Something that I really am not accustomed to doing myself is coming up with good design. Sure, I can do some basic styling with Tailwind, but that's about it. To start with the design, I let the AI propose and show me ideas using Ray. AI also assisted with non-coding talks writing copy, creating app icons, and taking screenshots for various app stores (showing in screenshot below). ![](https://freek.dev/admin-uploads/J3RuH5kXUE2oB146q1DU5IpTxdlP3eBxZlIdqqRW.jpg) When reading the above, you might think that all things were built sequentially, but in reality I was doing things at the same time. In the screenshot belong, you can see me using one Claude session working on animations, and in another one, I'm letting the AI work an icons and letting it preview them in [Ray](https://myray.app). ![](https://freek.dev/admin-uploads/xy1ZEQ4EiGrXKFBq9IVZpFqswPribdgXFRTyT9hG.jpg) In total, it took me about 10 days to build this app from start to finish. That includes the design, the backend Laravel API, the apps, the website, and setting up all services. ## WordStockt is open source I've decided to open source the entire project. It's a good example of a modern Laravel API powering a React Native mobile app, and I hope others can learn from it. The Laravel backend API is available at [github.com/spatie/wordstockt.com](https://github.com/spatie/wordstockt.com). The production version of this app is hosted on [Forge](https://forge.laravel.com). For real-time updates, so you instantly see when your opponent plays, I'm using [Reverb](https://reverb.laravel.com). The iOS and Android apps are built from a single [React Native](https://reactnative.dev) codebase. You'll find the code in this repo: [github.com/spatie/wordstockt-app](https://github.com/spatie/wordstockt-app). Also noteworthy is that [Expo](https://expo.dev) is used to send native push notifications. PRs to these codebases are welcome! If you find bugs, want to add features, or improve the AI's code (there's probably room for that), feel free to contribute. ## What does this all mean Building WordStockt in 10 days would have been impossible for me without AI. The productivity gains are real and significant. I built a complete product across technologies I don't fully master, in a fraction of the time it would have taken otherwise. Honestly, previously I couldn't have built this without the help of my team members. That said, I don't think this replaces the need to understand what you're building. Or to have passionate fellow developers around you. I could guide the Laravel side confidently because I know Laravel. The React Native side worked, but I have less confidence in its quality because I can't fully evaluate it. AI amplifies what you already know more than it substitutes what you don't. Therefore, like argued in my talk at Laracon India, I think it will remain important to invest in technical skills and knowledge. Where this goes, I honestly don't know. Some call this [the rise of industrial software](https://chrisloy.dev/post/2025/12/30/the-rise-of-industrial-software), comparing it to how manufacturing transformed other industries. There will be challenges for junior developers trying to learn, for teams figuring out how to do [code review when AI generates code faster than humans can review it](https://tidyfirst.substack.com/p/party-of-one-for-code-review), and for all of us adapting to a faster pace of change. But I'm optimistic. My buddy Mattias wrote that AI makes [web development fun again](https://ma.ttias.be/web-development-is-fun-again/) and that it's [no longer optional](https://ma.ttias.be/ai-no-longer-optional/). I fully agree with both. The barrier to building things is lower than ever. Ideas that would have stayed ideas because "I don't have time" or "I don't know that stack" are now within reach. That feels like an opportunity worth embracing. ## In closing WordStockt is completely free with no ads. I built it for fun and as a demo, not to make money. Download it now on the [App Store](https://apps.apple.com/app/wordstockt/id6757525145) or [Google Play](https://play.google.com/store/apps/details?id=com.wordstockt.app). ![](https://freek.dev/admin-uploads/tKJjTZCWGoHShM75n5yLI9DUwF1IaY7YuCdP9KmQ.png)

🌟 I built a native mobile word game in two weeks

31.01.2026 11:50 — 👍 1    🔁 0    💬 0    📌 0
Once again processing 11 million rows, now in seconds Brent continues his optimization journey processing 11 million database events. Starting from 50k events per second, he implements combined inserts, skips unnecessary deserialization, and reaches 400k events per second. A great deep dive into PHP performance optimization.

🔗 Once again processing 11 million rows, now in seconds

30.01.2026 13:45 — 👍 1    🔁 0    💬 0    📌 0
Semantic Diffusion Martin Fowler on how technical terms lose their meaning as they spread. When a useful concept becomes popular, it passes through a "telephone game" of explanations until the original definition gets diluted or even inverted. Think of how "agile" or "DevOps" are used today versus their original intent.

🔗 Semantic Diffusion

29.01.2026 13:05 — 👍 2    🔁 0    💬 0    📌 0
SQL performance improvements: automatic detection & regression testing The final part of Oh Dear's series on SQL performance. Mattias introduces [phpunit-query-count-assertions](https://github.com/mattiasgeniar/phpunit-query-count-assertions), a package that catches N+1 queries, duplicate queries, and missing indexes in your test suite. Since implementing this at Oh Dear, they've eliminated 15% of their SELECT queries.

🔗 SQL performance improvements: automatic detection & regression testing

28.01.2026 09:02 — 👍 2    🔁 0    💬 0    📌 0
How to automatically generate a commit message using Claude For years, my git history contains "wip" commit messages. I don't really often use git history myself, but my colleagues do. And when they're trying to understand a change I made six months ago, "wip" tells them absolutely nothing. Might as well not have commit messages at all. I knew I should write better commit messages, but the friction was real. Stopping to think about how to summarize my changes felt like it broke my flow. So I kept typing "wip". I added a bash function to my dotfiles that uses Claude to generate commit messages for me. ![](https://freek.dev/admin-uploads/hUISpOkDrkaEgefakRGH7urz7torFU7nWRNY7FJM.jpg) ## My commit function Here's the core of it the function: ```bash function commit() { commitMessage="$*" git add . if [ "$commitMessage" = "" ]; then diff_input=$(echo "=== Summary ===" && git diff --cached --stat && echo -e "\n=== Diff (truncated if large) ===" && git diff --cached | head -c 50000) commitMessage=$(echo "$diff_input" | claude -p "Write a single-line commit message for this diff. Output ONLY the message, no quotes, no explanation, no markdown.") git commit -m "$commitMessage" return fi eval "git commit -a -m '${commitMessage}'" } ``` If I call `commit` with no arguments, it stages everything and asks Claude to generate a commit message based on the diff. If I pass in a message like `commit "Fix bug in auth"`, it uses that instead. ## What are dotfiles anyway? Quick sidebar: "dotfiles" are configuration files that live in your home directory and typically start with a dot (like `.zshrc` or `.bashrc`). They control how your terminal, shell, and various command-line tools behave. Many developers (myself included) keep their dotfiles in a git repository so they can sync their entire development environment across machines. If I get a new laptop, I just clone my [dotfiles repo](https://github.com/freekmurze/dotfiles) and I'm back to my familiar setup in minutes. In my case, I have a [`.functions`](https://github.com/freekmurze/dotfiles/blob/main/home/.functions) file in my dotfiles that contains custom bash functions like this `commit` function. My `.zshrc` sources that file, so these functions are available in every terminal session. ## How the AI generation works The interesting part is what gets sent to Claude. I don't just send the raw diff - that could be huge and contain too much noise. Instead, I send two things: 1. A summary: `git diff --cached --stat` shows which files changed and how many lines were added/removed 2. The actual diff: But truncated to 50,000 characters to avoid overwhelming the AI with massive diffs Then I pass it to the `claude` CLI with a simple prompt: "Write a single-line commit message for this diff. Output ONLY the message, no quotes, no explanation, no markdown." The result? Commit messages that actually describe what changed: - `Add caching layer to user repository` - `Fix N+1 query in post index` - `Remove deprecated payment gateway integration` Instead of: - `wip` - `wip` - `wip` ## Still fast, still flexible The whole thing takes maybe 2-3 seconds. I type `commit`, and my changes are committed with a descriptive message. If I want to override it with my own message, I just pass it as an argument: `commit "Fix authentication bug"`. The function detects that I've provided a message and uses that instead of generating one. Best of both worlds. ## Adding a spinner for nicer output The function works fine as-is, but waiting 2-3 seconds staring at a blank terminal feels longer than it actually is. So I added a spinner animation to give some visual feedback. Here's the full version with the spinner: ```bash function commit() { commitMessage="$*" git add . if [ "$commitMessage" = "" ]; then # Start spinner in background { spinner="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏" while true; do for (( i=0; i<${#spinner}; i++ )); do printf "\r${spinner:$i:1} Generating commit message..." sleep 0.1 done done } &! spinner_pid=$! # Cleanup function for interrupt cleanup() { { kill $spinner_pid; wait $spinner_pid; } 2>/dev/null printf "\r\033[K" trap - INT return 1 } trap cleanup INT # Get diff and generate message diff_input=$(echo "=== Summary ===" && git diff --cached --stat && echo -e "\n=== Diff (truncated if large) ===" && git diff --cached | head -c 50000) commitMessage=$(echo "$diff_input" | claude -p "Write a single-line commit message for this diff. Output ONLY the message, no quotes, no explanation, no markdown.") # Stop spinner and clear line trap - INT { kill $spinner_pid; wait $spinner_pid; } 2>/dev/null printf "\r\033[K" git commit -m "$commitMessage" return fi eval "git commit -a -m '${commitMessage}'" } ``` Now when I run `commit`, I see: ![](https://freek.dev/admin-uploads/Fk8DMNcvmIz2dyO2UvNRNEukE4ONQGPCZeoGnGl5.jpg) It's purely aesthetic, but it makes the tool feel more polished. Little details like this turn a script into something that feels good to use. ## In closing This is a small automation, but it's made me a better teammate. My commit history is now actually useful for the people who do read it. When my colleagues are tracking down when a feature was added or trying to understand why something changed, they can scan through my commits and find what they're looking for. And I never have to type "wip" again. If you want to use this yourself, you can find the [full `commit` function in my dotfiles](https://github.com/freekmurze/dotfiles/blob/main/home/.functions). You'll need the [Claude CLI](https://github.com/anthropics/anthropic-quickstarts/tree/main/computer-use-demo) installed, but once you have that, just drop the function into your shell config and you're good to go.

🔗 How to automatically generate a commit message using Claude

26.01.2026 08:56 — 👍 2    🔁 0    💬 1    📌 0
New in Parental v1.5.0: Becoming, Integers, and Eager-Loading We released version v1.5.0 of Parental, our package that adds support for Single Table Inheritance (STI) in Laravel. This new version allows types to become other types and use numeric type columns instead of strings. It also adds new helper methods for eager-loading.

🔗 New in Parental v1.5.0: Becoming, Integers, and Eager-Loading

22.01.2026 13:33 — 👍 2    🔁 0    💬 0    📌 0
Running PHP 8.5 with Laravel Octane and FrankenPHP: The Missing Manual (Fix the 8.4 binary trap) A quick but essential fix for anyone pushing the edges of the Laravel ecosystem. The default FrankenPHP binary provided by artisan octane:install is static and locked to PHP 8.4, which causes major headaches if your application relies on PHP 8.5 features or specific system extensions.

🔗 Running PHP 8.5 with Laravel Octane and FrankenPHP: The Missing Manual (Fix the 8.4 binary trap)

21.01.2026 13:38 — 👍 3    🔁 0    💬 0    📌 0
From dd() to Ray: A Debugging Workflow That Doesn't Break Your Flow Evolving from dd() to Ray for a smoother workflow. Explore how Ray can boost your debugging game without stopping your code!

🔗 From dd() to Ray: A Debugging Workflow That Doesn't Break Your Flow

20.01.2026 13:18 — 👍 2    🔁 0    💬 0    📌 0
From dd() to Ray: A Debugging Workflow That Doesn't Break Your Flow I fully agree (but I might not be totally objective here 🙂)

🔗 From dd() to Ray: A Debugging Workflow That Doesn't Break Your Flow

19.01.2026 13:08 — 👍 0    🔁 0    💬 1    📌 0
Livewire 4 Deep Dive: Components, Performance & New Directives
Livewire 4 introduces powerful new features that make building Laravel applications even better. Livewire 4 Deep Dive: Components, Performance & New Directives

🔗 Livewire 4 Deep Dive: Components, Performance & New Directives

16.01.2026 13:50 — 👍 0    🔁 0    💬 0    📌 0
Behind the Terminal Why I made my portfolio feel like a terminal, and how it works

🔗 Behind the Terminal

15.01.2026 13:58 — 👍 1    🔁 1    💬 0    📌 0
What gets lost when everything is effortless? Not all friction is bad.

🔗 What gets lost when everything is effortless?

14.01.2026 13:02 — 👍 1    🔁 1    💬 1    📌 0
Symfony 20 year! This year, Symfony celebrates its 20 year anniversary. Let’s dive into some statistics of years of making web development history.

🔗 Symfony 20 year!

12.01.2026 13:37 — 👍 1    🔁 1    💬 0    📌 0
The rise of industrial software The open question, then, is not whether industrial software will dominate, but what that dominance does to the surrounding ecosystem.

🔗 The rise of industrial software

09.01.2026 13:35 — 👍 3    🔁 0    💬 0    📌 1
Party of One for Code Review Some good thoughts on code reviews in the emerging age of AI.

🔗 Party of One for Code Review

08.01.2026 13:33 — 👍 0    🔁 0    💬 0    📌 0
Eval Testing LLMs in PHPUnit Prompts break silently. Here's how to catch regressions with PHPUnit evals before your users do.

🔗 Eval Testing LLMs in PHPUnit

05.01.2026 13:42 — 👍 1    🔁 0    💬 0    📌 0
Build an AI-Powered Drawing Guessing Game with Laravel, Prism, and HTML Canvas Learn how to build an AI-powered drawing guessing game. In this little app, users will draw anything they like, and the AI will try to guess what it is.

🔗 Build an AI-Powered Drawing Guessing Game with Laravel, Prism, and HTML Canvas

30.12.2025 13:31 — 👍 1    🔁 0    💬 1    📌 0
Partial Function Application is coming in PHP 8.6 Partial Function Application in PHP 8.6 will let you write a “pre‑configured” callable by calling a function with some arguments and using placeholders for the rest.

🔗 Partial Function Application is coming in PHP 8.6

29.12.2025 11:56 — 👍 15    🔁 2    💬 0    📌 0

We're almost ready to sign off for the year. It's been a productive and exciting year for us. If you're interested, you can read our year-end review email here: spatie.mailcoach.app/webview/camp.... Wishing you happy holidays from everyone at Spatie!

19.12.2025 12:58 — 👍 9    🔁 1    💬 0    📌 0
Post image

📢 Sponsor Announcement!

We’re excited to welcome Ray by @spatie.be as a Gold Sponsor for #LaraconIN 2026.

Debug Laravel faster with Ray. Beautiful UI for inspecting variables, queries, mails and much more! myray.app

12.12.2025 06:08 — 👍 5    🔁 2    💬 1    📌 0

@freek is following 20 prominent accounts