[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"blog-all":3},{"posts":4,"tags":2842},[5,442,656,1328,2081,2293,2438,2735],{"title":6,"slug":7,"date":8,"excerpt":9,"coverImage":10,"body":11,"tags":439},"Code Reviews, Quality, and All the Ways We Screw It Up","code-reviews","2025-08-16","Code quality isn’t about nitpicking style or chasing 100% test coverage—it’s about habits. Small PRs, constructive feedback, shared ownership, and learning from mistakes turn quality from a buzzword into a culture teams actually live by.","https://images.ctfassets.net/26iu5vjpoesw/4l6Mb1emgPs48ipoyfW7Gg/03725fdbd00a148c26b653765d4fda9d/volodymyr-dobrovolskyy-KrYbarbAx5s-unsplash.jpg",{"data":12,"content":13,"nodeType":438},{},[14,23,30,37,44,53,60,67,74,81,88,96,103,110,117,124,132,139,146,153,208,215,223,230,237,244,252,259,266,273,280,288,295,302,309,342,349,356,363,371,378,431],{"data":15,"content":16,"nodeType":22},{},[17],{"data":18,"marks":19,"value":20,"nodeType":21},{},[],"Everyone loves to talk about “quality.” It’s one of those words that gets tossed around in kickoff meetings and strategy docs like it’s obvious what it means. But when you actually sit down with engineers and ask, you get wildly different answers.","text","paragraph",{"data":24,"content":25,"nodeType":22},{},[26],{"data":27,"marks":28,"value":29,"nodeType":21},{},[],"One person thinks it means zero bugs in production. Another thinks it means code that’s easy to read. Someone else swears it’s about test coverage. Then there’s the person who just wants it to be easy to onboard new hires.",{"data":31,"content":32,"nodeType":22},{},[33],{"data":34,"marks":35,"value":36,"nodeType":21},{},[],"They’re all right. And that’s the problem. “Quality” is a moving target.",{"data":38,"content":39,"nodeType":22},{},[40],{"data":41,"marks":42,"value":43,"nodeType":21},{},[],"What I’ve learned is that quality isn’t something you get by decree. You don’t just say “We care about quality” and magically end up with a clean codebase. It’s something you build into your team’s habits - how you write, review, and ship code, and how you deal with screwups when (not if) they happen.",{"data":45,"content":46,"nodeType":22},{},[47],{"data":48,"marks":49,"value":52,"nodeType":21},{},[50],{"type":51},"bold","Automate the Bikeshedding",{"data":54,"content":55,"nodeType":22},{},[56],{"data":57,"marks":58,"value":59,"nodeType":21},{},[],"I once spent 15 minutes in a review arguing about whether snake_case or camelCase should be used for a local variable. The PR wasn’t even that interesting - it was fixing a bug in a log parser - but we were deep in the weeds debating naming like it was a philosophy seminar.",{"data":61,"content":62,"nodeType":22},{},[63],{"data":64,"marks":65,"value":66,"nodeType":21},{},[],"That was the moment I realised: people should not be wasting oxygen on this.",{"data":68,"content":69,"nodeType":22},{},[70],{"data":71,"marks":72,"value":73,"nodeType":21},{},[],"This is why linters and formatters exist. Let them be the bad cops. Humans should focus on whether the code makes sense, not whether it’s indented correctly.",{"data":75,"content":76,"nodeType":22},{},[77],{"data":78,"marks":79,"value":80,"nodeType":21},{},[],"If you’re in JavaScript, lean on Prettier and ESLint. Don’t invent a whole new style guide unless you want future engineers cursing your name in Slack. And if you do deviate, document why. “Because I said so” doesn’t cut it when a new hire has no idea why your project bans default exports.",{"data":82,"content":83,"nodeType":22},{},[84],{"data":85,"marks":86,"value":87,"nodeType":21},{},[],"The best teams I’ve worked with had their CI set up so if your PR failed linting, it didn’t even get reviewed. End of story. It sounds harsh, but it saves hours of nitpicking and keeps reviews focused on what matters.",{"data":89,"content":90,"nodeType":22},{},[91],{"data":92,"marks":93,"value":95,"nodeType":21},{},[94],{"type":51},"The Curse of the Monster PR",{"data":97,"content":98,"nodeType":22},{},[99],{"data":100,"marks":101,"value":102,"nodeType":21},{},[],"Here’s a story: one Friday afternoon, a teammate dropped a PR that was nearly 2,500 lines. A new feature and a major refactor, rolled into one. Nobody wanted to touch it. Reviewers skimmed, left a couple of half-hearted comments, and rubber-stamped it.",{"data":104,"content":105,"nodeType":22},{},[106],{"data":107,"marks":108,"value":109,"nodeType":21},{},[],"Guess what happened the next Monday? Production broke. The bug wasn’t subtle either - it was buried somewhere in the 2,500 lines of “trust me, it works.”",{"data":111,"content":112,"nodeType":22},{},[113],{"data":114,"marks":115,"value":116,"nodeType":21},{},[],"Since then, I’ve been ruthless about PR size. Small PRs get reviewed quickly, merged quickly, and if something goes wrong, they’re easy to roll back. Large PRs rot in limbo or worse, slip through with hidden landmines.",{"data":118,"content":119,"nodeType":22},{},[120],{"data":121,"marks":122,"value":123,"nodeType":21},{},[],"So: one logical change per PR. Add a feature, or clean up a function, or refactor an interface - not all three. If you need to make a monster change, break it into stacked PRs. Your future self (and your teammates) will thank you.",{"data":125,"content":126,"nodeType":22},{},[127],{"data":128,"marks":129,"value":131,"nodeType":21},{},[130],{"type":51},"Checklists Are Your Friend",{"data":133,"content":134,"nodeType":22},{},[135],{"data":136,"marks":137,"value":138,"nodeType":21},{},[],"Reviews are exhausting. If you’ve been cranking out features all day and someone throws a PR at you, it’s way too easy to glance at the diff, nod, and hit approve.",{"data":140,"content":141,"nodeType":22},{},[142],{"data":143,"marks":144,"value":145,"nodeType":21},{},[],"That’s how things slip through the cracks.",{"data":147,"content":148,"nodeType":22},{},[149],{"data":150,"marks":151,"value":152,"nodeType":21},{},[],"The teams I’ve seen succeed use checklists. Not long, bureaucratic ones - just a handful of prompts:",{"data":154,"content":155,"nodeType":207},{},[156,167,177,187,197],{"data":157,"content":158,"nodeType":166},{},[159],{"data":160,"content":161,"nodeType":22},{},[162],{"data":163,"marks":164,"value":165,"nodeType":21},{},[],"Does this match our architecture patterns?","list-item",{"data":168,"content":169,"nodeType":166},{},[170],{"data":171,"content":172,"nodeType":22},{},[173],{"data":174,"marks":175,"value":176,"nodeType":21},{},[],"Are there tests? Do they cover the edges?",{"data":178,"content":179,"nodeType":166},{},[180],{"data":181,"content":182,"nodeType":22},{},[183],{"data":184,"marks":185,"value":186,"nodeType":21},{},[],"Any obvious performance issues?",{"data":188,"content":189,"nodeType":166},{},[190],{"data":191,"content":192,"nodeType":22},{},[193],{"data":194,"marks":195,"value":196,"nodeType":21},{},[],"Security landmines?",{"data":198,"content":199,"nodeType":166},{},[200],{"data":201,"content":202,"nodeType":22},{},[203],{"data":204,"marks":205,"value":206,"nodeType":21},{},[],"Enough logging and metrics to debug later?","unordered-list",{"data":209,"content":210,"nodeType":22},{},[211],{"data":212,"marks":213,"value":214,"nodeType":21},{},[],"I once caught a major security flaw because of a checklist. A teammate had added logging… but was logging user passwords in plain text. Easy to miss if you’re skimming. The checklist forced me to stop and ask, “Wait, how are secrets handled here?” That one line item saved us from a nightmare.",{"data":216,"content":217,"nodeType":22},{},[218],{"data":219,"marks":220,"value":222,"nodeType":21},{},[221],{"type":51},"Feedback Without Ego",{"data":224,"content":225,"nodeType":22},{},[226],{"data":227,"marks":228,"value":229,"nodeType":21},{},[],"If you’ve ever gotten a review that felt like an attack, you know how demoralising it can be. I had a reviewer once leave comments like “This is garbage” and “Rewrite this, it’s wrong.” They weren’t wrong about the code needing improvement - but the way it was delivered made me dread pushing anything else.",{"data":231,"content":232,"nodeType":22},{},[233],{"data":234,"marks":235,"value":236,"nodeType":21},{},[],"The best reviewers I’ve worked with do the opposite. They treat reviews as collaboration, not judgment. They’ll say things like, “I had a hard time following this loop - could we simplify or add a comment?” Or, “What if we tried this other pattern to reduce complexity?”",{"data":238,"content":239,"nodeType":22},{},[240],{"data":241,"marks":242,"value":243,"nodeType":21},{},[],"It’s not about sugarcoating. It’s about keeping the focus on the code, not the person. If you destroy someone’s confidence, you don’t just slow them down - you slow the whole team.",{"data":245,"content":246,"nodeType":22},{},[247],{"data":248,"marks":249,"value":251,"nodeType":21},{},[250],{"type":51},"Don’t Hoard Knowledge",{"data":253,"content":254,"nodeType":22},{},[255],{"data":256,"marks":257,"value":258,"nodeType":21},{},[],"There’s a dangerous pattern where one person becomes the owner of a module. They’re the only one who knows how it works, so every change goes through them. At first, it feels efficient. Over time, it becomes a single point of failure.",{"data":260,"content":261,"nodeType":22},{},[262],{"data":263,"marks":264,"value":265,"nodeType":21},{},[],"I was on a team once where the only person who understood a critical service went on vacation. Of course, that was the week it blew up. The rest of us were flying blind because all the knowledge was locked in their head. We eventually fixed it, but it was painful and avoidable.",{"data":267,"content":268,"nodeType":22},{},[269],{"data":270,"marks":271,"value":272,"nodeType":21},{},[],"The fix is simple: rotate reviewers. Have people outside the “home team” of a module review changes. Let junior engineers shadow reviews and gradually step up. Build buddy systems so knowledge gets spread around.",{"data":274,"content":275,"nodeType":22},{},[276],{"data":277,"marks":278,"value":279,"nodeType":21},{},[],"A healthy codebase is one where no single person is a bottleneck.",{"data":281,"content":282,"nodeType":22},{},[283],{"data":284,"marks":285,"value":287,"nodeType":21},{},[286],{"type":51},"Mistakes Are Gold (If You Treat Them Right)",{"data":289,"content":290,"nodeType":22},{},[291],{"data":292,"marks":293,"value":294,"nodeType":21},{},[],"Stuff will break. Deploys will fail. Bugs will sneak past reviews. What matters is how you respond.",{"data":296,"content":297,"nodeType":22},{},[298],{"data":299,"marks":300,"value":301,"nodeType":21},{},[],"The worst thing you can do is start pointing fingers. I’ve seen teams spend more time arguing about who caused the outage than actually fixing it. That’s a culture killer.",{"data":303,"content":304,"nodeType":22},{},[305],{"data":306,"marks":307,"value":308,"nodeType":21},{},[],"The best teams run blameless postmortems. They ask:",{"data":310,"content":311,"nodeType":207},{},[312,322,332],{"data":313,"content":314,"nodeType":166},{},[315],{"data":316,"content":317,"nodeType":22},{},[318],{"data":319,"marks":320,"value":321,"nodeType":21},{},[],"How did our process let this happen?",{"data":323,"content":324,"nodeType":166},{},[325],{"data":326,"content":327,"nodeType":22},{},[328],{"data":329,"marks":330,"value":331,"nodeType":21},{},[],"Where was the gap in our testing, logging, or review?",{"data":333,"content":334,"nodeType":166},{},[335],{"data":336,"content":337,"nodeType":22},{},[338],{"data":339,"marks":340,"value":341,"nodeType":21},{},[],"What do we need to change so it doesn’t happen again?",{"data":343,"content":344,"nodeType":22},{},[345],{"data":346,"marks":347,"value":348,"nodeType":21},{},[],"I’ll never forget one outage we had that took down a core service for hours. The root cause wasn’t that someone “screwed up” - it was that our system made it way too easy to screw up silently. Once we reframed it like that, we fixed the system, not the person.",{"data":350,"content":351,"nodeType":22},{},[352],{"data":353,"marks":354,"value":355,"nodeType":21},{},[],"Same goes for retros. The most productive ones aren’t just “what went well, what didn’t.” They’re about experimenting: trying new review habits, new testing practices, new tooling, and seeing what sticks.",{"data":357,"content":358,"nodeType":22},{},[359],{"data":360,"marks":361,"value":362,"nodeType":21},{},[],"And when you ship something? Don’t treat it as “done.” Treat it as a hypothesis. Measure the impact, adjust, and move forward. Some experiments win, some fail - but both are learning.",{"data":364,"content":365,"nodeType":22},{},[366],{"data":367,"marks":368,"value":370,"nodeType":21},{},[369],{"type":51},"Closing Thought",{"data":372,"content":373,"nodeType":22},{},[374],{"data":375,"marks":376,"value":377,"nodeType":21},{},[],"Code quality isn’t about pedantic style wars or chasing arbitrary metrics. It’s about building habits where:",{"data":379,"content":380,"nodeType":207},{},[381,391,401,411,421],{"data":382,"content":383,"nodeType":166},{},[384],{"data":385,"content":386,"nodeType":22},{},[387],{"data":388,"marks":389,"value":390,"nodeType":21},{},[],"Tools handle the trivial stuff.",{"data":392,"content":393,"nodeType":166},{},[394],{"data":395,"content":396,"nodeType":22},{},[397],{"data":398,"marks":399,"value":400,"nodeType":21},{},[],"PRs stay small and reviewable.",{"data":402,"content":403,"nodeType":166},{},[404],{"data":405,"content":406,"nodeType":22},{},[407],{"data":408,"marks":409,"value":410,"nodeType":21},{},[],"Feedback is constructive.",{"data":412,"content":413,"nodeType":166},{},[414],{"data":415,"content":416,"nodeType":22},{},[417],{"data":418,"marks":419,"value":420,"nodeType":21},{},[],"Knowledge is shared.",{"data":422,"content":423,"nodeType":166},{},[424],{"data":425,"content":426,"nodeType":22},{},[427],{"data":428,"marks":429,"value":430,"nodeType":21},{},[],"Mistakes become lessons, not weapons.",{"data":432,"content":433,"nodeType":22},{},[434],{"data":435,"marks":436,"value":437,"nodeType":21},{},[],"If you get those right, quality stops being a buzzword. It becomes the default. Not because someone said so, but because the team wouldn’t work any other way.","document",[440,441],"best-practices","opinion",{"title":443,"slug":444,"date":445,"excerpt":446,"coverImage":447,"body":448,"tag":653,"tags":654},"My Everyday Carry: A Refined Blend of Function and Style","my-everyday-carry","2025-05-15","Over the past few weeks, I’ve been dialing in my Everyday Carry (EDC) setup, and I’ve finally landed on a gear combination that hits the sweet spot between functionality, durability, and style.","https://images.ctfassets.net/26iu5vjpoesw/7mUoNxFyELpTZejOIqK1mX/5f6c3ec535b0c71881f0054a5ad2e678/057D8942-E80F-4CDA-8A33-C25008E7A642.JPG",{"data":449,"content":450,"nodeType":438},{},[451,458,462,471,487,512,515,523,539,555,558,566,591,598,601,609,625,628,636],{"data":452,"content":453,"nodeType":22},{},[454],{"data":455,"marks":456,"value":457,"nodeType":21},{},[],"Over the past few weeks, I’ve been dialing in my Everyday Carry (EDC) setup, and I’ve finally landed on a gear combination that hits the sweet spot between functionality, durability, and style. I’m a believer in carrying only what I actually use, but I also think there’s room for a bit of personality in the tools we keep close. Here’s a breakdown of what’s currently in my pockets (and on my wrist).",{"data":459,"content":460,"nodeType":461},{},[],"hr",{"data":463,"content":464,"nodeType":470},{},[465],{"data":466,"marks":467,"value":469,"nodeType":21},{},[468],{"type":51},"1. iPhone 16 Pro Max – Spigen C1 Case in Classic Orange","heading-3",{"data":472,"content":473,"nodeType":22},{},[474,478,483],{"data":475,"marks":476,"value":477,"nodeType":21},{},[],"Let’s start with the centerpiece: my ",{"data":479,"marks":480,"value":482,"nodeType":21},{},[481],{"type":51},"iPhone 16 Pro Max",{"data":484,"marks":485,"value":486,"nodeType":21},{},[],". It’s a powerhouse of a phone with the camera capabilities and processing muscle to handle everything from day-to-day tasks to spontaneous content creation. What really makes it stand out, though, is the case.",{"data":488,"content":489,"nodeType":22},{},[490,494,499,503,508],{"data":491,"marks":492,"value":493,"nodeType":21},{},[],"I’m rocking the ",{"data":495,"marks":496,"value":498,"nodeType":21},{},[497],{"type":51},"Spigen C1 case",{"data":500,"marks":501,"value":502,"nodeType":21},{},[]," in ",{"data":504,"marks":505,"value":507,"nodeType":21},{},[506],{"type":51},"classic orange",{"data":509,"marks":510,"value":511,"nodeType":21},{},[],", and it’s become a bit of a signature piece for me. It’s a nod to the original iMac G3 days, with a translucent design that merges retro vibes with modern protection. The orange pops just enough to feel bold without being flashy, and it adds a nice layer of grip without bulking things up.",{"data":513,"content":514,"nodeType":461},{},[],{"data":516,"content":517,"nodeType":470},{},[518],{"data":519,"marks":520,"value":522,"nodeType":21},{},[521],{"type":51},"2. Apple Watch Ultra 2 – Orange Alpine Loop Strap",{"data":524,"content":525,"nodeType":22},{},[526,530,535],{"data":527,"marks":528,"value":529,"nodeType":21},{},[],"On my wrist, I’m wearing the ",{"data":531,"marks":532,"value":534,"nodeType":21},{},[533],{"type":51},"Apple Watch Ultra 2",{"data":536,"marks":537,"value":538,"nodeType":21},{},[],", and if I had to pick one piece of tech I can't leave home without, this would be it. Between fitness tracking, navigation, and staying connected when my phone’s buried in a jacket pocket, it’s become indispensable.",{"data":540,"content":541,"nodeType":22},{},[542,546,551],{"data":543,"marks":544,"value":545,"nodeType":21},{},[],"The ",{"data":547,"marks":548,"value":550,"nodeType":21},{},[549],{"type":51},"orange Alpine Loop",{"data":552,"marks":553,"value":554,"nodeType":21},{},[]," complements the rugged design of the Ultra 2 perfectly. Not only is it insanely durable (and great for workouts or hikes), but the orange theme ties everything in my EDC together. Plus, the high-contrast look makes it easy to pair with both casual and outdoor fits.",{"data":556,"content":557,"nodeType":461},{},[],{"data":559,"content":560,"nodeType":470},{},[561],{"data":562,"marks":563,"value":565,"nodeType":21},{},[564],{"type":51},"3. Apple AirTag – Orange Leather Key Ring",{"data":567,"content":568,"nodeType":22},{},[569,573,578,582,587],{"data":570,"marks":571,"value":572,"nodeType":21},{},[],"Keys? Check. Location? Always accounted for. I’ve tucked an ",{"data":574,"marks":575,"value":577,"nodeType":21},{},[576],{"type":51},"Apple AirTag",{"data":579,"marks":580,"value":581,"nodeType":21},{},[]," into an ",{"data":583,"marks":584,"value":586,"nodeType":21},{},[585],{"type":51},"orange leather key ring",{"data":588,"marks":589,"value":590,"nodeType":21},{},[],", and it’s been one of those “set it and forget it” pieces that gives me peace of mind. The leather key ring feels premium and softens nicely with use, and yes – it continues the orange theme I’ve accidentally committed to.",{"data":592,"content":593,"nodeType":22},{},[594],{"data":595,"marks":596,"value":597,"nodeType":21},{},[],"It’s a small thing, but knowing I can track down my keys in seconds is a quality-of-life upgrade I didn’t know I needed until I had it.",{"data":599,"content":600,"nodeType":461},{},[],{"data":602,"content":603,"nodeType":470},{},[604],{"data":605,"marks":606,"value":608,"nodeType":21},{},[607],{"type":51},"4. Pirna Slim RFID Blocking Wallet",{"data":610,"content":611,"nodeType":22},{},[612,616,621],{"data":613,"marks":614,"value":615,"nodeType":21},{},[],"Last but definitely not least, I carry a ",{"data":617,"marks":618,"value":620,"nodeType":21},{},[619],{"type":51},"Pirna slim RFID-blocking wallet",{"data":622,"marks":623,"value":624,"nodeType":21},{},[],". Minimalist, sleek, and practical, this wallet does exactly what I need it to: keep my cards and a bit of cash secure, while fitting discreetly in a front pocket. The RFID protection is a nice bonus in today’s hyper-connected world, and the slim profile ensures I’m not lugging around a brick in my jeans.",{"data":626,"content":627,"nodeType":461},{},[],{"data":629,"content":630,"nodeType":470},{},[631],{"data":632,"marks":633,"value":635,"nodeType":21},{},[634],{"type":51},"Final Thoughts",{"data":637,"content":638,"nodeType":22},{},[639,643,649],{"data":640,"marks":641,"value":642,"nodeType":21},{},[],"My EDC might not be the flashiest, but it’s ",{"data":644,"marks":645,"value":648,"nodeType":21},{},[646],{"type":647},"italic","me",{"data":650,"marks":651,"value":652,"nodeType":21},{},[]," – clean, functional, and with just enough flair to stand out. Whether it's the consistent orange accents or the smart balance of tech and utility, each piece plays a role in making everyday life a little smoother. If you're looking to dial in your own carry, my advice is this: start with what you actually use, and let form follow function. The style part? That’ll come naturally.","edc",[655],"personal",{"title":657,"slug":658,"date":659,"excerpt":660,"coverImage":661,"body":662,"tag":1321,"tags":1322},"Embracing Cypress for Comprehensive Testing","cypress-testing","2025-05-03","This week, I explored Cypress for both end-to-end and component testing in my Laravel, Vue, and Inertia app. Discover how this approach enhanced my development workflow.","https://images.ctfassets.net/26iu5vjpoesw/3htXQqBIqsl8eO1DHCGoFN/359a390b3e1708845c8f485b996111bc/Cypress_Testing_End_to_End.jpg",{"data":663,"content":664,"nodeType":438},{},[665,703,710,713,721,728,735,798,805,812,859,876,884,920,927,934,950,975,999,1006,1013,1020,1270,1277,1284,1300,1307,1314],{"data":666,"content":667,"nodeType":22},{},[668,672,681,685,690,694,699],{"data":669,"marks":670,"value":671,"nodeType":21},{},[],"This week has been all about refining the testing strategy for my Laravel + Vue + Inertia app—and honestly, it’s been one of those productive deep-dives that makes you wonder why you didn’t do it sooner. I decided to go all-in on ",{"data":673,"content":675,"nodeType":680},{"uri":674},"https://www.cypress.io/",[676],{"data":677,"marks":678,"value":679,"nodeType":21},{},[],"Cypress","hyperlink",{"data":682,"marks":683,"value":684,"nodeType":21},{},[],", exploring both ",{"data":686,"marks":687,"value":689,"nodeType":21},{},[688],{"type":51},"end-to-end (E2E)",{"data":691,"marks":692,"value":693,"nodeType":21},{},[]," and ",{"data":695,"marks":696,"value":698,"nodeType":21},{},[697],{"type":51},"component testing",{"data":700,"marks":701,"value":702,"nodeType":21},{},[]," capabilities.",{"data":704,"content":705,"nodeType":22},{},[706],{"data":707,"marks":708,"value":709,"nodeType":21},{},[],"And spoiler alert: I’m not looking back at Laravel Dusk.",{"data":711,"content":712,"nodeType":461},{},[],{"data":714,"content":715,"nodeType":720},{},[716],{"data":717,"marks":718,"value":719,"nodeType":21},{},[],"Why Cypress?","heading-2",{"data":722,"content":723,"nodeType":22},{},[724],{"data":725,"marks":726,"value":727,"nodeType":21},{},[],"When I first started working with Laravel, Dusk seemed like the natural choice for browser testing. It integrates well with Laravel and can simulate user interactions. But as soon as I brought Vue and Inertia into the mix, things started to feel… clunky. Dusk is tightly coupled to Laravel’s backend, and working with modern, dynamic frontends became increasingly brittle.",{"data":729,"content":730,"nodeType":22},{},[731],{"data":732,"marks":733,"value":734,"nodeType":21},{},[],"Cypress offered me what Dusk didn’t:",{"data":736,"content":737,"nodeType":207},{},[738,753,768,783],{"data":739,"content":740,"nodeType":166},{},[741],{"data":742,"content":743,"nodeType":22},{},[744,749],{"data":745,"marks":746,"value":748,"nodeType":21},{},[747],{"type":51},"Frontend-first testing",{"data":750,"marks":751,"value":752,"nodeType":21},{},[],", which aligns beautifully with Vue components and SPA behavior.",{"data":754,"content":755,"nodeType":166},{},[756],{"data":757,"content":758,"nodeType":22},{},[759,764],{"data":760,"marks":761,"value":763,"nodeType":21},{},[762],{"type":51},"Visual feedback",{"data":765,"marks":766,"value":767,"nodeType":21},{},[],": The Cypress Test Runner shows you exactly what's happening in your app as the test runs.",{"data":769,"content":770,"nodeType":166},{},[771],{"data":772,"content":773,"nodeType":22},{},[774,779],{"data":775,"marks":776,"value":778,"nodeType":21},{},[777],{"type":51},"Fast dev loop",{"data":780,"marks":781,"value":782,"nodeType":21},{},[],": Changes are picked up instantly with auto-reload and no need to recompile the full Laravel app.",{"data":784,"content":785,"nodeType":166},{},[786],{"data":787,"content":788,"nodeType":22},{},[789,794],{"data":790,"marks":791,"value":793,"nodeType":21},{},[792],{"type":51},"Powerful debugging",{"data":795,"marks":796,"value":797,"nodeType":21},{},[],": You can time-travel through your test steps, inspect the DOM, and pinpoint exactly where things go wrong.",{"data":799,"content":800,"nodeType":470},{},[801],{"data":802,"marks":803,"value":804,"nodeType":21},{},[],"End-to-End Testing: Simulating Actual User Flows",{"data":806,"content":807,"nodeType":22},{},[808],{"data":809,"marks":810,"value":811,"nodeType":21},{},[],"I set up tests for full user journeys like:",{"data":813,"content":814,"nodeType":207},{},[815,826,837,848],{"data":816,"content":817,"nodeType":166},{},[818],{"data":819,"content":820,"nodeType":22},{},[821],{"data":822,"marks":823,"value":825,"nodeType":21},{},[824],{"type":51},"Logging in",{"data":827,"content":828,"nodeType":166},{},[829],{"data":830,"content":831,"nodeType":22},{},[832],{"data":833,"marks":834,"value":836,"nodeType":21},{},[835],{"type":51},"Navigating to a dashboard",{"data":838,"content":839,"nodeType":166},{},[840],{"data":841,"content":842,"nodeType":22},{},[843],{"data":844,"marks":845,"value":847,"nodeType":21},{},[846],{"type":51},"Creating a resource (e.g., a post)",{"data":849,"content":850,"nodeType":166},{},[851],{"data":852,"content":853,"nodeType":22},{},[854],{"data":855,"marks":856,"value":858,"nodeType":21},{},[857],{"type":51},"Logging out",{"data":860,"content":861,"nodeType":22},{},[862,866,872],{"data":863,"marks":864,"value":865,"nodeType":21},{},[],"With Cypress, I could stub API calls or hit my backend directly depending on the scenario. The ability to easily mock auth states or use actual logins with ",{"data":867,"marks":868,"value":871,"nodeType":21},{},[869],{"type":870},"code","cy.request()",{"data":873,"marks":874,"value":875,"nodeType":21},{},[]," opened up so many possibilities Dusk made painful.",{"data":877,"content":878,"nodeType":22},{},[879],{"data":880,"marks":881,"value":883,"nodeType":21},{},[882],{"type":51},"Example:",{"data":885,"content":918,"nodeType":919},{"target":886},{"metadata":887,"sys":890,"fields":906},{"tags":888,"concepts":889},[],[],{"space":891,"id":896,"type":897,"createdAt":898,"updatedAt":898,"environment":899,"publishedVersion":903,"revision":904,"locale":905},{"sys":892},{"type":893,"linkType":894,"id":895},"Link","Space","26iu5vjpoesw","2HdfBJjFxgqD8eS2IBT1dZ","Asset","2025-05-03T12:18:17.919Z",{"sys":900},{"id":901,"type":893,"linkType":902},"master","Environment",3,1,"en-US",{"title":907,"description":908,"file":909},"Screenshot 2025-05-03 at 13.17.51","",{"url":910,"details":911,"fileName":916,"contentType":917},"//images.ctfassets.net/26iu5vjpoesw/2HdfBJjFxgqD8eS2IBT1dZ/ab76cf72462651c7bbd2c537e6bd3e76/Screenshot_2025-05-03_at_13.17.51.png",{"size":912,"image":913},37105,{"width":914,"height":915},492,205,"Screenshot 2025-05-03 at 13.17.51.png","image/png",[],"embedded-asset-block",{"data":921,"content":922,"nodeType":22},{},[923],{"data":924,"marks":925,"value":926,"nodeType":21},{},[],"It’s readable, runs fast, and tells me exactly what went wrong when things fail.",{"data":928,"content":929,"nodeType":470},{},[930],{"data":931,"marks":932,"value":933,"nodeType":21},{},[],"Component Testing: Testing Vue Bits in Isolation",{"data":935,"content":936,"nodeType":22},{},[937,941,946],{"data":938,"marks":939,"value":940,"nodeType":21},{},[],"I’m using Vue/Nuxt 3 (with Vite), and Cypress has a built-in mode for ",{"data":942,"marks":943,"value":945,"nodeType":21},{},[944],{"type":51},"mounting individual components",{"data":947,"marks":948,"value":949,"nodeType":21},{},[]," without spinning up the full app.",{"data":951,"content":952,"nodeType":22},{},[953,957,962,966,971],{"data":954,"marks":955,"value":956,"nodeType":21},{},[],"Testing things like a ",{"data":958,"marks":959,"value":961,"nodeType":21},{},[960],{"type":870},"Modal.vue",{"data":963,"marks":964,"value":965,"nodeType":21},{},[]," or ",{"data":967,"marks":968,"value":970,"nodeType":21},{},[969],{"type":870},"Button.vue",{"data":972,"marks":973,"value":974,"nodeType":21},{},[]," component became as simple as:",{"data":976,"content":998,"nodeType":919},{"target":977},{"metadata":978,"sys":981,"fields":988},{"tags":979,"concepts":980},[],[],{"space":982,"id":984,"type":897,"createdAt":985,"updatedAt":985,"environment":986,"publishedVersion":903,"revision":904,"locale":905},{"sys":983},{"type":893,"linkType":894,"id":895},"19xlRFH8245xGx6EtquqTv","2025-05-03T12:18:49.955Z",{"sys":987},{"id":901,"type":893,"linkType":902},{"title":989,"description":908,"file":990},"Screenshot 2025-05-03 at 13.18.35",{"url":991,"details":992,"fileName":997,"contentType":917},"//images.ctfassets.net/26iu5vjpoesw/19xlRFH8245xGx6EtquqTv/9a4a7241c7f594bd17a4f204311a4e6a/Screenshot_2025-05-03_at_13.18.35.png",{"size":993,"image":994},34250,{"width":995,"height":996},403,328,"Screenshot 2025-05-03 at 13.18.35.png",[],{"data":1000,"content":1001,"nodeType":22},{},[1002],{"data":1003,"marks":1004,"value":1005,"nodeType":21},{},[],"This gave me quick feedback without needing to navigate through my app’s UI or backend. I could catch layout or logic bugs directly at the component level.",{"data":1007,"content":1008,"nodeType":720},{},[1009],{"data":1010,"marks":1011,"value":1012,"nodeType":21},{},[],"Cypress vs Laravel Dusk",{"data":1014,"content":1015,"nodeType":22},{},[1016],{"data":1017,"marks":1018,"value":1019,"nodeType":21},{},[],"Here’s a breakdown of how Cypress compares to Dusk from my personal experience:",{"data":1021,"content":1022,"nodeType":1269},{},[1023,1057,1091,1125,1159,1193,1227],{"data":1024,"content":1025,"nodeType":1056},{},[1026,1037,1047],{"data":1027,"content":1028,"nodeType":1036},{},[1029],{"data":1030,"content":1031,"nodeType":22},{},[1032],{"data":1033,"marks":1034,"value":1035,"nodeType":21},{},[],"Feature","table-cell",{"data":1038,"content":1039,"nodeType":1036},{},[1040],{"data":1041,"content":1042,"nodeType":22},{},[1043],{"data":1044,"marks":1045,"value":1046,"nodeType":21},{},[],"Laravel Dusk",{"data":1048,"content":1049,"nodeType":1036},{},[1050],{"data":1051,"content":1052,"nodeType":22},{},[1053],{"data":1054,"marks":1055,"value":679,"nodeType":21},{},[],"table-row",{"data":1058,"content":1059,"nodeType":1056},{},[1060,1071,1081],{"data":1061,"content":1062,"nodeType":1036},{},[1063],{"data":1064,"content":1065,"nodeType":22},{},[1066],{"data":1067,"marks":1068,"value":1070,"nodeType":21},{},[1069],{"type":51},"Frontend integration",{"data":1072,"content":1073,"nodeType":1036},{},[1074],{"data":1075,"content":1076,"nodeType":22},{},[1077],{"data":1078,"marks":1079,"value":1080,"nodeType":21},{},[],"Poor",{"data":1082,"content":1083,"nodeType":1036},{},[1084],{"data":1085,"content":1086,"nodeType":22},{},[1087],{"data":1088,"marks":1089,"value":1090,"nodeType":21},{},[],"Excellent",{"data":1092,"content":1093,"nodeType":1056},{},[1094,1105,1115],{"data":1095,"content":1096,"nodeType":1036},{},[1097],{"data":1098,"content":1099,"nodeType":22},{},[1100],{"data":1101,"marks":1102,"value":1104,"nodeType":21},{},[1103],{"type":51},"Vue/SPA support",{"data":1106,"content":1107,"nodeType":1036},{},[1108],{"data":1109,"content":1110,"nodeType":22},{},[1111],{"data":1112,"marks":1113,"value":1114,"nodeType":21},{},[],"Limited",{"data":1116,"content":1117,"nodeType":1036},{},[1118],{"data":1119,"content":1120,"nodeType":22},{},[1121],{"data":1122,"marks":1123,"value":1124,"nodeType":21},{},[],"Native",{"data":1126,"content":1127,"nodeType":1056},{},[1128,1139,1149],{"data":1129,"content":1130,"nodeType":1036},{},[1131],{"data":1132,"content":1133,"nodeType":22},{},[1134],{"data":1135,"marks":1136,"value":1138,"nodeType":21},{},[1137],{"type":51},"Live reloading",{"data":1140,"content":1141,"nodeType":1036},{},[1142],{"data":1143,"content":1144,"nodeType":22},{},[1145],{"data":1146,"marks":1147,"value":1148,"nodeType":21},{},[],"No",{"data":1150,"content":1151,"nodeType":1036},{},[1152],{"data":1153,"content":1154,"nodeType":22},{},[1155],{"data":1156,"marks":1157,"value":1158,"nodeType":21},{},[],"Yes",{"data":1160,"content":1161,"nodeType":1056},{},[1162,1173,1183],{"data":1163,"content":1164,"nodeType":1036},{},[1165],{"data":1166,"content":1167,"nodeType":22},{},[1168],{"data":1169,"marks":1170,"value":1172,"nodeType":21},{},[1171],{"type":51},"Debuggability",{"data":1174,"content":1175,"nodeType":1036},{},[1176],{"data":1177,"content":1178,"nodeType":22},{},[1179],{"data":1180,"marks":1181,"value":1182,"nodeType":21},{},[],"Console logs",{"data":1184,"content":1185,"nodeType":1036},{},[1186],{"data":1187,"content":1188,"nodeType":22},{},[1189],{"data":1190,"marks":1191,"value":1192,"nodeType":21},{},[],"Time travel UI",{"data":1194,"content":1195,"nodeType":1056},{},[1196,1207,1217],{"data":1197,"content":1198,"nodeType":1036},{},[1199],{"data":1200,"content":1201,"nodeType":22},{},[1202],{"data":1203,"marks":1204,"value":1206,"nodeType":21},{},[1205],{"type":51},"Speed",{"data":1208,"content":1209,"nodeType":1036},{},[1210],{"data":1211,"content":1212,"nodeType":22},{},[1213],{"data":1214,"marks":1215,"value":1216,"nodeType":21},{},[],"Slower",{"data":1218,"content":1219,"nodeType":1036},{},[1220],{"data":1221,"content":1222,"nodeType":22},{},[1223],{"data":1224,"marks":1225,"value":1226,"nodeType":21},{},[],"Faster",{"data":1228,"content":1229,"nodeType":1056},{},[1230,1241,1251],{"data":1231,"content":1232,"nodeType":1036},{},[1233],{"data":1234,"content":1235,"nodeType":22},{},[1236],{"data":1237,"marks":1238,"value":1240,"nodeType":21},{},[1239],{"type":51},"Mocking/Interception",{"data":1242,"content":1243,"nodeType":1036},{},[1244],{"data":1245,"content":1246,"nodeType":22},{},[1247],{"data":1248,"marks":1249,"value":1250,"nodeType":21},{},[],"Difficult",{"data":1252,"content":1253,"nodeType":1036},{},[1254,1261],{"data":1255,"content":1256,"nodeType":22},{},[1257],{"data":1258,"marks":1259,"value":1260,"nodeType":21},{},[],"Easy with ",{"data":1262,"content":1263,"nodeType":22},{},[1264],{"data":1265,"marks":1266,"value":1268,"nodeType":21},{},[1267],{"type":870},"cy.intercept()","table",{"data":1271,"content":1272,"nodeType":22},{},[1273],{"data":1274,"marks":1275,"value":1276,"nodeType":21},{},[],"I still think Dusk has its place—like for backend-heavy flows or where you want to test actual browser behavior end-to-end in a true Laravel context. But for any serious Vue-heavy app, Cypress just feels like the tool Laravel didn’t know it needed.",{"data":1278,"content":1279,"nodeType":720},{},[1280],{"data":1281,"marks":1282,"value":1283,"nodeType":21},{},[],"Why It Matters",{"data":1285,"content":1286,"nodeType":22},{},[1287,1291,1296],{"data":1288,"marks":1289,"value":1290,"nodeType":21},{},[],"As developers, we often skip tests when the tools don’t feel intuitive. That was me with Dusk. But Cypress made testing feel like ",{"data":1292,"marks":1293,"value":1295,"nodeType":21},{},[1294],{"type":51},"part of the dev workflow",{"data":1297,"marks":1298,"value":1299,"nodeType":21},{},[]," rather than a chore. And the confidence I’ve gained by knowing both my components and entire flows are solid? Game-changing.",{"data":1301,"content":1302,"nodeType":22},{},[1303],{"data":1304,"marks":1305,"value":1306,"nodeType":21},{},[],"If you're building apps with Laravel + Vue + Inertia—or really any modern frontend—Cypress is worth your time.",{"data":1308,"content":1309,"nodeType":22},{},[1310],{"data":1311,"marks":1312,"value":1313,"nodeType":21},{},[],"This week was just the start. I’ve already planned more tests, more component coverage, and even experimenting with CI integration next.",{"data":1315,"content":1316,"nodeType":22},{},[1317],{"data":1318,"marks":1319,"value":1320,"nodeType":21},{},[],"Happy testing 👨‍💻","Laravel",[1323,1324,1325,1326,1327],"testing","laravel","vue","cypress","tutorial",{"title":1329,"slug":1330,"date":1331,"excerpt":1332,"coverImage":1333,"body":1334,"tag":2076,"tags":2077},"Building a Scalable, Load-Balanced Laravel App","building-a-scalable-load-balanced-laravel-app","2025-04-26","This week I containerized a Laravel app and deployed it on AWS ECS with load balancing. After facing issues with Octane and RoadRunner, I pivoted to a stable Nginx setup — and learned a lot along the way.","https://images.ctfassets.net/26iu5vjpoesw/cmDsLBzzAfhRdJBBrbPro/8fc0587e035a0c537577f667c145ea0d/56AC5BCC-9299-453B-94E8-1EB5A4860E82.png",{"data":1335,"content":1336,"nodeType":438},{},[1337,1345,1361,1395,1398,1405,1412,1419,1426,1429,1436,1452,1459,1475,1482,1485,1492,1499,1506,1566,1578,1581,1588,1611,1627,1680,1687,1694,1701,1704,1711,1718,1725,1805,1821,1824,1831,1838,1845,1923,1938,1945,1948,1955,1962,2034,2041,2044,2050,2062,2069],{"data":1338,"content":1339,"nodeType":1344},{},[1340],{"data":1341,"marks":1342,"value":1343,"nodeType":21},{},[],"Building a Scalable, Load-Balanced Laravel App with Docker, Nginx, and AWS ECS","heading-1",{"data":1346,"content":1347,"nodeType":22},{},[1348,1352,1357],{"data":1349,"marks":1350,"value":1351,"nodeType":21},{},[],"If you’ve ever tried taking a \"normal\" Laravel app and making it ready for ",{"data":1353,"marks":1354,"value":1356,"nodeType":21},{},[1355],{"type":51},"cloud-native production",{"data":1358,"marks":1359,"value":1360,"nodeType":21},{},[],", you know the dream: sleek deployments, effortless scaling, no server babysitting.\n This week, I set out to finally make that dream real — containerising a Laravel app, setting up a proper Nginx reverse proxy, and launching the whole thing on AWS ECS Fargate with a load balancer in front.",{"data":1362,"content":1363,"nodeType":22},{},[1364,1368,1373,1377,1382,1386,1391],{"data":1365,"marks":1366,"value":1367,"nodeType":21},{},[],"What followed was one of the most ",{"data":1369,"marks":1370,"value":1372,"nodeType":21},{},[1371],{"type":51},"rewarding",{"data":1374,"marks":1375,"value":1376,"nodeType":21},{},[],", ",{"data":1378,"marks":1379,"value":1381,"nodeType":21},{},[1380],{"type":51},"frustrating",{"data":1383,"marks":1384,"value":1385,"nodeType":21},{},[],", and ",{"data":1387,"marks":1388,"value":1390,"nodeType":21},{},[1389],{"type":51},"eye-opening",{"data":1392,"marks":1393,"value":1394,"nodeType":21},{},[]," experiences I’ve had in a while.",{"data":1396,"content":1397,"nodeType":461},{},[],{"data":1399,"content":1400,"nodeType":720},{},[1401],{"data":1402,"marks":1403,"value":1404,"nodeType":21},{},[],"The Original Plan",{"data":1406,"content":1407,"nodeType":22},{},[1408],{"data":1409,"marks":1410,"value":1411,"nodeType":21},{},[],"I didn't start this week thinking about Nginx.",{"data":1413,"content":1414,"nodeType":22},{},[1415],{"data":1416,"marks":1417,"value":1418,"nodeType":21},{},[],"The original plan was Laravel Octane running on RoadRunner inside a container. It sounded perfect: high performance, super low latency, and \"production ready\" according to the docs. But the deeper I went, the messier it became.\n The documentation around Octane deployment at scale was thin. Setting up RoadRunner correctly inside an ECS container? Even thinner.",{"data":1420,"content":1421,"nodeType":22},{},[1422],{"data":1423,"marks":1424,"value":1425,"nodeType":21},{},[],"And when you're fighting the framework more than you're building the product, it’s time to take a step back.",{"data":1427,"content":1428,"nodeType":461},{},[],{"data":1430,"content":1431,"nodeType":720},{},[1432],{"data":1433,"marks":1434,"value":1435,"nodeType":21},{},[],"The Pivot: Choosing Stability",{"data":1437,"content":1438,"nodeType":22},{},[1439,1443,1448],{"data":1440,"marks":1441,"value":1442,"nodeType":21},{},[],"Midway through the week, after yet another dead-end with Octane config examples, I made the call: ",{"data":1444,"marks":1445,"value":1447,"nodeType":21},{},[1446],{"type":51},"ditch Octane and RoadRunner",{"data":1449,"marks":1450,"value":1451,"nodeType":21},{},[]," for now, and go back to a rock-solid Nginx + PHP-FPM setup.",{"data":1453,"content":1454,"nodeType":22},{},[1455],{"data":1456,"marks":1457,"value":1458,"nodeType":21},{},[],"It felt a little like giving up — but honestly, it turned out to be a huge win.",{"data":1460,"content":1461,"nodeType":22},{},[1462,1466,1471],{"data":1463,"marks":1464,"value":1465,"nodeType":21},{},[],"Nginx is boring. PHP-FPM is boring.\n But ",{"data":1467,"marks":1468,"value":1470,"nodeType":21},{},[1469],{"type":51},"boring is reliable",{"data":1472,"marks":1473,"value":1474,"nodeType":21},{},[],".\n And right now, reliability mattered more than novelty.",{"data":1476,"content":1477,"nodeType":22},{},[1478],{"data":1479,"marks":1480,"value":1481,"nodeType":21},{},[],"Making that pivot was a huge mental relief. I finally felt like I could move forward without second-guessing every choice.",{"data":1483,"content":1484,"nodeType":461},{},[],{"data":1486,"content":1487,"nodeType":720},{},[1488],{"data":1489,"marks":1490,"value":1491,"nodeType":21},{},[],"Dockerizing Laravel (For Real This Time)",{"data":1493,"content":1494,"nodeType":22},{},[1495],{"data":1496,"marks":1497,"value":1498,"nodeType":21},{},[],"Once I committed to Nginx + PHP-FPM, the Dockerfile almost wrote itself.",{"data":1500,"content":1501,"nodeType":22},{},[1502],{"data":1503,"marks":1504,"value":1505,"nodeType":21},{},[],"Instead of trying to juggle process managers and worker threads, I focused on the basics:",{"data":1507,"content":1508,"nodeType":207},{},[1509,1519,1529,1556],{"data":1510,"content":1511,"nodeType":166},{},[1512],{"data":1513,"content":1514,"nodeType":22},{},[1515],{"data":1516,"marks":1517,"value":1518,"nodeType":21},{},[],"A lean Alpine-based PHP-FPM image.",{"data":1520,"content":1521,"nodeType":166},{},[1522],{"data":1523,"content":1524,"nodeType":22},{},[1525],{"data":1526,"marks":1527,"value":1528,"nodeType":21},{},[],"An optimized Laravel build (caching configs, routes, etc).",{"data":1530,"content":1531,"nodeType":166},{},[1532],{"data":1533,"content":1534,"nodeType":22},{},[1535,1539,1544,1547,1552],{"data":1536,"marks":1537,"value":1538,"nodeType":21},{},[],"Proper permissions on ",{"data":1540,"marks":1541,"value":1543,"nodeType":21},{},[1542],{"type":870},"storage/",{"data":1545,"marks":1546,"value":693,"nodeType":21},{},[],{"data":1548,"marks":1549,"value":1551,"nodeType":21},{},[1550],{"type":870},"bootstrap/cache",{"data":1553,"marks":1554,"value":1555,"nodeType":21},{},[],".",{"data":1557,"content":1558,"nodeType":166},{},[1559],{"data":1560,"content":1561,"nodeType":22},{},[1562],{"data":1563,"marks":1564,"value":1565,"nodeType":21},{},[],"A clean Nginx config that served static assets directly and proxied the rest to PHP-FPM.",{"data":1567,"content":1568,"nodeType":22},{},[1569,1573],{"data":1570,"marks":1571,"value":1572,"nodeType":21},{},[],"Watching the first container actually start without any weird Octane errors felt amazing.\n ",{"data":1574,"marks":1575,"value":1577,"nodeType":21},{},[1576],{"type":51},"Small reward, big morale boost.",{"data":1579,"content":1580,"nodeType":461},{},[],{"data":1582,"content":1583,"nodeType":720},{},[1584],{"data":1585,"marks":1586,"value":1587,"nodeType":21},{},[],"Setting Up AWS ECS and Load Balancing",{"data":1589,"content":1590,"nodeType":22},{},[1591,1595,1600,1603,1608],{"data":1592,"marks":1593,"value":1594,"nodeType":21},{},[],"This is where things got both ",{"data":1596,"marks":1597,"value":1599,"nodeType":21},{},[1598],{"type":51},"exciting",{"data":1601,"marks":1602,"value":693,"nodeType":21},{},[],{"data":1604,"marks":1605,"value":1607,"nodeType":21},{},[1606],{"type":51},"painful",{"data":1609,"marks":1610,"value":1555,"nodeType":21},{},[],{"data":1612,"content":1613,"nodeType":22},{},[1614,1618,1623],{"data":1615,"marks":1616,"value":1617,"nodeType":21},{},[],"ECS is powerful, but it expects you to ",{"data":1619,"marks":1620,"value":1622,"nodeType":21},{},[1621],{"type":51},"know what you're doing",{"data":1624,"marks":1625,"value":1626,"nodeType":21},{},[],". The amount of options just to create a simple service is insane:",{"data":1628,"content":1629,"nodeType":207},{},[1630,1640,1650,1660,1670],{"data":1631,"content":1632,"nodeType":166},{},[1633],{"data":1634,"content":1635,"nodeType":22},{},[1636],{"data":1637,"marks":1638,"value":1639,"nodeType":21},{},[],"Do you want EC2 or Fargate?",{"data":1641,"content":1642,"nodeType":166},{},[1643],{"data":1644,"content":1645,"nodeType":22},{},[1646],{"data":1647,"marks":1648,"value":1649,"nodeType":21},{},[],"What networking mode?",{"data":1651,"content":1652,"nodeType":166},{},[1653],{"data":1654,"content":1655,"nodeType":22},{},[1656],{"data":1657,"marks":1658,"value":1659,"nodeType":21},{},[],"Which security groups?",{"data":1661,"content":1662,"nodeType":166},{},[1663],{"data":1664,"content":1665,"nodeType":22},{},[1666],{"data":1667,"marks":1668,"value":1669,"nodeType":21},{},[],"Public IP or internal only?",{"data":1671,"content":1672,"nodeType":166},{},[1673],{"data":1674,"content":1675,"nodeType":22},{},[1676],{"data":1677,"marks":1678,"value":1679,"nodeType":21},{},[],"What about autoscaling rules?",{"data":1681,"content":1682,"nodeType":22},{},[1683],{"data":1684,"marks":1685,"value":1686,"nodeType":21},{},[],"Some evenings, it felt like I was just staring at AWS console tabs and Googling acronyms.",{"data":1688,"content":1689,"nodeType":22},{},[1690],{"data":1691,"marks":1692,"value":1693,"nodeType":21},{},[],"When I finally got the Application Load Balancer (ALB) forwarding traffic properly to my containers — and I could see a real Laravel page being served — I literally fist-pumped at my desk.",{"data":1695,"content":1696,"nodeType":22},{},[1697],{"data":1698,"marks":1699,"value":1700,"nodeType":21},{},[],"There’s nothing like that first successful HTTP 200 after hours of configuring listeners, target groups, and security groups.",{"data":1702,"content":1703,"nodeType":461},{},[],{"data":1705,"content":1706,"nodeType":720},{},[1707],{"data":1708,"marks":1709,"value":1710,"nodeType":21},{},[],"Frustrations (and How I Overcame Them)",{"data":1712,"content":1713,"nodeType":22},{},[1714],{"data":1715,"marks":1716,"value":1717,"nodeType":21},{},[],"It wasn't smooth sailing the whole way.\n There were moments when it felt like I was one bad decision away from throwing my laptop out the window.",{"data":1719,"content":1720,"nodeType":22},{},[1721],{"data":1722,"marks":1723,"value":1724,"nodeType":21},{},[],"Here were the biggest frustrations:",{"data":1726,"content":1727,"nodeType":207},{},[1728,1752,1775,1790],{"data":1729,"content":1730,"nodeType":166},{},[1731],{"data":1732,"content":1733,"nodeType":22},{},[1734,1739,1743,1748],{"data":1735,"marks":1736,"value":1738,"nodeType":21},{},[1737],{"type":51},"Health checks",{"data":1740,"marks":1741,"value":1742,"nodeType":21},{},[],": Getting the ALB health checks working meant adding a dedicated ",{"data":1744,"marks":1745,"value":1747,"nodeType":21},{},[1746],{"type":870},"/health",{"data":1749,"marks":1750,"value":1751,"nodeType":21},{},[]," route to Laravel. Without it, my containers kept getting killed.",{"data":1753,"content":1754,"nodeType":166},{},[1755],{"data":1756,"content":1757,"nodeType":22},{},[1758,1763,1767,1772],{"data":1759,"marks":1760,"value":1762,"nodeType":21},{},[1761],{"type":51},"Port mismatches",{"data":1764,"marks":1765,"value":1766,"nodeType":21},{},[],": I accidentally mapped the wrong container port to the load balancer target group once. Debugging that took ",{"data":1768,"marks":1769,"value":1771,"nodeType":21},{},[1770],{"type":647},"hours",{"data":1773,"marks":1774,"value":1555,"nodeType":21},{},[],{"data":1776,"content":1777,"nodeType":166},{},[1778],{"data":1779,"content":1780,"nodeType":22},{},[1781,1786],{"data":1782,"marks":1783,"value":1785,"nodeType":21},{},[1784],{"type":51},"Logging black holes",{"data":1787,"marks":1788,"value":1789,"nodeType":21},{},[],": Without CloudWatch logs, it’s almost impossible to troubleshoot container failures inside ECS. Turning on logging early saved me from a lot of silent failures later.",{"data":1791,"content":1792,"nodeType":166},{},[1793],{"data":1794,"content":1795,"nodeType":22},{},[1796,1801],{"data":1797,"marks":1798,"value":1800,"nodeType":21},{},[1799],{"type":51},"Resource limits",{"data":1802,"marks":1803,"value":1804,"nodeType":21},{},[],": Forgetting to define CPU/memory limits caused the tasks to scale poorly under load.",{"data":1806,"content":1807,"nodeType":22},{},[1808,1812,1817],{"data":1809,"marks":1810,"value":1811,"nodeType":21},{},[],"Each of these mistakes cost me time and energy — but they also ",{"data":1813,"marks":1814,"value":1816,"nodeType":21},{},[1815],{"type":51},"forced me to learn",{"data":1818,"marks":1819,"value":1820,"nodeType":21},{},[]," exactly how ECS, load balancers, and containers behave in real-world scenarios.",{"data":1822,"content":1823,"nodeType":461},{},[],{"data":1825,"content":1826,"nodeType":720},{},[1827],{"data":1828,"marks":1829,"value":1830,"nodeType":21},{},[],"The Rewards",{"data":1832,"content":1833,"nodeType":22},{},[1834],{"data":1835,"marks":1836,"value":1837,"nodeType":21},{},[],"The hard work paid off.",{"data":1839,"content":1840,"nodeType":22},{},[1841],{"data":1842,"marks":1843,"value":1844,"nodeType":21},{},[],"By the end of the week, I had:",{"data":1846,"content":1847,"nodeType":207},{},[1848,1867,1885,1904],{"data":1849,"content":1850,"nodeType":166},{},[1851],{"data":1852,"content":1853,"nodeType":22},{},[1854,1858,1863],{"data":1855,"marks":1856,"value":1857,"nodeType":21},{},[],"A ",{"data":1859,"marks":1860,"value":1862,"nodeType":21},{},[1861],{"type":51},"Dockerized Laravel app",{"data":1864,"marks":1865,"value":1866,"nodeType":21},{},[]," that runs consistently.",{"data":1868,"content":1869,"nodeType":166},{},[1870],{"data":1871,"content":1872,"nodeType":22},{},[1873,1876,1881],{"data":1874,"marks":1875,"value":1857,"nodeType":21},{},[],{"data":1877,"marks":1878,"value":1880,"nodeType":21},{},[1879],{"type":51},"load-balanced service",{"data":1882,"marks":1883,"value":1884,"nodeType":21},{},[]," that auto-scales when needed.",{"data":1886,"content":1887,"nodeType":166},{},[1888],{"data":1889,"content":1890,"nodeType":22},{},[1891,1895,1900],{"data":1892,"marks":1893,"value":1894,"nodeType":21},{},[],"A clean ",{"data":1896,"marks":1897,"value":1899,"nodeType":21},{},[1898],{"type":51},"CI/CD pipeline",{"data":1901,"marks":1902,"value":1903,"nodeType":21},{},[]," that can trigger deployments.",{"data":1905,"content":1906,"nodeType":166},{},[1907],{"data":1908,"content":1909,"nodeType":22},{},[1910,1914,1919],{"data":1911,"marks":1912,"value":1913,"nodeType":21},{},[],"A deeper understanding of ",{"data":1915,"marks":1916,"value":1918,"nodeType":21},{},[1917],{"type":51},"AWS networking",{"data":1920,"marks":1921,"value":1922,"nodeType":21},{},[]," than I ever had before.",{"data":1924,"content":1925,"nodeType":22},{},[1926,1930,1935],{"data":1927,"marks":1928,"value":1929,"nodeType":21},{},[],"More importantly, I had built something that wasn’t just a proof of concept — it was ",{"data":1931,"marks":1932,"value":1934,"nodeType":21},{},[1933],{"type":51},"ready for production",{"data":1936,"marks":1937,"value":1555,"nodeType":21},{},[],{"data":1939,"content":1940,"nodeType":22},{},[1941],{"data":1942,"marks":1943,"value":1944,"nodeType":21},{},[],"That feeling — seeing all the moving parts finally click into place — made every frustrating moment worth it.",{"data":1946,"content":1947,"nodeType":461},{},[],{"data":1949,"content":1950,"nodeType":720},{},[1951],{"data":1952,"marks":1953,"value":1954,"nodeType":21},{},[],"What's Next",{"data":1956,"content":1957,"nodeType":22},{},[1958],{"data":1959,"marks":1960,"value":1961,"nodeType":21},{},[],"Even though the base architecture is live, there's always another level up:",{"data":1963,"content":1964,"nodeType":207},{},[1965,1989,2004,2019],{"data":1966,"content":1967,"nodeType":166},{},[1968],{"data":1969,"content":1970,"nodeType":22},{},[1971,1976,1980,1985],{"data":1972,"marks":1973,"value":1975,"nodeType":21},{},[1974],{"type":51},"Move environment variables into AWS Secrets Manager",{"data":1977,"marks":1978,"value":1979,"nodeType":21},{},[]," (no more hardcoded ",{"data":1981,"marks":1982,"value":1984,"nodeType":21},{},[1983],{"type":870},".env",{"data":1986,"marks":1987,"value":1988,"nodeType":21},{},[]," files).",{"data":1990,"content":1991,"nodeType":166},{},[1992],{"data":1993,"content":1994,"nodeType":22},{},[1995,2000],{"data":1996,"marks":1997,"value":1999,"nodeType":21},{},[1998],{"type":51},"Set up blue/green deployments",{"data":2001,"marks":2002,"value":2003,"nodeType":21},{},[]," for zero downtime releases.",{"data":2005,"content":2006,"nodeType":166},{},[2007],{"data":2008,"content":2009,"nodeType":22},{},[2010,2015],{"data":2011,"marks":2012,"value":2014,"nodeType":21},{},[2013],{"type":51},"Fine-tune autoscaling policies",{"data":2016,"marks":2017,"value":2018,"nodeType":21},{},[]," based on memory and request rates, not just CPU.",{"data":2020,"content":2021,"nodeType":166},{},[2022],{"data":2023,"content":2024,"nodeType":22},{},[2025,2030],{"data":2026,"marks":2027,"value":2029,"nodeType":21},{},[2028],{"type":51},"Improve observability",{"data":2031,"marks":2032,"value":2033,"nodeType":21},{},[]," with better CloudWatch dashboards and alarms.",{"data":2035,"content":2036,"nodeType":22},{},[2037],{"data":2038,"marks":2039,"value":2040,"nodeType":21},{},[],"The foundation is solid now. Next comes the polishing.",{"data":2042,"content":2043,"nodeType":461},{},[],{"data":2045,"content":2046,"nodeType":1344},{},[2047],{"data":2048,"marks":2049,"value":635,"nodeType":21},{},[],{"data":2051,"content":2052,"nodeType":22},{},[2053,2057],{"data":2054,"marks":2055,"value":2056,"nodeType":21},{},[],"If I could give one piece of advice to anyone going through this journey:\n ",{"data":2058,"marks":2059,"value":2061,"nodeType":21},{},[2060],{"type":51},"Be willing to pivot.",{"data":2063,"content":2064,"nodeType":22},{},[2065],{"data":2066,"marks":2067,"value":2068,"nodeType":21},{},[],"The biggest progress I made this week didn’t come from pushing harder — it came from letting go of a setup that wasn’t working, and choosing something stable and reliable instead.",{"data":2070,"content":2071,"nodeType":22},{},[2072],{"data":2073,"marks":2074,"value":2075,"nodeType":21},{},[],"Octane and RoadRunner might be perfect someday, and I’ll revisit them when the time’s right.\n But for now, plain old Nginx and PHP-FPM are powering a fast, scalable, cloud-native Laravel deployment — and that’s a win worth celebrating.","Infrastructure",[1324,2078,2079,2080],"aws","devops","architecture",{"title":2082,"slug":2083,"date":2084,"excerpt":2085,"coverImage":2086,"body":2087,"tags":2292},"Bag Hunting: My Search for the Ultimate Tech Backpack","the-ultimate-tech-backpack","2025-04-21","I’ve been hunting for the perfect tech backpack—and I think I’ve found it in the Legion by Wolph. Here’s why it’s standing out from the rest.","https://images.ctfassets.net/26iu5vjpoesw/3PQGImNAicTHfFrFqepAp9/792670a0a4cc4a11db5cb0c75ff88909/8DDD2042-3400-48C1-89AA-F15CAEE215E4.png",{"data":2088,"content":2089,"nodeType":438},{},[2090,2106,2121,2124,2132,2144,2156,2168,2171,2201,2208,2215,2248,2264,2271,2274,2285],{"data":2091,"content":2092,"nodeType":22},{},[2093,2097,2102],{"data":2094,"marks":2095,"value":2096,"nodeType":21},{},[],"I’ve been on the lookout for a tech travel backpack that hits that sweet spot: compact, stylish, and versatile enough to handle anything from a commute to a quick weekend trip. After hours of deep diving into specs, reviews, and way too many open tabs, I narrowed it down to four contenders. And while they all had something to offer, the ",{"data":2098,"marks":2099,"value":2101,"nodeType":21},{},[2100],{"type":51},"Legion Travel Backpack by Wolph",{"data":2103,"marks":2104,"value":2105,"nodeType":21},{},[]," has edged ahead.",{"data":2107,"content":2108,"nodeType":22},{},[2109,2113,2118],{"data":2110,"marks":2111,"value":2112,"nodeType":21},{},[],"Here’s how the competition stacked up—and why the Legion might just be ",{"data":2114,"marks":2115,"value":2117,"nodeType":21},{},[2116],{"type":647},"the one",{"data":2119,"marks":2120,"value":1555,"nodeType":21},{},[],{"data":2122,"content":2123,"nodeType":461},{},[],{"data":2125,"content":2126,"nodeType":22},{},[2127],{"data":2128,"marks":2129,"value":2131,"nodeType":21},{},[2130],{"type":51},"The Shortlist",{"data":2133,"content":2134,"nodeType":22},{},[2135,2140],{"data":2136,"marks":2137,"value":2139,"nodeType":21},{},[2138],{"type":51},"Three Peaks GBR Commuter 22L",{"data":2141,"marks":2142,"value":2143,"nodeType":21},{},[],"\n Solid commuter bag—minimalist design, decent protection, and just enough capacity for daily essentials. Great for city life, but a little too straightforward for travel or gear-heavy days.",{"data":2145,"content":2146,"nodeType":22},{},[2147,2152],{"data":2148,"marks":2149,"value":2151,"nodeType":21},{},[2150],{"type":51},"Nordace Aerial Infinity 15",{"data":2153,"marks":2154,"value":2155,"nodeType":21},{},[],"\n Feature-packed and compact with lots of thoughtful compartments. It’s a great option if you travel ultra-light. But for someone like me who likes a bit more breathing room and flexibility, 15L feels a bit too tight.",{"data":2157,"content":2158,"nodeType":22},{},[2159,2164],{"data":2160,"marks":2161,"value":2163,"nodeType":21},{},[2162],{"type":51},"Stubble & Co Everyday Backpack",{"data":2165,"marks":2166,"value":2167,"nodeType":21},{},[],"\n Visually, this one’s a favorite. Rugged yet refined, great materials, and weather-resistant. But it leans more toward lifestyle than functional travel. I needed something that felt a bit more dynamic.",{"data":2169,"content":2170,"nodeType":461},{},[],{"data":2172,"content":2173,"nodeType":22},{},[2174,2179,2183,2188,2192,2197],{"data":2175,"marks":2176,"value":2178,"nodeType":21},{},[2177],{"type":51},"Enter the Legion by Wolph",{"data":2180,"marks":2181,"value":2182,"nodeType":21},{},[],"\n If you value ",{"data":2184,"marks":2185,"value":2187,"nodeType":21},{},[2186],{"type":51},"compactibility, style, and freedom",{"data":2189,"marks":2190,"value":2191,"nodeType":21},{},[],", the Legion checks all the right boxes. It was designed with a clear intention: to be a daypack that looks good ",{"data":2193,"marks":2194,"value":2196,"nodeType":21},{},[2195],{"type":647},"and",{"data":2198,"marks":2199,"value":2200,"nodeType":21},{},[]," holds up in real-world travel conditions. What caught my attention wasn’t just the sleek design—it was how well-thought-out it is.",{"data":2202,"content":2203,"nodeType":22},{},[2204],{"data":2205,"marks":2206,"value":2207,"nodeType":21},{},[],"It features multiple internal and external compartments to carry laptops, tablets, books, accessories, water bottles—you name it. It even has a built-in charging port (just bring your power bank), so staying powered on the go is easy whether you're commuting or bouncing between airports.",{"data":2209,"content":2210,"nodeType":22},{},[2211],{"data":2212,"marks":2213,"value":2214,"nodeType":21},{},[],"Size-wise, it comes in two options:",{"data":2216,"content":2217,"nodeType":207},{},[2218,2233],{"data":2219,"content":2220,"nodeType":166},{},[2221],{"data":2222,"content":2223,"nodeType":22},{},[2224,2229],{"data":2225,"marks":2226,"value":2228,"nodeType":21},{},[2227],{"type":51},"Medium",{"data":2230,"marks":2231,"value":2232,"nodeType":21},{},[],": 38.5 x 27 x 13cm (ideal for up to 14\" devices)",{"data":2234,"content":2235,"nodeType":166},{},[2236],{"data":2237,"content":2238,"nodeType":22},{},[2239,2244],{"data":2240,"marks":2241,"value":2243,"nodeType":21},{},[2242],{"type":51},"Large",{"data":2245,"marks":2246,"value":2247,"nodeType":21},{},[],": 42 x 29 x 13cm (perfect for 15.6\" laptops and larger loads)",{"data":2249,"content":2250,"nodeType":22},{},[2251,2255,2260],{"data":2252,"marks":2253,"value":2254,"nodeType":21},{},[],"Both versions are fully ",{"data":2256,"marks":2257,"value":2259,"nodeType":21},{},[2258],{"type":51},"water-resistant",{"data":2261,"marks":2262,"value":2263,"nodeType":21},{},[],", right down to the zippers and lining. It's built to handle the elements—sun, snow, or rain (just don’t dunk it in a lake). Basically, it's made for movement.",{"data":2265,"content":2266,"nodeType":22},{},[2267],{"data":2268,"marks":2269,"value":2270,"nodeType":21},{},[],"What really sold me, though, was that it doesn’t feel like you're compromising. You get style without sacrificing structure, and functionality without it looking like a hiking pack.",{"data":2272,"content":2273,"nodeType":461},{},[],{"data":2275,"content":2276,"nodeType":22},{},[2277,2281],{"data":2278,"marks":2279,"value":635,"nodeType":21},{},[2280],{"type":51},{"data":2282,"marks":2283,"value":2284,"nodeType":21},{},[],"\n While I haven’t ruled out the others completely, the Legion just fits the kind of flexibility I’m after. It’s the kind of bag that adapts to your day, your trip, and your gear—without making a big deal about it.",{"data":2286,"content":2287,"nodeType":22},{},[2288],{"data":2289,"marks":2290,"value":2291,"nodeType":21},{},[],"Once it arrives, I’ll put it through its paces and report back. But right now, the Legion is looking like my next everyday sidekick.",[655],{"title":2294,"slug":2295,"date":2296,"excerpt":2297,"coverImage":2298,"body":2299,"tag":2435,"tags":2436},"Building a Terminal Component in Vue","building-a-terminal-component","2025-04-14","A quick breakdown of how I built a terminal-style UI in Vue with Nuxt 3, inspired by my love for vintage UIs and nerdy web experiments.","https://images.ctfassets.net/26iu5vjpoesw/5XTd0MXxfXXJXsA0SaKEH6/55a22eef2ee8bc9d0054b091fc96a686/EB2876D1-8A7C-4506-8C62-44AF906D75FD.PNG",{"data":2300,"content":2301,"nodeType":438},{},[2302,2309,2316,2324,2331,2338,2346,2353,2360,2413,2420,2428],{"data":2303,"content":2304,"nodeType":22},{},[2305],{"data":2306,"marks":2307,"value":2308,"nodeType":21},{},[],"There’s something inherently cool about terminal interfaces. They’re minimal, efficient, and a little nostalgic. I wanted to bring some of that charm to my site — not just as a design gimmick, but as an interactive way for visitors to explore who I am and what I do.",{"data":2310,"content":2311,"nodeType":22},{},[2312],{"data":2313,"marks":2314,"value":2315,"nodeType":21},{},[],"This blog post is a quick behind-the-scenes look at how I built my terminal-style component using Vue and Nuxt 3.",{"data":2317,"content":2318,"nodeType":22},{},[2319],{"data":2320,"marks":2321,"value":2323,"nodeType":21},{},[2322],{"type":51},"Where the Idea Came From",{"data":2325,"content":2326,"nodeType":22},{},[2327],{"data":2328,"marks":2329,"value":2330,"nodeType":21},{},[],"I’ve always loved sites that play with unconventional UIs — command line games, portfolio terminals, hacker-themed Easter eggs. One day I thought, “Why not just make my site talk like a terminal?” I didn’t want to overdo it, but it felt like a fun way to bring some personality into my portfolio without relying on overused templates.",{"data":2332,"content":2333,"nodeType":22},{},[2334],{"data":2335,"marks":2336,"value":2337,"nodeType":21},{},[],"Plus, it let me flex a bit of frontend muscle in a playful way.",{"data":2339,"content":2340,"nodeType":22},{},[2341],{"data":2342,"marks":2343,"value":2345,"nodeType":21},{},[2344],{"type":51},"How I Built It",{"data":2347,"content":2348,"nodeType":22},{},[2349],{"data":2350,"marks":2351,"value":2352,"nodeType":21},{},[],"I started with a basic Vue component and styled it using Tailwind CSS, keeping things simple and accessible. The terminal accepts commands like help, about, and contact, and responds accordingly — kind of like a mini shell emulator, but with just enough logic to make it feel interactive.",{"data":2354,"content":2355,"nodeType":22},{},[2356],{"data":2357,"marks":2358,"value":2359,"nodeType":21},{},[],"Some things I wanted to make sure it had:",{"data":2361,"content":2362,"nodeType":207},{},[2363,2373,2383,2393,2403],{"data":2364,"content":2365,"nodeType":166},{},[2366],{"data":2367,"content":2368,"nodeType":22},{},[2369],{"data":2370,"marks":2371,"value":2372,"nodeType":21},{},[],"Command history with arrow key navigation",{"data":2374,"content":2375,"nodeType":166},{},[2376],{"data":2377,"content":2378,"nodeType":22},{},[2379],{"data":2380,"marks":2381,"value":2382,"nodeType":21},{},[],"A realistic typing animation for responses",{"data":2384,"content":2385,"nodeType":166},{},[2386],{"data":2387,"content":2388,"nodeType":22},{},[2389],{"data":2390,"marks":2391,"value":2392,"nodeType":21},{},[],"Different output based on commands like sudo hire-me or coffee",{"data":2394,"content":2395,"nodeType":166},{},[2396],{"data":2397,"content":2398,"nodeType":22},{},[2399],{"data":2400,"marks":2401,"value":2402,"nodeType":21},{},[],"Dark mode support",{"data":2404,"content":2405,"nodeType":166},{},[2406],{"data":2407,"content":2408,"nodeType":22},{},[2409],{"data":2410,"marks":2411,"value":2412,"nodeType":21},{},[],"A nod to macOS terminal with the red/yellow/green window buttons",{"data":2414,"content":2415,"nodeType":22},{},[2416],{"data":2417,"marks":2418,"value":2419,"nodeType":21},{},[],"Nuxt 3 made this process smooth — I used \u003Cscript setup>, ref, and some lightweight logic to manage state. The typing effect uses await with a timed delay to animate the text, which adds a nice bit of feedback when a user runs a command.",{"data":2421,"content":2422,"nodeType":22},{},[2423],{"data":2424,"marks":2425,"value":2427,"nodeType":21},{},[2426],{"type":51},"What’s Next",{"data":2429,"content":2430,"nodeType":22},{},[2431],{"data":2432,"marks":2433,"value":2434,"nodeType":21},{},[],"I’m planning to hook it up with more content — like blog posts, project links, maybe even a text-based game mode if I get carried away. For now, it’s just a fun piece of the site that helps me stand out a bit.","Geek",[1325,2437,1327],"nuxt",{"title":2439,"slug":2440,"date":2441,"excerpt":2442,"coverImage":2443,"body":2444,"tag":2732,"tags":2733},"The Rise of Everyday AI: How Artificial Intelligence is Becoming Your Daily Sidekick","everyday-ai","2025-04-08","AI is no longer just sci-fi — it’s your daily sidekick. From smart suggestions to creative tools, artificial intelligence is quietly transforming the way we work, create, and live. Here's how it's becoming part of your everyday life.","https://images.ctfassets.net/26iu5vjpoesw/24rZBcQk89TpTL0XPDG0r5/3366d8b966796394c7f65fbe5db87c5f/ChatGPT_Image_Apr_8_2025.png",{"data":2445,"content":2446,"nodeType":438},{},[2447,2454,2461,2464,2472,2479,2527,2534,2537,2544,2551,2610,2617,2620,2627,2634,2667,2674,2677,2684,2708,2711,2718,2725],{"data":2448,"content":2449,"nodeType":22},{},[2450],{"data":2451,"marks":2452,"value":2453,"nodeType":21},{},[],"Artificial Intelligence used to sound like something out of a sci-fi movie. But today? It's showing up in your phone, your home, and even in your coffee recommendations.",{"data":2455,"content":2456,"nodeType":22},{},[2457],{"data":2458,"marks":2459,"value":2460,"nodeType":21},{},[],"Whether you're using ChatGPT to draft emails or asking Siri to play your favorite playlist, AI is no longer just for researchers and tech giants — it's in the hands of everyone.",{"data":2462,"content":2463,"nodeType":461},{},[],{"data":2465,"content":2466,"nodeType":2471},{},[2467],{"data":2468,"marks":2469,"value":2470,"nodeType":21},{},[],"🤖 AI in the Little Things","heading-4",{"data":2473,"content":2474,"nodeType":22},{},[2475],{"data":2476,"marks":2477,"value":2478,"nodeType":21},{},[],"We often overlook the subtle ways AI has integrated into our routines:",{"data":2480,"content":2481,"nodeType":207},{},[2482,2497,2512],{"data":2483,"content":2484,"nodeType":166},{},[2485],{"data":2486,"content":2487,"nodeType":22},{},[2488,2493],{"data":2489,"marks":2490,"value":2492,"nodeType":21},{},[2491],{"type":51},"Typing suggestions",{"data":2494,"marks":2495,"value":2496,"nodeType":21},{},[]," that finish your sentences.",{"data":2498,"content":2499,"nodeType":166},{},[2500],{"data":2501,"content":2502,"nodeType":22},{},[2503,2508],{"data":2504,"marks":2505,"value":2507,"nodeType":21},{},[2506],{"type":51},"Smart home devices",{"data":2509,"marks":2510,"value":2511,"nodeType":21},{},[]," that adjust lighting based on your habits.",{"data":2513,"content":2514,"nodeType":166},{},[2515],{"data":2516,"content":2517,"nodeType":22},{},[2518,2523],{"data":2519,"marks":2520,"value":2522,"nodeType":21},{},[2521],{"type":51},"Music and video recommendations",{"data":2524,"marks":2525,"value":2526,"nodeType":21},{},[]," that seem to read your mind.",{"data":2528,"content":2529,"nodeType":22},{},[2530],{"data":2531,"marks":2532,"value":2533,"nodeType":21},{},[],"All of that is powered by algorithms constantly learning from how you interact.",{"data":2535,"content":2536,"nodeType":461},{},[],{"data":2538,"content":2539,"nodeType":2471},{},[2540],{"data":2541,"marks":2542,"value":2543,"nodeType":21},{},[],"🧠 Creativity, Powered by Machines",{"data":2545,"content":2546,"nodeType":22},{},[2547],{"data":2548,"marks":2549,"value":2550,"nodeType":21},{},[],"One of the most fascinating shifts is how AI is enhancing creativity:",{"data":2552,"content":2553,"nodeType":207},{},[2554,2581,2600],{"data":2555,"content":2556,"nodeType":166},{},[2557],{"data":2558,"content":2559,"nodeType":22},{},[2560,2564,2569,2572,2577],{"data":2561,"marks":2562,"value":2563,"nodeType":21},{},[],"Designers use tools like ",{"data":2565,"marks":2566,"value":2568,"nodeType":21},{},[2567],{"type":51},"DALL·E",{"data":2570,"marks":2571,"value":693,"nodeType":21},{},[],{"data":2573,"marks":2574,"value":2576,"nodeType":21},{},[2575],{"type":51},"Runway",{"data":2578,"marks":2579,"value":2580,"nodeType":21},{},[]," to generate visual concepts.",{"data":2582,"content":2583,"nodeType":166},{},[2584],{"data":2585,"content":2586,"nodeType":22},{},[2587,2591,2596],{"data":2588,"marks":2589,"value":2590,"nodeType":21},{},[],"Developers are leveraging ",{"data":2592,"marks":2593,"value":2595,"nodeType":21},{},[2594],{"type":51},"GitHub Copilot",{"data":2597,"marks":2598,"value":2599,"nodeType":21},{},[]," to speed up coding workflows.",{"data":2601,"content":2602,"nodeType":166},{},[2603],{"data":2604,"content":2605,"nodeType":22},{},[2606],{"data":2607,"marks":2608,"value":2609,"nodeType":21},{},[],"Writers (like me, right now 😉) collaborate with language models to brainstorm or refine ideas.",{"data":2611,"content":2612,"nodeType":22},{},[2613],{"data":2614,"marks":2615,"value":2616,"nodeType":21},{},[],"Rather than replacing creativity, AI is becoming a powerful co-creator.",{"data":2618,"content":2619,"nodeType":461},{},[],{"data":2621,"content":2622,"nodeType":2471},{},[2623],{"data":2624,"marks":2625,"value":2626,"nodeType":21},{},[],"⚖️ The Double-Edged Sword",{"data":2628,"content":2629,"nodeType":22},{},[2630],{"data":2631,"marks":2632,"value":2633,"nodeType":21},{},[],"Of course, AI raises big questions:",{"data":2635,"content":2636,"nodeType":207},{},[2637,2647,2657],{"data":2638,"content":2639,"nodeType":166},{},[2640],{"data":2641,"content":2642,"nodeType":22},{},[2643],{"data":2644,"marks":2645,"value":2646,"nodeType":21},{},[],"How much should machines influence what we see or do?",{"data":2648,"content":2649,"nodeType":166},{},[2650],{"data":2651,"content":2652,"nodeType":22},{},[2653],{"data":2654,"marks":2655,"value":2656,"nodeType":21},{},[],"What happens to jobs that can be partially or fully automated?",{"data":2658,"content":2659,"nodeType":166},{},[2660],{"data":2661,"content":2662,"nodeType":22},{},[2663],{"data":2664,"marks":2665,"value":2666,"nodeType":21},{},[],"Where do we draw ethical boundaries, especially with data?",{"data":2668,"content":2669,"nodeType":22},{},[2670],{"data":2671,"marks":2672,"value":2673,"nodeType":21},{},[],"These are conversations we all need to be part of — not just the people building the models.",{"data":2675,"content":2676,"nodeType":461},{},[],{"data":2678,"content":2679,"nodeType":2471},{},[2680],{"data":2681,"marks":2682,"value":2683,"nodeType":21},{},[],"🛠️ Building With AI",{"data":2685,"content":2686,"nodeType":22},{},[2687,2691,2696,2699,2704],{"data":2688,"marks":2689,"value":2690,"nodeType":21},{},[],"As a developer, this is an incredibly exciting time. New APIs, tools, and SDKs are being released faster than ever. And with open-source frameworks like ",{"data":2692,"marks":2693,"value":2695,"nodeType":21},{},[2694],{"type":51},"LangChain",{"data":2697,"marks":2698,"value":965,"nodeType":21},{},[],{"data":2700,"marks":2701,"value":2703,"nodeType":21},{},[2702],{"type":51},"Hugging Face Transformers",{"data":2705,"marks":2706,"value":2707,"nodeType":21},{},[],", it’s easier than ever to build your own AI-powered app or assistant.",{"data":2709,"content":2710,"nodeType":461},{},[],{"data":2712,"content":2713,"nodeType":2471},{},[2714],{"data":2715,"marks":2716,"value":2717,"nodeType":21},{},[],"Final Thought",{"data":2719,"content":2720,"nodeType":22},{},[2721],{"data":2722,"marks":2723,"value":2724,"nodeType":21},{},[],"We’re at the beginning of something huge. The more we understand how AI works — and how to use it responsibly — the better we'll be at using it to improve our lives.",{"data":2726,"content":2727,"nodeType":22},{},[2728],{"data":2729,"marks":2730,"value":2731,"nodeType":21},{},[],"AI isn't the future anymore. It's the now.","AI",[2734,441],"ai",{"title":2736,"slug":2737,"date":2738,"excerpt":2739,"coverImage":2740,"body":2741,"tag":2840,"tags":2841},"How I Created My First Website with Nuxt.js: A Journey of Learning and Exploration","how-i-created-my-first-website-with-nuxt-js-a-journey-of-learning-and","2025-04-07","Building my first website with Nuxt.js was a great learning experience. It simplified development, and I explored Contentful for content management and Netlify for easy deployment, which helped me grow as a web developer.","https://images.ctfassets.net/26iu5vjpoesw/1k2nD4TdfCuWjRsQnM1CJ2/a7b1487338dc3906882880bd3b489beb/ChatGPT_Image_Apr_7_2025_from_Nuxt.js.png",{"data":2742,"content":2743,"nodeType":438},{},[2744,2751,2758,2765,2772,2779,2786,2793,2800,2833],{"data":2745,"content":2746,"nodeType":22},{},[2747],{"data":2748,"marks":2749,"value":2750,"nodeType":21},{},[],"Creating my first website was an exciting adventure, and I chose Nuxt.js to help me get started. As someone new to full-scale web development, I wanted a framework that would make things easier while allowing me to learn and experiment along the way. After some research, Nuxt.js caught my eye due to its simplicity, powerful features, and integration with Vue.js.",{"data":2752,"content":2753,"nodeType":470},{},[2754],{"data":2755,"marks":2756,"value":2757,"nodeType":21},{},[],"Why Nuxt.js?",{"data":2759,"content":2760,"nodeType":22},{},[2761],{"data":2762,"marks":2763,"value":2764,"nodeType":21},{},[],"Nuxt.js stood out because it provides out-of-the-box solutions like server-side rendering, automatic code splitting, and SEO optimization. Setting up the project was a breeze. I didn’t have to worry about routing—Nuxt.js handled it automatically by using a file-based system. This saved me time and effort, letting me focus on building the site’s content and layout instead of manual configurations.",{"data":2766,"content":2767,"nodeType":470},{},[2768],{"data":2769,"marks":2770,"value":2771,"nodeType":21},{},[],"Exploring Contentful",{"data":2773,"content":2774,"nodeType":22},{},[2775],{"data":2776,"marks":2777,"value":2778,"nodeType":21},{},[],"One feature I was eager to try was integrating a headless CMS. After exploring options, I chose Contentful. It allowed me to manage site content easily, creating content types like blogs and portfolio items through an intuitive interface. Connecting Contentful to Nuxt.js was smooth, and it made updating content a breeze without having to redeploy the site each time.",{"data":2780,"content":2781,"nodeType":470},{},[2782],{"data":2783,"marks":2784,"value":2785,"nodeType":21},{},[],"Deploying with Netlify",{"data":2787,"content":2788,"nodeType":22},{},[2789],{"data":2790,"marks":2791,"value":2792,"nodeType":21},{},[],"Once my site was ready, I deployed it using Netlify. The platform’s integration with GitHub meant that every time I pushed changes, Netlify automatically built and deployed my site. The process was fast, and I didn’t have to worry about server setup or complicated deployment pipelines.",{"data":2794,"content":2795,"nodeType":470},{},[2796],{"data":2797,"marks":2798,"value":2799,"nodeType":21},{},[],"Key Takeaways",{"data":2801,"content":2802,"nodeType":207},{},[2803,2813,2823],{"data":2804,"content":2805,"nodeType":166},{},[2806],{"data":2807,"content":2808,"nodeType":22},{},[2809],{"data":2810,"marks":2811,"value":2812,"nodeType":21},{},[],"Simplicity matters: Nuxt.js made the development process seamless with its intuitive setup.",{"data":2814,"content":2815,"nodeType":166},{},[2816],{"data":2817,"content":2818,"nodeType":22},{},[2819],{"data":2820,"marks":2821,"value":2822,"nodeType":21},{},[],"Don’t be afraid to experiment: Trying out Contentful and Netlify gave me a broader view of the web development ecosystem.",{"data":2824,"content":2825,"nodeType":166},{},[2826],{"data":2827,"content":2828,"nodeType":22},{},[2829],{"data":2830,"marks":2831,"value":2832,"nodeType":21},{},[],"Documentation is key: Excellent docs made learning these tools way easier.",{"data":2834,"content":2835,"nodeType":22},{},[2836],{"data":2837,"marks":2838,"value":2839,"nodeType":21},{},[],"In the end, building my first site with Nuxt.js was a fantastic learning experience. It wasn’t just about coding—it was about discovering new tools, tackling challenges, and growing as a developer. If you’re just starting out, I highly recommend giving Nuxt.js a shot!","Learning",[2437,1325,1327],[2843,2844,2846,2848,2850,2851,2853,2854,2856,2858,2860,2863,2865,2867,2870],{"name":2732,"slug":2734},{"name":2845,"slug":2080},"Architecture",{"name":2847,"slug":2078},"AWS",{"name":2849,"slug":440},"Best Practices",{"name":679,"slug":1326},{"name":2852,"slug":2079},"DevOps",{"name":1321,"slug":1324},{"name":2855,"slug":2437},"Nuxt",{"name":2857,"slug":441},"Opinion",{"name":2859,"slug":655},"Personal",{"name":2861,"slug":2862},"PHP","php",{"name":2864,"slug":1323},"Testing",{"name":2866,"slug":1327},"Tutorial",{"name":2868,"slug":2869},"TypeScript","typescript",{"name":2871,"slug":1325},"Vue"]