#58 The occasional toast can’t hurt

Some of the designers and frontend devs I’ve worked with may remember my rants against toast notifications: “Why do I need a success message for every action? I expect things to work. Only tell me when something breaks.” But… I’ll admit it: Sometimes toasts do have their merit. Two examples from poketto.me: 🔹 Copy to clipboard (Podcast feed URL): When users click the “copy” button, the action happens instantly. But without any feedback, it feels… awkward. ...

August 27, 2025

#57 Multi-threaded TTS: A bad idea

Running text-to-speech in the cloud is fun—until it isn’t. Early on, I didn’t think much about thread safety. During my own testing, rarely would more than one TTS task be running in parallel, so there were no big issues. But once more users started using the feature, strange bugs popped up: Errors like “Assertion srcIndex < srcSelectDimSize failed” started showing up in the logs—and worse, once triggered, the entire Cloud Run instance would become unusable until a redeploy. ...

August 26, 2025

#52 Object.assign(...) in JavaScript helps with asynchronous updates

In WebSockets: More than tech vanity, I talked about the implicit user value of asynchronous UIs. Today, here’s a neat little JavaScript trick I hadn’t been aware of that makes implementing them a bit easier: Object.assign(...). Here’s the situation: Your Angular frontend initially fetches a list of items from the backend. Thanks to Angular’s powerful data binding, many components reference properties of these objects. Later, the backend sends an updated version of one of these items via WebSocket. What now? ...

August 21, 2025

#51 WebSockets: More than tech vanity

As I said in Socket science isn’t rocket science, WebSockets are quite easy to use with Python + Flask + Socket.IO on the backend and Angular + Socket.IO + RxJS on the frontend. But why bother? Dealing with asynchronous requests is more of a hassle than not. And for a “boring” app like poketto.me, aren’t synchronous HTTP requests/responses good enough? Here are two use cases where, despite the app’s apparent simplicity, asynchronous backend/frontend communication adds real value: ...

August 20, 2025

#50 Prompt engineering: A task best left to the machines

Under the hood, poketto.me makes heavy use of LLMs. The podcast feature is a great example: Users can turn any web content into a podcast, but often that content isn’t well-suited for listening. LLMs are great at optimizing this—simplifying complex sentences, turning headlines into enumerations, describing images verbally, etc. But the challenge: How do you craft a single, generic prompt that works across all types of content and runs unsupervised via the API? ...

August 19, 2025

#47 Extracting Favicons: There’s No Bulletproof Way

A favicon is that tiny icon you see next to a site name in your browser tab or bookmarks bar. It's one of those small UX elements that quietly plays a big role in how we recognize and visually differentiate websites. In poketto.me, I wanted to bring favicons into play for a couple of UI elements—particularly when managing your saved news sources. Seeing a little logo beside each source makes skimming, scanning, and organizing much more intuitive than reading domain names alone. ...

August 16, 2025

#46 GCS Caching Can Be a Pain in the Neck

I love GCS (Google Cloud Storage). It’s a simple, robust, and powerful solution for storing files online and accessing them either programmatically or via HTTP. Storage is dirt cheap—especially if you don’t need global replication or sophisticated backups. And you can even turn a GCS bucket into an HTTPS-secured, internet-facing web server for static websites. https://poketto.me, for example, runs on that architecture. Another good use case: the #podcast feature in poketto.me. Naturally, the generated MP3 files need to live somewhere, and storing them in a database or serving them through my Python web server would be… silly. So I push the generated files to a GCS bucket, and all is well: HTTPS-secured, fast, and compatible with any podcast client in the world. ...

August 15, 2025

#44 The ABCD rule doesn’t cut it anymore

There was a time when the golden rule of consumer app development was as simple as ABCD: Always Be Collecting Data. The strategy? 1️⃣ Grow your user base as fast as possible. 2️⃣ Track every interaction, every event, every click. 3️⃣ Figure out how to monetize the data — usually through targeted advertising, if you couldn’t think of anything more creative. But that game is changing. Consumers are more privacy-aware than ever. Regulators — especially in the EU, California, Japan, and a few other regions — have stepped in. And both founders and investors are realizing that data-harvesting at scale is not a sustainable or ethical business model. ...

August 13, 2025

#43 You Don’t Need an nl2br Pipe

This one’s a pretty common hassle: You’ve got text with line breaks (encoded as \n or sometimes \r\n — thanks, Microsoft!), but when rendering that text on a webpage, those line breaks are nowhere to be seen. Naturally, browsers collapse ‘source’ line breaks and ignore them. The usual reaction? Reach for an nl2br utility — like nl2br-pipe—to manually replace \n with <br /> tags. It’s a well-known workaround in web dev. ...

August 12, 2025

#40 Parsing and Serializing XML Is (Still) a Pain in the Neck

It’s 2025, and I still can’t believe I have to say this — but handling XML, especially in Python, remains frustratingly painful. Take this example: For the upcoming podcast feature of poketto.me, the app will generate a personalized podcast feed for each user, populated with text-to-speech versions of their saved content. Users can subscribe to their custom feed in any podcast app—pretty handy. The feed itself isn’t complex: just an XML file hosted on a web server (in my case, a GCS bucket) containing metadata and links to episode MP3s. It just needs to comply with Apple’s Podcast RSS Feed Requirements so podcast clients can parse it correctly. Sounds simple, right? ...

August 9, 2025