[{"data":1,"prerenderedAt":1139},["ShallowReactive",2],{"blog-posts":3},[4,174,337,756,848,1021],{"_path":5,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":9,"description":10,"date":11,"tags":12,"image":16,"draft":7,"body":17,"_type":168,"_id":169,"_source":170,"_file":171,"_stem":172,"_extension":173},"/blog/hamburg-hackathon-innovate-the-skies","blog",false,"","50 Hackers, a Transit Strike, and One Incredible Day in Hamburg","We ran our first aviation-themed hackathon with the Social Developers Club. 50 people showed up despite a national transit strike, and the projects blew us away.","2026-03-01",[13,14,15],"Hackathon","AI","Community","/images/blog/hh-hackathon-02-26.webp",{"type":18,"children":19,"toc":160},"root",[20,28,33,40,45,58,64,69,74,80,85,96,106,116,122,127,133,138,143,148,152],{"type":21,"tag":22,"props":23,"children":24},"element","p",{},[25],{"type":26,"value":27},"text","Last Saturday, Julia, An, and I ran our first aviation-themed hackathon with the Social Developers Club. Honestly, I'm still riding the high from it.",{"type":21,"tag":22,"props":29,"children":30},{},[31],{"type":26,"value":32},"The idea was simple: what if we got a bunch of people in a room and challenged them to build real-time, AI-powered travel tools using Gemini 3.0 and live aviation data? LLMs are powerful, but they don't know your flight just got delayed. We wanted to fix that.",{"type":21,"tag":34,"props":35,"children":37},"h2",{"id":36},"the-day-the-trains-stopped",[38],{"type":26,"value":39},"The day the trains stopped",{"type":21,"tag":22,"props":41,"children":42},{},[43],{"type":26,"value":44},"Here's the thing nobody planned for: Germany decided to have a national public transport strike on the exact same Saturday.",{"type":21,"tag":22,"props":46,"children":47},{},[48,50,56],{"type":26,"value":49},"I genuinely wasn't sure what to expect with no trains running. But 50 people showed up anyway. Some of them ",{"type":21,"tag":51,"props":52,"children":53},"em",{},[54],{"type":26,"value":55},"walked over an hour",{"type":26,"value":57}," just to get to SAE Institute in Hamburg. During a national transit strike, that's dedication. If that doesn't tell you something about the energy of the dev community here, I don't know what does.",{"type":21,"tag":34,"props":59,"children":61},{"id":60},"_14-hours-of-building-and-eating",[62],{"type":26,"value":63},"14 hours of building (and eating)",{"type":21,"tag":22,"props":65,"children":66},{},[67],{"type":26,"value":68},"We kicked things off at 8 AM with breakfast and coffee, because nobody's building the future on an empty stomach. After a quick intro to the Gemini Grounding API, teams formed, ideas got pitched, and then it was just... heads down, deep work for the rest of the day.",{"type":21,"tag":22,"props":70,"children":71},{},[72],{"type":26,"value":73},"One thing I love about hackathons is watching strangers become teammates in minutes. We required teams of 2–4 (no solo acts allowed), and that constraint did exactly what we hoped: it got people talking, collaborating, and riffing off each other's ideas.",{"type":21,"tag":34,"props":75,"children":77},{"id":76},"the-projects-that-blew-us-away",[78],{"type":26,"value":79},"The projects that blew us away",{"type":21,"tag":22,"props":81,"children":82},{},[83],{"type":26,"value":84},"Every team built something cool, but our three winners really stood out:",{"type":21,"tag":22,"props":86,"children":87},{},[88,94],{"type":21,"tag":89,"props":90,"children":91},"strong",{},[92],{"type":26,"value":93},"Team Layla",{"type":26,"value":95}," built a smart layover assistant. Got a 3-hour layover in Istanbul? It tells you whether you have enough time to actually leave the airport, explores the city, and gives you a custom route with a timeline so you don't miss your connection. Brilliant.",{"type":21,"tag":22,"props":97,"children":98},{},[99,104],{"type":21,"tag":89,"props":100,"children":101},{},[102],{"type":26,"value":103},"Team Fenster",{"type":26,"value":105}," (German for \"window,\" love the name) created an app that tells you about points of interest at the exact geographical location you're flying over, in real-time. Imagine looking out your airplane window and knowing that the river below you is the Danube, with a little history lesson attached.",{"type":21,"tag":22,"props":107,"children":108},{},[109,114],{"type":21,"tag":89,"props":110,"children":111},{},[112],{"type":26,"value":113},"Team Discover",{"type":26,"value":115}," went a completely different direction with a browser extension. If you're browsing concert tickets in another city, it automatically starts planning your trip: flights, airport routes, the whole thing. It felt like the kind of tool that should already exist.",{"type":21,"tag":34,"props":117,"children":119},{"id":118},"the-messy-parts-keeping-it-real",[120],{"type":26,"value":121},"The messy parts (keeping it real)",{"type":21,"tag":22,"props":123,"children":124},{},[125],{"type":26,"value":126},"Not everything was smooth. We gave teams Google Cloud credits for using the Gemini API, but we underestimated how long it takes to onboard 50 people onto new accounts in a time-pressured setting. I ended up running around helping most teams get set up. Note to future me: prep a step-by-step guide and do a dry run the night before.",{"type":21,"tag":34,"props":128,"children":130},{"id":129},"why-i-keep-doing-this",[131],{"type":26,"value":132},"Why I keep doing this",{"type":21,"tag":22,"props":134,"children":135},{},[136],{"type":26,"value":137},"Honestly? Organizing this was a blast. Julia, An, and I had a great time putting it together, and on the day itself it all just clicked.",{"type":21,"tag":22,"props":139,"children":140},{},[141],{"type":26,"value":142},"There's something special about seeing someone's face light up when their API call finally returns real flight data. Or watching a team that met three hours ago high-five after their demo. Or hearing that someone walked an hour through a transit strike because they didn't want to miss it.",{"type":21,"tag":22,"props":144,"children":145},{},[146],{"type":26,"value":147},"If you've ever thought about organizing a hackathon, even a small one, just do it. The community will show up. Literally, even when the trains don't.",{"type":21,"tag":149,"props":150,"children":151},"hr",{},[],{"type":21,"tag":22,"props":153,"children":154},{},[155],{"type":21,"tag":51,"props":156,"children":157},{},[158],{"type":26,"value":159},"The Hamburg Hackathon: Innovate the Skies & Beyond was organized by the Social Developers Club on February 28, 2026, at SAE Institute Hamburg.",{"title":8,"searchDepth":161,"depth":161,"links":162},2,[163,164,165,166,167],{"id":36,"depth":161,"text":39},{"id":60,"depth":161,"text":63},{"id":76,"depth":161,"text":79},{"id":118,"depth":161,"text":121},{"id":129,"depth":161,"text":132},"markdown","content:blog:hamburg-hackathon-innovate-the-skies.md","content","blog/hamburg-hackathon-innovate-the-skies.md","blog/hamburg-hackathon-innovate-the-skies","md",{"_path":175,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":176,"description":177,"date":178,"tags":179,"image":183,"draft":7,"body":184,"_type":168,"_id":334,"_source":170,"_file":335,"_stem":336,"_extension":173},"/blog/demystifying-agents-devfest","Demystifying AI Agents at GDG Hamburg DevFest 2025","Key takeaways from my DevFest Hamburg talk on what AI agents actually are, why mega agents fail, and how multi-agent systems solve real problems.","2025-11-20",[14,180,181,182],"Agents","Google Cloud","Speaking","/images/blog/hh-devfest-11-25.webp",{"type":18,"children":185,"toc":327},[186,202,207,213,218,224,229,235,261,267,302,308,313],{"type":21,"tag":22,"props":187,"children":188},{},[189,191,200],{"type":26,"value":190},"I recently had the opportunity to speak at GDG Hamburg DevFest 2025, where I broke down what AI agents actually are and how to build them effectively. If you missed it, you can catch the full recording here: ",{"type":21,"tag":192,"props":193,"children":197},"a",{"href":194,"rel":195},"https://www.youtube.com/watch?v=JW5cZYJis_g",[196],"nofollow",[198],{"type":26,"value":199},"Demystifying Agents - GDG Hamburg DevFest 2025",{"type":26,"value":201},".",{"type":21,"tag":22,"props":203,"children":204},{},[205],{"type":26,"value":206},"Here are the core takeaways from the session:",{"type":21,"tag":34,"props":208,"children":210},{"id":209},"defining-agents",[211],{"type":26,"value":212},"Defining Agents",{"type":21,"tag":22,"props":214,"children":215},{},[216],{"type":26,"value":217},"At their core, intelligent agents plan, take steps, retrieve data, interact with their environment (via tool calls), and remember context.",{"type":21,"tag":34,"props":219,"children":221},{"id":220},"the-problem-with-mega-agents",[222],{"type":26,"value":223},"The Problem with \"Mega Agents\"",{"type":21,"tag":22,"props":225,"children":226},{},[227],{"type":26,"value":228},"Giving a single Large Language Model too much information and responsibility often causes it to lose focus and fail at specific tasks.",{"type":21,"tag":34,"props":230,"children":232},{"id":231},"the-multi-agent-solution",[233],{"type":26,"value":234},"The Multi-Agent Solution",{"type":21,"tag":22,"props":236,"children":237},{},[238,240,245,247,252,254,259],{"type":26,"value":239},"Instead of one massive agent, it's better to build a system of focused experts. I demonstrated a real-world plumbing business workflow where an ",{"type":21,"tag":89,"props":241,"children":242},{},[243],{"type":26,"value":244},"Orchestrator Agent",{"type":26,"value":246}," coordinates a ",{"type":21,"tag":89,"props":248,"children":249},{},[250],{"type":26,"value":251},"Field Service Agent",{"type":26,"value":253}," and an ",{"type":21,"tag":89,"props":255,"children":256},{},[257],{"type":26,"value":258},"Office Agent",{"type":26,"value":260},". This multi-agent setup successfully reduced a frustrating 40-minute manual communication loop down to just 40 seconds.",{"type":21,"tag":34,"props":262,"children":264},{"id":263},"the-tech-stack",[265],{"type":26,"value":266},"The Tech Stack",{"type":21,"tag":268,"props":269,"children":270},"ul",{},[271,282,292],{"type":21,"tag":272,"props":273,"children":274},"li",{},[275,280],{"type":21,"tag":89,"props":276,"children":277},{},[278],{"type":26,"value":279},"Models:",{"type":26,"value":281}," You don't always need massive pro models. Highly capable models like Gemini 2.5 Flash are perfect for focused domain experts.",{"type":21,"tag":272,"props":283,"children":284},{},[285,290],{"type":21,"tag":89,"props":286,"children":287},{},[288],{"type":26,"value":289},"Python:",{"type":26,"value":291}," Python's automatic function calling makes passing context and setting up tool calls practically seamless.",{"type":21,"tag":272,"props":293,"children":294},{},[295,300],{"type":21,"tag":89,"props":296,"children":297},{},[298],{"type":26,"value":299},"Google Agent Development Kit (ADK):",{"type":26,"value":301}," Building agents from scratch makes testing and debugging a nightmare. Using a framework like the ADK gives you complete UI visibility into inter-agent communication, execution timings, and tool payloads out of the box.",{"type":21,"tag":34,"props":303,"children":305},{"id":304},"the-takeaway",[306],{"type":26,"value":307},"The Takeaway",{"type":21,"tag":22,"props":309,"children":310},{},[311],{"type":26,"value":312},"Building multi-agent systems is incredibly powerful when you let humans focus on the emotional tasks (like client negotiation) and let specialized agents handle the data routing.",{"type":21,"tag":22,"props":314,"children":315},{},[316,318,325],{"type":26,"value":317},"Feel free to check out the video above, and you can find the sample code for the WhatsApp-integrated multi-agent system on ",{"type":21,"tag":192,"props":319,"children":322},{"href":320,"rel":321},"https://github.com/AlexAmin",[196],[323],{"type":26,"value":324},"my GitHub",{"type":26,"value":326},"!",{"title":8,"searchDepth":161,"depth":161,"links":328},[329,330,331,332,333],{"id":209,"depth":161,"text":212},{"id":220,"depth":161,"text":223},{"id":231,"depth":161,"text":234},{"id":263,"depth":161,"text":266},{"id":304,"depth":161,"text":307},"content:blog:demystifying-agents-devfest.md","blog/demystifying-agents-devfest.md","blog/demystifying-agents-devfest",{"_path":338,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":339,"description":340,"date":341,"tags":342,"youtube":345,"draft":7,"body":346,"_type":168,"_id":753,"_source":170,"_file":754,"_stem":755,"_extension":173},"/blog/building-with-gemini","Building Real-Time UIs with the Gemini API","Lessons learned from building streamed, generative UIs with structured output from Google's Gemini API and Vue.js.","2025-08-15",[14,343,344],"Gemini","Vue.js","BGSOtfZ-skk",{"type":18,"children":347,"toc":747},[348,353,358,364,369,374,380,385,660,665,671,676,681,687,692,736,741],{"type":21,"tag":22,"props":349,"children":350},{},[351],{"type":26,"value":352},"One of the talks I've given most recently — at Google Developer Groups in Lisbon and Lagos — focused on a question that kept coming up in my own work: how do you build reliable, real-time user interfaces powered by LLM output?",{"type":21,"tag":22,"props":354,"children":355},{},[356],{"type":26,"value":357},"The core challenge is simple to state but hard to solve: LLMs generate text. UIs need structure. How do you bridge that gap without your application breaking every time the model returns something unexpected?",{"type":21,"tag":34,"props":359,"children":361},{"id":360},"the-problem-with-unstructured-output",[362],{"type":26,"value":363},"The problem with unstructured output",{"type":21,"tag":22,"props":365,"children":366},{},[367],{"type":26,"value":368},"If you've ever tried to parse free-form LLM text into a UI component, you know the pain. The model might return valid JSON one time and a conversational response the next. Or it might nest objects differently than expected. Or include markdown in a field you expected to be plain text.",{"type":21,"tag":22,"props":370,"children":371},{},[372],{"type":26,"value":373},"For a demo, this is fine. For production, it's a dealbreaker.",{"type":21,"tag":34,"props":375,"children":377},{"id":376},"structured-output-to-the-rescue",[378],{"type":26,"value":379},"Structured output to the rescue",{"type":21,"tag":22,"props":381,"children":382},{},[383],{"type":26,"value":384},"The Gemini API's structured output feature changes this equation entirely. By defining a JSON schema in your API request, you get deterministic, parseable responses every time. The model is constrained to only produce output that matches your schema.",{"type":21,"tag":386,"props":387,"children":391},"pre",{"className":388,"code":389,"language":390,"meta":8,"style":8},"language-typescript shiki shiki-themes github-light github-dark","const schema = {\n  type: \"object\",\n  properties: {\n    title: { type: \"string\" },\n    items: {\n      type: \"array\",\n      items: {\n        type: \"object\",\n        properties: {\n          name: { type: \"string\" },\n          description: { type: \"string\" },\n          priority: { type: \"string\", enum: [\"high\", \"medium\", \"low\"] }\n        }\n      }\n    }\n  }\n}\n","typescript",[392],{"type":21,"tag":393,"props":394,"children":395},"code",{"__ignoreMap":8},[396,425,444,453,472,481,499,508,525,534,551,568,615,624,633,642,651],{"type":21,"tag":397,"props":398,"children":401},"span",{"class":399,"line":400},"line",1,[402,408,414,419],{"type":21,"tag":397,"props":403,"children":405},{"style":404},"--shiki-default:#D73A49;--shiki-dark:#F97583",[406],{"type":26,"value":407},"const",{"type":21,"tag":397,"props":409,"children":411},{"style":410},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[412],{"type":26,"value":413}," schema",{"type":21,"tag":397,"props":415,"children":416},{"style":404},[417],{"type":26,"value":418}," =",{"type":21,"tag":397,"props":420,"children":422},{"style":421},"--shiki-default:#24292E;--shiki-dark:#E1E4E8",[423],{"type":26,"value":424}," {\n",{"type":21,"tag":397,"props":426,"children":427},{"class":399,"line":161},[428,433,439],{"type":21,"tag":397,"props":429,"children":430},{"style":421},[431],{"type":26,"value":432},"  type: ",{"type":21,"tag":397,"props":434,"children":436},{"style":435},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[437],{"type":26,"value":438},"\"object\"",{"type":21,"tag":397,"props":440,"children":441},{"style":421},[442],{"type":26,"value":443},",\n",{"type":21,"tag":397,"props":445,"children":447},{"class":399,"line":446},3,[448],{"type":21,"tag":397,"props":449,"children":450},{"style":421},[451],{"type":26,"value":452},"  properties: {\n",{"type":21,"tag":397,"props":454,"children":456},{"class":399,"line":455},4,[457,462,467],{"type":21,"tag":397,"props":458,"children":459},{"style":421},[460],{"type":26,"value":461},"    title: { type: ",{"type":21,"tag":397,"props":463,"children":464},{"style":435},[465],{"type":26,"value":466},"\"string\"",{"type":21,"tag":397,"props":468,"children":469},{"style":421},[470],{"type":26,"value":471}," },\n",{"type":21,"tag":397,"props":473,"children":475},{"class":399,"line":474},5,[476],{"type":21,"tag":397,"props":477,"children":478},{"style":421},[479],{"type":26,"value":480},"    items: {\n",{"type":21,"tag":397,"props":482,"children":484},{"class":399,"line":483},6,[485,490,495],{"type":21,"tag":397,"props":486,"children":487},{"style":421},[488],{"type":26,"value":489},"      type: ",{"type":21,"tag":397,"props":491,"children":492},{"style":435},[493],{"type":26,"value":494},"\"array\"",{"type":21,"tag":397,"props":496,"children":497},{"style":421},[498],{"type":26,"value":443},{"type":21,"tag":397,"props":500,"children":502},{"class":399,"line":501},7,[503],{"type":21,"tag":397,"props":504,"children":505},{"style":421},[506],{"type":26,"value":507},"      items: {\n",{"type":21,"tag":397,"props":509,"children":511},{"class":399,"line":510},8,[512,517,521],{"type":21,"tag":397,"props":513,"children":514},{"style":421},[515],{"type":26,"value":516},"        type: ",{"type":21,"tag":397,"props":518,"children":519},{"style":435},[520],{"type":26,"value":438},{"type":21,"tag":397,"props":522,"children":523},{"style":421},[524],{"type":26,"value":443},{"type":21,"tag":397,"props":526,"children":528},{"class":399,"line":527},9,[529],{"type":21,"tag":397,"props":530,"children":531},{"style":421},[532],{"type":26,"value":533},"        properties: {\n",{"type":21,"tag":397,"props":535,"children":537},{"class":399,"line":536},10,[538,543,547],{"type":21,"tag":397,"props":539,"children":540},{"style":421},[541],{"type":26,"value":542},"          name: { type: ",{"type":21,"tag":397,"props":544,"children":545},{"style":435},[546],{"type":26,"value":466},{"type":21,"tag":397,"props":548,"children":549},{"style":421},[550],{"type":26,"value":471},{"type":21,"tag":397,"props":552,"children":554},{"class":399,"line":553},11,[555,560,564],{"type":21,"tag":397,"props":556,"children":557},{"style":421},[558],{"type":26,"value":559},"          description: { type: ",{"type":21,"tag":397,"props":561,"children":562},{"style":435},[563],{"type":26,"value":466},{"type":21,"tag":397,"props":565,"children":566},{"style":421},[567],{"type":26,"value":471},{"type":21,"tag":397,"props":569,"children":571},{"class":399,"line":570},12,[572,577,581,586,591,596,601,605,610],{"type":21,"tag":397,"props":573,"children":574},{"style":421},[575],{"type":26,"value":576},"          priority: { type: ",{"type":21,"tag":397,"props":578,"children":579},{"style":435},[580],{"type":26,"value":466},{"type":21,"tag":397,"props":582,"children":583},{"style":421},[584],{"type":26,"value":585},", enum: [",{"type":21,"tag":397,"props":587,"children":588},{"style":435},[589],{"type":26,"value":590},"\"high\"",{"type":21,"tag":397,"props":592,"children":593},{"style":421},[594],{"type":26,"value":595},", ",{"type":21,"tag":397,"props":597,"children":598},{"style":435},[599],{"type":26,"value":600},"\"medium\"",{"type":21,"tag":397,"props":602,"children":603},{"style":421},[604],{"type":26,"value":595},{"type":21,"tag":397,"props":606,"children":607},{"style":435},[608],{"type":26,"value":609},"\"low\"",{"type":21,"tag":397,"props":611,"children":612},{"style":421},[613],{"type":26,"value":614},"] }\n",{"type":21,"tag":397,"props":616,"children":618},{"class":399,"line":617},13,[619],{"type":21,"tag":397,"props":620,"children":621},{"style":421},[622],{"type":26,"value":623},"        }\n",{"type":21,"tag":397,"props":625,"children":627},{"class":399,"line":626},14,[628],{"type":21,"tag":397,"props":629,"children":630},{"style":421},[631],{"type":26,"value":632},"      }\n",{"type":21,"tag":397,"props":634,"children":636},{"class":399,"line":635},15,[637],{"type":21,"tag":397,"props":638,"children":639},{"style":421},[640],{"type":26,"value":641},"    }\n",{"type":21,"tag":397,"props":643,"children":645},{"class":399,"line":644},16,[646],{"type":21,"tag":397,"props":647,"children":648},{"style":421},[649],{"type":26,"value":650},"  }\n",{"type":21,"tag":397,"props":652,"children":654},{"class":399,"line":653},17,[655],{"type":21,"tag":397,"props":656,"children":657},{"style":421},[658],{"type":26,"value":659},"}\n",{"type":21,"tag":22,"props":661,"children":662},{},[663],{"type":26,"value":664},"With this schema, every response from Gemini will be valid, parseable JSON that your Vue.js component can render immediately — no try/catch, no fallback UI, no \"sorry, something went wrong.\"",{"type":21,"tag":34,"props":666,"children":668},{"id":667},"streaming-makes-it-real-time",[669],{"type":26,"value":670},"Streaming makes it real-time",{"type":21,"tag":22,"props":672,"children":673},{},[674],{"type":26,"value":675},"The second piece of the puzzle is streaming. Rather than waiting for the full response, you can stream structured output token by token and render partial results. This creates the feeling of a UI that's being \"built\" in real-time right in front of the user.",{"type":21,"tag":22,"props":677,"children":678},{},[679],{"type":26,"value":680},"In Vue.js, this maps beautifully to reactive data. As each chunk arrives, you update a reactive ref, and Vue's reactivity system handles the rest.",{"type":21,"tag":34,"props":682,"children":684},{"id":683},"key-takeaways",[685],{"type":26,"value":686},"Key takeaways",{"type":21,"tag":22,"props":688,"children":689},{},[690],{"type":26,"value":691},"After shipping this pattern in production at Agosto, here's what I've learned:",{"type":21,"tag":693,"props":694,"children":695},"ol",{},[696,706,716,726],{"type":21,"tag":272,"props":697,"children":698},{},[699,704],{"type":21,"tag":89,"props":700,"children":701},{},[702],{"type":26,"value":703},"Always use structured output in production",{"type":26,"value":705}," — free-form text parsing is too fragile",{"type":21,"tag":272,"props":707,"children":708},{},[709,714],{"type":21,"tag":89,"props":710,"children":711},{},[712],{"type":26,"value":713},"Stream everything",{"type":26,"value":715}," — users perceive streamed UIs as faster, even when total time is the same",{"type":21,"tag":272,"props":717,"children":718},{},[719,724],{"type":21,"tag":89,"props":720,"children":721},{},[722],{"type":26,"value":723},"Design your schema around your UI components",{"type":26,"value":725}," — the closer the schema matches your component props, the less transformation code you need",{"type":21,"tag":272,"props":727,"children":728},{},[729,734],{"type":21,"tag":89,"props":730,"children":731},{},[732],{"type":26,"value":733},"Handle partial state gracefully",{"type":26,"value":735}," — during streaming, your data is incomplete. Design components that look good at every stage of completion",{"type":21,"tag":22,"props":737,"children":738},{},[739],{"type":26,"value":740},"The combination of Gemini's structured output and Vue.js reactivity creates a developer experience that's genuinely enjoyable. You define what you want, and the AI fills it in — reliably, every time.",{"type":21,"tag":742,"props":743,"children":744},"style",{},[745],{"type":26,"value":746},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":8,"searchDepth":161,"depth":161,"links":748},[749,750,751,752],{"id":360,"depth":161,"text":363},{"id":376,"depth":161,"text":379},{"id":667,"depth":161,"text":670},{"id":683,"depth":161,"text":686},"content:blog:building-with-gemini.md","blog/building-with-gemini.md","blog/building-with-gemini",{"_path":757,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":758,"description":759,"date":760,"tags":761,"youtube":763,"draft":7,"body":764,"_type":168,"_id":845,"_source":170,"_file":846,"_stem":847,"_extension":173},"/blog/killing-side-project-graveyard","How I Killed My \"Side Project Graveyard\"","Boilerplate friction is the number one killer of side projects. Here's how I broke the cycle and built RaumRadar in just five days using AI.","2025-06-01",[14,762,344],"Productivity","5uTYu50de4w",{"type":18,"children":765,"toc":841},[766,771,776,782,815,820,824,829],{"type":21,"tag":22,"props":767,"children":768},{},[769],{"type":26,"value":770},"Are you tired of abandoning great side project ideas just because you don't want to deal with configuration hell after a long day at work? Boilerplate friction is the number one killer of side projects.",{"type":21,"tag":22,"props":772,"children":773},{},[774],{"type":26,"value":775},"In my latest video, I share how I finally broke this cycle and built my new app, RaumRadar (a platform for rating co-working spaces), in just five days using Google IDX and Gemini.",{"type":21,"tag":34,"props":777,"children":779},{"id":778},"the-secret-sauce-to-shipping-fast",[780],{"type":26,"value":781},"The Secret Sauce to Shipping Fast",{"type":21,"tag":268,"props":783,"children":784},{},[785,795,805],{"type":21,"tag":272,"props":786,"children":787},{},[788,793],{"type":21,"tag":89,"props":789,"children":790},{},[791],{"type":26,"value":792},"Don't just \"vibe code\":",{"type":26,"value":794}," Write a clear Product Requirements Document (PRD) first. Define what success looks like.",{"type":21,"tag":272,"props":796,"children":797},{},[798,803],{"type":21,"tag":89,"props":799,"children":800},{},[801],{"type":26,"value":802},"Define your stack:",{"type":26,"value":804}," I set clear boundaries using Nuxt 3, Tailwind, Postgres, and Cloud Run.",{"type":21,"tag":272,"props":806,"children":807},{},[808,813],{"type":21,"tag":89,"props":809,"children":810},{},[811],{"type":26,"value":812},"Treat AI like a junior engineer:",{"type":26,"value":814}," Give clear instructions and constraints, rather than treating it like a magic wand.",{"type":21,"tag":22,"props":816,"children":817},{},[818],{"type":26,"value":819},"The biggest revelation happened on day three. When I needed to make complex database refactors to my amenities data model, the AI didn't lose context, hallucinate, or break my app. It made surgical changes perfectly.",{"type":21,"tag":34,"props":821,"children":822},{"id":304},[823],{"type":26,"value":307},{"type":21,"tag":22,"props":825,"children":826},{},[827],{"type":26,"value":828},"AI isn't replacing engineers; it's turning our experience into pure leverage. We are no longer bottlenecked by our typing speed, but by our ability to see around corners and architect solutions.",{"type":21,"tag":22,"props":830,"children":831},{},[832,834],{"type":26,"value":833},"Watch the full breakdown of my 5-day build process here: ",{"type":21,"tag":192,"props":835,"children":838},{"href":836,"rel":837},"https://www.youtube.com/watch?v=5uTYu50de4w",[196],[839],{"type":26,"value":840},"youtube.com/watch?v=5uTYu50de4w",{"title":8,"searchDepth":161,"depth":161,"links":842},[843,844],{"id":778,"depth":161,"text":781},{"id":304,"depth":161,"text":307},"content:blog:killing-side-project-graveyard.md","blog/killing-side-project-graveyard.md","blog/killing-side-project-graveyard",{"_path":849,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":850,"description":851,"date":852,"tags":853,"draft":7,"body":856,"_type":168,"_id":1018,"_source":170,"_file":1019,"_stem":1020,"_extension":173},"/blog/call-thing-postmortem","Building Call-Thing: A 7-Month AI Phone Agent Post-Mortem","I built a fully autonomous AI phone agent for restaurant reservations. Then OpenAI released Voice Mode and I killed the project. Here's what was under the hood.","2024-06-15",[14,854,855],"Python","Post-Mortem",{"type":18,"children":857,"toc":1013},[858,863,868,873,879,884,937,943,953,963,982,992,998,1003,1008],{"type":21,"tag":22,"props":859,"children":860},{},[861],{"type":26,"value":862},"Over seven months, I built Call-Thing—a fully autonomous AI phone agent designed to handle restaurant reservations. It dialed out via SIP, listened, transcribed, generated LLM responses, and spoke back in real-time.",{"type":21,"tag":22,"props":864,"children":865},{},[866],{"type":26,"value":867},"Then OpenAI released Voice Mode, and I killed the project.",{"type":21,"tag":22,"props":869,"children":870},{},[871],{"type":26,"value":872},"Here is a look under the hood of what I built before a single API update made it obsolete.",{"type":21,"tag":34,"props":874,"children":876},{"id":875},"the-architecture-7-parallel-processes",[877],{"type":26,"value":878},"The Architecture: 7 Parallel Processes",{"type":21,"tag":22,"props":880,"children":881},{},[882],{"type":26,"value":883},"To achieve real-time conversational speeds, I split the pipeline into seven parallel worker processes communicating via TCP sockets and a custom 10-byte header IPC protocol.",{"type":21,"tag":268,"props":885,"children":886},{},[887,897,907,917,927],{"type":21,"tag":272,"props":888,"children":889},{},[890,895],{"type":21,"tag":89,"props":891,"children":892},{},[893],{"type":26,"value":894},"Audio Capture:",{"type":26,"value":896}," Baresip streaming Opus 48kHz audio.",{"type":21,"tag":272,"props":898,"children":899},{},[900,905],{"type":21,"tag":89,"props":901,"children":902},{},[903],{"type":26,"value":904},"VAD & Silence Detection:",{"type":26,"value":906}," Silero VAD processing 400ms chunks, paired with a custom silence detector to stop Whisper from hallucinating on dead air.",{"type":21,"tag":272,"props":908,"children":909},{},[910,915],{"type":21,"tag":89,"props":911,"children":912},{},[913],{"type":26,"value":914},"Transcription & Agentic LLM:",{"type":26,"value":916}," Whisper translated speech to text, passing it to a fine-tuned GPT-3.5-turbo. By hooking it up with function calling for live reservation modifications, it was essentially doing \"agentic AI\" before everyone started using the buzzword.",{"type":21,"tag":272,"props":918,"children":919},{},[920,925],{"type":21,"tag":89,"props":921,"children":922},{},[923],{"type":26,"value":924},"Caching:",{"type":26,"value":926}," Qdrant checked for semantic similarity (>0.85) to serve cached responses, saving both latency and cost.",{"type":21,"tag":272,"props":928,"children":929},{},[930,935],{"type":21,"tag":89,"props":931,"children":932},{},[933],{"type":26,"value":934},"TTS:",{"type":26,"value":936}," Google Cloud TTS generated audio for immediate SIP playback.",{"type":21,"tag":34,"props":938,"children":940},{"id":939},"the-hardest-battles",[941],{"type":26,"value":942},"The Hardest Battles",{"type":21,"tag":22,"props":944,"children":945},{},[946,951],{"type":21,"tag":89,"props":947,"children":948},{},[949],{"type":26,"value":950},"Python's GIL:",{"type":26,"value":952}," Threading could not beat the Global Interpreter Lock. Switching to multiprocessing in spawn mode (required for CUDA) was the breakthrough that finally enabled low-latency conversations.",{"type":21,"tag":22,"props":954,"children":955},{},[956,961],{"type":21,"tag":89,"props":957,"children":958},{},[959],{"type":26,"value":960},"Local vs. Cloud:",{"type":26,"value":962}," After weeks of testing local fine-tunes (Gemma, Mistral) on WSL, I pivoted to GPT-3.5-turbo. Relying on local models meant downtime whenever my PC in Germany was turned off—a dealbreaker for availability.",{"type":21,"tag":22,"props":964,"children":965},{},[966,971,973,980],{"type":21,"tag":89,"props":967,"children":968},{},[969],{"type":26,"value":970},"The TTS Rabbit Hole:",{"type":26,"value":972}," I spent a good chunk of time trying to get ",{"type":21,"tag":192,"props":974,"children":977},{"href":975,"rel":976},"https://github.com/coqui-ai/TTS",[196],[978],{"type":26,"value":979},"Coqui TTS",{"type":26,"value":981}," running locally for faster response times. The quality was promising, but getting it to produce reliable, low-latency output in a real-time pipeline was a battle I eventually lost—Google Cloud TTS won on consistency.",{"type":21,"tag":22,"props":983,"children":984},{},[985,990],{"type":21,"tag":89,"props":986,"children":987},{},[988],{"type":26,"value":989},"The Docker Diet:",{"type":26,"value":991}," Packing ML models and audio libraries together initially resulted in a massive 65GB Docker image. Shrinking that down to 15GB through multi-stage builds and strict dependency pruning was a war story in itself.",{"type":21,"tag":34,"props":993,"children":995},{"id":994},"the-end",[996],{"type":26,"value":997},"The End",{"type":21,"tag":22,"props":999,"children":1000},{},[1001],{"type":26,"value":1002},"By the end, Call-Thing was deployed on Scaleway. A TypeScript Job Coordinator watched a Firestore queue, spawning parallel Docker containers on demand. It supported English, German, Spanish, and Dutch, autonomously detecting and switching languages at runtime.",{"type":21,"tag":22,"props":1004,"children":1005},{},[1006],{"type":26,"value":1007},"It was an incredible technical gauntlet—right up until OpenAI's Voice Mode turned seven months of latency optimization and pipeline orchestration into a few lines of code. May it rest in peace.",{"type":21,"tag":22,"props":1009,"children":1010},{},[1011],{"type":26,"value":1012},"Maybe one day I'll resurrect it using ElevenLabs or Gemini Live.",{"title":8,"searchDepth":161,"depth":161,"links":1014},[1015,1016,1017],{"id":875,"depth":161,"text":878},{"id":939,"depth":161,"text":942},{"id":994,"depth":161,"text":997},"content:blog:call-thing-postmortem.md","blog/call-thing-postmortem.md","blog/call-thing-postmortem",{"_path":1022,"_dir":6,"_draft":7,"_partial":7,"_locale":8,"title":1023,"description":1024,"date":1025,"tags":1026,"draft":7,"body":1029,"_type":168,"_id":1136,"_source":170,"_file":1137,"_stem":1138,"_extension":173},"/blog/hello-world","Hello World: Why I Started Writing","After years of building products and speaking at events, I'm finally putting thoughts to paper. Here's why a blog felt like the right next step.","2023-03-01",[1027,1028],"Personal","Writing",{"type":18,"children":1030,"toc":1132},[1031,1036,1041,1047,1052,1095,1101,1106,1111],{"type":21,"tag":22,"props":1032,"children":1033},{},[1034],{"type":26,"value":1035},"For years, my way of sharing knowledge has been through code and conference talks. Building products like Agosto and RaumRadar, speaking at Google DevFest, and teaching at the Frankfurt University of Applied Sciences — these have all been ways to contribute to the developer community.",{"type":21,"tag":22,"props":1037,"children":1038},{},[1039],{"type":26,"value":1040},"But I've increasingly felt the need for a different format. Talks are great for energy and live interaction, but they're ephemeral. A blog post lives on. It can be referenced, shared, and revisited.",{"type":21,"tag":34,"props":1042,"children":1044},{"id":1043},"what-to-expect",[1045],{"type":26,"value":1046},"What to expect",{"type":21,"tag":22,"props":1048,"children":1049},{},[1050],{"type":26,"value":1051},"I'll be writing about the things I know best:",{"type":21,"tag":268,"props":1053,"children":1054},{},[1055,1065,1075,1085],{"type":21,"tag":272,"props":1056,"children":1057},{},[1058,1063],{"type":21,"tag":89,"props":1059,"children":1060},{},[1061],{"type":26,"value":1062},"AI in production",{"type":26,"value":1064}," — not just demos, but the messy reality of shipping AI-powered features to real users",{"type":21,"tag":272,"props":1066,"children":1067},{},[1068,1073],{"type":21,"tag":89,"props":1069,"children":1070},{},[1071],{"type":26,"value":1072},"Cloud architecture",{"type":26,"value":1074}," — lessons from building on Google Cloud across multiple products",{"type":21,"tag":272,"props":1076,"children":1077},{},[1078,1083],{"type":21,"tag":89,"props":1079,"children":1080},{},[1081],{"type":26,"value":1082},"The founder-engineer perspective",{"type":26,"value":1084}," — what changes when you're both building the product and running the business",{"type":21,"tag":272,"props":1086,"children":1087},{},[1088,1093],{"type":21,"tag":89,"props":1089,"children":1090},{},[1091],{"type":26,"value":1092},"Vue.js and frontend engineering",{"type":26,"value":1094}," — practical patterns from production applications",{"type":21,"tag":34,"props":1096,"children":1098},{"id":1097},"why-now",[1099],{"type":26,"value":1100},"Why now?",{"type":21,"tag":22,"props":1102,"children":1103},{},[1104],{"type":26,"value":1105},"Honestly, I should have started years ago. The best time to plant a tree was twenty years ago. The second best time is now.",{"type":21,"tag":22,"props":1107,"children":1108},{},[1109],{"type":26,"value":1110},"Every talk I give generates questions I can't fully answer in a 30-minute slot. Every project I ship teaches lessons that deserve more than a tweet. This blog is where those deeper thoughts will live.",{"type":21,"tag":22,"props":1112,"children":1113},{},[1114,1116,1123,1125,1131],{"type":26,"value":1115},"If you find something useful here, I'd love to hear about it. Reach out on ",{"type":21,"tag":192,"props":1117,"children":1120},{"href":1118,"rel":1119},"https://linkedin.com/in/alex-amin",[196],[1121],{"type":26,"value":1122},"LinkedIn",{"type":26,"value":1124}," or ",{"type":21,"tag":192,"props":1126,"children":1128},{"href":320,"rel":1127},[196],[1129],{"type":26,"value":1130},"GitHub",{"type":26,"value":201},{"title":8,"searchDepth":161,"depth":161,"links":1133},[1134,1135],{"id":1043,"depth":161,"text":1046},{"id":1097,"depth":161,"text":1100},"content:blog:hello-world.md","blog/hello-world.md","blog/hello-world",1773128206634]