This is an older post from Jim Nielsen on building user interfaces I came back to last week.
With software, the fact is that sometimes there are just too many variables to know and test and smooth out. So I click around, using the UI over and over, until I finally cannot give myself any more splinters.
I feel this. I’m better at my job when I’m actually using something. Even better when I’m using something because I want to use it.
I’ve been building a side project on and off for a few months now. Having it my phone’s homescreen and actually using it every day gives me a different perspective on it than just building it and shipping it to others, like is often the case for client work.
Sand it, feel the grain, get a splinter, sand again, and repeat until smooth.
When I experimented with Elixir a few years ago, I fell in love with the pipe operator |>. Since then, I’ve always dreamt of a similar tool in PHP.
Last year, we finally got it! Unfortunately, the standard array & string manipulation functions in PHP aren’t exactly known for their API consistency, which makes the pipe operator awkward to use.
I ported Laravel’s excellent collection & string helpers to a pipe operator-optimized API. Piper is an attempt to wrap the standard library to make it compatible with the pipe operator.
use function Spatie\Piper\Arr\{filter, join, map};
use function Spatie\Piper\Str\{prefix, suffix};
[1, 2, 3, 4, 5, 6]
|> filter(fn (int $i) => $i % 2 === 0)
|> map(fn (int $i) => pow($i, 2))
|> join(', ', ', and ')
|> prefix('The winning numbers are ')
|> suffix('.');
// "The winning numbers are 4, 16, and 36."
Read more
This blog has gone through a few technical iterations. Most recently: from a Laravel app with Sheets, to a static site with Hugo, to a CMS-driven site with Statamic. And now, back to Hugo!
I love Hugo. It’s purposefully built with the right abstractions, and incredibly fast. The kind of fast that makes you wonder why other software can be that fast. Fast software, the best software. But the templating language… it might be an acquired taste, but I never acquired it. Updating my theme or adding a custom page was always a chore. I got tired of it. Coincidentally, I wanted to experiment with more dynamic workflows on here (posting to social media, more control over scheduling…) so I migrated to Statamic (a lovely CMS!)
Fast forward three years and a blogging hiatus, and I started to crave the simplicity of the good old static site. Having a dynamic system was fun, but it requires maintenance and having it made me realize I didn’t need it. Add to that we’ve commoditized robots building things for us, and switching back to Hugo was the right move. A single prompt and 15 minutes later, my entire blog was translated from Statamic to Hugo. The best part: I don’t need to care about the templates anymore. Sure, I’ll dive in occasionally for some surgical styling tweaks. But generally, changes are handled with a quick prompt.
GitHub pings my webserver to auto-deploy on push. Scheduling is solved by running the deploy script every hour. As a static site, everything’s fast by default and there are almost no points of failure. Life is simple. All that remains is to write!
I have a blank canvas problem. I’m way better at staying in flow when I have something to work through, so these days, the first thing I do is ramble.
I open Codex or Spiral, turn on Monologue (my preferred text-to-speech tool), and dump everything I can think of on the topic. Before Monologue, I never considered typing speed to be a bottleneck. My fingers move faster than the speed I shape ideas. But when I want to vomit a bunch of unstructured thoughts onto a canvas, they suddenly feel slow and error-prone.
Instead, I can start a task by talking through it to generate a transcript. Then I ask an agent to structure my ramblings (the other day, the agent responded with “Your brain dump is rich."—probably the nicest compliment I’ve ever gotten from a robot!). This gives me a great starting point to really dig into the problem. Before I know it, I will have rewritten all that rambling into coherent a string of thought.
I still can’t shake the dystopian feeling of talking to my computer. But text-to-speech has made some tasks feel so natural, I almost can’t imagine working without it anymore.
I wrote a piece on the Spatie blog about the place packages/library have in the ecosystem when first-party code is cheaper than ever with AI.
Scrolling to the end:
The question before every composer require is no longer “is there a package for this?” Almost certainly there is. The new question is: “do I want to own this problem?”
This thought came across my mind as I was reading Every’s philosophy on compound engineering:
The first three steps (plan, work, review) produce a feature. The fourth step produces a system that builds features better each time. […]
- Capture the solution. Ask yourself: What worked? What didn’t? What’s the reusable insight?
- Make it findable. Add YAML frontmatter to make sure it is tagged with the right metadata, tags, and categories for retrieval.
- Update the system. Add new patterns into CLAUDE.md, the file the agent reads at the start of every session. Create new agents when warranted.
- Verify the learning. Ask yourself: Would the system catch this automatically next time?
As virtuous developers we try to adhere to the boyscout rule: leave the campsite better than you found it. We do this so our codebase doesn’t start rotting because we slowly let the trash take over.
When agents are writing our code, these effects are multiplied. Every time we make an explicit improvement to the system, agents can pick the pattern up and apply it to both old and new. The campsite will slowly but surely start cleaning itself up, and fresh trash will be thrown straight into the bin.
Andy Matuschak’s notes on working in public. Reading this post was part of the spark that got me blogging again.
It needs to both support tasks that are due today and tasks that are due “sometime this week”.
I like this distinction and wish task managers had this concept by default. Overall a refreshingly simple approach to personal task management.
Alex Woods:
The goal of writing is not to have written. It is to have increased your understanding, and then the understanding of those around you. When you are tasked to write something, your job is to go into the murkiness and come out of it with structure and understanding. To conquer the unknown.
The second order goal of writing is to become more capable. It is like working out. Every time you do a rep on the boundary of what you can do, you get stronger. It is uncomfortable and effortful.
Letting an LLM write for you is like paying somebody to work out for you.
This comes close to what I was talking about in my last newsletter:
I don’t want AI to replace my thinking, and this is easier said than done. Thinking now requires self-discipline. It’s easier to effortlessly prompt the machine and forwarding the output as is. I don’t want to aim for good enough. I want AI to be a lever for better work.
It’s time to forget about the limits we’re used to working in. We need to step out of our magic circle.
To really take advantage of what AI can do, developers need to start thinking about software differently— not as the means to but as the ends. Don’t deliver tools that allow users to achieve outcomes. Just deliver the outcomes.
[…] Instead of requiring users to learn a templating system and string replacements, you can simply write custom messages en mass.
Markdown.new is a nice little tool to convert a webpage into Markdown without any fuss.
To make it even easier, I created a bookmarklet to instantly convert the page you’re viewing to Markdown.
javascript:(()=>{location.href='https://markdown.new/'+location.href})()
I’m a sucker for a good cooking analogy. Liam Hammett on the difference between “it works”, and “it feels right”.
You can’t really condense this article into a single quote. You’re gonna have to read it to feel it.
I’m not good at blank canvases. Unfortunately that’s where feature specs always start. When I have a bunch of notes, transcripts, screenshots, and ideas to consolidate on an empty piece of paper, it can be hard to get going and stay in flow.
I write specs in three steps:
- Collect all input
- Consolidate a cohesive list of requirements
- Review & edit
Lately, I’ve added a few tools to my workflow to guide me through the process.
Read more
Matt Pocock shared a /grill me agent skill:
Interview me relentlessly about every aspect of this plan until
we reach a shared understanding. Walk down each branch of the design
tree resolving dependencies between decisions one by one.
If a question can be answered by exploring the codebase, explore
the codebase instead.
For each question, provide your recommended answer.
Incredibly simple, but the times I’ve used it so far it has been incredibly effective. (Almost annoyingly considering the amount of follow up questions you get; for a good cause!)
I’ve recently switched to Current as my main RSS reader. Reeder will always have a place in my heart, but at some point you get to so much unread stuff that it becomes a chore to get through.
Current solves this with “the river”.
The main screen is a river. Not a river that moves on its own. You’re not watching content drift past like a screensaver. It’s a river in the sense that matters: content arrives, lingers for a time, and then fades away.
Each article has a velocity, a measure of how quickly it ages. Breaking news burns bright for three hours. A daily article stays relevant for eighteen. An essay lingers for three days. An evergreen tutorial might sit in your river for a week.
Outlets like The Verge have always been frustrating to follow with RSS because they generate 20+ posts a day and take over your feed. Essays from my favorite blogs often only appear once every few weeks and drown between the noise. The river balances them all out.
Current is made by Terry Godier. He also wrote a beautiful essay on the design decisions he made building it.