#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

#39 Consistency Beats Accuracy (Part 2)

The bigger your codebase grows, the more important it becomes to stay consistent — in naming things and in how you use features of your programming language. Take input parameters and variables, for example. Is the ID of a Save object sometimes named save_id, other times saveId, and occasionally just id? Is the function to load it called load_save(...), get_save(...), or fetch_save(...)? Or maybe it’s load_save(...) for saves, but get_settings(...) and fetch_tag(...) elsewhere? If so, confusion is only a matter of time. ...

August 8, 2025

#37 Google Requires a New versionCode for Every App Bundle You Publish

When publishing a new version of your Android app through the Google Play Console, you must increment the versionCode in your app’s build.gradle file. This is required even if the versionName (the human-readable version string) stays the same. I found this a bit confusing at first — but the rule is simple: Before you build the bundle you upload to Google Play, make sure you’ve updated the versionCode. If you don’t, your upload will be rejected immediately. ...

August 6, 2025

#33 Mind the Gap — flex-gap!

I use flex-based layouts a lot in the #Angular frontend of poketto.me. But I never realized there’s a neat little property called gap (or flex-gap) that lets you define spacing between flex items directly. For the longest time, I worked around this with clumsy constructs where I’d set margins on child elements — and then unset them on the last child: .container { display: flex; flex-direction: column; .child { margin-bottom: 1rem; &:last-child { margin-bottom: unset; } } } ``` When really, the world could be so much simpler: ``` .container { display: flex; flex-direction: column; gap: 1rem; } ``` No more margin hacks.

August 2, 2025