Posts in Html (20 found)
Manuel Moreale 4 days ago

Linda Ma

This week on the People and Blogs series we have an interview with Linda Ma, whose blog can be found at midnightpond.com . Tired of RSS? Read this in your browser or sign up for the newsletter . The People and Blogs series is supported by Aleem Ali and the other 120 members of my "One a Month" club. If you enjoy P&B, consider becoming one for as little as 1 dollar a month. Hey, I’m Linda. I grew up in Budapest in a Chinese family of four, heavily influenced by the 2000s internet. I was very interested in leaving home and ended up in the United Kingdom—all over, but with the most time spent in Edinburgh, Scotland. I got into design, sociology, and working in tech and startups. Then, I had enough of being a designer, working in startups, and living in the UK, so I left. I moved to Berlin and started building a life that fits me more authentically. My interests change a lot, but the persistent ones have been: journaling with a fountain pen, being horizontal in nature, breathwork, and ambient music. I was struck by a sudden need to write in public last year. I’d been writing in private but never felt the need to put anything online because I have this thing about wanting to remain mysterious. At least, that’s the story I was telling myself. In hindsight, the 'sudden need' was more of a 'wanting to feel safe to be seen.' I also wanted to find more people who were like-minded. Not necessarily interested in the same things as me, but thinking in similar ways. Through writing, I discovered that articulating your internal world with clarity takes time and that I was contributing to my own problems because I wasn't good at expressing myself. I write about these kinds of realizations in my blog. It’s like turning blurriness and stories into clarity and facts. I also do the opposite sometimes, where I reframe experiences and feelings into semi-fictional stories as a way to release them. I enjoy playing in this space between self-understanding through reality and self-soothing through fantasy. I also just enjoy the process of writing and the feeling of hammering on the keyboard. I wanted the blog to be formless and open-ended, so it didn’t have a name to begin with, and it was hanging out on my personal website. The name just kinda happened. I like the sound of the word “pond” and the feeling I get when I think of a pond. Then I thought: if I were a pond, what kind of pond would I be? A midnight pond. It reflects me, my writing, and the kind of impression I’d like to leave. It’s taken on a life of its own now, and I’m curious to see how it evolves. Nowadays, it seems I’m interested in writing shorter pieces and poems. I get a lot of inspiration from introspection, often catalyzed by conversations with people, paragraphs from books, music, or moments from everyday life. In terms of the writing process, the longer blogposts grow into being like this: I'll have fleeting thoughts and ideas that come to me pretty randomly. I try to put them all in one place (a folder in Obsidian or a board in Muse ). I organically return to certain thoughts and notes over time, and I observe which ones make me feel excited. Typically, I'll switch to iA Writer to do the actual writing — something about switching into another environment helps me get into the right mindset. Sometimes the posts are finished easily and quickly, sometimes I get stuck. When I get stuck, I take the entire piece and make it into a pile of mess in Muse. Sometimes the mess transforms into a coherent piece, sometimes it gets abandoned. When I finish something and feel really good about it, I let it sit for a couple days and look at it again once the post-completion high has faded. This is advice from the editors of the Modern Love column , and it’s very good advice. I occasionally ask a friend to read something to gauge clarity and meaning. I like the idea of having more thinking buddies. Please feel free to reach out if you think we could be good thinking buddies. Yes, I do believe the physical space influences my creativity. And it’s not just the immediate environment (the room or desk I'm writing at) but also the thing or tool I'm writing with (apps and notebook) as well as the broader environment (where I am geographically). There’s a brilliant book by Vivian Gornick called The Situation and the Story: The Art of Personal Narrative and a quote in it: “If you don’t leave home you suffocate, if you go too far you lose oxygen.” It’s her comment on one of the example pieces she discusses. This writer was talking about how he couldn’t write when he was too close or too far from home. It’s an interesting perspective to consider, and I find it very relatable. Though I wouldn’t have arrived at this conclusion had I not experienced both extremes. My ideal creative environment is a relatively quiet space where I can see some trees or a body of water when I look up. The tools I mentioned before and my physical journal are also essential to me. My site is built with Astro , the code is on GitHub, and all deploys through Netlify. The site/blog is really just a bunch of .md and .mdx files with some HTML and CSS. I code in VS Code. I wouldn’t change anything about the content or the name. Maybe I would give the tech stack or platform more thought if I started it now? In moments of frustration with Astro or code, I’ve often wondered if I should just accept that I’m not a techie and use something simpler. It’s been an interesting journey figuring things out though. Too deep into it, can’t back out now. The only running cost I have at the moment is the domain which is around $10 a year. iA Writer was a one-time purchase of $49.99. My blog doesn’t generate revenue. I don’t like the idea of turning personal blogs into moneymaking machines because it will most likely influence what and how you write. But — I am supportive of creatives wanting to be valued for what they create and share from an authentic place. I like voluntary support based systems like buymeacoffee.com or ko-fi.com . I also like the spirit behind platforms like Kickstarter or Metalabel . I started a Substack earlier this year where I share the longer posts from my blog. I’m not sure how I feel about this subscription thing, but I now use the paywall to protect posts that are more personal than others. I’ve come across a lot of writing I enjoy though and connected with others through writing. Here are a few I’ve been introduced to or stumbled upon: Interesting, no-longer-active blogs: I like coming across sites that surprise me. Here’s one that boggles my mind, and here’s writer Catherine Lacey’s website . There’s also this online documentary and experience of The Garden of Earthly Delights by Jheronimous Bosch that I share all the time, and Spencer Chang’s website is pretty cool. Now that you're done reading the interview, go check the blog and subscribe to the RSS feed . If you're looking for more content, go read one of the previous 110 interviews . Make sure to also say thank you to Ben Werdmuller and the other 120 supporters for making this series possible. Lu’s Wikiblogardenite — Very real and entertaining blog of a "slightly-surreal videos maker and coder". Romina’s Journal — Journal of a graphic designer and visual artist. Maggie Appleton’s Garden — Big fan of Maggie’s visual essays on programming, design, and anthropology. Elliott’s memory site — This memory site gives me a cozy feeling. Where are Kay and Phil? — Friends documenting their bike tours and recipes. brr — Blog of an IT professional who was deployed to Antarctica during 2022-2023. The late Ursula K. Le Guin’s blog — She started this at the age of 81 in 2010.

2 views
baby steps 1 weeks ago

SymmACP: extending Zed's ACP to support Composable Agents

This post describes SymmACP – a proposed extension to Zed’s Agent Client Protocol that lets you build AI tools like Unix pipes or browser extensions. Want a better TUI? Found some cool slash commands on GitHub? Prefer a different backend? With SymmACP, you can mix and match these pieces and have them all work together without knowing about each other. This is pretty different from how AI tools work today, where everything is a monolith – if you want to change one piece, you’re stuck rebuilding the whole thing from scratch. SymmACP allows you to build out new features and modes of interactions in a layered, interoperable way. This post explains how SymmACP would work by walking through a series of examples. Right now, SymmACP is just a thought experiment. I’ve sketched these ideas to the Zed folks, and they seemed interested, but we still have to discuss the details in this post. My plan is to start prototyping in Symposium – if you think the ideas I’m discussing here are exciting, please join the Symposium Zulip and let’s talk! I’m going to explain the idea of “composable agents” by walking through a series of features. We’ll start with a basic CLI agent 1 tool – basically a chat loop with access to some MCP servers so that it can read/write files and execute bash commands. Then we’ll show how you could add several features on top: The magic trick is that each of these features will be developed as separate repositories. What’s more, they could be applied to any base tool you want, so long as it speaks SymmACP. And you could also combine them with different front-ends, such as a TUI, a web front-end, builtin support from Zed or IntelliJ , etc. Pretty neat. My hope is that if we can centralize on SymmACP, or something like it, then we could move from everybody developing their own bespoke tools to an interoperable ecosystem of ideas that can build off of one another. SymmACP begins with ACP, so let’s explain what ACP is. ACP is a wonderfully simple protocol that lets you abstract over CLI agents. Imagine if you were using an agentic CLI tool except that, instead of communication over the terminal, the CLI tool communicates with a front-end over JSON-RPC messages, currently sent via stdin/stdout. When you type something into the GUI, the editor sends a JSON-RPC message to the agent with what you typed. The agent responds with a stream of messages containing text and images. If the agent decides to invoke a tool, it can request permission by sending a JSON-RPC message back to the editor. And when the agent has completed, it responds to the editor with an “end turn” message that says “I’m ready for you to type something else now”. OK, let’s tackle our first feature. If you’ve used a CLI agent, you may have noticed that they don’t know what time it is – or even what year it is. This may sound trivial, but it can lead to some real mistakes. For example, they may not realize that some information is outdated. Or when they do web searches for information, they can search for the wrong thing: I’ve seen CLI agents search the web for “API updates in 2024” for example, even though it is 2025. To fix this, many CLI agents will inject some extra text along with your prompt, something like . This gives the LLM the context it needs. So how could use ACP to build that? The idea is to create a proxy . This proxy would wrap the original ACP server: This proxy will take every “prompt” message it receives and decorate it with the date and time: Simple, right? And of course this can be used with any editor and any ACP-speaking tool. Let’s look at another feature that basically “falls out” from ACP: injecting personality. Most agents give you the ability to configure “context” in various ways – or what Claude Code calls memory . This is useful, but I and others have noticed that if what you want is to change how Claude “behaves” – i.e., to make it more collaborative – it’s not really enough. You really need to kick off the conversation by reinforcing that pattern. In Symposium, the “yiasou” prompt (also available as “hi”, for those of you who don’t speak Greek 😛) is meant to be run as the first thing in the conversation. But there’s nothing an MCP server can do to ensure that the user kicks off the conversation with or something similar. Of course, if Symposium were implemented as an ACP Server, we absolutely could do that: Some of you may be saying, “hmm, isn’t that what hooks are for?” And yes, you could do this with hooks, but there’s two problems with that. First, hooks are non-standard, so you have to do it differently for every agent. The second problem with hooks is that they’re fundamentally limited to what the hook designer envisioned you might want. You only get hooks at the places in the workflow that the tool gives you, and you can only control what the tool lets you control. The next feature starts to show what I mean: as far as I know, it cannot readily be implemented with hooks the way I would want it to work. Let’s move on to our next feature, long-running asynchronous tasks. This feature is going to have to go beyond the current capabilities of ACP into the expanded “SymmACP” feature set. Right now, when the server invokes an MCP tool, it executes in a blocking way. But sometimes the task it is performing might be long and complicated. What you would really like is a way to “start” the task and then go back to working. When the task is complete, you (and the agent) could be notified. This comes up for me a lot with “deep research”. A big part of my workflow is that, when I get stuck on something I don’t understand, I deploy a research agent to scour the web for information. Usually what I will do is ask the agent I’m collaborating with to prepare a research prompt summarizing the things we tried, what obstacles we hit, and other details that seem relevant. Then I’ll pop over to claude.ai or Gemini Deep Research and paste in the prompt. This will run for 5-10 minutes and generate a markdown report in response. I’ll download that and give it to my agent. Very often this lets us solve the problem. 2 This research flow works well but it is tedious and requires me to copy-and-paste. What I would ideally want is an MCP tool that does the search for me and, when the results are done, hands them off to the agent so it can start processing immediately. But in the meantime, I’d like to be able to continue working with the agent while we wait. Unfortunately, the protocol for tools provides no mechanism for asynchronous notifications like this, from what I can tell. So how would I do it with SymmACP? Well, I would want to extend the ACP protocol as it is today in two ways: In that case, we could implement our Research Proxy like so: What’s cool about this is that the proxy encapsulates the entire flow: it knows how to do the research, and it manages notifying the various participants when the research completes. (Also, this leans on one detail I left out, which is that ) Let’s explore our next feature, Q CLI’s mode . This feature is interesting because it’s a simple (but useful!) example of history editing. The way works is that, when you first type , Q CLI saves your current state. You can then continue as normal but when you next type , your state is restored to where you were. This, as the name suggests, lets you explore a side conversation without polluting your main context. The basic idea for supporting tangent in SymmACP is that the proxy is going to (a) intercept the tangent prompt and remember where it began; (b) allow the conversation to continue as normal; and then (c) when it’s time to end the tangent, create a new session and replay the history up until the point of the tangent 3 . You can almost implement “tangent” in ACP as it is, but not quite. In ACP, the agent always owns the session history. The editor can create a new session or load an older one; when loading an older one, the agent “replays” “replays” the events so that the editor can reconstruct the GUI. But there is no way for the editor to “replay” or construct a session to the agent . Instead, the editor can only send prompts, which will cause the agent to reply. In this case, what we want is to be able to say “create a new chat in which I said this and you responded that” so that we can setup the initial state. This way we could easily create a new session that contains the messages from the old one. So how this would work: One of the nicer features of Symposium is the ability to do interactive walkthroughs . These consist of an HTML sidebar as well as inline comments in the code: Right now, this is implemented by a kind of hacky dance: It works, but it’s a giant Rube Goldberg machine. With SymmACP, we would structure the passthrough mechanism as a proxy. Just as today, it would provide an MCP tool to the agent to receive the walkthrough markdown. It would then convert that into the HTML to display on the side along with the various comments to embed in the code. But this is where things are different. Instead of sending that content over IPC, what I would want to do is to make it possible for proxies to deliver extra information along with the chat. This is relatively easy to do in ACP as is, since it provides for various capabilities, but I think I’d want to go one step further I would have a proxy layer that manages walkthroughs. As we saw before, it would provide a tool. But there’d be one additional thing, which is that, beyond just a chat history, it would be able to convey additional state. I think the basic conversation structure is like: but I think it’d be useful to (a) be able to attach metadata to any of those things, e.g., to add extra context about the conversation or about a specific turn (or even a specific prompt ), but also additional kinds of events. For example, tool approvals are an event . And presenting a walkthrough and adding annotations are an event too. The way I imagine it, one of the core things in SymmACP would be the ability to serialize your state to JSON. You’d be able to ask a SymmACP paricipant to summarize a session. They would in turn ask any delegates to summarize and then add their own metadata along the way. You could also send the request in the other direction – e.g., the agent might present its state to the editor and ask it to augment it. This would mean a walkthrough proxy could add extra metadata into the chat transcript like “the current walkthrough” and “the current comments that are in place”. Then the editor would either know about that metadata or not. If it doesn’t, you wouldn’t see it in your chat. Oh well – or perhaps we do something HTML like, where there’s a way to “degrade gracefully” (e.g., the walkthrough could be presented as a regular “response” but with some metadata that, if you know to look, tells you to interpret it differently). But if the editor DOES know about the metadata, it interprets it specially, throwing the walkthrough up in a panel and adding the comments into the code. With enriched histories, I think we can even say that in SymmACP, the ability to load, save, and persist sessions itself becomes an extension, something that can be implemented by a proxy; the base protocol only needs the ability to conduct and serialize a conversation. Let me sketch out another feature that I’ve been noodling on that I think would be pretty cool. It’s well known that there’s a problem that LLMs get confused when there are too many MCP tools available. They get distracted. And that’s sensible, so would I, if I were given a phonebook-size list of possible things I could do and asked to figure something out. I’d probably just ignore it. But how do humans deal with this? Well, we don’t take the whole phonebook – we got a shorter list of categories of options and then we drill down. So I go to the File Menu and then I get a list of options, not a flat list of commands. I wanted to try building an MCP tool for IDE capabilities that was similar. There’s a bajillion set of things that a modern IDE can “do”. It can find references. It can find definitions. It can get type hints. It can do renames. It can extract methods. In fact, the list is even open-ended, since extensions can provide their own commands. I don’t know what all those things are but I have a sense for the kinds of things an IDE can do – and I suspect models do too. What if you gave them a single tool, “IDE operation”, and they could use plain English to describe what they want? e.g., . Hmm, this is sounding a lot like a delegate, or a sub-agent. Because now you need to use a second LLM to interpret that request – you probably want to do something like, give it a list of sugested IDE capabilities and the ability to find out full details and ask it to come up with a plan (or maybe directly execute the tools) to find the answer. As it happens, MCP has a capability to enable tools to do this – it’s called (somewhat oddly, in my opinion) “sampling”. It allows for “callbacks” from the MCP tool to the LLM. But literally nobody implements it, from what I can tell. 4 But sampling is kind of limited anyway. With SymmACP, I think you could do much more interesting things. The key is that ACP already permits a single agent to “serve up” many simultaneous sessions. So that means that if I have a proxy, perhaps one supplying an MCP tool definition, I could use it to start fresh sessions – combine that with the “history replay” capability I mentioned above, and the tool can control exactly what context to bring over into that session to start from, as well, which is very cool (that’s a challenge for MCP servers today, they don’t get access to the conversation history). Ok, this post sketched a variant on ACP that I call SymmACP. SymmACP extends ACP with Most of these are modest extensions to ACP, in my opinion, and easily doable in a backwards fashion just by adding new capabilities. But together they unlock the ability for anyone to craft extensions to agents and deploy them in a composable way. I am super excited about this. This is exactly what I wanted Symposium to be all about. It’s worth noting the old adage: “with great power, comes great responsibility”. These proxies and ACP layers I’ve been talking about are really like IDE extensions. They can effectively do anything you could do. There are obvious security concerns. Though I think that approaches like Microsoft’s Wassette are key here – it’d be awesome to have a “capability-based” notion of what a “proxy layer” is, where everything compiles to WASM, and where users can tune what a given proxy can actually do . I plan to start sketching a plan to drive this work in Symposium and elsewhere. My goal is to have a completely open and interopable client, one that can be based on any agent (including local ones) and where you can pick and choose which parts you want to use. I expect to build out lots of custom functionality to support Rust development (e.g., explaining and diagnosting trait errors using the new trait solver is high on my list…and macro errors…) but also to have other features like walkthroughs, collaborative interaction style, etc that are all language independent – and I’d love to see language-focused features for other langauges, especially Python and TypeScript (because “the new trifecta” ) and Swift and Kotlin (because mobile). If that vision excites you, come join the Symposium Zulip and let’s chat! One question I’ve gotten when discussing this is how it compares to the other host of protocols out there. Let me give a brief overview of the related work and how I understand its pros and cons: Everybody uses agents in various ways. I like Simon Willison’s “agents are models using tools in a loop” definition; I feel that an “agentic CLI tool” fits that definition, it’s just that part of the loop is reading input from the user. I think “fully autonomous” agents are a subset of all agents – many agent processes interact with the outside world via tools etc. From a certain POV, you can view the agent “ending the turn” as invoking a tool for “gimme the next prompt”.  ↩︎ Research reports are a major part of how I avoid hallucination. You can see an example of one such report I commissioned on the details of the Language Server Protocol here ; if we were about to embark on something that required detailed knowledge of LSP, I would ask the agent to read that report first.  ↩︎ Alternatively: clear the session history and rebuild it, but I kind of prefer the functional view of the world, where a given session never changes.  ↩︎ I started an implementation for Q CLI but got distracted – and, for reasons that should be obvious, I’ve started to lose interest.  ↩︎ Yes, you read that right. There is another ACP. Just a mite confusing when you google search. =)  ↩︎ Addressing time-blindness by helping the agent know what time it is. Injecting context and “personality” to the agent. Spawning long-running, asynchronous tasks. A copy of Q CLI’s mode that lets you do a bit of “off the books” work that gets removed from your history later. Implementing Symposium’s interactive walkthroughs , which give the agent a richer vocabulary for communicating with you than just text. Smarter tool delegation. I’d like the ACP proxy to be able to provide tools that the proxy will execute. Today, the agent is responsible for executing all tools; the ACP protocol only comes into play when requesting permission . But it’d be trivial to have MCP tools where, to execute the tool, the agent sends back a message over ACP instead. I’d like to have a way for the agent to initiate responses to the editor . Right now, the editor always initiatives each communication session with a prompt; but, in this case, the agent might want to send messages back unprompted. The agent invokes an MCP tool and sends it the walkthrough in markdown. This markdown includes commands meant to be placed on particular lines, identified not by line number (agents are bad at line numbers) but by symbol names or search strings. The MCP tool parses the markdown, determines the line numbers for comments, and creates HTML. It sends that HTML over IPC to the VSCode extension. The VSCode receives the IPC message, displays the HTML in the sidebar, and creates the comments in the code. Conversation Turn User prompt(s) – could be zero or more Response(s) – could be zero or more Tool use(s) – could be zero or more the ability for either side to provide the initial state of a conversation, not just the server the ability for an “editor” to provide an MCP tool to the “agent” the ability for agents to respond without an initial prompt the ability to serialize conversations and attach extra state (already kind of present) Model context protocol (MCP) : The queen of them all. A protocol that provides a set of tools, prompts, and resources up to the agent. Agents can invoke tools by supplying appropriate parameters, which are JSON. Prompts are shorthands that users can invoke using special commands like or , they are essentially macros that expand “as if the user typed it” (but they can also have parameters and be dynamically constructed). Resources are just data that can be requested. MCP servers can either be local or hosted remotely. Remote MCP has only recently become an option and auth in particular is limited. Comparison to SymmACP: MCP provides tools that the agent can invoke. SymmACP builds on it by allowing those tools to be provided by outer layers in the proxy chain. SymmACP is oriented at controlling the whole chat “experience”. Zed’s Agent Client Protocol (ACP) : The basis for SymmACP. Allows editors to create and manage sessions. Focused only on local sessions, since your editor runs locally. Comparison to SymmACP: That’s what this post is all about! SymmACP extends ACP with new capabilities that let intermediate layers manipulate history, provide tools, and provide extended data upstream to support richer interaction patterns than jus chat. PS I expect we may want to support more remote capabilities, but it’s kinda orthogonal in my opinion (e.g., I’d like to be able to work with an agent running over in a cloud-hosted workstation, but I’d probably piggyback on ssh for that). Google’s Agent-to-Agent Protocol (A2A) and IBM’s Agent Communication Protocol (ACP) 5 : From what I can tell, Google’s “agent-to-agent” protocol is kinda like a mix of MCP and OpenAPI. You can ping agents that are running remotely and get them to send you “agent cards”, which describe what operations they can perform, how you authenticate, and other stuff like that. It looks to me quite similar to MCP except that it has richer support for remote execution and in particular supports things like long-running communication, where an agent may need to go off and work for a while and then ping you back on a webhook. Comparison to MCP: To me, A2A looks like a variant of MCP that is more geared to remote execution. MCP has a method for tool discovery where you ping the server to get a list of tools; A2A has a similar mechanism with Agent Cards. MCP can run locally, which A2A cannot afaik, but A2A has more options about auth. MCP can only be invoked synchronously, whereas A2A supports long-running operations, progress updates, and callbacks. It seems like the two could be merged to make a single whole. Comparison to SymmACP: I think A2A is orthogonal from SymmACP. A2A is geared to agents that provide services to one another. SymmACP is geared towards building new development tools for interacting with agents. It’s possible you could build something like SymmACP on A2A but I don’t know what you would really gain by it (and I think it’d be easy to do later). Everybody uses agents in various ways. I like Simon Willison’s “agents are models using tools in a loop” definition; I feel that an “agentic CLI tool” fits that definition, it’s just that part of the loop is reading input from the user. I think “fully autonomous” agents are a subset of all agents – many agent processes interact with the outside world via tools etc. From a certain POV, you can view the agent “ending the turn” as invoking a tool for “gimme the next prompt”.  ↩︎ Research reports are a major part of how I avoid hallucination. You can see an example of one such report I commissioned on the details of the Language Server Protocol here ; if we were about to embark on something that required detailed knowledge of LSP, I would ask the agent to read that report first.  ↩︎ Alternatively: clear the session history and rebuild it, but I kind of prefer the functional view of the world, where a given session never changes.  ↩︎ I started an implementation for Q CLI but got distracted – and, for reasons that should be obvious, I’ve started to lose interest.  ↩︎ Yes, you read that right. There is another ACP. Just a mite confusing when you google search. =)  ↩︎

0 views
Preah's Website 1 weeks ago

(Guide) Intro To Social Blogging

Social networks have rapidly become so vital to many people's lives on the internet. People want to see what their friends are doing, where they are, and photos of what they're doing. They also want to share these same things with their friends, all without having to go through the manual and sometimes awkward process of messaging them directly and saying "Hey, how're you doing?" Developers and companies have complied with this desire for instant connection. We see the launch of Friendster in 2002, MySpace and a job-centered one we all know, LinkedIn , in 2003. Famously, Facebook in 2004, YouTube in 2005, Twitter (now X) in 2006. Followed by Instagram , Snapchat , Google+ (RIP), TikTok , and Discord . People were extremely excited about this. We are more connected than ever. But we are losing in several ways. These companies that own these platforms want to make maximum profit, leading them to offer subscription-based services in some cases, or more distressing, sell their users' data to advertisers. They use algorithms to serve cherry-picked content that creates dangerous echo-chambers, and instill the need for users to remain on their device for sometimes hours just to see what's new, exacerbating feelings of FOMO and wasting precious time. Facebook has been found to conduct experiments on its users to fuel rage and misinformation for the purpose of engagement. 1 2 When did socializing online with friends and family become arguing with strangers, spreading misinformation, and experiencing panic attacks because of the constant feed of political and social unrest? I don't expect anyone to drop their social media. Plenty of people use it in healthy ways. We even have decentralized social media, such as the fediverse (think Mastodon) and the AT Protocol (think Bluesky) to reduce the problem of one person or company owning everything. I think this helps, and seeing a feed of your friends' short thoughts or posts occasionally is nice if you're not endlessly scrolling. I also think it's vital to many people to be able to explore recommendations frequently to get out of their bubble and experience variety. There is another option, one I am personally more fond of. It can sit nicely alongside your existing social media or replace it. It serves a different purpose than something like Twitter (X) or Instagram. It's meant to be a slower, more nuanced form of socializing and communicating, inspired by the pre-social media era, or at least the early one. For the purposes of this guide, I will refer to this as "Blog Feeds." A little intro in one page can be explained by blogfeeds.net , 3 which includes an aggregation of blogs to follow, essentially creating a network of people similar to a webring. 4 This will help you explore new blogs you find interesting and create a tighter group. Another gem is ooh.directory , which sorts blogs by category and interest, allows you to flip through random blogs, and visit the most recently-updated blogs for ideas of who to follow. Basically, a blog feed involves making a personal blog, which can have literally whatever you want on it, and following other people. The "following" aspect can be done through RSS (most common), or email newsletter if their site supports it. If the blog is part of the AT Protocol, you may be able to follow it using a Bluesky account. More about that later. Making a blog sounds scary and technical, but it doesn't have to be. If you know web development or want to learn, you can customize a site to be whatever your heart desires. If you're not into that, there are many services that make it incredibly easy to get going. You can post about your day, about traveling, about gaming, theme it a specific way, or post short thoughts on nothing much at all if you want. All I ask is that you do this because you want to, not solely because you might make a profit off of your audience. Also, please reconsider using AI to write posts if you are thinking of doing that! It's fully up to you, but in my opinion, why should I read something no one bothered to write? Hosted Services: Bear Blog: In the creator's own words, "A privacy-first, no-nonsense, super-fast blogging platform." Sign up, select a pre-made theme if you want and modify it to your liking, make post templates, and connect a custom domain if desired. Comes with ready-to-go RSS, and pretty popular among bloggers currently. This site runs on it. Pika: “An editor that makes you want to write, designed to get out of your way and perfectly match what readers will see.” With Pika you can sign up, choose a theme, customize without code, write posts in a clean editor, export your content, and connect your own domain, with a focus on privacy and design. You can start for free (up to ~50 posts) and upgrade later if you want unlimited posts, newsletter subscribers, analytics, etc. Substack: You might have seen this around before, it's quite popular. It's a platform built for people to publish posts and sometimes make money doing it. You can start a newsletter or blog, choose what’s free and what’s paid, send posts (and even podcasts or video) to subscribers’ inboxes, build a community, and access basic analytics. It’s simple and user-friendly, with a 10% fee if you monetize. This may not be the most loved option by other small bloggers due to its association with newsletter-signup popups and making a profit. It is also the most similar to other social media among blogging options . Ghost: An open-source platform focused on publishing and monetization. Ghost provides an editor (with live previews, Markdown + embeds, and an admin UI), built-in SEO, newsletter tools, membership & subscription support, custom themes, and control over your domain and data. You can self-host (free, for full flexibility) or use their managed Ghost(Pro) hosting, and benefit from faster performance, email delivery, and extensible APIs. Wordpress: The world’s most popular website and blogging platform, powering over 40% of the web. WordPress lets you create a simple blog or a business site using free and premium themes and plugins. You can host it yourself with full control, or use their hosted service (WordPress.com) for convenience. It supports custom domains, rich media, SEO tools, and extensibility through code or plugins. Squarespace: You might have heard of this on your favorite YouTuber's channel during a sponsorship (you don't sit through those, do you?). It is a platform for building websites, blogs, and online stores with no coding required. Squarespace offers templates, a drag-and-drop editor, built-in SEO, analytics, and e-commerce tools under a subscription. You can connect a custom domain, publish blog posts, and manage newsletters. Self-hosted, if you're more technical: Astro: A modern web framework built for speed, content, and flexibility. Astro lets you build blogs, portfolios, and full sites using any UI framework, or none at all, with zero JavaScript by default. 5 It supports Markdown, MDX, and server-side rendering, plus integrations for CMSs, themes, and deployment platforms. Hugo: An open-source static site generator built for efficiency and flexibility. It lets you create blogs and websites using Markdown, shortcodes, and templates. It supports themes, taxonomies, custom content types, and control over site structure without needing a database. Zola: Another open-source static site generator. Zola uses Markdown for content, Tera templates for layouts, and comes with built-in features like taxonomies, RSS feeds, and syntax highlighting. It requires no database, and is easy to configure. 11ty: Pronounced Eleventy. A flexible static site generator that lets you build content-focused websites using plain HTML, Markdown, or templating languages like Nunjucks, Liquid, and others. 11ty requires no database, supports custom data structures, and gives full control over your site’s output. Jekyll: A popular static site generator that transforms plain text into self-hosted websites and blogs. Jekyll uses Markdown, Liquid templates, and simple configuration to generate content without a database. It supports themes, plugins, and custom layouts, and integrates seamlessly with GitHub Pages for free hosting. Honorable mention: Wow, that's a lot of options! Don't get overwhelmed. Here are the basics for a simple site like Bear Blog or a static site generator. You write a post. This post tends to be in Markdown, which is a markup language (like HTML) for creating formatted text. It's actually not too far from something like Microsoft Word. In this case, if you want a header, you can put a pound symbol in front of your header text to tell your site that it should be formatted as one. Same with quotation blocks, bolding, italics and all that. Here is a simple Markdown cheatsheet provided by Bear Blog. Some other blogging platforms have even more options for formatting, like informational or warning boxes. After you've written it, you can usually preview it before posting. While you're writing, you might want to use a live-preview to make sure you're formatting it how you intend. After posting, people can go read your post and possibly interact with it in some ways if you want that. I'm not going to attempt to describe AT Protocol when there is another post that does an excellent job. But what I am going to mention, briefly, is using this protocol to follow blogs via Bluesky or another AT Protocol handle. Using something like leaflet.pub , you can create a blog on there, and follow other similar blogs. Here is an example of a blog on leaflet , and if you have Bluesky, go ahead and test subscribing using it. They also support comments and RSS. You don't have to memorize what RSS stands for (Really Simple Syndication, if you're curious). This is basically how you create a feed, like a Twitter (X) timeline or a Facebook homepage. When you subscribe to someone's blog, 6 you can get a simple, consolidated aggregation of new posts. At this point, RSS is pretty old but still works exactly as intended, and most sites have RSS feeds. What you need to start is a newsreader app. There are a lot of options, so it depends on what you value most. When you subscribe to a website, you put that into your newsreader app, and it fetches the content and displays it for you, among other neat features. Usually they include nice themes, no ads to bother you, and folder or tag organization. You may have to find a site's feed and copy the link, like , or your reader app may be able to find it automatically from a browser shortcut or from pasting in the normal link for the website. To learn more about adding a new subscription, see my feeds page . Here are some suggestions. Feel free to explore multiple and see what sticks: Feedly: A cloud-based, freemium RSS aggregator with apps and a browser interface. You can use a free account that supports a limited number of sources (about 100 feeds) and basic folders, but many advanced features—such as hiding ads, notes/highlights, power search, integration with Evernote/Pocket, and “Leo” AI filtering—require paid tiers. It supports iOS, Android, and web (desktop browsers). Feedly is not open source, it is a commercial service. Inoreader: Also a freemium service, available via web and on iOS and Android, with synchronization of your reading across devices. The free plan includes many of the core features (RSS subscription, folders, basic filtering), while more powerful features (such as advanced rules, full-text search, premium support, more feed limits) are gated behind paid tiers. Inoreader is not open source, it is a proprietary service with a freemium model. NetNewsWire: A native, free, and open-source RSS reader for Apple platforms (macOS, iPhone, iPad). It offers a clean, native experience and tracks your read/unread status locally or via syncing. Because it’s open source (MIT-licensed), you can inspect or contribute to its code. Its main limitation is platform since it’s focused on Apple devices. It's also not very visually flashy, if you care about that. feeeed (with four Es) : An iOS/iPadOS (and recent macOS) app that emphasizes a private, on-device reading experience without requiring servers or accounts. It is free (no ads or in-app purchases) and supports RSS subscriptions, YouTube, Reddit, and other sources, plus some AI summarization. Because it is designed to run entirely on device, there is no paid subscription for backend features, and it is private by design. It is not open-source. One personal note from me, I use this as my daily driver, and it has some minor bugs you may notice. It's developed by one person, so it happens. Reeder: A client app (primarily for Apple platforms: iOS, iPadOS, macOS) that fetches feed data from external services, such as Feedly, Inoreader, or local RSS sources. The new Reeder version supports unified timeline, filters, and media integration. It is not itself a feed-hosting service but a front end; thus, many of its features (such as sync or advanced filtering) depend on which backend you use. It uses iCloud to sync subscription and timeline state between devices. Reeder is proprietary (closed source) and uses a paid model or in-app purchases for more advanced versions. Unread: Another client app for Apple platforms with a focus on elegant reading. It relies on external feed services for syncing (you provide your own RSS or use a service like Feedly). Because Unread is a reader app, its features are more about presentation and gesture support; many of the syncing, feed limits, or premium capabilities depend on your chosen backend service. I would say Unread is my favorite so far, as it offers a lot for being free, has great syncing, tag organization, and a pleasing interface. It also fetches entire website content to get around certain limitations with some websites' feeds, allowing you to read everything in the app without visiting the website directly. FreshRSS: A self-hostable, open-source RSS/Atom aggregator that you run on your own server (like via Docker) and which supports reading through its own web interface or via third-party client apps. It allows full control over feed limits, filtering, theming, extensions, and it can generate feeds by scraping or filtering content. Because it is open source, there is no paid tier in the software itself (though you may incur hosting costs). Many client apps can connect to a FreshRSS instance for mobile or desktop reading. If you're interested in something interesting you can do with its API, check out Steve's post about automating feeds with FreshRSS. Click this for a more detailed breakdown of available RSS newsreaders. Additional resource on RSS and Feeds. Okay, soooo... I have a blog, I have RSS stuff, now what do I subscribe to, and how do I make this social? I'll let blogfeeds.net describe this: This takes us to our final point: Feeds. You can probably get away with just the first two items and then sharing it with people you already know, but what about meeting or talking to people you don't know? That's where Feeds come in. The idea is to create another page on your blog that has all the RSS feeds you're subscribed to. By keeping this public and always up to date, someone can visit your page, find someone new and follow them. Perhaps that person also has a feeds page, and the cycle continues until there is a natural and organic network of people all sharing with each other. So if you have a blog, consider making a feeds page and sharing it! If your RSS reader supports OPML file exports and imports, perhaps you can share that file as well to make it easier to share your feeds. Steve has an example of a feeds page , and blogfeeds.net has an aggregation of known blogs using feeds pages , to create a centralized place to follow blogs who have this same mindset. Once you make a feeds page, you can submit it to the site to get added to it. Then people can find your blog! There is debate on the best method for interaction with others via blogs. You have a few options. And the accompanying CSS, 7 which Bear Blog lets you edit: For each post, I do change the subject line (Re: {{post_title}}) manually to whatever the post title is. That way, someone can click the button and open their mail client already ready to go with a subject line pertaining to the post they want to talk about. Change the values and to whatever colors you want to match your site! See the bottom of this post to see what it looks like. Next: Comments: Comments are a tricky one. It's looked down on by some because of their lack of nuance and moderation stress, which is why Bear Blog doesn't natively have them. There are various ways to do comments, and it heavily depends on what blogging platform you choose, so here is Bear Blog's stance on it and some recommendations for setting up comments if you want . Guestbooks: This is an old form of website interaction that dates back to at least Geocities . The concept is that visitors to your site can leave a quick thought, their name, and optionally their own website to let you know they visited. You can see an example on my website , and my recommended service for a free guestbook is Guestbooks . You can choose a default theme and edit it if you want to match the rest of your site, implement spam protection, and access a dashboard for managing and deleting comments if needed. Here are some ideas to get you started and inspired: Add new pages, like a link to your other social media or music listening platforms, or a page dedicated to your pet. Email a random person on a blog to give your thoughts on a post of theirs or simply tell them their site is cool. Create an email just for this and for your website for privacy and separation, if desired. Add a Now page. It's a section specifically to tell others what you are focused on at this point of your life. Read more about it at nownownow.com . See an example on Clint McMahon's blog . A /now page shares what you’d tell a friend you hadn’t seen in a year. Write a post about a cool rock you found in your yard, or something similarly asinine. Revel in the lack of effort. Or, Make a post containing 1-3 sentences only. Join a webring . Make a page called Reviews, to review movies, books, TV shows, games, kitchen appliances, etc. That's all from me for now. Subscribe to my RSS feed , email me using the button at the bottom to tell me this post sucks, or that it's great, or if you have something to suggest to edit it, and bring back the old web. Subscribe via email or RSS Washington Post – Five points for anger, one for a ‘like’: How Facebook’s formula fostered rage and misinformation. Link . • Unpaywalled . ↩ The Guardian – Facebook reveals news feed experiment to control emotions. Link . ↩ This website was created by Steve, who has their own Bear Blog . Read Resurrect the Old Web , which inspired this post. ↩ A webring is a collection of websites linked together in a circular structure, organized around a specific theme. Each site has navigation links to the next and previous members, forming a ring. A central site usually lists all members to prevent breaking the ring if someone's site goes offline. ↩ Take a look at this Reddit discussion on why less JavaScript can be better . ↩ Or news site, podcast, or supported social media platform like Bluesky, and even subreddits. ↩ If you don't know what HTML and CSS is, basically, the first snippet of code I shared is HTML, used for the basic text and formatting of a website; CSS is used to apply fancy styles and color, among other things. ↩ Bear Blog: In the creator's own words, "A privacy-first, no-nonsense, super-fast blogging platform." Sign up, select a pre-made theme if you want and modify it to your liking, make post templates, and connect a custom domain if desired. Comes with ready-to-go RSS, and pretty popular among bloggers currently. This site runs on it. Pika: “An editor that makes you want to write, designed to get out of your way and perfectly match what readers will see.” With Pika you can sign up, choose a theme, customize without code, write posts in a clean editor, export your content, and connect your own domain, with a focus on privacy and design. You can start for free (up to ~50 posts) and upgrade later if you want unlimited posts, newsletter subscribers, analytics, etc. Substack: You might have seen this around before, it's quite popular. It's a platform built for people to publish posts and sometimes make money doing it. You can start a newsletter or blog, choose what’s free and what’s paid, send posts (and even podcasts or video) to subscribers’ inboxes, build a community, and access basic analytics. It’s simple and user-friendly, with a 10% fee if you monetize. This may not be the most loved option by other small bloggers due to its association with newsletter-signup popups and making a profit. It is also the most similar to other social media among blogging options . Ghost: An open-source platform focused on publishing and monetization. Ghost provides an editor (with live previews, Markdown + embeds, and an admin UI), built-in SEO, newsletter tools, membership & subscription support, custom themes, and control over your domain and data. You can self-host (free, for full flexibility) or use their managed Ghost(Pro) hosting, and benefit from faster performance, email delivery, and extensible APIs. Wordpress: The world’s most popular website and blogging platform, powering over 40% of the web. WordPress lets you create a simple blog or a business site using free and premium themes and plugins. You can host it yourself with full control, or use their hosted service (WordPress.com) for convenience. It supports custom domains, rich media, SEO tools, and extensibility through code or plugins. Squarespace: You might have heard of this on your favorite YouTuber's channel during a sponsorship (you don't sit through those, do you?). It is a platform for building websites, blogs, and online stores with no coding required. Squarespace offers templates, a drag-and-drop editor, built-in SEO, analytics, and e-commerce tools under a subscription. You can connect a custom domain, publish blog posts, and manage newsletters. Astro: A modern web framework built for speed, content, and flexibility. Astro lets you build blogs, portfolios, and full sites using any UI framework, or none at all, with zero JavaScript by default. 5 It supports Markdown, MDX, and server-side rendering, plus integrations for CMSs, themes, and deployment platforms. Hugo: An open-source static site generator built for efficiency and flexibility. It lets you create blogs and websites using Markdown, shortcodes, and templates. It supports themes, taxonomies, custom content types, and control over site structure without needing a database. Zola: Another open-source static site generator. Zola uses Markdown for content, Tera templates for layouts, and comes with built-in features like taxonomies, RSS feeds, and syntax highlighting. It requires no database, and is easy to configure. 11ty: Pronounced Eleventy. A flexible static site generator that lets you build content-focused websites using plain HTML, Markdown, or templating languages like Nunjucks, Liquid, and others. 11ty requires no database, supports custom data structures, and gives full control over your site’s output. Jekyll: A popular static site generator that transforms plain text into self-hosted websites and blogs. Jekyll uses Markdown, Liquid templates, and simple configuration to generate content without a database. It supports themes, plugins, and custom layouts, and integrates seamlessly with GitHub Pages for free hosting. Neocities: This is a modern continuation of Geocities , mainly focused on hand-coding HTML and CSS to create a custom site from scratch. Not ideal for blogging, but cool for showcasing a site and learning web development. It's free and open-source, and you can choose to pay for custom domains and more bandwidth, with no ads or data selling. You can see my silly site I made using Neocities for a D&D campaign I'm a part of at thepub.neocities.org . Feedly: A cloud-based, freemium RSS aggregator with apps and a browser interface. You can use a free account that supports a limited number of sources (about 100 feeds) and basic folders, but many advanced features—such as hiding ads, notes/highlights, power search, integration with Evernote/Pocket, and “Leo” AI filtering—require paid tiers. It supports iOS, Android, and web (desktop browsers). Feedly is not open source, it is a commercial service. Inoreader: Also a freemium service, available via web and on iOS and Android, with synchronization of your reading across devices. The free plan includes many of the core features (RSS subscription, folders, basic filtering), while more powerful features (such as advanced rules, full-text search, premium support, more feed limits) are gated behind paid tiers. Inoreader is not open source, it is a proprietary service with a freemium model. NetNewsWire: A native, free, and open-source RSS reader for Apple platforms (macOS, iPhone, iPad). It offers a clean, native experience and tracks your read/unread status locally or via syncing. Because it’s open source (MIT-licensed), you can inspect or contribute to its code. Its main limitation is platform since it’s focused on Apple devices. It's also not very visually flashy, if you care about that. feeeed (with four Es) : An iOS/iPadOS (and recent macOS) app that emphasizes a private, on-device reading experience without requiring servers or accounts. It is free (no ads or in-app purchases) and supports RSS subscriptions, YouTube, Reddit, and other sources, plus some AI summarization. Because it is designed to run entirely on device, there is no paid subscription for backend features, and it is private by design. It is not open-source. One personal note from me, I use this as my daily driver, and it has some minor bugs you may notice. It's developed by one person, so it happens. Reeder: A client app (primarily for Apple platforms: iOS, iPadOS, macOS) that fetches feed data from external services, such as Feedly, Inoreader, or local RSS sources. The new Reeder version supports unified timeline, filters, and media integration. It is not itself a feed-hosting service but a front end; thus, many of its features (such as sync or advanced filtering) depend on which backend you use. It uses iCloud to sync subscription and timeline state between devices. Reeder is proprietary (closed source) and uses a paid model or in-app purchases for more advanced versions. Unread: Another client app for Apple platforms with a focus on elegant reading. It relies on external feed services for syncing (you provide your own RSS or use a service like Feedly). Because Unread is a reader app, its features are more about presentation and gesture support; many of the syncing, feed limits, or premium capabilities depend on your chosen backend service. I would say Unread is my favorite so far, as it offers a lot for being free, has great syncing, tag organization, and a pleasing interface. It also fetches entire website content to get around certain limitations with some websites' feeds, allowing you to read everything in the app without visiting the website directly. FreshRSS: A self-hostable, open-source RSS/Atom aggregator that you run on your own server (like via Docker) and which supports reading through its own web interface or via third-party client apps. It allows full control over feed limits, filtering, theming, extensions, and it can generate feeds by scraping or filtering content. Because it is open source, there is no paid tier in the software itself (though you may incur hosting costs). Many client apps can connect to a FreshRSS instance for mobile or desktop reading. If you're interested in something interesting you can do with its API, check out Steve's post about automating feeds with FreshRSS. Email: Share an email people can contact you at, and when someone has something to say, they can email you about it. This allows for intential, nuanced discussion. Here is a template I use at the end of every post to facilitate this (totally stolen from Steve, again) : Comments: Comments are a tricky one. It's looked down on by some because of their lack of nuance and moderation stress, which is why Bear Blog doesn't natively have them. There are various ways to do comments, and it heavily depends on what blogging platform you choose, so here is Bear Blog's stance on it and some recommendations for setting up comments if you want . Guestbooks: This is an old form of website interaction that dates back to at least Geocities . The concept is that visitors to your site can leave a quick thought, their name, and optionally their own website to let you know they visited. You can see an example on my website , and my recommended service for a free guestbook is Guestbooks . You can choose a default theme and edit it if you want to match the rest of your site, implement spam protection, and access a dashboard for managing and deleting comments if needed. Add new pages, like a link to your other social media or music listening platforms, or a page dedicated to your pet. Email a random person on a blog to give your thoughts on a post of theirs or simply tell them their site is cool. Create an email just for this and for your website for privacy and separation, if desired. Add a Now page. It's a section specifically to tell others what you are focused on at this point of your life. Read more about it at nownownow.com . See an example on Clint McMahon's blog . Write a post about a cool rock you found in your yard, or something similarly asinine. Revel in the lack of effort. Or, Make a post containing 1-3 sentences only. Join a webring . Make a page called Reviews, to review movies, books, TV shows, games, kitchen appliances, etc. Washington Post – Five points for anger, one for a ‘like’: How Facebook’s formula fostered rage and misinformation. Link . • Unpaywalled . ↩ The Guardian – Facebook reveals news feed experiment to control emotions. Link . ↩ This website was created by Steve, who has their own Bear Blog . Read Resurrect the Old Web , which inspired this post. ↩ A webring is a collection of websites linked together in a circular structure, organized around a specific theme. Each site has navigation links to the next and previous members, forming a ring. A central site usually lists all members to prevent breaking the ring if someone's site goes offline. ↩ Take a look at this Reddit discussion on why less JavaScript can be better . ↩ Or news site, podcast, or supported social media platform like Bluesky, and even subreddits. ↩ If you don't know what HTML and CSS is, basically, the first snippet of code I shared is HTML, used for the basic text and formatting of a website; CSS is used to apply fancy styles and color, among other things. ↩

0 views
iDiallo 1 weeks ago

How to Get Started Programming: Build a Blog

The moment I learned how to program, I wanted to experiment with my new super powers. Building a BMI calculator in the command line wouldn't cut it. I didn't want to read another book, or follow any other tutorial. What I wanted was to experience chaos. Controlled, beautiful, instructive chaos that comes from building something real and watching it spectacularly fail. That's why whenever someone asks me how they can practice their new found skill, I suggest something that might sound old-fashioned in our framework-obsessed world. Build your own blog from scratch. Not with WordPress. Not with Next.js or Gatsby or whatever the cool kids are using this week. I mean actually build it. Write every messy, imperfect line of code. A blog is deceptively simple. On the surface, it's just text on a page. But underneath? It's a complete web application in miniature. It accepts input (your writing). It stores data (your posts). It processes logic (routing, formatting, displaying). It generates output (the pages people read). When I was in college, I found myself increasingly frustrated with the abstract nature of what we were learning. We'd implement different sorting algorithms, and I'd think: "Okay, but when does this actually matter ?" We'd study data structures in isolation, divorced from any practical purpose. It all felt theoretical, like memorizing chess moves without ever playing a game. Building a blog changed that completely. Suddenly, a data structure wasn't just an abstract concept floating in a textbook. It was the actual list of blog posts I needed to sort by date. A database wasn't a theoretical collection of tables; it was the real place where my article drafts lived, where I could accidentally delete something important at 2 AM and learn about backups the hard way. This is what makes a blog such a powerful learning tool. You can deploy it. Share it. Watch people actually read the words your code is serving up. It's real. That feedback loop, the connection between your code and something tangible in the world, is irreplaceable. So how do you start? I'm not going to give you a step-by-step tutorial. You've probably already done a dozen of those. You follow along, copy the code, everything works perfectly, and then... you close the browser tab and realize you've learned almost nothing. The code evaporates from your memory because you never truly owned it. Instead, I'm giving you permission to experiment. To fumble. To build something weird and uniquely yours. You can start with a single file. Maybe it's an that clumsily echoes "Hello World" onto a blank page. Or perhaps you're feeling adventurous and fire up a Node.js server with an that doesn't use Express to handle a simple GET request. Pick any language you are familiar with and make it respond to a web request. That's your seed. Everything else grows from there. Once you have that first file responding, the questions start arriving. Not abstract homework questions, but real problems that need solving. Where do your blog posts live? Will you store them as simple Markdown or JSON files in a folder? Or will you take the plunge into databases, setting up MySQL or PostgreSQL and learning SQL to and your articles? I started my first blog with flat files. There's something beautiful about the simplicity. Each post is just a text file you can open in any editor. But then I wanted tags, and search, and suddenly I was reinventing databases poorly. That's when I learned why databases exist. Not from a lecture, but from feeling the pain of their absence. You write your first post. Great! You write your second post. Cool! On the third post, you realize you're copying and pasting the same HTML header and footer, and you remember learning something about DRY (don't repeat yourself) in class. This is where you'll inevitably invent your own primitive templating system. Maybe you start with simple includes: at the top of each page in PHP. Maybe you write a JavaScript function that stitches together HTML strings. Maybe you create your own bizarre templating syntax. It will feel like magic when it works. It will feel like a nightmare when you need to change something and it breaks everywhere. And that's the moment you'll understand why templating engines exist. I had a few blog posts written down on my computer when I started thinking about this next problem: How do you write a new post? Do you SSH into your server and directly edit a file with vim? Do you build a crude, password-protected page with a textarea that writes to your flat files? Do you create a whole separate submission form? This is where you'll grapple with forms, authentication (or a hilariously insecure makeshift version of it), file permissions, and the difference between GET and POST requests. You'll probably build something that would make a security professional weep, and that's okay. You'll learn by making it better. It's one thing to write code in a sandbox, but a blog needs to be accessible on the Internet. That means getting a domain name (ten bucks a year). Finding a cheap VPS (five bucks a month). Learning to into that server. Wrestling with Nginx or Apache to actually serve your files. Discovering what "port 80" means, why your site isn't loading, why DNS takes forever to propagate, and why everything works on your laptop but breaks in production. These aren't inconveniences, they're the entire point. This is the knowledge that separates someone who can write code from someone who can ship code. Your blog won't use battle-tested frameworks or well-documented libraries. It will use your solutions. Your weird routing system. Your questionable caching mechanism. Your creative interpretation of MVC architecture. Your homemade caching will fail spectacularly under traffic ( what traffic?! ). Your clever URL routing will throw mysterious 404 errors. You'll accidentally delete a post and discover your backup system doesn't work. You'll misspell a variable name and spend three hours debugging before you spot it. You'll introduce a security vulnerability so obvious that even you'll laugh when you finally notice it. None of this is failure. This is the entire point. When your blog breaks, you'll be forced to understand the why behind everything. Why do frameworks exist? Because you just spent six hours solving a problem that Express handles in three lines. Why do ORMs exist? Because you just wrote 200 lines of SQL validation logic that Sequelize does automatically. Why do people use TypeScript? Because you just had a bug caused by accidentally treating a string like a number. You'll emerge from this experience not just as someone who can use tools, but as someone who understands what problems those tools were built to solve. That understanding is what transforms a code-copier into a developer. Building your own blogging engine used to be a rite of passage. Before Medium and WordPress and Ghost, before React and Vue and Svelte, developers learned by building exactly this. A simple CMS. A place to write. Something that was theirs. We've lost a bit of that spirit. Now everyone's already decided they'll use React on the frontend and Node on the backend before they even know why. The tools have become the default, not the solution. Your blog is your chance to recover that exploratory mindset. It's your sandbox. Nobody's judging. Nobody's watching. You're not optimizing for scale or maintainability or impressing your coworkers. You're learning, deeply and permanently, by building something that matters to you. So here's my challenge: Stop reading. Stop planning. Stop researching the "best" way to do this. Create a folder. Create a file. Pick a language and make it print "Hello World" in a browser. Then ask yourself: "How do I make this show a blog post?" And then: "How do I make it show two blog posts?" And then: "How do I make it show the most recent one first?" Build something uniquely, personally, wonderfully yours. Make it ugly. Make it weird. Make it work, then break it, then fix it again. Embrace the technical chaos. This is how you learn. Not by following instructions, but by discovering problems, attempting solutions, failing, iterating, and eventually (accidentally) building something real. Your blog won't be perfect. It will probably be kind of a mess. But it will be yours, and you will understand every line of code in it, and that understanding is worth more than any tutorial completion certificate. If you don't know what that first blog post will be, I have an idea. Document your process of building your very own blog from scratch. The blog you build to learn programming becomes the perfect place to share what programming taught you. Welcome to development. The real kind, where things break and you figure out why. You're going to love it.

0 views

ChatGPT Killed the Web: For the Better?

I haven’t used Google in a year. No search results, no blue links. ChatGPT became my default web browser in December 2024, and it has completely replaced the entire traditional web for me. Soon, no one will use search engine. No one will click on 10 blue links. But there is more: No one will navigate to websites. Hell, no one will even read a website again. The original web was simple. Static HTML pages. You could read about a restaurant—its menu, hours, location. But that was it. Pure consumption. Then came interactivity. Databases. User accounts. Now you could *do* things like reserve a table at that restaurant, leave a review, upload photos. The web became bidirectional. Every click was an action, every form a transaction. Now we’re entering a new evolution. You don’t navigate and read the restaurant’s website. You don’t fill out the reservation form. An LLM agent does both for you. Look at websites today. Companies spend millions building elaborate user interfaces—frontend frameworks, component libraries, animations that delight users, complex backends orchestrating data flows. Teams obsess over pixel-perfect designs, A/B test button colors, and optimize conversion funnels. All of this sophisticated web infrastructure exists for one purpose: to present information to humans and let them take actions. But if the information is consumed by a LLM - why does it need any of this? You don’t need a website. You need a text file: That’s it. That’s all an LLM needs to answer any question about a restaurant. No need for UI, clean UX etc. Here’s what nobody’s talking about: we don’t need thousands of websites anymore. Take a French boeuf bourguignon recipe. Today, there are hundreds of recipe websites, each with their own version: - AllRecipes with its community ratings - Serious Eats with detailed techniques - Food Network with celebrity chef branding - Marmiton for French speakers - Countless food blogs with personal stories Why do all these exist? They differentiated through: - Better UI design - Fewer ads - Faster load times - Native language content - Unique photography - Personal narratives before the recipe But LLMs don’t care about any of this. They don’t see your beautiful photos. They skip past your childhood story about grandma’s kitchen. They ignore your pop-up ads. They just need the recipe: Language barriers? Irrelevant. The LLM translates instantly. French, Italian, Japanese. It doesn’t matter. What this means: Instead of 10,000 cooking websites, we need maybe... a couple? or a single, comprehensive markdown repository of recipes. This pattern repeats everywhere: - Travel guides - Product reviews - News sites - Educational content The web doesn’t need redundancy when machines are the readers. Wait, there is more: LLM machines can create content too. Web 2.0’s breakthrough was making everyone a writer. YouTube, Instagram, TikTok—billions of people creating content for billions of people to read. But here’s the thing: why do you need a million human creators when AI can be all of them? Your favorite cooking influencer? Soon it’ll be an AI chef who knows exactly what’s in your fridge, your dietary restrictions, and your skill level. No more scrolling through 50 recipe videos to find one that works for you. Your trusted news anchor? An AI that only covers YOUR interests—your stocks, your sports teams, your neighborhood. Not broadcasting to millions, but narrowcasting to one. That fitness instructor you follow? An AI trainer that adapts to your fitness level, your injuries, your equipment. Every video made just for you, in real-time. Web 2.0 writing : Humans create content → Millions read the same thing Web 3.0 writing : AI creates content → Each person reads something unique The entire creator economy—the crown jewel of Web 2.0—collapses into infinite personalized AI agents. Social media feeds won’t be filled with human posts anymore. They’ll be generated in real-time, specifically for you. Every scroll, unique. Every video, personalized. Every post, tailored. The paradox: We’ll have infinite content variety with zero human creators. Maximum personalization through total artificial generation. Just as 10,000 recipe websites collapse into one markdown file for LLMs to read, millions of content creators collapse into personalized AI agents. The “write” revolution of Web 2.0 is being replaced by AI that writes everything, for everyone, individually. Ok what about taking actions like booking a restaurant? Web 2.0 gave us APIs—structured endpoints for programmatic interaction: - `POST /api/reservations` - Rigid schemas: exact field names, specific formats - Documentation hell: dozens of pages explaining endpoints - Integration nightmare: every API different, nothing interoperable APIs assumed developers would read documentation, write integration code, and handle complex error scenarios. They were built for humans to program against; requiring manual updates whenever the API changed, breaking integrations, and forcing developers to constantly maintain compatibility. MCP isn’t just another API. It’s designed for LLM agents: - Dynamic discovery : Agents explore capabilities in real-time through tool introspection - Flexible schemas : Natural language understanding, not rigid fields - Universal interoperability : One protocol, infinite services - Context-aware : Maintains conversation state across actions What makes MCP special technically: - Three primitives : Tools (functions agents can call), Resources (data agents can read), and Prompts (templates for common tasks) - Transport agnostic : Works over STDIO for local servers or HTTP/SSE for remote services - Stateful sessions : Unlike REST APIs, MCP maintains context between calls - Built-in tool discovery : Agents can query `listTools()` to understand capabilities dynamically—no documentation parsing needed Traditional APIs are like giving someone a thick manual and saying “ follow these exact steps. ” MCP is like having a smart assistant who can figure out what’s possible just by looking around . When you walk into that restaurant, the agent doesn’t need a 50-page guide—it instantly knows it can check tables, make reservations, or view the menu. And unlike APIs that forget everything between requests (like talking to someone with amnesia!), MCP remembers the whole conversation—so when you say “ actually, make it 8pm instead ,” it knows exactly what reservation you’re talking about. With traditional API: The agent handles all complexity. No documentation needed. No rigid formats. Just natural interaction. Even better: when the restaurant adds new capabilities—like booking the entire venue for private events, adding wine pairings, or offering chef’s table experiences—there’s no developer work required. The LLM agent automatically discovers the expanded schema and adapts. Traditional APIs would break existing integrations or require manual updates. MCP just works. With markdown for reading and MCP for acting, the entire web infrastructure becomes invisible: - Read : LLM ingests markdown → understands everything about your service - Act : LLM uses MCP → performs any action a user needs Websites become obsolete. Users never leave their chat interface. The web started as simple text documents linked together. We spent 30 years adding complexity such as animations, interactivity, rich media. Now we’re stripping it all away again. But this time, the simplicity isn’t for humans. It’s for machines. And that changes everything . The web as we know it is disappearing. What replaces it will be invisible, powerful, and fundamentally different from anything we’ve built before. For someone like me who love designing beautiful UIs, this is bittersweet. All those carefully crafted interfaces, micro-interactions, and pixel-perfect layouts will be obsolete. But I’m genuinely excited because it’s all about the user experience, and the UX of chatting (or even calling) your agent is infinitely better than website navigation. I can’t wait. Thanks for reading! Subscribe for free to receive new posts and support my work.

0 views
Manuel Moreale 1 weeks ago

Blake Watson

This week on the People and Blogs series we have an interview with Blake Watson, whose blog can be found at blakewatson.com . Tired of RSS? Read this in your browser or sign up for the newsletter . The People and Blogs series is supported by Jaga Santagostino and the other 121 members of my "One a Month" club. If you enjoy P&B, consider becoming one for as little as 1 dollar a month. Sure! I’m Blake. I live in a small city near Jackson, Mississippi, USA. I work for MRI Technologies as a frontend engineer, building bespoke web apps for NASA. Previously I worked at an ad agency as an interactive designer. I have a neuromuscular condition called spinal muscular atrophy (SMA). It's a progressive condition that causes my muscles to become weaker over time. Because of that, I use a power wheelchair and a whole host of assistive technologies big and small. I rely on caregivers for most daily activities like taking a shower, getting dressed, and eating—just to name a few. I am able to use a computer on my own. I knew from almost the first time I used one that it was going to be important in my life. I studied Business Information Systems in college as a way to take computer-related courses without all the math of computer science (which scared me at the time). When I graduated, I had a tough time finding a job making websites. I did a bit of freelance work and volunteer work to build up a portfolio, but was otherwise unemployed for several years. I finally got my foot in the door and I recently celebrated a milestone of being employed for a decade . When I'm not working, I'm probably tinkering on side projects . I'm somewhat of a side project and home-cooked app enthusiast. I just really enjoy making and using my own tools. Over the last 10 years, I've gotten into playing Dungeons and Dragons and a lot of my side projects have been related to D&D. I enjoy design, typography, strategy games, storytelling, writing, programming, gamedev, and music. I got hooked on making websites in high school and college in the early 2000s. A friend of mine in high school had a sports news website. I want to say it was made with the Homestead site builder or something similar. I started writing for it and helping with it. I couldn’t get enough so I started making my own websites using WYSIWYG page builders. But I became increasingly frustrated with the limitations of page builders. Designing sites felt clunky and I couldn’t get elements to do exactly what I wanted them to do. I had a few blogs on other services over the years. Xanga was maybe the first one. Then I had one on Blogger for a while. In 2005, I took a course called Advanced Languages 1. It turned out to be JavaScript. Learning JavaScript necessitated learning HTML. Throughout the course I became obsessed with learning HTML, CSS, and JavaScript. Eventually, in August of 2005— twenty years ago —I purchased the domain blakewatson.com . I iterated on it multiple times a year at first. It morphed from quirky design to quirkier design as I learned more CSS. It was a personal homepage, but I blogged at other services. Thanks to RSS, I could list my recent blog posts on my website. When I graduated from college, my personal website became more of a web designer's portfolio, a professional site that I would use to attract clients and describe my services. But around that time I was learning how to use WordPress and I started a self-hosted WordPress blog called I hate stairs . It was an extremely personal disability-related and life journaling type of blog that I ran for several years. When I got my first full-time position and didn't need to freelance any longer, I converted blakewatson.com back into a personal website. But this time, primarily a blog. I discontinued I hate stairs (though I maintain an archive and all the original URLs work ). I had always looked up to various web designers in the 2000s who had web development related blogs. People like Jeffery Zeldman , Andy Clarke , Jason Santa Maria , and Tina Roth Eisenberg . For the past decade, I've blogged about web design, disability, and assistive tech—with the odd random topic here or there. I used to blog only when inspiration struck me hard enough to jolt my lazy ass out of whatever else I was doing. That strategy left me writing three or four articles a year (I don’t know why, but I think of my blog posts as articles in a minor publication, and this hasn’t helped me do anything but self-edit—I need to snap out of it and just post ). In March 2023, however, I noticed that I had written an article every month so far that year. I decided to keep up the streak. And ever since then, I've posted at least one article a month on my blog. I realize that isn't very frequent for some people, but I enjoy that pacing, although I wouldn't mind producing a handful more per year. Since I'm purposefully posting more, I've started keeping a list of ideas in my notes just so I have something to look through when it's time to write. I use Obsidian mostly for that kind of thing. The writing itself almost always happens in iA Writer . This app is critical to my process because I am someone who likes to tinker with settings and fonts and pretty much anything I can configure. If I want to get actual writing done, I need constraints. iA Writer is perfect because it looks and works great by default and has very few formatting options. I think I paid $10 for this app one time ten or more years ago. That has to be the best deal I've ever gotten on anything. I usually draft in Writer and then preview it on my site locally to proofread. I have to proofread on the website, in the design where it will live. If I proofread in the editor I will miss all kinds of typos. So I pop back and forth between the browser and the editor fixing things as I go. I can no longer type on a physical keyboard. I use a mix of onscreen keyboard and dictation when writing prose. Typing is a chore and part of the reason I don’t blog more often. It usually takes me several hours to draft, proofread, and publish a post. I mostly need to be at my desk because I have my necessary assistive tech equipment set up there. I romanticize the idea of writing in a comfy nook or at a cozy coffee shop. I've tried packing up my setup and taking it to a coffee shop, but in practice I get precious little writing done that way. What I usually do to get into a good flow state is put on my AirPods Pro, turn on noise cancellation, maybe have some ambient background noise or music , and just write. Preferably while sipping coffee or soda. But if I could have any environment I wanted, I would be sitting in a small room by a window a few stories up in a quaint little building from the game Townscaper , clacking away on an old typewriter or scribbling in a journal with a Parker Jotter . I've bounced around a bit in terms of tech stack, but in 2024, I migrated from a self-hosted WordPress site to a generated static site with Eleventy . My site is hosted on NearlyFreeSpeech.NET (NFSN)—a shared hosting service I love for its simplistic homemade admin system, and powerful VPS-like capabilities. My domain is registered with them as well, although I’m letting Cloudflare handle my DNS for now. I used Eleventy for the first time in 2020 and became a huge fan. I was stoked to migrate blakewatson.com . The source code is in a private repo on GitHub. Whenever I push to the main branch, DeployHQ picks it up and deploys it to my server. I also have a somewhat convoluted setup that checks for social media posts and displays them on my website by rebuilding and deploying automatically whenever I post. It's more just a way for me to have an archive of my posts on Mastodon than anything. Because my website is so old, I have some files not in my repo that live on my server. It is somewhat of a sprawling living organism at this point, with various small apps and tools (and even games !) deployed to sub-directories. I have a weekly scheduled task that runs and saves the entire site to Backblaze B2 . You know, I'm happy to say that I'd mostly do the same thing. I think everyone should have their own website. I would still choose to blog at my own domain name. Probably still a static website. I might structure things a bit differently. If I were designing it now, I might make more allowances for title-less, short posts (technically I can do this now, but they get lumped into my social feed, which I'm calling my Microblog, and kind of get lost). I might design it to be a little weirder rather than buttoned up as it is now. And hey, it's my website. I still might do that. Tinkering with your personal website is one of life's great joys. If you can't think of anything to do with your website, here are a hundred ideas for you . I don't make money from my website directly, but having a website was critical in getting my first job and getting clients before that. So, in a way, all the money I've made working could be attributed to having a personal website. I have a lot of websites and a lot of domains, so it's a little hard to figure out exactly what blakewatson.com itself costs. NFSN is a pay-as-you-go service. I'm currently hosting 13 websites of varying sizes and complexity, and my monthly cost aside from domains is about $23.49. $5 of that is an optional support membership. I could probably get the cost down further by putting the smaller sites together on a single shared server. I pay about $14 per year for the domain these days. I pay $10.50 per month for DeployHQ , but I use it for multiple sites including a for-profit side project, so it doesn’t really cost anything to use it for my blog (this is the type of mental gymnastics I like to do). I pay $15 per month for Fathom Analytics . In my mind, this is also subsidized by my for-profit side project. I mentioned that I backup my website to Backblaze B2. It's extremely affordable, and I think I'm paying below 50 cents per month currently for the amount of storage I'm using (and that also includes several websites). If you also throw in the cost of tools like Tower and Sketch , then there's another $200 worth of costs per year. But I use those programs for many things other than my blog. When you get down to it, blogs are fairly inexpensive to run when they are small and personal like mine. I could probably get the price down to free, save for the domain name, if I wanted to use something like Cloudflare Pages to host it—or maybe a free blogging service. I don't mind people monetizing their blogs at all. I mean if it's obnoxious then I'm probably not going to stay on your website very long. But if it's done tastefully with respect to the readers then good for you. I also don't mind paying to support bloggers in some cases. I have a number of subscriptions for various people to support their writing or other creative output. Here are some blogs I'm impressed with in no particular order. Many of these people have been featured in this series before. I'd like to take this opportunity to mention Anne Sturdivant . She was interviewed here on People & Blogs. When I first discovered this series, I put her blog in the suggestion box. I was impressed with her personal website empire and the amount of content she produced. Sadly, Anne passed away earlier this year. We were internet buddies and I miss her. 💜 I'd like to share a handful of my side projects for anyone who might be interested. Now that you're done reading the interview, go check the blog and subscribe to the RSS feed . If you're looking for more content, go read one of the previous 109 interviews . Make sure to also say thank you to Bomburache and the other 121 supporters for making this series possible. Chris Coyier . " Mediocre ideas, showing up, and persistence. " <3 Jim Nielsen . Continually produces smart content. Don't know how he does it. Nicole Kinzel . She has posted nearly daily for over two years capturing human struggles and life with SMA through free verse poetry. Dave Rupert . I enjoy the balance of tech and personal stuff and the honesty of the writing. Tyler Sticka . His blog is so clean you could eat off of it. A good mix of tech and personal topics. Delightful animations. Maciej Cegłowski . Infrequent and longform. Usually interesting regardless of whether I agree or disagree. Brianna Albers . I’m cheating because this is a column and not a blog per se. But her writing reads like a blog—it's personal, contemplative, and compelling. There are so very few representations of life with SMA online that I'd be remiss not to mention her. Daring Fireball . A classic blog I’ve read for years. Good for Apple news but also interesting finds in typography and design. Robb Knight . To me, Robb’s website is the epitome of the modern indieweb homepage. It’s quirky, fun, and full of content of all kinds. And that font. :chefskiss: Katherine Yang . A relatively new blog. Beautiful site design. Katherine's site feels fresh and experimental and exudes humanity. HTML for People . I wrote this web book for anyone who is interested in learning HTML to make websites. I wrote this to be radically beginner-friendly. The focus is on what you can accomplish with HTML rather than dwelling on a lot of technical information. A Fine Start . This is the for-profit side project I mentioned. It is a new tab page replacement for your web browser. I originally made it for myself because I wanted all of my favorite links to be easily clickable from every new tab. I decided to turn it into a product. The vast majority of the features are free. You only pay if you want to automatically sync your links with other browsers and devices. Minimal Character Sheet . I mentioned enjoying Dungeons and Dragons. This is a web app for managing a D&D 5th edition character. I made it to be a freeform digital character sheet. It's similar to using a form fillable PDF, except that you have a lot more room to write. It doesn't force many particular limitations on your character since you can write whatever you want. Totally free.

0 views
Dayvster 1 weeks ago

My Battle Tested React Hooks Are Now Open Source

## Don't yap just give me the links Ok I get it you're busy here you go: - [GitHub Repo](https://github.com/dayvster/react-kata) - [NPM Package](https://www.npmjs.com/package/react-kata) Have a great day! ## What's This? I've been developing react since 2013 it's been through a lot of changes and updates over the years. We've seen patterns come and go, however when hooks were introduced in 2019, I was instantly ... hooked. No wait please don't leave, that was a good joke and you know it! ![](https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExYjdtenpmZ2E1MnIwcTIxeGZ1cTV5OXQ1bGtobWN3am1wOTRkczlhbCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/28STqyG0HPzIQ/giphy.gif) Anyhow now that that's out of the way, I wanted to share with you a collection of various hooks that I have written over and over and over again in my past projects. These hooks have been battle tested in production applications and multiple client, personal and professional projects. ## Why Open Source? Well I have this post on my blog about why I believe everyone should open source their work, [Open Source Your Projects](https://dayvster.com/blog/open-source-your-projects/) I've been recently redesigning my website and fixing up my old blog posts and I stumbled upon this one decided to re-read it fully and discovered I am a hypocrite by value of pure laziness. Yes I did not open source these hooks sooner, because I was simply lazy. I had them in various different projects, repos, side projects etc. etc. But I never took the time to actually put them together in a single package and publish it. I just kept either navigating to these projects and constantly copy pasting them around OR worse yet I would rewrite them from scratch every time I needed them, which is just plain dumb. So I decided to stop being a hypocrite and finally open source a bunch of my stuff part of that was the task of collecting all of these react hooks and putting them together in a single package. So here it is ## Introducing React-Kata - [GitHub Repo](https://github.com/dayvster/react-kata) - [NPM Package](https://www.npmjs.com/package/react-kata) React-Kata is a collection of battle-tested React hooks that I've used in various projects over the years. These hooks are designed to make your life easier and help you write cleaner, more efficient code. ### Why React-Kata? Kata is a term used in martial arts to describe a set of movements and techniques that are practiced repeatedly to improve skill and mastery. Similarly, React-Kata is a collection of hooks that I've rewritten and reused and refined over and over again. So I thought it would be appropriate to name it React-Kata. Also I did martial arts as a younger person and wanted to show off a bit, lay off. ## Examples: - `useLocalStorage`: A hook that makes it easy to work with local storage in your React applications. - `useDebounce`: A hook that debounces a value, making it easier to work with user input and avoid unnecessary re-renders. - `useThrottle`: A hook that throttles a value, making it easier to work with user input and avoid unnecessary re-renders. - `useTimeout`: A hook that makes it easy to work with timeouts in your React applications. - `useInterval`: A hook that makes it easy to work with intervals in your React applications - `useSessionStorage`: A hook that makes it easy to work with session storage in your React applications. and many many more in fact to see a lot of all of them check out the [GitHub Repo Hooks overview](https://github.com/dayvster/react-kata?tab=readme-ov-file#-hooks-overview) ## Code Examples ### useShimmer This hook generates a shimmer effect SVG that can be used as a placeholder while content is loading. It takes width and height as parameters and returns an SVG string. ````tsx import { useShimmer } from 'react-kata'; function ShimmerDemo() { const shimmer = useShimmer(400, 300); return ; } ```` ### useWhyDidYouUpdate This hook helps you identify why a component re-rendered by logging the changed props to the console or handling them with a custom callback. I can not overstate how often this one has saved my ass in the past. ```tsx import React, { useState } from 'react'; import { useWhyDidYouUpdate } from 'react-kata'; function Demo(props) { // Logs changed props to console by default useWhyDidYouUpdate('Demo', props); // Or provide a callback to handle changes useWhyDidYouUpdate('Demo', props, changedProps => { // Custom handling, e.g. send to analytics alert('Changed: ' + JSON.stringify(changedProps)); }); return {props.value} ; } ``` ### useTheme Let's you simply manage themes in your application. Supports auto, light, dark, and custom themes. For custom themes you simply input the name of the theme and it will be applied as the data-theme attribute on the html element. It will also be stored in local storage and retrieved from local storage on page load. ```tsx import React from 'react'; import { useTheme } from 'react-kata'; function Demo() { // Supports auto, light, dark, and custom themes const [theme, setTheme, toggleTheme] = useTheme(["auto", "light", "dark", "solarized"]); return ( Current theme: {theme} Toggle Theme setTheme("solarized")}>Solarized setTheme("auto")}>Auto ); } ``` ### useReload Allows you to pass a custom condition that when met will trigger a page reload. ```tsx import React from 'react'; import { useReload } from 'react-kata'; function Demo() { // Only reload if user confirms const reload = useReload(() => window.confirm('Reload?')); return Reload Page ; } ``` ## Conclusion I hope you find these hooks as useful as I have. If you have any suggestions for new hooks or improvements to existing ones, please feel free to open an issue or submit a pull request on GitHub, I'll always appreciate feedback, criticism, suggestions and especially Pull Requests. Happy coding!

1 views
Den Odell 2 weeks ago

HTML’s Best Kept Secret: The &lt;output&gt; Tag

Every developer knows . It’s the workhorse of the web. But ? Most have never touched it. Some don’t even know it exists. That’s a shame, because it solves something we’ve been cobbling together with s and ARIA for years: dynamic results that are announced to screen readers by default. It’s been in the spec for years. Yet it’s hiding in plain sight . Here’s what the HTML5 spec says: The element represents the result of a calculation performed by the application, or the result of a user action. It’s mapped to in the accessibility tree . In plain terms, it announces its value when it changes, as if it already had . In practice, that means updates do not interrupt the user. They are read shortly after, and the entire content is spoken rather than just the part that changed. You can override this behavior by setting your own ARIA properties if needed. Usage is straightforward: That’s it. Built-in assistive technology support. No attributes to memorize. Just HTML doing what it was always meant to do. I discovered on an accessibility project with a multi-step form. The form updated a risk score as fields changed. It looked perfect in the browser, but screen reader users had no idea the score was updating. Adding an ARIA live region fixed it. But I’ve always believed in reaching for semantic HTML first, and live regions often feel like a patch. That’s when I scoured the spec and jumped out. It understands forms without requiring one, and it announces its changes natively. Turns out the simplest fix had been in the spec all along. Because we forgot. It’s not covered in most tutorials. It doesn’t look flashy. When I searched GitHub public repos, it barely showed up at all. It gets overlooked in patterns and component libraries too. That absence creates a feedback loop: if no one teaches it, no one uses it. Like , has a attribute. Here you list the s of any elements the result depends on, separated by spaces: For most users, nothing changes visually. But in the accessibility tree it creates a semantic link, letting assistive technology users connect the inputs with their calculated result. It doesn’t require a either. You can use it anywhere you are updating dynamic text on the page based on the user’s input. By default is inline, so you’ll usually want to style it for your layout, just as you would a or . And because it has been part of the spec since 2008, support is excellent across browsers and screen readers. It also plays nicely with any JavaScript framework you might be using, like React or Vue. Update 7 Oct 2025 : Some screen readers have been found not to announce updates to the tag, so explicitly emphasising the attribute might be worthwhile for now until support improves: . One thing to note: is for results tied to user inputs and actions, not global notifications like toast messages. Those are better handled with or on a generic element, since they represent system feedback rather than calculated output. So what does this look like in practice? I’ve personally reached for in multiple real-world projects since discovering it: During a recent 20-minute coding challenge, I used to display calculation results. Without adding a single ARIA role, the screen reader announced each result as it updated. No hacks required. At Volvo Cars, we displayed user-friendly versions of slider values. Internally the slider might hold , but the output showed . We wrapped the slider and in a container with and a shared label, creating a cohesive React component: I found that password strength indicators and real-time validation messages work beautifully with . The tag even fits modern patterns where you might fetch prices from APIs, show tax calculations, or display server-generated recommendations. Here, a shipping cost calculator updates an tag, informing users once the cost has been calculated: There’s something satisfying about using a native HTML element for what it was designed for, especially when it makes your UI more accessible with less code. might be HTML’s best kept secret, and discovering gems like this shows how much value is still hiding in the spec. Sometimes the best tool for the job is the one you didn’t even know you had. Update 11 Oct 2025 : The ever-excellent Bob Rudis has produced a working example page to support this post. Find it here: https://rud.is/drop/output.html

0 views
Lea Verou 2 weeks ago

In the economy of user effort, be a bargain, not a scam

Alan Kay [source] One of my favorite product design principles is Alan Kay’s “Simple things should be simple, complex things should be possible” . [1] I had been saying it almost verbatim long before I encountered Kay’s quote. Kay’s maxim is deceptively simple, but its implications run deep. It isn’t just a design ideal — it’s a call to continually balance friction, scope, and tradeoffs in service of the people using our products. This philosophy played a big part in Prism’s success back in 2012, helping it become the web’s de facto syntax highlighter for years, with over 2 billion npm downloads. Highlighting code on a page took including two files. No markup changes. Styling used readable CSS class names. Even adding new languages — the most common “complex” use case — required far less knowledge and effort than alternatives. At the same time, Prism exposed a deep extensibility model so plugin authors could patch internals and dramatically alter behavior. These choices are rarely free. The friendly styling API increased clash risk, and deep extensibility reduced encapsulation. These were conscious tradeoffs, and they weren’t easy. Simple refers to use cases that are simple from the user’s perspective , i.e. the most common use cases. They may be hard to implement, and interface simplicity is often inversely correlated with implementation simplicity. And which things are complex , depends on product scope . Instagram’s complex cases are vastly different than Photoshop’s complex cases, but as long as there is a range, Kay’s principle still applies. Since Alan Kay was a computer scientist, his quote is typically framed as a PL or API design principle, but that sells it short. It applies to a much, much broader class of interfaces. This class hinges on the distribution of use cases . Products often cut scope by identifying the ~20% of use cases that drive ~80% of usage — aka the Pareto Principle . Some products, however, have such diverse use cases that Pareto doesn’t meaningfully apply to the product as a whole. There are common use cases and niche use cases, but no clean 20-80 split. The long tail of niche use cases is so numerous, it becomes significant in aggregate . For lack of a better term, I’ll call these long‑tail UIs . Nearly all creative tools are long-tail UIs. That’s why it works so well for programming languages and APIs — both are types of creative interfaces. But so are graphics editors, word processors, spreadsheets, and countless other interfaces that help humans create artifacts — even some you would never describe as creative. Yes, programming languages and APIs are user interfaces . If this surprises you, watch my DotJS 2024 talk titled “API Design is UI Design” . It’s only 20 minutes, but covers a lot of ground, including some of the ideas in this post. I include both code and GUI examples to underscore this point; if the API examples aren’t your thing, skip them and the post will still make sense. You wouldn’t describe Google Calendar as a creative tool, but it is a tool that helps humans create artifacts (calendar events). It is also a long-tail product: there is a set of common, conceptually simple cases (one-off events at a specific time and date), and a long tail of complex use cases (recurring events, guests, multiple calendars, timezones, etc.). Indeed, Kay’s maxim has clearly been used in its design. The simple case has been so optimized that you can literally add a one hour calendar event with a single click (using a placeholder title). A different duration can be set after that first click through dragging [2] . But almost every edge case is also catered to — with additional user effort. Google Calendar is also an example of an interface that digitally encodes real-life, demonstrating that complex use cases are not always power user use cases . Often, the complexity is driven by life events. E.g. your taxes may be complex without you being a power user of tax software, and your family situation may be unusual without you being a power user of every form that asks about it. The Pareto Principle is still useful for individual features , as they tend to be more narrowly defined. E.g. there is a set of spreadsheet formulas (actually much smaller than 20%) that drives >80% of formula usage. While creative tools are the poster child of long-tail UIs, there are long-tail components in many transactional interfaces such as e-commerce or meal delivery (e.g. result filtering & sorting, product personalization interfaces, etc.). Filtering UIs are another big category of long-tail UIs, and they involve so many tradeoffs and tough design decisions you could literally write a book about just them. Airbnb’s filtering UI here is definitely making an effort to make simple things easy with (personalized! 😍) shortcuts and complex things possible via more granular controls. Picture a plane with two axes: the horizontal axis being the complexity of the desired task (again from the user’s perspective, nothing to do with implementation complexity), and the vertical axis the cognitive and/or physical effort users need to expend to accomplish their task using a given interface. Following Kay’s maxim guarantees these two points: But even if we get these two points — what about all the points in between? There are a ton of different ways to connect them, and they produce vastly different overall user experiences. How does your interface fare when a use case is only slightly more complex? Are users yeeted into the deep end of interface complexity (bad), or do they only need to invest a proportional, incremental amount of effort to achieve their goal (good)? Meet the complexity-to-effort curve , the most important usability metric you’ve never heard of. For delightful user experiences, making simple things easy and complex things possible is not enough — the transition between the two should also be smooth. You see, simple use cases are the spherical cows in space of product design . They work great for prototypes to convince stakeholders, or in marketing demos, but the real world is messy . Most artifacts that users need to create to achieve their real-life goals rarely fit into your “simple” flows completely, no matter how well you’ve done your homework. They are mostly simple — with a liiiiitle wart here and there. For a long-tail interface to serve user needs well in practice , we also need to design the curve, not just its endpoints . A model with surprising predictive power is to treat user effort as a currency that users are spending to buy solutions to their problems. Nobody likes paying it; in an ideal world software would read our mind and execute perfectly with zero user effort. Since we don’t live in such a world, users are typically willing to pay more in effort when they feel their use case warrants it. Just like regular pricing, actual user experience often depends more on the relationship between cost and expectation (budget) than on the absolute cost itself. If you pay more than you expected, you feel ripped off. You may still pay it because you need the product in the moment, but you’ll be looking for a better deal in the future. And if you pay less than you expected, you feel like you got a bargain, with all the delight and loyalty that entails. Incremental user effort cost should be proportional to incremental value gained. Suppose you’re ordering pizza. You want a simple cheese pizza with ham and mushrooms. You use the online ordering system, and you notice that adding ham to your pizza triples its price. We’re not talking some kind of fancy ham where the pigs were fed on caviar and bathed in champagne, just a regular run-of-the-mill pizza topping. You may still order it if you’re starving and no other options are available, but how does it make you feel? It’s not that different when the currency is user effort. The all too familiar “ But I just wanted to _________, why is it so hard? ”. When a slight increase in complexity results in a significant increase in user effort cost, we have a usability cliff . Usability cliffs make users feel resentful, just like the customers of our fictitious pizza shop. A usability cliff is when a small increase in use case complexity requires a large increase in user effort. Usability cliffs are very common in products that make simple things easy and complex things possible through entirely separate flows with no integration between them: a super high level one that caters to the most common use case with little or no flexibility, and a very low-level one that is an escape hatch: it lets users do whatever, but they have to recreate the solution to the simple use case from scratch before they can tweak it. Simple things are certainly easy: all we need to get a video with a nice sleek set of controls that work well on every device is a single attribute: . We just slap it on our element and we’re done with a single line of HTML: Now suppose use case complexity increases just a little . Maybe I want to add buttons to jump 10 seconds back or forwards. Or a language picker for subtitles. Or just to hide the volume control on a video that has no audio track. None of these are particularly niche, but the default controls are all-or-nothing: the only way to change them is to reimplement the whole toolbar from scratch, which takes hundreds of lines of code to do well. Simple things are easy and complex things are possible. But once use case complexity crosses a certain (low) threshold, user effort abruptly shoots up. That’s a usability cliff. For Instagram’s photo editor, the simple use case is canned filters, whereas the complex ones are those requiring tweaking through individual low-level controls. However, they are implemented as separate flows: you can tweak the filter’s intensity , but you can’t see or adjust the primitives it’s built from. You can layer both types of edits on the same image, but they are additive, which doesn’t work well. Ideally, the two panels would be integrated, so that selecting a filter would adjust the low-level controls accordingly, which would facilitate incremental tweaking AND would serve as a teaching aid for how filters work. My favorite end-user facing product that gets this right is Coda , a cross between a document editor, a spreadsheet, and a database. All over its UI, it supports entering formulas instead of raw values, which makes complex things possible. To make simple things easy, it also provides the GUI you’d expect even without a formula language. But here’s the twist: these presets generate formulas behind the scenes that users can tweak ! Whenever users need to go a little beyond what the UI provides, they can switch to the formula editor and adjust what was generated — far easier than writing it from scratch. Another nice touch: “And” is not just communicating how multiple filters are combined, but is also a control that lets users edit the logic. Defining high-level abstractions in terms of low-level primitives is a great way to achieve a smooth complexity-to-effort curve, as it allows you to expose tweaking at various intermediate levels and scopes. The downside is that it can sometimes constrain the types of high-level solutions that can be implemented. Whether the tradeoff is worth it depends on the product and use cases. If you like eating out, this may be a familiar scenario: — I would like the rib-eye please, medium-rare. — Thank you sir. How would you like your steak cooked? Keep user effort close to the minimum necessary to declare intent Annoying, right? And yet, this is how many user interfaces work; expecting users to communicate the same intent multiple times in slightly different ways. If incremental value should require incremental user effort , an obvious corollary is that things that produce no value should not require user effort . Using the currency model makes this obvious: who likes paying without getting anything in return? Respect user effort. Treat it as a scarce resource — just like regular currency — and keep it close to the minimum necessary to declare intent . Do not require users to do work that confers them no benefit, and could have been handled by the UI. If it can be derived from other input, it should be derived from other input. Source: NNGroup (adapted). A once ubiquitous example that is thankfully going away, is the credit card form which asks for the type of credit card in a separate dropdown. Credit card numbers are designed so that the type of credit card can be determined from the first four digits. There is zero reason to ask for it separately. Beyond wasting user effort, duplicating input that can be derived introduces an unnecessary error condition that you now need to handle: what happens when the entered type is not consistent with the entered number? User actions that meaningfully communicate intent to the interface are signal . Any other step users need to take to accomplish their goal, is noise . This includes communicating the same input more than once, providing input separately that could be derived from other input with complete or high certainty, transforming input from their mental model to the interface’s mental model, and any other demand for user effort that does not serve to communicate new information about the user’s goal. Some noise is unavoidable. The only way to have 100% signal-to-noise ratio would be if the interface could mind read. But too much noise increases friction and obfuscates signal. A short yet demonstrative example is the web platform’s methods for programmatically removing an element from the page. To signal intent in this case, the user needs to communicate two things: (a) what they want to do (remove an element), and (b) which element to remove. Anything beyond that is noise. The modern DOM method has an extremely high signal-to-noise ratio. It’s hard to imagine a more concise way to signal intent. However, the older method that it replaced had much worse ergonomics. It required two parameters: the element to remove, and its parent. But the parent is not a separate source of truth — it would always be the child node’s parent! As a result, its actual usage involved boilerplate , where developers had to write a much noisier [3] . Boilerplate is repetitive code that users need to include without thought, because it does not actually communicate intent. It’s the software version of red tape : hoops you need to jump through to accomplish your goal, that serve no obvious purpose in furthering said goal except for the fact that they are required. In this case, the amount of boilerplate may seem small, but when viewed as a percentage of the total amount of code, the difference is staggering. The exact ratio (81% vs 20% here) varies based on specifics such as variable names, but when the difference is meaningful, it transcends these types of low-level details. Of course, it was usually encapsulated in utility functions, which provided a similar signal-to-noise ratio as the modern method. However, user-defined abstractions don’t come for free, there is an effort (and learnability) tax there, too. Improving signal-to-noise ratio is also why the front-end web industry gravitated towards component architectures: they increase signal-to-noise ratio by encapsulating boilerplate. As an exercise for the reader, try to calculate the signal-to-noise ratio of a Bootstrap accordion (or any other complex Bootstrap component). Users are much more vocal about things not being possible, than things being hard. When pointing out friction issues in design reviews , I have sometimes heard “ users have not complained about this ”. This reveals a fundamental misunderstanding about the psychology of user feedback . Users are much more vocal about things not being possible, than about things being hard. The reason becomes clear if we look at the neuroscience of each. Friction is transient in working memory (prefrontal cortex). After completing a task, details fade. The negative emotion persists and accumulates, but filing a complaint requires prefrontal engagement that is brief or absent. Users often can’t articulate why the software feels unpleasant: the specifics vanish; the feeling remains. Hard limitations, on the other hand, persist as conscious appraisals. The trigger doesn’t go away, since there is no workaround, so it’s far more likely to surface in explicit user feedback. Both types of pain points cause negative emotions, but friction is primarily processed by the limbic system (emotion), whereas hard limitations remain in the prefrontal cortex (reasoning). This also means that when users finally do reach the breaking point and complain about friction, you better listen. Friction is primarily processed by the limbic system, whereas hard limitations remain in the prefrontal cortex Second, user complaints are filed when there is a mismatch in expectations . Things are not possible but the user feels they should be, or interactions cost more user effort than the user had budgeted, e.g. because they know that a competing product offers the same feature for less (work). Often, users have been conditioned to expect poor user experiences, either because all options in the category are high friction, or because the user is too novice to know better [4] . So they begrudgingly pay the price, and don’t think they have the right to complain, because it’s just how things are. You might ask, “If all competitors are equally high-friction, how does this hurt us?” An unmet need is a standing invitation to disruption that a competitor can exploit at any time. Because you’re not only competing within a category; you’re competing with all alternatives — including nonconsumption (see Jobs‑to‑be‑Done ). Even for retention, users can defect to a different category altogether (e.g., building native apps instead of web apps). Historical examples abound. When it comes to actual currency, a familiar example is Airbnb : Until it came along, nobody would complain that a hotel of average price is expensive — it was just the price of hotels. If you couldn’t afford it, you just couldn’t afford to travel, period. But once Airbnb showed there is a cheaper alternative for hotel prices as a whole , tons of people jumped ship. It’s no different when the currency is user effort. Stripe took the payment API market by storm when it demonstrated that payment APIs did not have to be so high friction. iPhone disrupted the smartphone market when it demonstrated that no, you did not have to be highly technical to use a smartphone. The list goes on. Unfortunately, friction is hard to instrument. With good telemetry you can detect specific issues (e.g., dead clicks), but there is no KPI to measure friction as a whole. And no, NPS isn’t it — and you’re probably using it wrong anyway . Instead, the emotional residue from friction quietly drags many metrics down (churn, conversion, task completion), sending teams in circles like blind men touching an elephant . That’s why dashboards must be paired with product vision and proactive, first‑principles product leadership . Steve Jobs exemplified this posture: proactively, aggressively eliminating friction presented as “inevitable.” He challenged unnecessary choices, delays, and jargon, without waiting for KPIs to grant permission. Do mice really need multiple buttons? Does installing software really need multiple steps? Do smartphones really need a stylus? Of course, this worked because he had the authority to protect the vision; most orgs need explicit trust to avoid diluting it. So, if there is no metric for friction, how do you identify it? Reducing friction rarely comes for free, just because someone had a good idea. These cases do exist, and they are great, but it usually takes sacrifices. And without it being an organizational priority, it’s very hard to steer these tradeoffs in that direction. The most common tradeoff is implementation complexity. Simplifying user experience is usually a process of driving complexity inwards and encapsulating it in the implementation. Explicit, low-level interfaces are far easier to implement, which is why there are so many of them. Especially as deadlines loom, engineers will often push towards externalizing complexity into the user interface, so that they can ship faster. And if Product leans more data-driven than data-informed, it’s easy to look at customer feedback and conclude that what users need is more features ( it’s not ) . The first faucet is a thin abstraction : it exposes the underlying implementation directly, passing the complexity on to users, who now need to do their own translation of temperature and pressure into amounts of hot and cold water. It prioritizes implementation simplicity at the expense of wasting user effort. The second design prioritizes user needs and abstracts the underlying implementation to support the user’s mental model. It provides controls to adjust the water temperature and pressure independently, and internally translates them to the amounts of hot and cold water. This interface sacrifices some implementation simplicity to minimize user effort. This is why I’m skeptical of blanket calls for “simplicity.”: they are platitudes. Everyone agrees that, all else equal, simpler is better. It’s the tradeoffs between different types of simplicity that are tough. In some cases, reducing friction even carries tangible financial risks, which makes leadership buy-in crucial. This kind of tradeoff cannot be made by individual designers — it requires usability as a priority to trickle down from the top of the org chart. The Oslo airport train ticket machine is the epitome of a high signal-to-noise interface. You simply swipe your credit card to enter and you swipe your card again as you leave the station at your destination. That’s it. No choices to make. No buttons to press. No ticket. You just swipe your card and you get on the train. Today this may not seem radical, but back in 2003, it was groundbreaking . To be able to provide such a frictionless user experience, they had to make a financial tradeoff: it does not ask for a PIN code, which means the company would need to simply absorb the financial losses from fraudulent charges (stolen credit cards, etc.). When user needs are prioritized at the top, it helps to cement that priority as an organizational design principle to point to when these tradeoffs come along in the day-to-day. Having a design principle in place will not instantly resolve all conflict, but it helps turn conflict about priorities into conflict about whether an exception is warranted, or whether the principle is applied correctly, both of which are generally easier to resolve. Of course, for that to work everyone needs to be on board with the principle. But here’s the thing with design principles (and most principles in general): they often seem obvious in the abstract, so it’s easy to get alignment in the abstract. It’s when the abstract becomes concrete that it gets tough. The Web Platform has its own version of this principle, which is called Priority of Constituencies : “User needs come before the needs of web page authors, which come before the needs of user agent implementors, which come before the needs of specification writers, which come before theoretical purity.” This highlights another key distinction. It’s more nuanced than users over developers; a better framing is consumers over producers . Developers are just one type of producer. The web platform has multiple tiers of producers: Even within the same tier there are producer vs consumer dynamics. When it comes to web development libraries, the web developers who write them are producers and the web developers who use them are consumers. This distinction also comes up in extensible software, where plugin authors are still consumers when it comes to the software itself, but producers when it comes to their own plugins. It also comes up in dual sided marketplace products (e.g. Airbnb, Uber, etc.), where buyer needs are generally higher priority than seller needs. In the economy of user effort, the antithesis of overpriced interfaces that make users feel ripped off are those where every bit of user effort required feels meaningful and produces tangible value to them. The interface is on the user’s side, gently helping them along with every step, instead of treating their time and energy as disposable. The user feels like they’re getting a bargain : they get to spend less than they had budgeted for! And we all know how motivating a good bargain is. User effort bargains don’t have to be radical innovations; don’t underestimate the power of small touches. A zip code input that auto-fills city and state, a web component that automatically adapts to its context without additional configuration, a pasted link that automatically defaults to the website title (or the selected text, if any), a freeform date that is correctly parsed into structured data, a login UI that remembers whether you have an account and which service you’ve used to log in before, an authentication flow that takes you back to the page you were on before. Sometimes many small things can collectively make a big difference. In some ways, it’s the polar opposite of death by a thousand paper cuts : Life by a thousand sprinkles of delight! 😀 In the end, “ simple things simple, complex things possible ” is table stakes. The key differentiator is the shape of the curve between those points. Products win when user effort scales smoothly with use case complexity, cliffs are engineered out, and every interaction declares a meaningful piece of user intent . That doesn’t just happen by itself. It involves hard tradeoffs, saying no a lot, and prioritizing user needs at the organizational level . Treating user effort like real money, forces you to design with restraint. A rule of thumb is place the pain where it’s best absorbed by prioritizing consumers over producers . Do this consistently, and the interface feels delightful in a way that sticks. Delight turns into trust. Trust into loyalty. Loyalty into product-market fit. Kay himself replied on Quora and provided background on this quote . Don’t you just love the internet? ↩︎ Yes, typing can be faster than dragging, but minimizing homing between input devices improves efficiency more, see KLM ↩︎ Yes, today it would have been , which is a little less noisy, but this was before the optional chaining operator. ↩︎ When I was running user studies at MIT, I’ve often had users exclaim “I can’t believe it! I tried to do the obvious simple thing and it actually worked!” ↩︎

1 views
マリウス 2 weeks ago

Updates 2025/Q3

This post includes personal updates and some open source project updates. Q3 has been somewhat turbulent, marked by a few unexpected turns. Chief among them, changes to my home base . In mid-Q3, the owner of the apartment I was renting abruptly decided to sell the property, effectively giving me two weeks to vacate. Thanks to my lightweight lifestyle , moving out wasn’t a major ordeal. However, in a global housing landscape distorted by corporations, wealthy boomers, and trust-fund heirs, securing a new place on such short notice proved nearly impossible. Embracing the fact that I am the master of my own time and destiny, and guided by the Taoist principle of Wu Wei , I chose not to force a solution. Instead, I placed all my belongings (including my beloved desk ) into storage and set off for a well-earned break from both the chaos and the gloom of the wet season. Note: If you ever feel you’re not being treated with the respect you deserve, the wisest response is often to simply walk away. Go where you’re treated best. Give yourself the space to reflect, to regain clarity, and most importantly, to reconnect with your sense of self. Resist the urge to look back or second-guess your instincts. Never make life-altering decisions in haste; Make them on your terms, not someone else’s. And remember, when life gives you onions, make onionade . On a different note, my coffee equipment has been extended by a new addition that is the Bookoo Themis Mini Coffee Scale , a super lightweight (~140g) and portable (8cm x 8cm x 1.5cm) coffee scale that allows me to precisely measure the amount of coffee that I’m grinding and brewing up . So far I’m very happy with the device. I don’t use its Bluetooth features at all, but when I initially tried, out of curiosity, their Android app didn’t really work. Speaking of brewing: Unfortunately at the end of Q3 my 9barista espresso maker seemingly broke down . While there are no electronics or mechanics that can actually break, I suspect that during my last descaling procedure enough limestone was removed for the boiler O-ring to not properly seal the water chamber any longer. I took the 9barista apart and couldn’t visually see anything else that could make it misbehave. I have hence ordered a repair kit from the manufacturer’s online store and am waiting for it to be delivered before I can continue enjoying self-made, awesome cups of coffee. Europe is continuing to build its surveillance machinery under claims of online safety , with the UK enforcing online age verification for major platforms, followed by the EU piloting similar acts in several member states . Even though the changes don’t affect me, I find this trend frightening, especially considering the looming threat to online privacy that is Chat Control . Even presumed safe-havens for censorship and surveillance like Matrix have rolled over and implemented age verification on the Matrix.org homeserver. The CEO of Element ( New Vector Limited ) gave the following explanation for it : Now, the Online Safety Act is a very sore point. The fact is that Matrix.org or Element is based in the UK, but even if we weren’t we would still be required to comply to the Online Safety Act for users who are in the UK, because it is British law. That statement is not quite accurate, however. If the Matrix.org homeserver was run by an entity in a non-cooperative jurisdiction they wouldn’t need to implement any of this. This is important, because people need to understand that despite the all the globalism that is being talked about, not every place on earth part-takes in implementing these mindless laws, even if your government would like you to think that it’s the norm. Obviously it’s not exactly easy to run a platform from within an otherwise (at least partially) sanctioned country, especially when user data is at stake. However with regulations like these becoming more and more widespread my pirate mind imagines a future where such setups are becoming viable options, given that the countries in question are scrambling for income streams and would be more than happy to gain leverage over other countries. We’ve already seen this in several instances (e.g. Yandex, Alibaba, ByteDance ( TikTok ), Telegram, DiDi, Tencent ( WeChat ), …) and given the global political climate I can imagine more services heading towards jurisdictions that allow them to avoid requesting IDs from their users or breaking security measures only so government agencies can siphon out data at will. However, a different future outcome might be an increased focus on decentralization (or at least federation ), which would as well be a welcome change. As Matrix correctly pointed out, individually run homeservers are not affected by any of this. Similarly, I haven’t heard of any instances of XMPP service operators being approached by UK officials. Unlike on centralized platforms like Discord, and wannabe-decentralized platforms like Bluesky, enforcing something like age verification on an actual federated/decentralized network is near impossible, especially with services that are being hosted outside of the jurisdiction’s reach. In the future, federated protocols, as well as peer-to-peer projects are going to become more important than ever to counter the mindless policies enacted by the people in power. Looking at this mess from the bright side, with major big tech platforms requiring users to present IDs we can hope for a growing number of people to cut ties with those platforms, driving them, and their perpetrators , into the ground in the long run. If you are looking for decentralized alternatives to centralized services, here is a non-exhaustive list: Since I began publishing code online, I’ve typically used the GPL or MIT license for my projects. However, given the current global climate and the direction the world seems to be heading, I’ve grown increasingly skeptical of these traditional licenses. While I still want to offer free and open source software to people , I find myself more and more reluctant to grant unrestricted use, particularly to organizations whose values or missions I fundamentally disagree with. Unfortunately, exclusions or prohibitions were never part of the vision behind the GNU or OSI frameworks, making most conventional open source licenses unsuitable for this kind of selective restriction. Recently, however, I came across the Hippocratic License , which is designed to address exactly these concerns. In fact, the HL3 already includes three of the four exclusions I would like to enforce: Mass surveillance, military activities, and law enforcement. The fourth, government revenue services, could likely be added in a similar manner. That said, HL3 does overreach in some areas, extending into domains where I don’t believe a software license should have jurisdiction, such as: 3.2. The Licensee SHALL: 3.2.1. Provide equal pay for equal work where the performance of such work requires equal skill, effort, and responsibility, and which are performed under similar working conditions, except where such payment is made pursuant to: 3.2.1.1. A seniority system; 3.2.1.2. A merit system; 3.2.1.3. A system which measures earnings by quantity or quality of production; or 3.2.1.4. A differential based on any other factor other than sex, gender, sexual orientation, race, ethnicity, nationality, religion, caste, age, medical disability or impairment, and/or any other like circumstances (See 29 U.S.C.A. § 206(d)(1); Article 23, United Nations Universal Declaration of Human Rights; Article 7, International Covenant on Economic, Social and Cultural Rights; Article 26, International Covenant on Civil and Political Rights); and 3.2.2. Allow for reasonable limitation of working hours and periodic holidays with pay (See Article 24, United Nations Universal Declaration of Human Rights; Article 7, International Covenant on Economic, Social and Cultural Rights). These aspects of the Hippocratic License have already drawn significant criticism, and I would personally remove them in any variation I choose to adopt. However, a far greater concern lies with the license’s stewardship, the Organization for Ethical Source ( OES ). While supporting a good cause is typically straightforward, the organization’s founder and current president has unfortunately earned a reputation for unprofessional conduct , particularly in addressing the very issues the organization was created to confront. I’m reluctant to have my projects associated with the kind of “drama” that seems to follow the organization’s leadership. For this reason, I would likely need to distance any variation of the license as far as possible from its heritage, to avoid direct association with the OES and the leadership’s behavior. Hence, I’m still on the lookout for alternative licenses, specifically ones that maintain the permissiveness of something like the GPL, but allow for clearly defined, legally enforceable exceptions. If you have experience in working with such licenses, I would very much appreciate your input. PS: I’m fully aware that adopting such a license would render my software non-free in the eyes of organizations like GNU or the OSI. However, those organizations were founded in a different era and have, in my view, failed to adapt to the realities of today’s world. It’s curious how many advocates of GNU/OSI philosophies call for limitations on freedom of speech, yet insist on software being usable without restriction in order to qualify as free and open source . This site has received what some might consider a useless or even comical update, which, however, is meant to further the goal of raising awareness about the role JavaScript plays in browsers. I got the inspiration for this from this post by sizeof.cat , a site I discovered thanks to the friendly folks in the VT100 community room . While sizeof.cat uses this feature purely for the lulz , I believe it can serve as an effective way to encourage people to disable JavaScript in their browsers by default, and to be very selective about which websites they enable it for. As a result, this website now features a similar (but edgier ) option, which you can test by enabling JavaScript for this domain and then sending this tab to the background. Go ahead, I’ll wait. :-) Like sizeof.cat ’s original implementation, this site will randomly alternate between different services . However, unlike the original, you’ll see an overlay when you return to the site, explicitly encouraging you to disable JavaScript in your browser. After having used neomutt for several years, I grew tired of the many cogs ( notmuch , mbsync , w3m , reader , etc.) I had to maintain for the setup to function the way I expected it to do, especially when my primary requirement is to not leave e-mails on the server for more time than really needed. Eventually I got fed up with my e-mail client breaking whenever I needed it most, and with having to deal with HTML e-mail on the command line, thinking that if I’d use an actual GUI things would be much simpler. Little did I know. I moved to Mozilla Thunderbird as my primary e-mail client a while ago. I set up all my IMAP accounts, and I created a “Local Folder” that Mozilla sold me as maildir : Fast-forward to today and I’m stuck with a setup where I cannot access my “Local Folder” maildir by any other maildir -compliant software besides Thunderbird, because even though Mozilla called it maildir , it is not an actual maildir format : Note this is NOT full maildir in the sense that most people, particularly linux users or mail administrators, know as maildir. On top of that, my OpenSnitch database is overflowing with deny rules for Mozilla’s supposed “privacy respecting” software. At this point I’m not even wondering what the heck is wrong with this company anymore. Mozilla has lost it, with Firefox , and seemingly also with other software they maintain. With my e-mails now locked-in into something that Mozilla titles maildir even though it is not, I am looking forward to go back to where I came from. I might however replace the overly complex neomutt setup with a more modern and hopefully lightweight aerc configuration. Unfortunately, I have used Thunderbird ’s “Local Folder” account for too long and I’ll have to figure out a way to get those e-mails into an actual maildir format before I can leave Mozilla’s ecosystem once and for all. Note on Firefox: I don’t care what your benchmark says, in everyday use Firefox is annoyingly slow despite all its wRiTtEn In RuSt components. For reasons that I didn’t care to investigate, it also seemingly hangs and waits for connections made by its extensions (e.g. password managers) and meanwhile prevents websites from loading. The amount of obscure problems that I’ve encountered with Firefox over the past years is unmatched by any other browser. Not to mention the effort that goes into checking the configuration editor with every new release and disabling all the privacy invasive bs that Mozilla keeps adding. At this point I’m not supporting Firefox any longer, despite the world’s need for a Chromium alternative. Firefox is sucking out the air in the room and with it dead hopefully more effort will be put into alternatives. I had to bring my Anker A1257 power bank to a “recycling” facility, due to it being recalled by Anker : There’s an interesting post by lumafield if you want to know the details. However, what Anker calls a recall is effectively a throw it away and we give you a voucher , because apparently we’re too stupid as societies to demand for manufacturers to take back their broken junk and recycle it properly . I tried to be better by not tossing the device into the trash but bring it to a dedicated “recycling” facility, even when I know for sure that they won’t actually recycle it or even dispose of it in a proper way. But that’s pretty much all I, as a consumer, can do in this case. While I, too, got a lousy voucher from Anker, none of their current options fit the specifications of the A1257. I therefor decided to look for alternatives and found the Sharge Pouch Mini P2. I needed something that is lightweight, has a relatively small form factor and doesn’t rely on a single integrated charging cable which would render the device useless the moment it would break. Given how bad Anker USB-C cables usually are in terms of longevity, I would never buy into a power bank from Anker that comes with an integrated USB cable, especially when it’s the only option to charge the power bank. While the Sharge also has a fixed USB cable, it is nevertheless possible to use the USB-C port for re-charging the device. If the integrated red cable ever breaks, I can still continue using the device. As I have zero experience with this manufacturer it remains to be seen how this 213g- heavy power bank will perform long-term. So far the power bank appears sufficient. While charging it barely gets warm, and even though the device lacks a display for indicating the charge level, the LED ring around the power button is sliced into four segments that make it easy to guesstimate the remaining charge. Charging it full takes around an hour. One thing that is slightly annoying is the USB-C port, which won’t fit significantly thicker cable-heads. The maximum that I could fit were my Cable Matters USB4 cables. The situation with GrapheneOS devices (and Android in general) mentioned in my previous updates has prompted me to revive my dormant Pinephone Pro . Trying to do so, however, I found that the original Pinephone battery was pretty much dead. Hence, I ordered a new battery that is compatible with the Samsung Galaxy J7 (models / ) – primarily because Pine64 doesn’t appear to be selling batteries for the Pinephone Pro anymore; Update: Pine64 officially discontinued the Pinephone Pro – and gave the latest version of postmarketOS (with KDE Plasma Mobile) a go. While Pinephone Pro support has become better over the years, with at least the selfie-camera finally “working” , the Pinephone hardware unfortunately remains a dead-end. Even with a new battery the phone discharges within a few hours (with sleep enabled). In fact, it even discharges over night when turned off completely. I don’t know whether newer versions of the Pine64 hardware have fixed the hardware bugs, but judging by the search results that I’m getting I doubt so. The UI has certainly become more usable with hardware acceleration seemingly working fine now, however the Pinephone is still too weak for most use cases. Retrospectively, the Pinephone Pro was a bad investment, as it’s effectively a wire-bound device with an integrated UPS at most, that I would argue isn’t even suitable as a development platform with all its hardware glitches ( Hello %0 battery boot loop! , just to name one). It is in fact so bad that you cannot even boot the device when there is no battery in it, to use it as a regular SBC with integrated display. This is sad because the Pinephone hardware tarnishes the reputation of Linux on mobile, given that it is one of the most prominent options. If you’re considering to give Linux on mobile a try, I do not recommend the Pinephone, and I am somewhat happy that Pine64 decided to discontinue it. They did not discontinue the original Pinephone, yet, however. Having that said, I have been following the progress that Luca Weiss ( Z3ntu ) made with running pmOS on the Fairphone 6 and I have to admit that I’m intrigued. While it’s still a long way to go , it is nice to see a Fairphone engineer that is actively working on bringing mobile Linux to the device. I don’t know whether his efforts are partially funded by his employer, or whether it’s his personal interest, but I truly hope for the former. The Fairphone is an odd value proposition for the average Android user. The native Fairphone Android experience seems average , and their Murena /e/OS partnership is highly questionable at best and might tarnish their reputation in the long run. However, I feel like they could gain a pretty large nerd-following by officially supporting mobile Linux, and actively pushing for it. At least in my books, having full-fledged postmarketOS support on their phones would be an instant money-magnet from the tech sphere, especially with the current bar being as low as the Pinephone. I will keep an eye on the progress, because I would be more than happy to give it a go once UFS support, 3D acceleration and WiFi connectivity issues are fixed. Alternatively, it appears that the OnePlus 6T is among the best supported postmarketOS devices at this point, and from several videos I came across on YouTube it appears that performance is significantly better than the Pinephone. However, a 7-years-old phone battery is probably cooked, and replacing it requires removal of the glued backcover. At an average price (on eBay) of around $100, plus another $30 for the replacement battery, the phone is not a particularly attractive option from a hardware standpoint. I invested quite some time in pursuing my open source projects in the past quarter, hence there are a few updates to share. With 📨🚕 going live Overpush has received a lot of updates over the past months, most of which are as beneficial for self-hosted versions as they are for the hosted service. You can find an overview of the changes on the releases page. zpoweralertd 0.0.2 was released with compatibility for Zig 0.15.1. Apart from the adjustments to compile with the latest Zig release no new things were introduced. Nearly five years after its initial release, zeit has weathered the test of time ( hah ) relatively well and continues to grow in popularity on GitHub . What started as a minimal command-line time-tracking utility has evolved into a program packed with a wide range of features and options. Depending on your preferences, you might however say that it now has one too many these days. zeit began as a personal pet project, with no clear long-term plan. Whenever users requested a new feature or option, I either implemented it myself or accepted their pull requests without much second thought. My mantra was simple: If a small enhancement made the software more useful to even one other person, I was happy to introduce it. Fast forward to today, and the very first version of zeit (dubbed zeit v0 ) has strayed far from its roots as a minimal and clean command-line tool. Instead, it has grown into a somewhat unwieldy UX experience, cluttered with features that are neither intuitive nor well thought out. From a code perspective, some of the decisions that made sense a few years ago now seem less ideal, particularly as we look ahead. While I could have sifted through the original v0 codebase to clean it up and remove features that were added by contributors who ultimately didn’t maintain them long-term, I chose instead to rewrite zeit from the ground up. This new version will be based on more modern dependencies and, hopefully, will be cleaner, more streamlined, and free of the “one-off” features that were added for single users who eventually stopped using zeit altogether. That said, I’ve learned a lot from the feature requests submitted over the past five years. With this new version, I’m working to implement the most useful and practical requests in a way that feels more cohesive and polished from a UX perspective, and less like an afterthought. I’m nearing the release of the first version of this complete rewrite, which will be called zeit v1 and carry the version number v1.0.0 . This new version will not be compatible with your existing zeit v0 database. However, if you’re currently using zeit v0 , you can export your entries using , and then import them into v1 with the new command. If you’re interested in a command-line utility for time tracking, especially if you’re already using a different tracker, I’d love to hear from you . Let me know your top three feature requests for a tool like zeit and which platform(s) you currently use or would like to switch from. Footnote: The artwork was generated using AI and further botched by me using the greatest image manipulation program . Twitter X: Mastodon Facebook: See Twitter X. Reddit: Lemmy Instagram: Pixelfed YouTube: PeerTube Spotify: Funkwhale , or simply host your own Jellyfin server WhatsApp: plenty to choose from The Fed, ECB, etc.: Monero , Bitcoin , et al.

0 views
Nick Khami 2 weeks ago

Use the Accept Header to serve Markdown instead of HTML to LLMs

Agents don't need to see websites with markup and styling; anything other than plain Markdown is just wasted money spent on context tokens. I decided to make my Astro sites more accessible to LLMs by having them return Markdown versions of pages when the header has or preceding . This was very heavily inspired by this post on X from bunjavascript . Hopefully this helps SEO too, since agents are a big chunk of my traffic. The Bun team reported a 10x token drop for Markdown and frontier labs pay per token, so cheaper pages should get scraped more, be more likely to end up in training data, and give me a little extra lift from assistants and search. Note: You can check out the feature live by running or in your terminal. Static site generators like Astro and Gatsby already generate a big folder of HTML files, typically in a or folder through an command. The only thing missing is a way to convert those HTML files to markdown. It turns out there's a great CLI tool for this called html-to-markdown that can be installed with and run during a build step using . Here's a quick Bash script an LLM wrote to convert all HTML files in to Markdown files in , preserving the directory structure: Once you have the conversion script in place, the next step is to make it run as a post-build action. Here's an example of how to modify your scripts section: Moving all HTML files to first is only necessary if you're using Cloudflare Workers, which will serve existing static assets before falling back to your Worker. If you're using a traditional reverse proxy, you can skip that step and just convert directly from to . Note: I learned after I finished the project that I could have added to my so I didn't have to move any files around. That field forces the worker to always run frst. Shoutout to the kind folks on reddit for telling me. I pushed myself to go out of my comfort zone and learn Cloudflare Workers for this project since my company uses them extensively. If you're using a traditional reverse proxy like Nginx or Caddy, you can skip this section (and honestly, you'll have a much easier time). If you're coming from traditional reverse proxy servers, Cloudflare Workers force you into a different paradigm. What would normally be a simple Nginx or Caddy rule becomes custom configuration, moving your entire site to a shadow directory so Cloudflare doesn't serve static assets by default, writing JavaScript to manually check headers and using to serve files. SO MANY STEPS TO MAKE A SIMPLE FILE SERVER! This experience finally made Next.js 'middleware' click for me. It's not actually middleware in the traditional sense of a REST API; it's more like 'use this where you would normally have a real reverse proxy.' Both Cloudflare Workers and Next.js Middleware are essentially JavaScript-based reverse proxies that intercept requests before they hit your application. While I'd personally prefer Terraform with a hyperscaler or a VPS for a more traditional setup, new startups love this pattern, so it's worth understanding. Here's an example of a working file to refer to a new worker script and also bind your build output directory as a static asset namespace: Below is a minimal worker script that inspects the header and serves markdown when requested, otherwise falls back to HTML: Pro tip: make the root path serve your sitemap.xml instead of markdown content for your homepage such that an agent visiting your root URL can see all the links on your site. It's likely much easier to set this system up with a traditional reverse proxy file server like Caddy or Nginx. Here's a simple Caddyfile configuration that does the same thing: I will leave Nginx configuration as an exercise for the reader or perhaps the reader's LLM of choice. By serving lean, semantic Markdown to LLM agents, you can achieve a 10x reduction in token usage while making your content more accessible and efficient for the AI systems that increasingly browse the web. This optimization isn't just about saving money; it's about GEO (Generative Engine Optimization) for a changed world where millions of users discover content through AI assistants. Astro's flexibility made this implementation surprisingly straightforward. It only took me a couple of hours to get both the personal blog you're reading now and patron.com to support this feature. If you're ready to make your site agent-friendly, I encourage you to try this out. For a fun exercise, copy this article's URL and ask your favorite LLM to "Use the blog post to write a Cloudflare Worker for my own site." See how it does! You can also check out the source code for this feature at github.com/skeptrunedev/personal-site to get started. I'm excited to see the impact of this change on my site's analytics and hope it inspires others. If you implement this on your own site, I'd love to hear about your experience! Connect with me on X or LinkedIn .

1 views
Michael Lynch 2 weeks ago

Get xkcd Cartoons at 2x Resolution

I recently learned a neat trick from Marcus Buffett : xkcd has an undocumented way to get images of the cartoons at double their normal resolution. For example, xkcd #2582 “Data Trap” lists this URL as the direct link: That leads to this 275x275px image, quite small by today’s standards: But there’s a trick! Add a to the filename in the URL: That gives you a version that’s 2x the original resolution, at 550x550px: I couldn’t find the 2x versions documented or linked anywhere except for some old , obscure reddit posts. I hope this knowledge saves you from stretching out poor, low-resolution xkcd comics. Update (2025-10-02) : My friend Matthew Riley let me know that the URL actually appears in xkcd’s HTML in the image’s attribute : Matthew also did some sleuthing at the Internet Archive and noticed that Randall Munroe added the version for older cartoons recently, as #124 “Blogofractal” had no attribute in this 2023 snapshot , but this 2024 snapshot includes . Not every xkcd is availble in 2x resolution. I vibe-coded a Python script to check which cartoons have a 2x-resolution version and published the current list: The earliest version with 2x available is #124, “Blogofractal” ( 2x version ). Starting with #1084, “Server Problem” ( 2x version ), almost every xkcd is available at 2x-resolution. https://imgs.xkcd.com/comics/data_trap.png https://imgs.xkcd.com/comics/data_trap_2x.png List of 2x-resolution xkcd Cartoons #1683 “Digital Data” : The 2x version is lower quality than the original .

0 views
Manuel Moreale 2 weeks ago

Kris Howard

This week on the People and Blogs series we have an interview with Kris Howard, whose blog can be found at web-goddess.org . Tired of RSS? Read this in your browser or sign up for the newsletter . The People and Blogs series is supported by Ilja Panić and the other 120 members of my "One a Month" club. If you enjoy P&B, consider becoming one for as little as 1 dollar a month. Heya! I'm Kris Howard, and as of September 2025, I've been blogging continuously for 25 years. 😳 I grew up in rural Indiana, had a couple of brief working stints in London and Munich, and have lived nearly all of my adult life in Sydney, Australia with my husband Rodd (aka The Snook). I started my career as a web developer in the dotcom boom and eventually went on to manage projects and lead teams. I ended up in Developer Relations for AWS, where I travelled all over telling folks why they should use the cloud. It was a lot of fun until everything became all about AI. In 2024 I joined the Snook in early retirement (at age 47), and for the past year I've been reading books (remember books?!), hanging out with friends, indulging in my craft hobbies (knitting, sewing, and now weaving), volunteering, travelling around Australia, and generally trying to spend a lot less time in front of a computer. I was on work-study in university, and they assigned me to work in the dining hall. That sucked , so I scrambled to find something better. I ended up working in the Computer Department just as my university was connecting everyone to the Internet. I learned HTML and built websites for myself and my dorm, and in 1996 I launched a fan site for my favourite author that's been running ever since. Fun trivia: I hosted that on roalddahl.org until the Dahl estate reached out about acquiring the domain name. I happily transferred it to them, and in return I got to meet Felicity Dahl and visit his writing hut in Buckinghamshire! By that time I had left uni and was working as a web developer in London. I started testing out Blogger (which back then would actually FTP static files to your own webhosting!) in September 2000 and launched my blog properly a couple months later at web-goddess.co.uk. The name was a bit of a joke, a nickname that a Scottish friend gave me because of how much time I spent online. Originally my goal was just to document my life overseas for friends and family, and to share silly things I found on the Internet. When the dotcom crash hit at the end of 2001, I ended up moving to Sydney with my Australian boyfriend and changed the domain to web-goddess.org instead. For a long time I ran off my own custom PHP CMS (which I even distributed ) before moving to Wordpress ten years ago. My blogging energy has waxed and waned over the years. There were long stretches when I just syndicated content from places like Instagram and Google Reader to the blog rather than write. Since retiring from full-time work I've really renewed my focus on it as the home for all my content online. I've migrated all my posts from Twitter, Instagram, and Facebook to the blog, and I now syndicate out from the blog to Mastodon and Bluesky. Every day I go back through my archives and clean up broken links. (So much linkrot.) I miss the Web of the early 2000s and I'm trying to keep my little corner of it thriving. My process is fairly casual, and I don't usually write extensive drafts before publishing. Back in the olden days, I used it basically like Twitter - just dashing off a thought or sharing a link multiple times a day. Nowadays I tend to be a bit more deliberate with what I share. Occasionally I'll get Rodd to review something, especially if it touches on our personal life (like my posts about our retirement finances ). I've occasionally dabbled with creating a series of posts on a theme, like when we tried cooking our way through the Jamie's 30 Minute Meals cookbook (still by far my most popular posts ever!). When we were living in Munich, I made a point of documenting all of our travels around Europe, knowing I'd want to look back on those trips later. I tend to write my posts in the Wordpress classic editor in a web browser. I'm comfortable with that workflow, but I've also used the Gutenberg block editor on occasion. I also occasionally post from the Wordpress app on my phone. Last year I figured out that I could post via Apple Shortcuts using the Wordpress API , which has made it really easy for me to share photos when I'm away from the computer. Definitely. We renovated our house last year and I took the opportunity to design the office-slash-craft-room of my dreams. I've got a ridiculously wide curved monitor, a super clicky Keychron mechanical keyboard , and a very silly NES mouse . I've got a comfy chair and a desk I can adjust up and down with the press of a button. I've got everything I need within arm's reach whether I'm blogging, knitting, or sewing. Having a pegboard with lots of tools on it fills a part of my soul I didn't realise was empty! I also tend to listen to music when I'm at the computer (Apple Music via Echo Dot speaker). I started out with my own hand-coded PHP-based CMS, which I used for fifteen years on various shared hosting providers. Eventually I realised that my rudimentary coding skills weren't meeting the security bar anymore, and I reluctantly let a friend persuade me try out Wordpress . To my surprise, I loved it and ended up migrating roalddahlfans.com over to it as well. It's not without its occasional headaches, but I haven't had any major problems in the last decade. During the initial months of the pandemic, I decided to migrate both sites from their shared webhosting over to Amazon Lightsail (since I was working for AWS at the time and wanted to learn more about the service). Both sites are hosted in the US and use the Wordpress blueprint on Linux (2GB memory, 2 vCPUs, 60GB SSD) with CloudFront as a CDN. The domain names were registered with Google Domains, which have since been migrated to Squarespace. For a couple years now I've been toying with the idea of converting one or both the sites to be static, served off S3 or similar. It would be a lot cheaper, faster, and more secure. The challenge is that I'm not starting from scratch; I have thousands of posts and pages that I've written over the years. I also really like being able to post from anywhere, including my phone. My husband Rodd was an SRE at Google and is a much better coder than me, so he's been working on a script that would allow us to run Wordpress from our home server and generate static HTML. We'll try it out with roalddahlfans.com and then decide where to go from there. Haha, I'd definitely choose a different name! I realised pretty early on that if you typed my domain incorrectly, nearly every other permutation was adult content. Whoops. I'd probably go with a static site today, just to keep things as simple as possible. I love the old school tiny websites that folks are building these days, and I'd go full IndieWeb - write on my site and syndicate everywhere else. I would be a bit more... considerate with how I write. I realised this year as I've been tidying up the broken links in my archives that I wasn't always kind in how I wrote about some people or topics. The early 2000s were a much snarkier time, I guess. I've chosen to leave those posts up, but I've added a disclaimer that they don't necessarily reflect who I am today. (Similarly, I've been adding a disclaimer on all my posts about Harry Potter. Rowling sucks, and it makes me sad.) I held off on monetising for many years, especially on roalddahlfans.com since most of the audience are kids. Eventually I added some hand-picked Google Ads to the site and to my blog archives, restricting the placement and ad categories. I also added Amazon affiliate links to the Dahl site, since I figured a lot of parents would be likely to buy the books. These more than covered the hosting costs over the years. (In fact, Rodd had to declare my websites when he joined Google in case there was a conflict of interest!) When we were living in Munich a few years back, I discovered that Germany has stringent tax laws about website monetisation and would require me to register as a side business. It was all too complicated, so the easiest thing was to just remove the ads. I didn't bother putting them back when we got to Australia, so for several years now there's been no income. Hosting is generally $40-50 AUD a month (for both sites), with an additional $125 AUD per site annually for Jetpack (which gives me automated backups, comment spam protection, and some other security features). This is honestly way overkill, and I reckon I can reduce that a lot in the future (especially if I move to a static site). I don't like the idea that everything on the web needs to be monetised or that everybody needs to have a side hustle. I love it when people share things online just because they want to. That's how both my sites started, and that's why I keep them going. That said, I financially support a couple bloggers and my Mastodon instance admin via Patreon, because I want them to continue. So many sites disappear, and it's just sad. One of the people I think of when I think of blogging is Matt Haughey from A Whole Lotta Nothing: a.wholelottanothing.org . He was the founder of Metafilter.com , and I think I've been reading his site for literally decades now. He was there when blogging was invented. Definitely someone worth talking to! Does a newsletter count as a blog these days? The person whose words have given me the most hope for humanity in 2025 is Mike Monteiro . Every time he writes it's just so, so good and I end up blogging it or sending it around to all my friends. (Archives are here .) I've also been really enjoying the blog of Robb Knight: rknight.me . He built Echofeed , the service I use to syndicate my blog posts out to Mastodon and Bluesky. He's always building something nerdy, or geeking out over pens and stationery. This is so absolutely dorky, but I am currently reading the Aubrey-Maturin "Master and Commander" books . I always figured they were quintessential "Dad books" (given they are historical fiction about naval voyages in the Napoleonic Wars) and therefore Not For Me, but this amazing essay convinced me to give them a shot. I've discovered that the books themselves are fantastic, and there's this whole fandom subculture I never knew about! They refer to reading the series as a "circumnavigation," and there are charming map websites and podcasts and subreddits with fans all talking (in character!) about the books. I'm loving it. It's been a welcome escape from the news over the past few months, and I can highly recommend the series. Now that you're done reading the interview, go check the blog and subscribe to the RSS feed . If you're looking for more content, go read one of the previous 108 interviews . Make sure to also say thank you to Markus Heurung and the other 120 supporters for making this series possible.

0 views
baby steps 3 weeks ago

Symposium: exploring new AI workflows

This blog post gives you a tour of Symposium , a wild-and-crazy project that I’ve been obsessed with over the last month or so. Symposium combines an MCP server, a VSCode extension, an OS X Desktop App, and some mindful prompts to forge new ways of working with agentic CLI tools. Symposium is currently focused on my setup, which means it works best with VSCode, Claude, Mac OS X, and Rust. But it’s meant to be unopinionated, which means it should be easy to extend to other environments (and in particular it already works great with other programming languages). The goal is not to compete with or replace those tools but to combine them together into something new and better. In addition to giving you a tour of Symposium, this blog post is an invitation: Symposium is an open-source project , and I’m looking for people to explore with me! If you are excited about the idea of inventing new styles of AI collaboration, join the symposium-dev Zulip . Let’s talk! I’m not normally one to watch videos online. But in this particular case, I do think a movie is going to be worth 1,000,000 words. Therefore, I’m embedding a short video (6min) demonstrating how Symposium works below. Check it out! But don’t worry, if videos aren’t your thing, you can just read the rest of the post instead. Alternatively, if you really love videos, you can watch the first version I made, which went into more depth . That version came in at 20 minutes, which I decided was…a bit much. 😁 The Symposium story begins with , an OS X desktop application for managing taskspaces . A taskspace is a clone of your project 1 paired with an agentic CLI tool that is assigned to complete some task. My observation has been that most people doing AI development spend a lot of time waiting while the agent does its thing. Taskspaces let you switch quickly back and forth. Before I was using taskspaces, I was doing this by jumping between different projects. I found that was really hurting my brain from context switching. But jumping between tasks in a project is much easier. I find it works best to pair a complex topic with some simple refactorings. Here is what it looks like to use Symposium: Each of those boxes is a taskspace. It has both its own isolated directory on the disk and an associated VSCode window. When you click on the taskspace, the app brings that window to the front. It can also hide other windows by positioning them exactly behind the first one in a stack 2 . So it’s kind of like a mini window manager. Within each VSCode window, there is a terminal running an agentic CLI tool that has the Symposium MCP server . If you’re not familiar with MCP, it’s a way for an LLM to invoke custom tools; it basically just gives the agent a list of available tools and a JSON scheme for what arguments they expect. The Symposium MCP server does a bunch of things–we’ll talk about more of them later–but one of them is that it lets the agent interact with taskspaces. The agent can use the MCP server to post logs and signal progress (you can see the logs in that screenshot); it can also spawn new taskspaces. I find that last part very handy. It often happens to me that while working on one idea, I find opportunities for cleanups or refactorings. Nowadays I just spawn out a taskspace with a quick description of the work to be done. Next time I’m bored, I can switch over and pick that up. It’s probably worth mentioning that the Symposium app is written in Swift. I did not know Swift three weeks ago. But I’ve now written about 6K lines and counting. I feel like I’ve got a pretty good handle on how it works. 3 Well, it’d be more accurate to say that I have reviewed about 6K lines, since most of the time Claude generates the code. I mostly read it and offer suggestions for improvement 4 . When I do dive in and edit the code myself, it’s interesting because I find I don’t have the muscle memory for the syntax. I think this is pretty good evidence for the fact that agentic tools help you get started in a new programming language. So, while taskspaces let you jump between tasks, the rest of Symposium is dedicated to helping you complete an individual task. A big part of that is trying to go beyond the limits of the CLI interface by connecting the agent up to the IDE. For example, the Symposium MCP server has a tool called which lets the agent present you with a markdown document that explains how some code works. These walkthroughs show up in a side panel in VSCode: As you can see, the walkthroughs can embed mermaid, which is pretty cool. It’s sometimes so clarifying to see a flowchart or a sequence diagram. Walkthroughs can also embed comments , which are anchored to particular parts of the code. You can see one of those in the screenshot too, on the right. Each comment has a Reply button that lets you respond to the comment with further questions or suggest changes; you can also select random bits of text and use the “code action” called “Discuss in Symposium”. Both of these take you back to the terminal where your agent is running. They embed a little bit of XML ( ) and then you can just type as normal. The agent can then use another MCP tool to expand that reference to figure out what you are referring to or what you are replying to. To some extent, this “reference the thing I’ve selected” functionality is “table stakes”, since Claude Code already does it. But Symposium’s version works anywhere (Q CLI doesn’t have that functionality, for example) and, more importantly, it lets you embed multiple refrences at once. I’ve found that to be really useful. Sometimes I’ll wind up with a message that is replying to one comment while referencing two or three other things, and the system lets me do that no problem. Symposium also includes an tool that lets the agent connect to the IDE to do things like “find definitions” or “find references”. To be honest I haven’t noticed this being that important (Claude is surprisingly handy with awk/sed) but I also haven’t done much tinkering with it. I know there are other MCP servers out there too, like Serena , so maybe the right answer is just to import one of those, but I think there’s a lot of interesting stuff we could do here by integrating deeper knowledge of the code, so I have been trying to keep it “in house” for now. Continuing our journey down the stack, let’s look at one more bit of functionality, which are MCP tools aimed at making agents better at working with Rust code. By far the most effective of these so far is one I call . It is very simple: given the name of a crate, it just checks out the code into a temporary directory for the agent to use. Well, actually, it does a bit more than that. If the agent supplies a search string, it also searches for that string so as to give the agent a “head start” in finding the relevant code, and it makes a point to highlight code in the examples directory in particular. My experience has been that this tool makes all the difference. Without it, Claude just geneates plausible-looking APIs that don’t really exist. With it, Claude generally figures out exactly what to do. But really it’s just scratching the surface of what we can do. I am excited to go deeper here now that the basic structure of Symposium is in place – for example, I’d love to develop Rust-specific code reviewers that can critique the agent’s code or offer it architectural advice 5 , or a tool like CWhy to help people resolve Rust trait errors or macro problems. But honestly what I’m most excited about is the idea of decentralizing . I want Rust library authors to have a standard way to attach custom guidance and instructions that will help agents use their library. I want an AI-enhanced variant of that automatically bridges over major versions, making use of crate-supplied metadata about what changed and what rewrites are needed. Heck, I want libraries to be able to ship with MCP servers implemented in WASM ( Wassette , anyone?) so that Rust developers using that library can get custom commands and tools for working with it. I don’t 100% know what this looks like but I’m keen to explore it. If there’s one thing I’ve learned from Rust, it’s always bet on the ecosystem. One of the things I am very curious to explore is how we can use agents to help humans collaborate better. It’s oft observed that coding with agents can be a bit lonely 6 . But I’ve also noticed that structuring a project for AI consumption requires relatively decent documentation. For example, one of the things I did recently for Symposium was to create a Request for Dialogue (RFD) process – a simplified version of Rust’s RFC process. My motivation was partly in anticipation of trying to grow a community of contributors, but it was also because most every major refactoring or feature work I do begins with iterating on docs. The doc becomes a central tracking record so that I can clear the context and rest assured that I can pick up where I left off. But a nice side-effect is that the project has more docs than you might expect, considering, and I hope that will make it easier to dive in and get acquainted. And what about other things? Like, I think that taskspaces should really be associated with github issues. If we did that, could we do a better job at helping new contributors pick up an issue? Or at providing mentoring instructions to get started? What about memory? I really want to add in some kind of automated memory system that accumulates knowledge about the system more automatically. But could we then share that knowledge (or a subset of it) across users, so that when I go to hack on a project, I am able to “bootstrap” with the accumulated observations of other people who’ve been working on it? Can agents help in guiding and shepherding design conversations? At work, when I’m circulating a document, I will typically download a copy of that document with people’s comments embedded in it. Then I’ll use pandoc to convert that into Markdown with HTML comments and then ask Claude to read it over and help me work through the comments systematically. Could we do similar things to manage unwieldy RFC threads? This is part of what gets me excited about AI. I mean, don’t get me wrong. I’m scared too. There’s no question that the spread of AI will change a lot of things in our society, and definitely not always for the better. But it’s also a huge opportunity. AI is empowering! Suddenly, learning new things is just vastly easier. And when you think about the potential for integrating AI into community processes, I think that it could easily be used to bring us closer together and maybe even to make progress on previously intractable problems in open-source 7 . As I said in the beginning, this post is two things. Firstly, it’s an advertisement for Symposium. If you think the stuff I described sounds cool, give Symposium a try! You can find installation instructions here. I gotta warn you, as of this writing, I think I’m the only user, so I would not at all be surprised to find out that there’s bugs in setup scripts etc. But hey, try it out, find bugs and tell me about them! Or better yet, fix them! But secondly, and more importantly, this blog post is an invitation to come out and play 8 . I’m keen to have more people come and hack on Symposium. There’s so much we could do! I’ve identified a number of “good first issue” bugs . Or, if you’re keen to take on a larger project, I’ve got a set of invited “Request for Dialogue” projects you could pick up and make your own. And if none of that suits your fancy, feel free to pitch you own project – just join the Zulip and open a topic! Technically, a git worktree.  ↩︎ That’s what the “Stacked” box does; if you uncheck it, the windows can be positioned however you like. I’m also working on a tiled layout mode.  ↩︎ Well, mostly. I still have some warnings about something or other not being threadsafe that I’ve been ignoring. Claude assures me they are not a big deal (Claude can be so lazy omg).  ↩︎ Mostly: “Claude will you please for the love of God stop copying every function ten times.”  ↩︎ E.g., don’t use a tokio mutex you fool, use an actor . That is one particular bit of advice I’ve given more than once.  ↩︎ I’m kind of embarassed to admit that Claude’s dad jokes have managed to get a laugh out of me on occassion, though.  ↩︎ Narrator voice: burnout. he means maintainer burnout.   ↩︎ Tell me you went to high school in the 90s without telling me you went to high school in the 90s.  ↩︎

0 views
Neil Madden 1 months ago

Rating 26 years of Java changes

I first started programming Java at IBM back in 1999 as a Pre-University Employee. If I remember correctly, we had Java 1.1.8 installed at that time, but were moving to Java 1.2 (“Java 2”), which was a massive release—I remember engineers at the time grumbling that the ever-present “ Java in a Nutshell ” book had grown to over 600 pages. I thought I’d take a look back at 26 years of Java releases and rate some of the language and core library changes (Java SE only) that have occurred over this time. It’s a very different language to what I started out with! I can’t possibly cover every feature of those releases , as there are just way too many. So I’m just going to cherry-pick some that seemed significant at the time, or have been in retrospect. I’m not going to cover UI- or graphics-related stuff (Swing, Java2D etc), or VM/GC improvements. Just language changes and core libraries. And obviously this is highly subjective. Feel free to put your own opinions in the comments! The descriptions are brief and not intended as an introduction to the features in question: see the links from the Wikipedia page for more background. NB: later features are listed from when they were first introduced as a preview. The Collections Framework : before the collections framework, there was just raw arrays, Vector, and Hashtable. It gets the job done, but I don’t think anyone thinks the Java collections framework is particularly well designed. One of the biggest issues was a failure to distinguish between mutable and immutable collections, strange inconsistencies like why Iterator as a remove() method (but not, say, update or insert), and so on. Various improvements have been made over the years, and I do still use it in preference to pulling in a better alternative library, so it has shown the test of time in that respect. 4/10 The keyword: I remember being somewhat outraged at the time that they could introduce a new keyword! I’m personally quite fond of asserts as an easy way to check invariants without having to do complex refactoring to make things unit-testable, but that is not a popular approach. I can’t remember the last time I saw an assert in any production Java code. 3/10 Regular expressions: Did I really have to wait 3 years to use regex in Java? I don’t remember ever having any issues with the implementation they finally went for. The Matcher class is perhaps a little clunky, but gets the job done. Good, solid, essential functionality. 9/10 “New” I/O (NIO): Provided non-blocking I/O for the first time, but really just a horrible API (still inexplicably using 32-bit signed integers for file sizes, limiting files to 2GB, confusing interface). I still basically never use these interfaces except when I really need to. I learnt Tcl/Tk at the same time that I learnt Java, and Java’s I/O always just seemed extraordinarily baroque for no good reason. Has barely improved in 2 and a half decades. 0/10 Also notable in this release was the new crypto APIs : the Java Cryptography Extensions (JCE) added encryption and MAC support to the existing signatures and hashes, and we got JSSE for SSL. Useful functionality, dr eadful error-prone APIs . 1/10 Absolutely loads of changes in this release. This feels like the start of modern Java to me. Generics : as Go discovered on its attempt to speed-run Java’s mistakes all over again, if you don’t add generics from the start then you’ll have to retrofit them later, badly. I wouldn’t want to live without them, and the rapid and universal adoption of them shows what a success they’ve been. They certainly have complicated the language, and there are plenty of rough edges (type erasure, reflection, etc), but God I wouldn’t want to live without them. 8/10 . Annotations: sometimes useful, sometimes overused. I know I’ve been guilty of abusing them in the past. At the time it felt like they were ushering a new age of custom static analysis, but that doesn’t really seem to be used much. Mostly just used to mark things as deprecated or when overriding a method. Meh. 5/10 Autoboxing: there was a time when, if you wanted to store an integer in a collection, you had to manually convert to and from the primitive int type and the Integer “boxed” class. Such conversion code was everywhere. Java 5 got rid of that, by getting the compiler to insert those conversions for you. Brevity, but no less inefficient. 7/10 Enums : I’d learned Haskell by this point, so I couldn’t see the point of introducing enums without going the whole hog and doing algebraic datatypes and pattern-matching. (Especially as Scala launched about this time). Decent feature, and a good implementation, but underwhelming. 6/10 Vararg methods: these have done quite a lot to reduce verbosity across the standard library. A nice small improvement that’s had a good quality of life enhancement. I still never really know when to put @SafeVarargs annotations on things though. 8/10 The for-each loop: cracking, use it all the time. Still not a patch on Tcl’s foreach (which can loop over multiple collections at once), but still very good. Could be improved and has been somewhat replaced by Streams. 8/10 Static imports: Again, a good simple change. I probably would have avoided adding * imports for statics, but it’s quite nice for DSLs. 8/10 Doug Lea’s java.util.concurrent etc : these felt really well designed. So well designed that everyone started using them in preference to the core collection classes, and they ended up back-porting a lot of the methods. 10/10 After the big bang of Java 5, Java 6 was mostly performance and VM improvements, I believe, so we had to wait until 2011 for more new language features. Strings in switch: seems like a code smell to me. Never use this, and never see it used. 1/10 Try-with-resources : made a huge difference in exception safety. Combined with the improvements in exception chaining (so root cause exceptions are not lost), this was a massive win. Still use it everywhere. 10/10 Diamond operator for type parameter inference: a good minor syntactic improvement to cut down the visual noise. 6/10 Binary literals and underscores in literals: again, minor syntactic sugar. Nice to have, rarely something I care about much. 4/10 Path and Filesystem APIs: I tend to use these over the older File APIs, but just because it feels like I should. I couldn’t really tell you if they are better or not. Still overly verbose. Still insanely hard to set file permissions in a cross-platform way. 3/10 Lambdas: somewhat controversial at the time. I was very in favour of them, but only use them sparingly these days, due to ugly stack traces and other drawbacks. Named method references provide most of the benefit without being anonymous. Deciding to exclude checked exceptions from the various standard functional interfaces was understandable, but also regularly a royal PITA. 4/10 Streams: Ah, streams. So much potential, but so frustrating in practice. I was hoping that Java would just do the obvious thing and put filter/map/reduce methods onto Collection and Map, but they went with this instead. The benefits of functional programming weren’t enough to carry the feature, I think, so they had to justify it by promising easy parallel computing. This scope creep enormously over-complicated the feature, makes it hard to debug issues, and yet I almost never see parallel streams being used. What I do still see quite regularly is resource leaks from people not realising that the stream returned from Files.lines() has to be close()d when you’re done—but doing so makes the code a lot uglier. Combine that with ugly hacks around callbacks that throw checked exceptions, the non-discoverable API (where are the static helper functions I need for this method again?), and the large impact on lots of very common code, and I have to say I think this was one of the largest blunders in modern Java. I blogged what I thought was a better approach 2 years earlier, and I still think it would have been better. There was plenty of good research that different approaches were better , since at least Oleg Kiselyov’s work in the early noughties . 1/10 Java Time: Much better than what came before, but I have barely had to use much of this API at all, so I’m not in a position to really judge how good this is. Despite knowing how complex time and dates are, I do have a nagging suspicion that surely it doesn’t all need to be this complex? 8/10 Modules: I still don’t really know what the point of all this was. Enormous upheaval for minimal concrete benefit that I can discern. The general advice seems to be that modules are (should be) an internal detail of the JRE and best ignored in application code (apart from when they spuriously break things). Awful. -10/10 (that’s minus 10!) jshell: cute! A REPL! Use it sometimes. Took them long enough. 6/10 The start of time-based releases, and a distinct ramp-up of features from here on, trying to keep up with the kids. Local type inference (“var”) : Some love this, some hate it. I’m definitely in the former camp. 9/10 New HTTP Client : replaced the old URL.openStream() approach by creating something more like Apache HttpClient. It works for most purposes, but I do find the interface overly verbose. 6/10 This release also added TLS 1.3 support, along with djb-suite crypto algorithms. Yay. 9/10 Switch expressions : another nice mild quality-of-life improvement. Not world changing, but occasionally nice to have. 6/10 Text blocks: on the face of it, what’s not to like about multi-line strings? Well, apparently there’s a good reason that injection attacks remain high on the OWASP Top 10, as the JEP introducing this feature seemed intent on getting everyone writing SQL, HTML and JavaScript using string concatenation again. Nearly gave me a heart attack at the time, and still seems like a pointless feature. Text templates (later) are trying to fix this, but seem to be currently in limbo . 3/10 Pattern matching in : a little bit of syntactic sugar to avoid an explicit cast. But didn’t we all agree that using was a bad idea decades ago? I’m really not sure who was doing the cost/benefit analysis on these kinds of features. 4/10 Records: about bloody time! Love ‘em. 10/10 Better error messages for NullPointerExceptions: lovely. 8/10 Sealed classes: in principal I like these a lot. We’re slowly getting towards a weird implementation of algebraic datatypes. I haven’t used them very much yet so far. 8/10 EdDSA signatures: again, a nice little improvement in the built-in cryptography. Came with a rather serious bug though… 8/10 Vector (SIMD) API: this will be great when it is finally done, but still baking several years later. ?/10 Pattern matching switch: another piece of the algebraic datatype puzzle. Seems somehow more acceptable than instanceof, despite being largely the same idea in a better form. 7/10 UTF-8 by default: Fixed a thousand encoding errors in one fell swoop. 10/10 Record patterns: an obvious extension, and I think we’re now pretty much there with ADTs? 9/10 Virtual threads: being someone who never really got on with async/callback/promise/reactive stream-based programming in Java, I was really happy to see this feature. I haven’t really had much reason to use them in anger yet, so I don’t know how well they’ve been done. But I’m hopeful! ?/10 String templates: these are exactly what I asked for in A few programming language features I’d like to see , based on E’s quasi-literal syntax, and they fix the issues I had with text blocks. Unfortunately, the first design had some issues, and so they’ve gone back to the drawing board. Hopefully not for too long. I really wish they’d not released text blocks without this feature. 10/10 (if they ever arrive). Sequenced collections: a simple addition that adds a common super-type to all collections that have a defined “encounter order”: lists, deques, sorted sets, etc. It defines convenient getFirst() and getLast() methods and a way to iterate items in the defined order or in reverse order. This is a nice unification, and plugs what seems like an obvious gap in the collections types, if perhaps not the most pressing issue? 6/10 Wildcards in patterns: adds the familiar syntax from Haskell and Prolog etc of using as a non-capturing wildcard variable in patterns when you don’t care about the value of that part. 6/10 Simplified console applications: Java finally makes simple programs simple for beginners, about a decade after universities stopped teaching Java to beginners… Snark aside, this is a welcome simplification. 8/10 This release also adds support for KEMs , although in the simplest possible form only. Meh. 4/10 The only significant change in this release is the ability to have statements before a call to super() in a constructor. Fine. 5/10 Primitive types in patterns: plugs a gap in pattern matching. 7/10 Markdown javadoc comments: Does anyone really care about this? 1/10 The main feature here from my point of view as a crypto geek is the addition of post-quantum cryptography in the form of the newly standardised ML-KEM and ML-DSA algorithms, and support in TLS. Stable values: this is essentially support for lazily-initialised final variables. Lazy initialisation is often trickier than it should be in Java, so this is a welcome addition. Remembering Alice ML , I wonder if there is some overlap between the proposed StableValue and a Future? 7/10 ? PEM encoding of cryptographic objects is welcome from my point of view, but someone will need to tell me why this is not just ? Decoding support is useful though, as that’s a frequent reason I have to grab Bouncy Castle still. 7/10 Well, that brings us pretty much up to date. What do you think? Agree, disagree? Are you a passionate defender of streams or Java modules? Have at it in the comments.

0 views
iDiallo 1 months ago

Why AI should be a form, not a conversation

Maybe it was a decade ago when it seemed like every single game developer started believing that all games should be open world. Infinite possibilities, player freedom, emergent storytelling, who wouldn't want that? But open world design introduced new problems that linear games never faced. Players would get lost in meaningless side quests while the main story waited forgotten. Developers would pile on features that didn't serve the core experience. Worst of all, players often couldn't tell when they'd actually "won." I sank an unhealthy amount of time into Metal Gear Solid V when it came out, wandering its open world long after I'd completed every meaningful objective, never quite sure if I was done or just... tired. The problem with open world design is scope creep disguised as feature richness. When everything is possible, nothing feels special. Today, with this new wave of AI, we're making the exact same mistake. We're taking the "open world" approach to problem-solving, and the results are just as messy. When the chatbot craze was at its peak, I worked for a startup where we automated customer service. Many of our competitors were building what I now recognize as "open world chatbots". AI systems with infinite conversational possibilities. These chatbots would greet you, ask about your day, make jokes, and try to make you forget that you had a very specific reason for starting the session. Mind you, this was before ChatGPT, when LLMs weren't widely available. Each competitor's demo showcased the chatbot's vast capabilities through highly choreographed scenarios. Like game developers showing off sprawling landscapes and complex skill trees, they were proud of the sheer scope of what their AI could theoretically handle. The problem was identical to open world games: the moment real users engaged with these systems, everything collapsed. Customers, like players, are unpredictable. They don't follow the golden path you've designed. Our approach was fundamentally different. We built what I'd call a "linear gameplay" AI. Our chatbot didn't try to converse with you. It didn't greet you, didn't chat about the weather, didn't pretend to be your friend. It appeared as a form or email address, a clear single point of entry with one mission: solve your customer service problem. When you sent your fully formed message, our application took over with purposeful, sequential steps. It read the message, classified it, analyzed sentiment, then moved through our integrations (Zendesk, Shopify, Magento, USPS) like a player progressing through carefully designed levels. When it retrieved your tracking information or order details, it crafted a response that directly answered your questions. If at any point it couldn't complete the mission, it executed a clean handoff to a human agent who received a comprehensive summary. Like a save file with all the relevant progress data. This agent was designed for one specific quest: resolve customer service problems . Nothing else. Just like how the best linear games focus relentlessly on their core mechanics rather than trying to be everything to everyone. A lot of our "open world" competitors have either pivoted or gone out of business since then. Which makes it even more surprising to watch Taco Bell's drive-thru AI failing in exactly the same way. Just a few weeks back, Taco Bell went through a PR nightmare when a customer ordered 18,000 cups of water, and the system dutifully added every single cup to the order. This wasn't malicious hacking; this was a classic open world design failure. The system was built to handle "anything," so it handled everything, including completely absurd requests that broke the entire experience. Taco Bell uses a service called Omilia to power their AI drive-thru. On their website, they explicitly describe their open world approach: Taco Bell recognized the importance of providing a seamless, human-like experience for customers, so Omilia's Voice AI Solution was meticulously tuned to provide clear, accurate responses, delivering a conversational experience that mirrors human interaction and improves the customer's ordering experience. Notice the language: "human-like experience," "conversational," "mirrors human interaction." They built an open world when they needed linear gameplay. The conversational nature invites exactly the kind of scope creep that breaks systems. Regular customers report their perfectly normal orders failing spectacularly, with the AI getting stuck in loops, asking "Would you like a drink with that?" even after drinks were clearly specified. I couldn't find a Taco Bell with AI drive-thru in my neighborhood, but I did find Rally's. They use a different company called HI Auto , and to my surprise, it worked flawlessly. The experience felt like a well-designed level progression: clear prompts, sequential steps, defined objectives. "What would you like to drink?" "What size?" Next level unlocked. It wasn't a conversation, it was a voice-powered form. No philosophical debates, no jokes, no attempts at charm. Just a transaction with guided rails and clear success criteria. The software knew exactly what it was supposed to accomplish, and users knew exactly what was expected of them. This is the difference between open world chaos and linear focus. A drive-thru isn't a space for exploration and emergent dialogue. It's a hyper-linear experience where success is defined as "correct order, minimal time." Any system that invites deviation from this core mission is poorly designed for its context. You could theoretically plug ChatGPT into a drive-thru experience, just like you could theoretically add an open world to any game. But you'd be guaranteed to receive unexpected inputs that break the core experience. Instead, treat AI applications like carefully designed HTML forms with proper validation, clear fields, and predictable outcomes. The goal is always the solution (a correct order, a resolved support ticket, a completed task), not the conversational medium. In fact, I think voice itself is not the most optimal for these linear experiences. The most efficient drive-thru model might be a QR code at the menu board that lets you complete the entire "form" on your phone before reaching the speaker. Choose linear when story and progression matter, open world when exploration serves the core purpose. The best AI applications should match their interaction model to their mission. Maybe OpenAI's goal is to build an AI. But your drive thru AI is perfectly fine being specialized. Start building AI systems that do one thing exceptionally well, with clear boundaries, defined success criteria, and linear paths to completion. Your users will thank you, and you won't end up in viral videos featuring 18,000 cups of water.

0 views
(think) 1 months ago

Wayward Predicates in Ruby

Recently I’ve been wondering how to name Ruby methods that have predicate looking names (e.g. ), but don’t behave like predicates - namely they return other values besides the canonical and . 1 Here are a few classic examples: Naming is hard and it took me a while to narrow my list of ideas to two options: In the end I felt that “wayward predicates” was the way to go, as it sounds less deviant. 2 Common vocabulary is important when discussing domain-specific topics and it makes it easier for people to refer to things within the domains. That’s why I’m writing this short article - perhaps it will be a first step to establishing a common name for a pretty common thing. I’ve always been a believer that naming should be both informative and fun, and that’s certainly one fun name. Wayward predicates can surprise us unpleasantly every now and then, but they are also a classic example of the flexibility of Ruby and its continued resistance to sticking to narrowly defined rules and guidelines. That’s all I have for you today. Keep hacking! This came up in the context of RuboCop, in case someone’s wondering.  ↩ See also https://docs.rubocop.org/rubocop/cops_naming.html#namingpredicatemethod for the first “official” usage of the term.  ↩ wayward predicates deviant predicates This came up in the context of RuboCop, in case someone’s wondering.  ↩ See also https://docs.rubocop.org/rubocop/cops_naming.html#namingpredicatemethod for the first “official” usage of the term.  ↩

0 views
matklad 1 months ago

Links

If you have a blog, consider adding a “links” page to it, which references resources that you find notable: https://matklad.github.io/links.html I’ve started my links page several years ago, mostly because I found myself referring to the same few links repeatedly in various discussions, and not all the links were easily searchable. Note that the suggestion is different from more typical “monthly links roundup”, which is nice to maintain Substack engagement/community, but doesn’t contribute to long-term knowledge distilling. It is also different from the exhaustive list of everything I’ve read on the Internet. It is relatively short, considering its age.

0 views
matklad 1 months ago

Retry Loop Retry

Some time ago I lamented that I don’t know how to write a retry loop such that: https://matklad.github.io/2023/12/21/retry-loop.html To recap, we have and we need to write I’ve received many suggestions, and the best one was from https://www.joachimschipper.nl , though it was somewhat specific to Python: A couple of days ago I learned to think better about the problem. You see, the first requirement, that the number of retries is bounded syntactically, was leading me down the wrong path. If we start with that requirement, we get code shape like: The salient point here is that, no matter what we do, we need to get or out as a result, so we’ll have to call at least once. But could be zero. Looking at the static semantics, any non loop’s body can be skipped completely, so we’ll have to have some runtime asserts explaining to the compiler that we really did run at least once. The part of the loop which is guaranteed to be executed at least once is a condition. So it’s more fruitful to flip this around: it’s not that we are looping until we are out of attempts, but, rather, we are looping while the underlying action returns an error, and then retries are an extra condition to exit the loop early: This shape of the loop also works if the condition for retries is not attempts based, but, say, time based. Sadly, this throws “loop is obviously bounded” requirement out of the window. But it can be restored by adding upper bound to the infinite loop: I still don’t like it (if you forget that , you’ll get a panic!), but that’s where I am at!

0 views
Xe Iaso 1 months ago

My responses to The Register

Today my quotes about generative AI scrapers got published in The Register . For transparency's sake, here's a copy of the questions I was asked and my raw, unedited responses. Enjoy! First, do you see the growth in crawler traffic slowing any time soon? I can only see a few things that can stop this: government regulation, or the hype finally starting to die down. There is too much hype in the mix that causes us to funnel billions of dollars into this technology instead of curing cancer, solving world hunger, or making people’s lives genuinely better. Is it likely to continue growing? I see no reason why it would not grow. People are using these tools to replace knowledge and gaining skills instead of augmenting knowledge and augmenting skills. Even if they are intended to be used for letting us focus on the fun parts of our work and automating away the chores, there are some bad apples that are spoiling the bunch and making this technology about replacing people, not drudgery and toil. This technology was obviously meant well, but at some level the output of AI superficially resembles the finished work product of human labour, superficially. As someone asked to Charles Babbage: if you put in the wrong numbers, you get the wrong answer. This isn’t necessarily a bubble popping, this is a limitation of how well AI can function without direct and constant human input. Even so, we’ll hit the limit on data that can be scraped that hasn’t been touched by AI before the venture capital runs out. I see no value in the need for scrapers to hit the same 15 year old commit of the Linux kernel over and over and over every 30 minutes like they are now. There are ways to do this ethically that don’t penalize open source infrastructure such as using the Common Crawl dataset. If so, how can that be sustainable? It's not lol. We are destroying the commons in order to get hypothetical gains. The last big AI breakthrough happened with GPT-4 in 2023. The rest has been incremental improvements in tokenization, multimodal inputs (also tokenization), tool calling (also tokenization), and fill-in-the-middle completion (again, also tokenization). Even with scrapers burning everything in their wake, there is not enough training data to create another exponential breakthrough. All we can do now is make it more efficient to run GPT-4 level models on lesser hardware. I can (and regularly do) run a model just as good as GPT-4 on my MacBook at this point, which is really cool. Would broader deployment of Anubis and other active countermeasures help? This is a regulatory issue. The thing that needs to happen is that governments need to step in and give these unethical scrapers that are destroying the digital common good existentially threatening fines and make them pay reparations to the communities they are harming. Ironically enough, most of these unethical scraping activities rely on the products of the communities they are destroying. This presents the kind of paradox that I would expect to read in a Neal Stephenson book from the '90s, not CBC's front page. Anubis helps mitigate a lot of the badness by making attacks more computationally expensive. Anubis (even in configurations that omit proof of work) makes attackers have to retool their scraping to use headless browsers instead of blindly scraping HTML. This increases the infrastructure costs of the scrapers propagating this abusive traffic. The hope is that this makes it fiscally unviable for the unethical scrapers to scrape by making them have to dedicate much more hardware to the problem. In essence: it makes the scrapers have to spend more money to do the same work. Is regulation required to prevent abuse of the open web? Yes, but this regulation would have to be global, simultaneous, and permanent to have any chance of this actually having a positive impact. Our society cannot currently regulate against similar existential threats like climate change. I have no hope for such regulation to be made regarding generative AI. Fastly's claims that 80% of bot traffic is now AI crawlers In some cases for open source projects, we've seen upwards of 95% of traffic being AI crawlers. Not just bot traffic, but traffic in general . For one, deploying Anubis almost instantly caused server load to crater by so much that it made them think they accidentally took their site offline. One of my customers had their power bills drop by a significant fraction after deploying Anubis. It's nuts. The ecological impact of these scrapers is probably a significant fraction of the ecological impact of generative AI as a whole. Personally, deploying Anubis to my blog has reduced the amount of ad impressions I've been giving by over 50%. I suspect that there is a lot of unreported click fraud for online advertising. I hope this helps. Keep up the good fight!

0 views