[{"data":1,"prerenderedAt":174},["ShallowReactive",2],{"work-\u002Fwork\u002Fopen-source-migration":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"tags":10,"role":16,"duration":17,"client":18,"client_named":6,"permission":6,"featured":19,"order":20,"body":21,"_type":167,"_id":168,"_source":169,"_file":170,"_stem":171,"_extension":172,"sitemap":173},"\u002Fwork\u002Fopen-source-migration","work",false,"","Nuxt 2 to Nuxt 3 migration on an open-source platform","Migrated a large open-source no-code database platform from Nuxt 2 to Nuxt 3, traced a long-standing search race condition, and worked through a backlog of production Sentry errors.",[11,12,13,14,15],"Vue","Nuxt 3","Vuex","TypeScript","Sentry","Frontend engineer","6+ months","An open-source no-code database platform",true,1,{"type":22,"children":23,"toc":159},"root",[24,33,39,44,49,55,60,65,71,82,92,102,112,122,132,138,143,148,154],{"type":25,"tag":26,"props":27,"children":29},"element","h2",{"id":28},"context",[30],{"type":31,"value":32},"text","Context",{"type":25,"tag":34,"props":35,"children":36},"p",{},[37],{"type":31,"value":38},"A large open-source no-code database platform — a self-hostable alternative to the well-known\nspreadsheet-database tools. The frontend is a substantial Nuxt application with a long history and a\nreal installed base of self-hosted users.",{"type":25,"tag":34,"props":40,"children":41},{},[42],{"type":31,"value":43},"I worked on moving that frontend from Nuxt 2 to Nuxt 3.",{"type":25,"tag":34,"props":45,"children":46},{},[47],{"type":31,"value":48},"Product and client details are withheld: the engagement is under NDA.",{"type":25,"tag":26,"props":50,"children":52},{"id":51},"the-hard-problem",[53],{"type":31,"value":54},"The hard problem",{"type":25,"tag":34,"props":56,"children":57},{},[58],{"type":31,"value":59},"A framework migration on a codebase this size is rarely about the framework. Nuxt 2 to Nuxt 3\nchanges the module system, the composition API surface and the store layer all at once, and the\napplication had years of workarounds built against the old behaviour.",{"type":25,"tag":34,"props":61,"children":62},{},[63],{"type":31,"value":64},"The constraint that shaped everything: users self-host this product. A migration that breaks on\nsomeone else's server is not something you can hotfix for them.",{"type":25,"tag":26,"props":66,"children":68},{"id":67},"what-i-did",[69],{"type":31,"value":70},"What I did",{"type":25,"tag":34,"props":72,"children":73},{},[74,80],{"type":25,"tag":75,"props":76,"children":77},"strong",{},[78],{"type":31,"value":79},"Read before changing.",{"type":31,"value":81}," I spent my first weeks mapping which parts of the codebase were stable and\nwhich were fragile, rather than starting on the migration immediately. The migration order came out\nof that map.",{"type":25,"tag":34,"props":83,"children":84},{},[85,90],{"type":25,"tag":75,"props":86,"children":87},{},[88],{"type":31,"value":89},"Migrated incrementally.",{"type":31,"value":91}," The work shipped in pieces that could each be reviewed and reverted,\nrather than as one large branch that would be impossible to review honestly.",{"type":25,"tag":34,"props":93,"children":94},{},[95,100],{"type":25,"tag":75,"props":96,"children":97},{},[98],{"type":31,"value":99},"Traced a race condition in the view search context.",{"type":31,"value":101}," Users hit intermittent search failures that\nonly appeared under fast typing, and it had resisted reproduction. The cause was debounce cleanup\nnot running when the component remounted, so overlapping async calls resolved out of order. The fix\nwas in the cleanup path, not in the debounce interval — a longer debounce would have hidden it\nrather than fixed it.",{"type":25,"tag":34,"props":103,"children":104},{},[105,110],{"type":25,"tag":75,"props":106,"children":107},{},[108],{"type":31,"value":109},"Worked through the Sentry backlog.",{"type":31,"value":111}," I triaged production errors by impact and fixed them in\ngroups. Several shared a root cause, so single fixes cleared multiple reported errors.",{"type":25,"tag":34,"props":113,"children":114},{},[115,120],{"type":25,"tag":75,"props":116,"children":117},{},[118],{"type":31,"value":119},"Built notification UI over WebSockets",{"type":31,"value":121},", and integrated a third-party analytics product for usage\ntracking.",{"type":25,"tag":34,"props":123,"children":124},{},[125,130],{"type":25,"tag":75,"props":126,"children":127},{},[128],{"type":31,"value":129},"Wrote handover documentation",{"type":31,"value":131}," — issue workflows, debugging guides and log analysis notes for\nwhoever picked the work up next.",{"type":25,"tag":26,"props":133,"children":135},{"id":134},"outcome",[136],{"type":31,"value":137},"Outcome",{"type":25,"tag":34,"props":139,"children":140},{},[141],{"type":31,"value":142},"The migration shipped incrementally rather than as a single release. The search race condition was\nfixed at its cause. The Sentry backlog came down.",{"type":25,"tag":34,"props":144,"children":145},{},[146],{"type":31,"value":147},"I am deliberately not putting numbers on this page. I would rather describe the work accurately than\nquantify it loosely.",{"type":25,"tag":26,"props":149,"children":151},{"id":150},"stack",[152],{"type":31,"value":153},"Stack",{"type":25,"tag":34,"props":155,"children":156},{},[157],{"type":31,"value":158},"Vue, Nuxt 3, Vuex, TypeScript, WebSockets, Sentry.",{"title":7,"searchDepth":160,"depth":160,"links":161},2,[162,163,164,165,166],{"id":28,"depth":160,"text":32},{"id":51,"depth":160,"text":54},{"id":67,"depth":160,"text":70},{"id":134,"depth":160,"text":137},{"id":150,"depth":160,"text":153},"markdown","content:work:open-source-migration.md","content","work\u002Fopen-source-migration.md","work\u002Fopen-source-migration","md",{"loc":4},1790598049477]