# katpadi.log > a repository for my caffeinated thoughts Public Ghost content for AI and LLM tooling. This file includes a bounded export of public pages first, then recent public posts. Append `.md` to any post or page URL to get the content in Markdown (for example, `/example-post.md`). ## Pages ### about URL: https://blog.katpadi.ph/about/ Last updated: 2026-06-10T04:52:47.000Z Welcome to my little corner of the internet! This is a repository for my caffeinated thoughts—no particular niche. Just my thoughts in general, though they will probably be mostly about dev (mis)adventures and life musings. Reach out to me at [hello \[at\] katpadi.ph](mailto:hello@katpadi.ph) ### projs URL: https://blog.katpadi.ph/projs/ Last updated: 2026-07-22T07:12:52.000Z - [**katpadi.log**](https://github.com/katpadi/katpadi.log?ref=blog.katpadi.ph) \- this blog's theme: a minimalist terminal-style Ghost theme with Nord/Obsidian dark and warm paper light modes. - [**compromise-app**](https://compromise-app.katpadi.ph/?ref=blog.katpadi.ph) \- i originally called this MeetHalfway back in 2018 but it's cheesy so i renamed it and idk if that makes sense at all. it's a simple app that finds the halfway point between two places and looks for potential meeting spots within that midpoint's radius. i recently gave it an overhaul with some help from Claude. **NOTE:* i recently used Claude to migrate to TailwindCSS from Boostrap3 and migrated old/deprecated Google Maaps APIs to make it work again. it's very useful.* - [**sungka**](https://sungka.katpadi.ph/?ref=blog.katpadi.ph) \- this is a popular Filipino "count and capture" game. i built it using Claude. my goal was just to poke around and check how good it is. i vibe-coded this to life in less than 30 minutes, i think. ok now Claude, i see you. - [**(daily) motivation**](https://motivation.katpadi.ph/?ref=blog.katpadi.ph) \- Just me playing around with the Anthropic API. Every day it generates a motivational quote tied to something that happened in history on that date. - [**rizzume**](https://rizzume.katpadi.ph/?ref=blog.katpadi.ph) a local-first, ATS-friendly résumé builder built on JSON Resume. It lets you create structured résumés, instantly switch templates, preview changes in real time and export to PDF. ## Posts ### Recall vs. Recognition: When Your Brain Needs a Hint URL: https://blog.katpadi.ph/recall-vs-recognition-when-your-brain-needs-a-hint/ Last updated: 2026-08-24T08:37:42.000Z I like answering quiz shows. Or at least I used to think I was pretty good at them. Lately, I've noticed something mildly annoying. A question comes up, I know I know the answer, but I can't produce the word. I sit there staring at the TV, trying to pull it out of nowhere. Then the multiple choice options appear. Oh. Obviously C. That started bothering me more than it probably should have. Is my memory getting worse? Am I just slower now? Is this one of those things you suddenly notice in your late 30s and then spend an evening Googling? So I went down the rabbit hole (as usual). ## Why multiple choice feels easier The thing I learned is that “remembering” isn't really one operation. There's recall, where you have to produce the answer yourself. And there's recognition, where the answer is already in front of you and you have to identify it. Those sound similar, but they put very different demands on memory. If someone asks me, “What's the name of the boundary around a black hole beyond which nothing can escape?”, I might know exactly what they're describing and still fail to retrieve the term. But put “event horizon” among four choices and suddenly it's obvious. The problem has changed. I'm no longer trying to produce the answer from scratch. I'm recognizing it. My brain isn't literally a database, but the metaphor is useful: free recall is like having to write the search query yourself. Recognition is more like being handed a page of results and spotting the one you wanted. That explains the very specific feeling of: I know this. I cannot tell you what it is. Ah, yes. That one. The information wasn't necessarily gone. I just couldn't retrieve it from the cue I had. That distinction made me feel less alarmed by the whole thing. There's even a name for one version of this: the tip-of-the-tongue state. You know you know a word, sometimes you can remember its first letter or how many syllables it has, but the actual word refuses to arrive. Then someone says it and your brain immediately goes, yes, obviously. Annoying, but very different from never knowing it in the first place. ## Retrieval gets noisy Of course, this doesn't mean every memory problem is “just retrieval.” But it gave me a better mental model for some of the small failures I was noticing. There are also a bunch of things that make retrieval harder. Sleep is one. Stress is another. Mental fatigue probably explains more of my evening quiz performance than I'd like to admit. After spending hours reading specs, attending meetings, keeping track of code, edge cases, browser tabs, Slack messages and whatever else is competing for attention, maybe “name this actor from a movie I watched in 2007” just isn't getting top priority. I originally thought of this as a “full hard drive” problem. I've accumulated more information, so naturally searches take longer. It reminded me of [Inside Out](https://www.centreforbrainhealth.ca/news/neuroscience-in-film-inside-out-with-dr-daniela-palombo?ref=blog.katpadi.ph), where long-term memory is basically an enormous archive of glowing memory balls. Workers wander around deciding which old memories are no longer useful and dump them. It's a great visual, but probably not a great model of how memory actually works. Brains probably don't simply fill up and start deleting old files to make room. The problem seems to be more complicated than storage capacity. Sometimes a memory may still be there, but the cue I have isn't enough to retrieve it. Other memories and associations can compete with it, making the thing I'm looking for harder to pull out. Names seem especially good at disappearing this way. For instance, I love *Breaking Bad*. I remember the characters, the scenes, and all sorts of random details. But recently I was trying to remember the guy who's in a wheelchair and rings the bell. I could picture him perfectly. Couldn't remember the name. So I Googled it. Of course. It's freaking [Hector Salamanca](https://breakingbad.fandom.com/wiki/Hector%5FSalamanca?ref=blog.katpadi.ph). I remembered everything around the name. I just couldn't pull out the name itself. Ugh. ## Maybe don't Google it immediately One thing I've started doing is waiting a little longer when something is on the tip of my tongue. My old habit was: Can't remember. Google. Done. Which is efficient, but maybe not ideal if the goal is to get better at retrieval. Trying to retrieve something is itself useful practice. So now, if I know the answer is somewhere in there, I give it a little time before reaching for the external brain. Sometimes it comes back. Sometimes I eventually Google it anyway. I'm not turning trivia into a training regimen! ## The part I found reassuring The main thing I got from all of this wasn't "my memory is fine." I can't really conclude that from a few quiz-show questions, and noticeable changes in memory that start affecting everyday life are a different thing entirely. I also started noticing that on days when I'd slept well, recall seemed easier. Words and names came to me faster. When I was tired, I had more of those annoying “I know this” moments. Maybe that's part of why this started feeling worse in the first place. What changed most, though, was my definition of forgetting. I used to think there were basically two states: 1. I remember it. 2. I forgot it. Now I think there's a surprisingly large space in between. Sometimes the information is there. I just can't find the key that gets me to it. Maybe forgetting isn't always a storage problem. Sometimes it's a lookup problem. ### Rizzumé: A Local-First Résumé Builder on JSON Resume URL: https://blog.katpadi.ph/rizzume-a-local-first-resume-builder-on-json-resume/ Last updated: 2026-07-22T07:15:34.000Z ## **Why** I built it Every few years I update my résumé, and every few years I run into the same problems. Most résumé builders require an account, store my data somewhere and eventually export a PDF. If I want to move to another tool, I'm usually back to copying and pasting everything. I also tried paying for Standard Resume. Unfortunately, I hit a billing bug where I was charged three times but never received Pro access. To their credit, they eventually refunded everything, although it took a while through customer support. It wasn't the reason I built Rizzumé, but it did reinforce that I wanted a tool I completely owned. To me, a résumé is structured data. The PDF is just one representation of it. I should be able to keep the source, move it between tools and generate whatever output I need. Rizzumé is my attempt at building that. ## What it does - Local-first - Built on JSON Resume - Printable templates - Browser-based PDF export - Live preview - Multiple résumé versions - Import and export - Deterministic résumé checks - No accounts - No backend ## Why JSON Resume? I didn't want another proprietary format. Everything in Rizzumé is stored as a standard JSON Resume document, which means your data remains portable. The application only adds a small `meta.rizzume` section for UI preferences like templates and résumé versions. **The goal is simple: if Rizzumé disappeared tomorrow, your résumé would still be usable.** ## Local-first Once I chose JSON Resume, I realised I didn't need a backend. Everything lives in `localStorage` through a Zustand store. There are no accounts to create, no authentication flow and no server storing your résumé. Your data never leaves your browser. ## Templates Rizzumé currently includes 14 templates. Each template is just a React component reading the same underlying JSON Resume data, so switching templates doesn't transform your résumé or duplicate data. Pick a different template and the data stays exactly the same. ## Resume checks Instead of inventing an "ATS score", Rizzumé performs deterministic checks that can actually be explained. Some examples: - Missing contact information - Empty sections - Long bullet points - Passive or first-person writing - Missing quantified achievements Every result links back to where it can be fixed. ## Multiple résumé versions Rather than maintaining separate files, Rizzumé lets you keep multiple versions together. One master résumé. Several tailored versions. ## One backup file. It makes applying for different roles much easier without duplicating everything. ## Import and export Export works in two ways. - Standard JSON Resume - Complete Rizzumé backup Import detects the difference automatically, validates the file and restores the appropriate data. That means you can move between devices without creating an account. ## Welp? Building Rizzumé reminded me how enjoyable small projects can be. It started because I wanted a better résumé builder for myself. Along the way it became an excuse to explore local-first architecture, open standards and a few ideas around making résumé editing simpler. 🌐 **Live Demo:** [https://rizzume.katpadi.ph](https://rizzume.katpadi.ph/?ref=blog.katpadi.ph) 💻 **GitHub:** [https://github.com/katpadi/rizzume](https://github.com/katpadi/rizzume?ref=blog.katpadi.ph) 📄 **JSON Resume:** [https://jsonresume.org](https://jsonresume.org/?ref=blog.katpadi.ph) ### AI Hasn't Changed My Management Philosophy URL: https://blog.katpadi.ph/ai-hasnt-changed-my-management-philosophy/ Last updated: 2026-07-16T13:05:07.000Z Every few weeks, I come across another post predicting how AI will redefine engineering management. Some argue managers need to become AI experts. Others believe they'll spend less time with people and more time orchestrating agents. I think AI has changed a lot about how we work. But it hasn't changed how I lead. If I had to summarize my management philosophy today, it would be three words: - Delegate. - Dive In. - Deliver. Interestingly, that's not the order I learned them. When I started my career, there was no delegation. There was only **Dive In**. Over time, I learned **Delegate**. And only after leading teams did I realize the importance of **Deliver**. ## Dive In I started my career as an engineer, so diving in has always been my instinct. When there's a difficult bug, a problem or a production incident, my natural reaction is to get my hands dirty. I enjoy solving problems. Even today, I still write code, build prototypes and pair with engineers. Staying technically involved helps me understand the trade-offs the team is making and keeps me grounded in the realities of software development. AI hasn't changed that. I still believe there's value in staying close enough to the code to understand the problems my team is solving. ## Delegate The biggest lesson I had to learn as a manager was that not every problem should be mine to solve. Delegation isn't stepping away from engineering. It's creating opportunities for others to grow while keeping ownership clear. Sometimes the highest-leverage thing I can do is dive in. Other times, it's trusting an engineer to own the solution while I provide context, coaching, and support when needed. Most of the time, they don't need me to jump in and that's exactly how it should be. Good engineering management is knowing the difference. ## Deliver None of it matters if the work doesn't reach the finish line. Delivering means more than shipping code. It's making decisions when discussions stall. It's following up on loose ends. It's resolving dependencies. It's closing the loop until everyone is clear on what's happening next. Whether the work was done by an engineer, an AI agent, or me is secondary. What matters is that the team delivers value. ## New Tools. Same Principles. AI has undoubtedly changed how I work. It helps me prototype faster, learn faster and explore ideas with far less friction than ever before. But those are changes in execution, not in philosophy. The longer I lead, the more I realize that engineering management isn't defined by the tools we use. It's defined by the decisions we make, the people we empower and the outcomes we deliver. I'm still learning from other engineering leaders, from my teams and from my own mistakes. I'm sure my philosophy will continue to evolve. But after years of writing software and leading engineers, these three ideas are the ones I keep coming back to. - Delegate. - Dive In. - Deliver. AI has changed many things but this isn't one of them. ### Cornell Notes on Context Layer URL: https://blog.katpadi.ph/cornell-notes-on-context-layer/ Last updated: 2026-06-23T14:50:34.000Z An LLM can only reason about information inside its context window. It doesn't automatically know my docs, APIs, databases, Jira tickets or company data. Something has to decide what information gets shown to the model. 💡 That is the ***Context Layer**. ## Core Concepts | Cue | Notes | | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Context Layer? | My current mental model is that the Context Layer is responsible for deciding what information enters the model's working memory. | | Context Package? | The final collection of facts, documents, relationships, and tool results that gets sent to the model. Ideally only the information needed to answer the question. | | Why not send everything? | More context isn't automatically better. It increases cost, latency and noise. Relevance matters more than volume. | | Context Engineering? | The practice of deciding what information should enter the context window, what should be excluded, how sources should be ranked and how information should be structured. | | Where can context come from? | Documents, databases, knowledge graphs, APIs, tools, memory systems, and other knowledge sources. | | What's RAG? | Retrieval-Augmented Generation. Instead of stuffing everything into context, retrieve only the relevant pieces when needed. | | What problem does RAG solve? | Large collections of information that won't fit inside the context window. | | Typical RAG sources | Confluence pages, PDFs, Jira tickets, documentation, wikis, and databases. | | What are embeddings? | Numerical representations of meaning. Similar concepts end up near each other in vector space. | | Why do embeddings matter? | Most semantic search and RAG systems depend on embeddings to find relevant information. | | vector database? | A database optimized for storing and searching embeddings. | | Knowledge Graph? | A system that stores relationships between entities, not just the entities themselves. | | Mental model for Knowledge Graphs | A database tells me a customer exists. A Knowledge Graph tells me how that customer relates to campaigns, products, segments, and other customers. | | What problem does a Knowledge Graph solve? | Understanding how things connect and influence one another. | | Is a Knowledge Graph the same as RAG? | No. RAG retrieves information. Knowledge Graphs model relationships. | | Can RAG and Knowledge Graphs work together? | Yes. A Knowledge Graph can simply be another source used by the Context Layer. | | What are tools? | Systems the model can call when it needs information it doesn't already have. | | Examples of tools | Snowflake, Jira, GitHub, CRM systems, internal APIs and databases. | | What is MCP? | Model Context Protocol. A standard way for AI systems to discover and interact with tools. | | Why use tools instead of storing everything in context? | Some information changes constantly. It's usually better to fetch it on demand than try to keep it in memory. | | Examples of tool usage | Current revenue, latest churn rate, open incidents, deployment status, customer profile lookups. | | Separation of concerns | Facts should remain factual. Relationships belong in graphs. Live values come from tools. Interpretation happens in the model. | ## Diagram ```text Knowledge Sources │ ├── Documents ├── Databases ├── Knowledge Graphs ├── APIs └── Tools ↓ Context Layer ↓ Context Package ↓ Context Window ↓ LLM ↓ Answer ``` ## Summary - Context Layer is the orchestrator. - RAG retrieves information. - Knowledge Graphs provide relationships. - Tools provide real-time data. - The Context Layer decides what enters the model's working memory. - The LLM reasons over whatever makes it through. ### My Notes on LLMs URL: https://blog.katpadi.ph/my-notes-on-llms/ Last updated: 2026-06-22T13:38:13.000Z These notes are my attempt to capture the concepts, observations and references that have helped me make sense of LLMs. ## What is an LLM A Large Language Model is a machine learning model trained to predict the next token in a sequence. The first thing I had to unlearn: **it's not a search engine**. It doesn't look anything up by default. It generates responses based on patterns from training data. That distinction sounds small but it changes how you think about everything else. ## Key concepts **Token** The unit everything is measured in. Roughly 3-4 characters in English text, which loosely maps to about 3/4 of a word. It varies by language and model though. Every message, every reply, every system instruction are all tokens. Most models have a hard cap on how many they can handle at once. **Parameters** The weights a model learned during training. Bigger models tend to perform better but it's not a clean relationship. Architecture, training data quality and what happened after training all matter too. Parameter count alone stopped being a reliable proxy for capability a while back. **Training vs inference** **Training** is the model learning from data. It happens once and is genuinely expensive. **Inference** is me talking to it in real time. I only ever interact during inference. The model's weights don't change mid-conversation. Whatever I tell it lives in the current context window, not in the model itself. **Knowledge cutoff** Training stopped at some date. Anything after that, the model doesn't know unless it has a search tool attached. **Hallucination** When the model generates confident-sounding but false information. It's pattern-matching without a fact-checker. More likely with obscure topics, long conversations or vague prompts. The rule I follow: verify anything that actually matters. **System prompt** Hidden instructions loaded before my first message. Sets the model's persona, rules and constraints. I never see it in most interfaces. **Temperature** Controls how predictable vs adventurous the model's word choices are. ****Low (0-0.3):** consistent, predictable. Good for code and factual answers. ****High (0.7-1.0):** varied, creative. Good for brainstorming and writing. If AI-generated code keeps being weird, temperature is probably too high. If brainstorming outputs all sound identical, too low. Most interfaces default around 0.7\. In higher-level interfaces like ChatGPT or Claude.ai, you can't set it directly so prompt phrasing does the steering instead. ## Turns A turn is one exchange: I send a message, the model replies. That's it. The model doesn't pick up where it left off. Every turn, it re-reads the entire conversation from the beginning: the system prompt, every message I've sent, every reply it's given, any files I've attached. All of it, every single time. There's no memory carried over between turns. No state. The conversation history only exists because it gets passed back in as part of the input. If it's not in the context window, it's gone. This is also why the model can feel inconsistent mid-conversation. It's not drifting. It's reading more and more tokens each turn and, past a certain point, the earlier parts of the conversation start getting less attention. That re-read behavior is exactly what makes the context window worth understanding. ## The context window Everything the model can "see" at once, measured in tokens. Not memory. More like a working scratchpad. Everything the model needs to know about the current task has to fit inside it. ### The smart zone and the dumb zone Despite huge advertised context windows, the model doesn't process all tokens equally. This framing comes from [Dex Horthy at AI Engineer World's Fair](https://www.youtube.com/watch?v=rmvDxxNubIg&ref=blog.katpadi.ph) and it's the most useful mental model I've found for it. The ***smart zone*** is the earlier portion of the context window. Full attention, good reasoning, follows instructions well. The ***dumb zone*** kicks in as the context fills up. Tokens are still technically there, nothing gets deleted, but coherence starts degrading. The model forgets things, gets repetitive, gets vague and starts agreeing with everything. ![](https://blog.katpadi.ph/content/images/2026/06/image-2.png) Degradation tends to kick in well before the model hits its actual context limit. The exact threshold varies by model. **The U-curve** LLMs pay the most attention to the beginning and end of context. The middle gets ignored. So as a conversation grows, the early stuff, the instructions I set at the start, those drift into exactly the part the model stops caring about. [Stanford researchers](https://arxiv.org/abs/2307.03172?ref=blog.katpadi.ph) found real performance drops when relevant information sat in the middle of long contexts. **Context rot** The gradual degradation of LLM performance as context grows. [Chroma](https://trychroma.com/interview-with-a-context-window?ref=blog.katpadi.ph) ran a study across 18 frontier models and found performance decreasing with longer inputs across all tested models, not just some of them. 💡 When the AI starts saying "you're absolutely right" to everything or forgets what I told it earlier, that's the dumb zone. The fix is starting fresh. Warning signs: - Repeating suggestions it already made - "You're absolutely right" then ignores what I said - Forgets rules or constraints from earlier - Answers get generic and vague What eats context fast: - Long conversations - Pasted files or big code blocks - Tool integrations (can eat a significant chunk before I type anything) - The model's own long replies, those go back in too ## Context engineering Managing what's in the context window deliberately so the model stays in the smart zone. A bit different from prompt engineering. **Prompt engineering:** writing a better single prompt. **Context engineering:** designing what information reaches the model at all, instructions, retrieved documents, tool outputs, conversation history. What I do in practice to stay in the smart zone: - Keep prompts specific. (Vague in, vague out!) - Start fresh often. New topic means new conversation. - Front-load the important stuff. Given the U-curve, critical instructions buried mid-conversation get less attention. - When context gets full, prefer a manual handoff over letting the model auto-summarize. A model that's already degraded writing its own summary is not ideal. I use [Matt Pocock's /handoff skill](https://github.com/mattpocock/skills/blob/main/skills/productivity/handoff/SKILL.md?ref=blog.katpadi.ph) for this. - Don't trust large context windows blindly. 1M tokens doesn't mean 1M tokens of reliable reasoning. - Verify anything that matters. Hallucination is real. 💡 On the auto-summarize thing: by the time the context needs summarizing, the model is already in the dumb zone. A degraded model writing its own summary is not a reliable summary! ## What I'm still figuring out **RAG (Retrieval-Augmented Generation)** Instead of stuffing everything into the context window, the system retrieves only the relevant pieces when needed, pulling from a database, a document store or a knowledge base. The model never sees the full corpus, just what's relevant to the current query. **Embeddings** Numerical representations of text that capture meaning. Similar ideas end up close together in vector space, which makes semantic search possible. Embeddings are the foundation of RAG, vector databases and many AI memory systems. **Training** I understand the basic idea: expose the model to enormous amounts of text and for each token it predicts, adjust the weights slightly based on whether it was right. Repeat billions of times. But I don't have a clear picture of what that actually looks like at scale yet. ## References - [Lost in the Middle: How Language Models Use Long Contexts — Liu et al., Stanford](https://arxiv.org/abs/2307.03172?ref=blog.katpadi.ph) - [Chroma: Context Rot Research](https://trychroma.com/interview-with-a-context-window?ref=blog.katpadi.ph) - [Dex Horthy: Smart Zone / Dumb Zone (AI Engineer World's Fair)](https://www.youtube.com/watch?v=rmvDxxNubIg&ref=blog.katpadi.ph) - [Lost in the Middle: DEV Community explainer](https://dev.to/diggidydale/escaping-the-dumbzone-part-1-why-your-ai-gets-stupider-the-more-you-talk-to-it-4d8k?ref=blog.katpadi.ph) - [Don't Trust Large Context Windows — Garrit's Notes](https://garrit.xyz/posts/2026-05-06-dont-trust-large-context-windows?ref=blog.katpadi.ph) ### 9 Hours Down and I Still Haven't Perfected NSW Roads URL: https://blog.katpadi.ph/9-hours-down-and-i-still-havent-perfected-nsw-roads/ Last updated: 2026-06-19T11:35:14.000Z I'm 9 hours into learning to drive in NSW. That's nine 1-hour sessions at 70 dollars each, which means I've already dropped 630 bucks on this. I want to pass the driving test on first try. Which means I need to actually understand what's happening on these roads instead of just hoping for the best. ## The Road Markings Are Chaos There are so many road markings. Lane dividers, bike lanes, bus lanes, zones you can't enter, zigzag, zebra, dragon's teeth, S-lines, zip line, zones that change depending on the time. It's genuinely ridiculous. You're trying to steer and remember which gear you're in, and the road is screaming different instructions at you with white lines. ## Speed Limits Make No Sense A 40kph zone sounds simple until you realize it only counts during school hours. 8:30-9:30am and 2:30-4pm. After that? 60kph. Same road. Same everything. Just different rules. WTF. Now I'm constantly doing mental math about time and whether a school is nearby. My brain is split between being a GPS and a clock and a paranoid passenger all at once. ## Painted "Islands" These aren't real raised islands. Just paint on the road. Center dividers. And you cannot drive on them. They sit in the middle separating opposing traffic directions. The weird part? You don't even need to indicate when you're avoiding them. They're just there. You navigate around them. No signal. Nothing. Just don't hit them. ## Blind Spot Checks Are Intense Not the mirror checks. The over-the-shoulder ones. Changing lanes? Check. Turning? Check. It's constant. My neck is getting a serious workout and my instructor probably thinks I have a spasm. ## Shared Traffic Lights So you pull up to a green light. You think you can go. But the cars on the right also have a green light. Now what? You move forward, let their cars pass, then you turn. It's like a dance nobody taught me the steps to. The light is green for multiple directions at once and everyone just has to figure it out. ## What I Always Forget Road engagement is apparently my biggest enemy. I get tunnel vision. I'm focused on the mechanics instead of actually engaging with what's happening around me. Speed management gets sloppy when I'm not thinking ahead. And those blind spot checks? I'll forget them when I'm concentrating too hard on something else. The independence piece is coming together, but only when I'm actually paying attention to the road instead of just executing moves. ## So Am I Better? IDK. I'm not hitting things. I'm thinking about the road before I drive on it. But NSW roads are designed to keep you humble. Just when you think you've got it, a new marking shows up that makes no sense, or a zone switches speeds on you, or you're navigating around another invisible island without signaling while a shared light tells you to go. Nine down. Probably one to three more sessions before the test. At 70 dollars a session, I'm hoping hard that road engagement clicks before then. My neck is ready. My wallet is definitely not. ### It's Markdown's World And We're All Just Living In It URL: https://blog.katpadi.ph/its-markdowns-world-and-were-all-just-living-in-it/ Last updated: 2026-06-06T07:58:16.000Z Funny how I randomly thought: how the heck did `.md` files end up basically running the show now? Not just in repos as READMEs. They're literally load-bearing walls in the AI ecosystem and I would've laughed if you told me this a few years back. It's a weird arc for a plain text file. ## The README Era For most of my career, Markdown meant one thing: README files. You wrote one when you pushed a repo. It explained what the project was, how to run it, maybe a quick example. Nobody was precious about it. It was the file you wrote last, usually when you were already done with your project and slightly exhausted. GitHub made it the default in 2008 and that was pretty much that. It lived in repos. It rendered nicely. That's it. Nobody sat down and thought: *this is going to matter.* And yet here I am, writing this very post in Markdown as this blog runs on Ghost, and Ghost is Markdown under the hood. Even my note-taking app is Obsidian, which stores every single note as a `.md` file. I've been living in Markdown this whole time without really registering it. ## The Quiet Takeover That thought made me search. And the history is almost too on-the-nose. [John Gruber](https://daringfireball.net/projects/markdown/?ref=blog.katpadi.ph) made it in 2004 because he just didn't want to write HTML by hand. The entire origin story is "this was annoying, let me fix it for myself." Aaron Swartz helped. The first converter was a Perl script. That's it. That's the founding mythology of what is now, without exaggeration, critical infrastructure. [Stack Overflow shipped with Markdown support](https://stackoverflow.blog/2008/06/25/three-markdown-gotcha/?ref=blog.katpadi.ph) in 2008\. [GitHub made it the face of every open source project](https://github.blog/engineering/user-experience/a-formal-spec-for-github-markdown/?ref=blog.katpadi.ph). Static site generators ran on it. Note-taking apps adopted it. And then slowly, quietly, it became the format developers just used, not because someone mandated it, but because plain text that renders well is genuinely hard to beat. It won because it was portable. Humans can read it even without rendering. Machines can parse it easily. Copy-pasting between systems just works. It's not perfect. Not elegant. Just compatible with almost everything. And then AI showed up. ## Plot Twist AI models output Markdown by default now. Every response, every structured answer, every tool output. Headers, bullet points, bold text, code blocks. All of it formatted in a syntax that one blogger invented because he hated angle brackets. Agents read it. Agents write it. Headings become context boundaries. Bullet points become instructions. Code fences become executable blocks. Systems are being built where `.md` files are the interface between humans and AI, the thing you edit to change how an agent thinks, what it knows, what it does. **SKILL.md. AGENT.md. README.md as configuration.** It's not a document format anymore. It's an instruction layer. The format is simple enough for humans but structured enough for machines. That balance is surprisingly rare. And that's exactly why it stuck. ## Kind of Wild Gruber didn't design it for agents. He designed it to get out of his own way while writing. And now that's exactly why it works, because it carries meaning without ceremony. No tags. No schemas. No overhead. The trillion-dollar AI industry is running on a [plain text format someone made for their blog in 2004](https://daringfireball.net/2004/03/introducing%5Fmarkdown?ref=blog.katpadi.ph). Not because it was engineered for this. Because it was simple enough to survive everything. I don't know what that says exactly. But I find it kind of wild. ### Brain Dump: AgentCore Gateway URL: https://blog.katpadi.ph/brain-dump-agentcore-gateway/ Last updated: 2026-06-05T12:30:56.000Z As you build AI agents, they need to call tools: APIs, databases, services. Traditionally you'd write an MCP server for each one. Server scaffolding, tool descriptions, error handling, auth, observability, repeated across every team, every API. ## **TIL: You don't always need to write an MCP server** If you already have REST APIs with OpenAPI specs, [AgentCore Gateway](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway.html?ref=blog.katpadi.ph) can wrap them and expose them as MCP-compatible tools automatically. Just point it at your spec and your endpoints become tools agents can call. It also handles inbound auth (who can call the gateway) and outbound auth (how the gateway calls your backend) which are all managed by AWS. ## **Targets** AgentCore Gateway supports multiple [target types](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-core-concepts.html?ref=blog.katpadi.ph#gateway-core-concepts-target-types). You can wire in AWS Lambda functions, native MCP servers and prebuilt connectors (Slack, Jira, etc. with managed OAuth). One gateway can manage multiple targets, so you end up with a single MCP endpoint that fronts many different backends. ## **Tool descriptions quality = agent quality** The documentation in your OpenAPI spec becomes what the agent reads to decide which tool to call and how. Vague or incomplete descriptions directly hurt agent behavior. It may pick the wrong tool or misuse parameters. So apparently, good descriptions aren't just nice to have anymore, they're part of making the agent work correctly. ## **Size matters in response payloads** If your REST endpoints return large, noisy payloads, that gets passed straight to the agent. This is a real concern. Bloated responses eat context window and can confuse the agent. Something to think about before wrapping an API as-is. ## **Request and response interceptors** AgentCore Gateway supports interceptors on both sides of a call. [Request interceptors](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-interceptors-types.html?ref=blog.katpadi.ph#gateway-interceptors-types-request) run before the call reaches your backend. Useful for injecting auth, adding headers or modifying the request payload on the way in. [Response interceptors](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/gateway-interceptors-types.html?ref=blog.katpadi.ph#gateway-interceptors-types-response) run before the result goes back to the agent. The main use I have in mind is trimming down fat responses so they don't bloat the agent's context. That said, I'm not sure yet if response interceptors are actually the right tool for payload trimming or if it even works well in practice. *Still to be validated.* ## **tl;dr** ``` Agent -> AgentCore Gateway -> your REST APIs / MCP Servers ``` Not a silver bullet but it cuts out the MCP server grunt work if you have existing REST endpoints. ### Too Many Tabs Open URL: https://blog.katpadi.ph/too-many-open-tabs/ Last updated: 2026-05-21T14:39:51.000Z The past month made me realize that "happy or sad" does not always capture it. I don't think I'm sad. I'm still curious. Still working. Still learning. Still trying to make sense of complicated things at work and in life. But I also feel like I am moving through things faster than I can understand them. There have been too many things happening lately. Work decisions, non-work work stuff, things I need to understand, family plans, health worries, adulting spreadsheets. Nothing impossible on its own, but all of it stacked together starts to feel heavy. Like too many tabs open in my head. And then two of my uncles passed away within a month. I don't know if I would call what I feel grief exactly. Maybe it is too quiet for that. Or maybe I just don't recognize it that way. I read somewhere about the word ***Sehnsucht –*** a deep longing or ache for something you cannot fully name. I don't know if that is the right word either, but it feels closer. Not sadness exactly. More like wanting things to feel lighter, clearer and quieter. Wanting life to pause for a bit, even though it never really does. So I keep doing normal things. Meetings. Coffee. Walking. Dinner. Sleep. Repeat. But something is still there in the background. So if you ask me how I feel, the answer is probably: Not sad exactly. Not fully happy either. I still want to build. I still want to learn. I still want to get better. But I am learning that being okay does not always mean knowing what to do next or knowing how to solve something immediately. Sometimes being okay just means continuing, while making room for things that I don't completely understand. ### Today I Baked Brownies URL: https://blog.katpadi.ph/today-i-baked-brownies/ Last updated: 2026-05-03T11:35:07.000Z I don't bake. But I had a brownie mix and some time. I get bored easily. So I started baking. The instructions said: > Preheat to 200°C (180°C fan forced) I guessed the right icon, set it to 180°C… and then realized preheating is just **waiting** for a tiny orange light to turn off. That's it. I don't usually skip steps. I just get bored. So instead of doing something stupid like putting the tray in early, I stood there. Waiting. Doing nothing. The actual steps were fine. Mix in a bowl. Add 2 eggs. Microwave the butter, then mix it in. Pour it into the tray. The light turned off eventually. I put the tray in… and the light turned on again. For a second I thought I broke it. Turns out that's normal...you open the door, temp drops, it heats back up. Then comes the hardest part: **Do nothing.** Don't open it. Don't check. Don't peek. Just let it do its thing. In the end, it was simple: - Set it properly - Wait until it's actually ready - Don't rush it - Don't interfere The brownies turned out okay. One thing I messed up though. I forgot to grease the tray. So when it was time to take it out… it didn't come out. I spent more time slicing and scraping than actually baking. Some pieces came out clean. Some didn't. Weird shapes. Broken edges. One corner just decided to stay in the tray. But a few survived. And they looked like actual brownies. The lesson: You can follow most of the process right and still miss a small step that makes things harder later. It doesn't ruin everything. It just makes the last part messier than it needed to be. Here are the survivors: ![](https://blog.katpadi.ph/content/images/2026/05/1000053002-1.jpg) Here's the tray before I murdered them: ![](https://blog.katpadi.ph/content/images/2026/05/1000053000.jpg) ### The Frictions In Between URL: https://blog.katpadi.ph/tracing-the-slow-parts/ Last updated: 2026-05-01T08:33:03.000Z When delivery feels slow, the instinct is usually to look at execution. Are engineers shipping fast enough? Are PRs taking too long? Are releases getting stuck? But if you trace the work end to end, the slow parts are often somewhere else. Most of the friction sits in the gaps between the steps, not in the steps themselves. Below is a rough field guide to where it tends to show up. ## 1\. Vague tickets Tickets that are enough to start, but not enough to finish. Descriptions are thin, acceptance criteria are incomplete, edge cases are missing, and it's unclear who owns which decisions. Engineers pick up the work and then ask questions, wait, make assumptions, adjust later. The work moves, but with interruptions. From the outside, it looks like slow delivery. From the inside, it's ongoing clarification. ## 2\. Design that's still evolving Implementation starts while design is still being worked out. Partially ready, details finalized along the way. Layouts change mid-implementation, flows get adjusted, components are reworked. Even when iteration is intended, the target keeps moving during execution. ## 3\. Decisions that don't get captured A lot of decisions happen in chats or quick calls. They don't make it back to the ticket or any durable doc. The next person asks again, or assumes something different. Over time, teams end up depending on whoever remembers, instead of shared context. ## 4\. Broad alignment without clear ownership To reduce ambiguity, teams sometimes pull in architecture reviews and larger stakeholder groups. Sometimes this helps. Other times, many people are involved, decision ownership is unclear, and nothing gets written down. Decisions take longer or stay partially unresolved. ## 5\. Code that needs updating mid-flight Logic shifts, scope expands a little, an assumption turns out to be wrong. The change itself is usually small, but it pulls the engineer back into a part of the code they thought was done. This rework rarely shows up in estimates, but it adds up. ## 6\. PRs waiting on review The work is technically complete, but it's not moving. And when review comments come in, they often surface gaps from earlier in the process. Design questions, edge cases, assumptions. Which means more code updates, and another round of waiting. ## 7\. Undocumented operational steps Even after merge, the path to production has its own friction. Infra and config changes often need their own review, and the steps for that aren't always well documented. They live with whoever has done it before. So work pauses again. Not because deployment is inherently complex, but because no one knows where to look. ## 8\. Scheduled release windows Code can sit in staging for days waiting for the next scheduled deploy. Not because anything is broken, just because that's the cadence. This one is easy to miss because it doesn't feel like friction in the moment. It feels like "that's just how we ship." ## 9\. Priority changes mid-flight The cross-cutting one. Priority can shift at any stage. Something more urgent comes in, the team pivots, and whatever was in flight gets paused or worse gets trashed completely. ## The pattern underneath Most of these aren't about coding. They're about decisions that weren't written down, questions that had to be asked twice, context that lives in someone's head, interruptions that pull a team out of flow. When delivery looks slow, it's worth checking whether the team is actually executing slowly, or just working through gaps that were left earlier. A lot of the time, it's the second one. And those gaps tend to be easier to close than execution speed is to improve. The goal isn't to move faster. It's to remove the things that are slowing teams down for reasons that aren't really about the work itself. ### Dev Journal: Jot (Chrome Extension) URL: https://blog.katpadi.ph/dev-journal-jot-chrome-extension/ Last updated: 2026-04-04T04:51:08.000Z ## Motivation I am working on a 14" personal laptop and I needed a fast, minimal notepad directly in the browser because because context switching is a pain. Existing options required accounts or had unnecessary features that I do not want. *P.S. This is practically a side quest. I was working on a different project when I got bothered switching tabs and desktop spaces.* ## Goal/s - Learn how to build a simple Chrome Extension - Build a Chrome extension popup notepad ## Dev Log - Browsed [https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world](https://developer.chrome.com/docs/extensions/get-started/tutorial/hello-world?ref=blog.katpadi.ph) as a primer. - Created a Chrome extension with `manifest.json`, `popup.html`, `popup.css`, and `popup.js` - Implemented a textarea in `popup.html` and wired it to `localStorage` in `popup.js` - Added a clear button and hooked it to reset both UI state and stored data - Added a character counter (just for the heck of it) - Structured the UI into header, body, and footer sections in `popup.css` - Added a light/dark mode toggle and persisted the selected theme in `localStorage` - Implemented a "Copy all" button using the Clipboard API - Wrote the Chrome Web Store listing fields: - `manifest.json` description (105 chars) - single-purpose statement - privacy policy page (ikr?) - Packaged the extension into a zip - Submitted the extension through the Chrome Web Store Developer Dashboard This blog is harder than [popup.js](https://github.com/katpadi/jot/blob/main/popup.js?ref=blog.katpadi.ph) itself because it simply does the following: 1. Load = restore note and theme from `localStorage` on popup open 2. Auto-save = persist note on every keystroke 3. Count = update character count 4. Copy = copy content to clipboard with feedback 5. Clear = reset note and storage ## Result ![](https://blog.katpadi.ph/content/images/2026/04/Screenshot-2026-04-03-at-6.36.03---pm.png) Currently, the extension has been packaged and submitted to the Chrome Web Store. Internet says it usually takes 2-5 days. ![](https://blog.katpadi.ph/content/images/2026/04/Screenshot-2026-04-03-at-6.33.54---pm.png) I am already able to use it via Developer Tools. I just had to put it on Chrome Web Store and see what the process is like in case I want to build other extensions in the future. ## Lessons - Chrome Web Store requires a privacy policy URL even if no data leaves the browser - `localStorage` in extensions is scoped to the extension origin, not the regular webpage context. So, inspecting extension storage requires opening DevTools on the popup, not the main page **Github Repo:** [https://github.com/katpadi/jot/](https://github.com/katpadi/jot/?ref=blog.katpadi.ph) --- Update: it's now published [https://chromewebstore.google.com/detail/jot/gogmghfcgficidobmgjbpfdejaaekgid](https://chromewebstore.google.com/detail/jot/gogmghfcgficidobmgjbpfdejaaekgid?ref=blog.katpadi.ph). Only took less than 24h to get approval. ### Dev Journal: Compromise Enhancements URL: https://blog.katpadi.ph/dev-journal-for-compromise-enhancements/ Last updated: 2026-04-03T06:20:38.000Z ## The Idea The [app](https://compromise-app.katpadi.ph/?ref=blog.katpadi.ph) started as a simple tool: enter two locations, find the geographic midpoint, search for nearby venues. Clean, useful, but fundamentally flawed — geographic halfway ignores the fact that roads, traffic, and transport options mean one person almost always travels longer than the other. 💪 **The goal became: find the point where both people spend roughly the same travel time to get there. Not the middle of the map. The fair middle.** ## The Original Implementation The first version was a basic Sinatra app with a few moving parts: - Two location inputs using Google Places autocomplete (via the `google_places` gem) - A geometric midpoint computed as the average of the two coordinates - A `GET /spots` endpoint that called the Places API with a fixed 500m radius - Results rendered as a list in the sidebar - Google Maps JavaScript API for the map display - jQuery + Select2 for the autocomplete dropdowns It worked, but "worked" in the loosest sense. The midpoint was purely mathematical. Someone driving from a highway suburb and someone in a dense city grid would get the same result — a point that's geographically central but travel-time unequal. ## Upgrading to a Fair Midpoint Algorithm ### The approach Instead of one midpoint, generate **13 candidate points** around the geometric midpoint and evaluate each one: - A 3×3 grid (9 points) using lat/lng offsets scaled to the distance between A and B - 4 wider cardinal points (north, south, east, west) to extend the search - Spread formula: `clamp(distance × 0.20, 0.005, 0.04)` so the grid scales with how far apart the two people are 💡 ****Why 13?** 3 lat offsets × 3 lng offsets = 9 grid points, plus 4 cardinal points at double the spread. The grid covers the immediate area around the midpoint; the cardinal points extend further out in case the fairest spot is offset in one direction rather than clustered around the centre. For each candidate, compute the travel time from Person A and Person B using the [**Google Routes Matrix API**](https://developers.google.com/maps/documentation/routes/compute%5Froute%5Fmatrix?ref=blog.katpadi.ph) (`computeRouteMatrix`). ### Scoring Each candidate is scored: ``` score = fairness_gap × 0.75 + total_time × 0.25 ``` Where `fairness_gap = |timeA - timeB|`. The formula prioritizes **equal arrival over raw speed** — a point where both travel 30 minutes beats a point where one travels 10 and the other 25, even though the latter has a lower total. The candidate with the lowest score wins. Top 3 are returned so the frontend has fallbacks. ### Fairness verdict The travel time gap of the best candidate is classified: - **Very fair** — gap under 3 minutes - **Mostly fair** — 3–8 minutes apart - **Uneven** — more than 8 minutes (still the best found) I probably need to adjust the fairness verdict at some point but since I'm the developer, I just made up my own rules. ## Per-Person Transport Modes Each person independently selects **Drive** or **Commute** (transit). Travel times are computed per mode. A departure time input (defaulting to now) feeds into transit lookups so bus/train schedules are factored in. ### A subtle bug I encountered When `departureTime` is passed to `computeRouteMatrix` with `DRIVE` mode and `routingPreference: TRAFFIC_AWARE`, the Routes API requires the time to be in the future. Since the meetup time defaults to "now" and the request takes a few seconds, the time is already in the past by the time it hits the API — causing a `400 INVALID_ARGUMENT` response and empty results. Fix: only pass `departureTime` to the matrix for **TRANSIT** mode. Drive uses base travel times, which is fine for fairness comparison since we're scoring candidates relative to each other. ## Server-Side API Proxying Two Google API keys are used: | Key | Restrictions | Used for | | ----------------- | ------------- | ---------------------------------------------- | | API\_KEY\_BROWSER | HTTP referrer | Maps JS rendering, Places detail lookups | | API\_KEY\_SERVER | IP address | Routes API, Places autocomplete, nearby search | Anything heavier goes through the server. All Routes API calls (`computeRoutes`, `computeRouteMatrix`) are proxied via the Sinatra backend. The browser never sees the server key, so it not just sitting there waiting to be copied. Probably overkill for a tiny app, but it keeps things clean and gives me a bit more control. ## Venue Search: Auto-Escalating Radius The original (my initial naive implementation) fixed 500m radius was replaced with an escalating search: ``` 500m → 1000m → 1500m → 2000m → 3000m → 5000m ``` The search stops as soon as **5 or more** venues are found. The radius is also capped at **half the distance between A and B** — beyond that, venues are no longer genuinely "in the middle." The backend returns `{ spots: [...], radius: N }` so the frontend knows the actual radius used and can size the search circle on the map accordingly. ## Map UI Changes - Removed the A-to-B route polyline — replaced with two lines: A→meeting point and B→meeting point, in different colours - Venue results removed from the sidebar — replaced with numbered pins on the map - Clicking a pin shows: photo, place type, price level (`$`/`$$`/etc.), open/closed status, address, phone, website, Google Maps link - After spots are found, the map zooms to fit the search circle (centered on the meeting point, sized to the used radius) ## Removing jQuery The original app used jQuery 1.12.0 (from 2016) and Select2 for the autocomplete. Both were replaced: - **Tom Select** — modern, zero-dependency Select2 replacement. Handles AJAX autocomplete, same UX - **Vanilla JS** — all `$.ajax`, `$().addClass`, `$().prop`, `$().on` etc. replaced with native DOM APIs (`fetch`, `classList`, `addEventListener`, `disabled`, `textContent`) The app already used `fetch` and `async/await` for the map interactions — jQuery was only hanging around for Select2 and a handful of convenience methods. ## Security Fixes Five issues identified and fixed: 1. **XSS** — all user-facing data (venue names, addresses, phone numbers) concatenated into HTML was wrapped in an `esc()` helper that encodes `& < > " '` 2. **`javascript:` protocol** — `data.website` is validated against `/^https?:\/\//` before being used as an `href` 3. **Mode parameter whitelist** — `mode` parameter in all POST endpoints is validated against `["DRIVE", "TRANSIT"]` and defaults to `"DRIVE"` if invalid 4. **Debug logging removed** — `warn "[MATRIX]..."` lines added during debugging were cleaned up before production 5. **jQuery CVEs** — eliminated entirely by removing jQuery ## Automated Deployment with GitHub Actions The live server is my DigitalOcean droplet running Docker. Deployment is automated via a GitHub Actions workflow: ```yaml on: push: branches: - main ``` On every push to `main`, the workflow SSHes into the droplet and runs: ```bash git pull origin main docker compose up --build -d docker image prune -f ``` No Docker Hub involved (as opposed to initial manual process) — the image is built directly on the server from source. Secrets stored in GitHub. ## Key Takeaways - **Geographic midpoint ≠ fair midpoint.** Roads, modes, and traffic make the real middle asymmetric. - **Google Routes Matrix API** is the right tool for multi-destination travel time lookups — not the Distance Matrix API (which requires a separate enablement and has different quota). - **`departureTime` with DRIVE mode** requires a future timestamp and `TRAFFIC_AWARE` routing preference. For a fairness comparison use case, omitting it for drive mode is simpler and more reliable. - **Two API keys** (browser-restricted + server-restricted) is the right pattern for Google Maps apps. The browser key handles rendering and Places details; the server key handles everything that shouldn't be exposed client-side. - **Removing jQuery** in a small app is straightforward. The only real dependency was Select2 — once that's replaced, vanilla JS covers everything else cleanly. - **Claude is fun** for weekend projects. ### Built a Tiny Thing URL: https://blog.katpadi.ph/built-a-tiny-thing/ Last updated: 2026-03-22T14:07:42.000Z Lately the software industry feels… loud. There's been a lot going on in the industry. Big companies making moves, things shifting around. Enough noise that I just wanted to build something small again. So I did. Nothing fancy. Just me poking at the Anthropic API to see if I could make something out of it. ## The idea Every day, the app generates one motivational quote tied to something that happened on that date. You open the page, it figures out your “today”, checks if a quote already exists, and if not, generates one. Then it caches it and serves the same thing for the rest of the day. So instead of random quotes, it's: > “What happened today, and what can I take from it?” Under the hood it's just a single flow: check if today's quote exists, otherwise generate it, store it in TinyDB, return it. I cached it so I don't accidentally burn money on Anthropic. I used `claude-haiku-4-5-20251001` which is the tiniest one since this only needs one call per day at most, even if people hit the page multiple times. ## The prompt Early versions returned generic quotes that could've come from a fortune cookie. So I tightened it — give it a category list (Science, Technology, Music, Pop Culture, Space, etc.), tell it to pick a high-impact event, explain it in two sentences, then ground the quote in that specific event. Under 25 words. No clichés. The difference was noticeable. It went from "believe in yourself" energy to quotes that actually feel tied to something real. ## The frontend It's a fake terminal. The page loads with a little typing animation, pretends to run a command, then reveals the quote. There's a dark/light toggle, a database modal styled like a psql shell where you can browse past entries, and a copy button. None of it was necessary. I just liked the aesthetic. ![](https://blog.katpadi.ph/content/images/2026/03/Screenshot-2026-03-22-at-9.21.22---PM.png) ## Cost Around \~400 tokens per generation. That's roughly: \~$0.002/day <$0.06/month Because of caching, it's usually just one API call per day. **Edge case:** if someone in a different timezone is already in the next day, it can trigger the next day's quote early. So worst case, you get \~2 calls around the day boundary. The browser actually passes its own timezone, so "today" is genuinely your today — not the server's. ## The rough edges A few things that annoyed me while building this: 1. ***No per-API-key spending limits*** You can only set limits at the workspace level. So if you want fine-grained control over how much a single project can spend, you're out of luck. It's all or nothing. 1. **No console API for usage data.** I can't query my actual spend or token usage programmatically. The only way to track it is to pull the token counts from each API response and store them yourself — which is exactly what I ended up doing. 1. **Claude.ai and the Anthropic API are completely separate.** If you're paying for Claude Pro thinking you can use it to build things — you can't. API access is billed separately. Your subscription gives you zero API credits. So anyway, the stack is intentionally boring. Flask, TinyDB, gunicorn, Docker, Nginx. Python because I haven't touched it in a while. ***Well… technically I didn't touch much code either.*** The app is live at [motivation.katpadi.ph](https://motivation.katpadi.ph/?ref=blog.katpadi.ph). Just wanted to build something small, understand the tooling a bit more and ship it. I just need a daily motivation from all of the chaos. ### AI as Another Industrial Revolution URL: https://blog.katpadi.ph/is-ai-the-new-industrial-revolution/ Last updated: 2026-03-16T15:01:08.000Z People often reach for comparisons when talking about AI in software. Calculators for engineers. Tractors replacing farmers. They're useful analogies, but they feel a bit small for what's actually happening. The one that keeps making sense to me is the **Industrial Revolution**. ## Before the Machines Before it, production depended almost entirely on human labor. If you wanted more output, you needed more people. There was no other way. **Then machines appeared.** A single machine could produce what used to require many workers. Not perfectly at first. But enough to change how work was fundamentally organized. ![](https://blog.katpadi.ph/content/images/2026/03/17736731436231314731341180913091.jpg) Factories didn't just add machines and call it a day. They reorganized around them. Software development feels like it might be going through the same thing right now. When I started my career, most of the work began with a blank editor. I wrote the first line. I structured the function. I filled in the details. The work started from nothing and I was the one who started it. That's not really true anymore. AI can generate entire functions, modules, even whole features now. Not just autocomplete. Not just suggestions. Working code, end to end. The developer's job is basically to decide if it's right and not to write it in the first place. It's a bigger shift than it sounds. It changes how most programmers have approached their work for most of their careers. ## How the work changed During the Industrial Revolution, machines didn't eliminate workers. They changed what workers did. Instead of producing by hand, people started operating, overseeing and directing systems that produced. Something similar seems to be happening now. Less time writing code. More time deciding what the system should do, whether the generated code is actually correct and how the pieces should fit together. The judgment work – that part hasn't gone anywhere. If anything, it matters more now, because someone has to know when the machine is wrong. ## The Job Is Different Now What interests me is that this doesn't just make coding faster. It changes what programmers actually spend their time doing. Writing code was the job. Now the job is increasingly closer to directing — knowing what to ask for, evaluating what comes back and understanding the system well enough to catch what the AI confidently got wrong. That's a different kind of work. Even if the title stays the same. The way factories reorganized around machines, software teams might reorganize around systems that can already generate code. Not because developers are disappearing, but because the work is shifting toward judgment, architecture and oversight... away from production. It's still early and it's hard to know where this ends up. But if the Industrial Revolution analogy holds, the biggest change won't be the tools themselves. The machines didn't define the Industrial Revolution. The people who learned to work with them did. Maybe that's the job now. 🤷🏻‍♀️ ### Reluctant Vibe Coder URL: https://blog.katpadi.ph/learnings-of-a-reluctant-vibe-coder/ Last updated: 2026-03-02T06:31:12.000Z Everyone's talking about "vibe coding." Although I still cringe when I hear the term, the Internet has already decided that's what we should call it. I'm not anti-AI at all. If anything, I'm leaning into AI-assisted coding more and more everyday. It does feel like an evolution of how we solve problems. Earlier in my career, [Stack Overflow](https://stackoverflow.com/?ref=blog.katpadi.ph) used to be my default helpline. I'll surely miss having that space where you ask humans and wait for thoughtful replies, but AI trades that for speed. The help shows up instantly. Not always correct, but fast enough to keep momentum. ### AI as a Junior Dev People often compare AI to a junior developer and I think that analogy is fair. A good junior can take rough instructions, fill in gaps and sometimes deliver more than you expected. They listen, infer, adjust. But the result still depends on how clearly you communicate intent. That's where the discipline comes in. AI isn't a genie. You can’t be vague and expect magical results. Vague prompting is usually where hallucinations thrive. If you overload it with half-formed ideas and unclear constraints, the model will try to stitch meaning out of chaos. Sometimes it works. Sometimes it confidently gives you something that sounds right but isn’t. What AI really forces is clarity. You have to articulate the problem, define constraints, and explain intent. Prompting becomes less about commanding a machine and more about organizing your own thinking. There are moments when I finish writing a prompt and realize I already know the answer. AI just becomes the confirmation layer... a surprisingly good rubber duck. ![](https://blog.katpadi.ph/content/images/2026/03/image.png) **Image: “Rubber duck assisting with debugging” by* [**Tom Morris*](https://commons.wikimedia.org/wiki/User:Tom%5FMorris?ref=blog.katpadi.ph)**, via* [**Wikimedia Commons*](https://commons.wikimedia.org/wiki/File:Rubber%5Fduck%5Fassisting%5Fwith%5Fdebugging.jpg?ref=blog.katpadi.ph)**, licensed under* [**CC BY-SA 3.0*](https://creativecommons.org/licenses/by-sa/3.0?ref=blog.katpadi.ph)**.* ### The Part That Makes Me Uncomfortable I'll admit though that I'm a bit worried for the future. IDE assistants are incredibly convenient. They help with debugging, tracing unfamiliar code paths and rubber ducking. Somehow there's a quiet fear of over-reliance. When answers come instantly, it's easy to default to asking instead of understanding. That habit can slowly rust the brain we rely on as engineers. I don't want to be that engineer. Maybe that's what makes me a reluctant vibe coder. ### What I've Learned So Far #### Use AI with a learning mindset Treat it like a collaborator, not a shortcut. If it generates code, read it. Question it. Understand why it works before moving on. Ask why one solution is better than another. Challenge it. I've had moments where I pushed back and said, “Can we do X instead of Y? Isn't that more efficient?” and it responded with, “You're right to question that…” That's when it feels useful. #### AI is great as a primer When stepping into an unfamiliar domain or repository, it helps sketch a mental map before the deeper dive. It's not perfect, but it reduces that overwhelming feeling when staring at a huge codebase for the first time. #### Don't outsource your brain! GenAI can feel like fast food for thinking: quick, convenient and easy to overconsume. The danger isn't the tool. It's the habit of skipping critical thinking. AI doesn’t replace engineers, but it definitely speeds up development. Less time hunting for syntax or boilerplate, more time defining intent and validating output. The pace increases , but so does the responsibility. So yes, I vibe code now. Just not blindly. Maybe being a reluctant vibe coder means moving fast with AI while keeping enough judgment to hesitate, just for a bit, before I hit merge to main. ### Reset URL: https://blog.katpadi.ph/reset/ Last updated: 2026-02-27T12:52:48.000Z I finally moved this blog from WordPress to Ghost. That has been my plan for so long I just didn't have time. The old site had years of posts that no longer felt like me, so consider this a quiet redirect to */dev/null*. Clean slate. My idea is simple: make this a log of all my dev notes, engineering management lessons and life musings. I've always felt communication was my weakest skill, so this is part of the improvement plan. One post at a time.