[{"data":1,"prerenderedAt":382},["ShallowReactive",2],{"gate-notes":3,"layout-site":134,"layout-footer":152,"gate-uses":160,"gate-gallery":324,"gallery":369},[4],{"id":5,"title":6,"body":7,"cover":114,"date":119,"description":105,"draft":120,"extension":121,"meta":122,"navigation":123,"path":124,"placeholder":120,"readingMinutes":125,"seo":126,"stem":127,"summary":128,"tags":129,"__hash__":133},"blog\u002Fblog\u002Fwhat-renaming-a-plugin-actually-touches.md","What renaming a plugin actually touches",{"type":8,"value":9,"toc":104},"minimark",[10,15,19,22,25,29,37,71,74,78,81,84,87,91,94,97,101],[11,12,14],"h2",{"id":13},"the-second-rename-was-not-my-idea","The second rename was not my idea",[16,17,18],"p",{},"Defer Forms for Contact Form 7 has had two names before this one. The first rename\nwas mine — the old name said nothing about what the plugin did, and its folder name\nbroke a wordpress.org naming rule I'd rather have caught earlier. That one I chose,\non my own schedule.",[16,20,21],{},"The second was not a choice. It went into wordpress.org review under its new name\nand came back rejected: the name led with a generic word and sat too close to an\nexisting plugin's. Review doesn't negotiate. You get a reason and a rename.",[16,23,24],{},"The part worth writing down isn't the naming — plenty has been written about naming\nthings. It's that a rename looks like a one-line change and isn't one, the moment\nthe plugin has been installed anywhere.",[11,26,28],{"id":27},"a-name-is-not-just-a-label","A name is not just a label",[16,30,31,32,36],{},"The visible rename — the string in the plugin header, the text in the admin menu —\ntook an afternoon. Everywhere else the old name was actually ",[33,34,35],"em",{},"encoded"," took four\nmore releases to find and fix, one at a time:",[38,39,40,48,59,65],"ul",{},[41,42,43,47],"li",{},[44,45,46],"strong",{},"The database table."," Submissions, roles, everything the plugin stores lived\nunder a table prefixed with the old internal short name.",[41,49,50,53,54,58],{},[44,51,52],{},"The developer hooks."," Seven of them, documented, with real signatures — anyone\nwho had already filtered on ",[55,56,57],"code",{},"old_prefix_submission_created"," needed to know it was\nnow something else, not silently stop firing.",[41,60,61,64],{},[44,62,63],{},"The REST namespace."," Every request the builder makes goes through it. Miss one\nreference and the admin looks fine until you open the one screen that calls it.",[41,66,67,70],{},[44,68,69],{},"Post meta keys, script and style handles, admin page URLs."," The last one meant\ntelling people to update their own bookmarks — a genuinely small cost, and still\na cost, and still something I had to remember to say.",[16,72,73],{},"None of this shows up in a diff of the plugin's header comment. All of it is the\nactual rename.",[11,75,77],{"id":76},"the-migration-has-to-run-once-and-only-once","The migration has to run once, and only once",[16,79,80],{},"The database table was the one that mattered most, because it's the one with a\nvisitor's data in it. Renaming a table isn't a decision you make in code and walk\naway from — every install that already has the old table needs something to run,\nonce, that moves its data to the new one before anything tries to read from the new\nname and finds nothing there.",[16,82,83],{},"That migration shipped in the same release as the prefix change. It checked for the\nold table, moved what it found, and got out of the way — the kind of code that is\neasy to write defensively and easy to forget about afterward, because once it's\nshipped it mostly just sits there working correctly for installs that need it and\ndoing nothing for installs that don't.",[16,85,86],{},"Sitting there is not free, though. Every release after that carried one more piece\nof logic whose entire job was reasoning about a table name nothing was going to use\ngoing forward. That's a real, if small, tax — one more thing to read when you're\ntrying to understand what a fresh install actually does, one more thing that could\nin principle misfire against data it was never meant to touch.",[11,88,90],{"id":89},"knowing-when-its-safe-to-delete","Knowing when it's safe to delete",[16,92,93],{},"The migration came out four releases later, and the reason it was safe to remove is\nthe part I'd actually recommend paying attention to: not \"it's probably fine by\nnow,\" but a specific, checkable claim — this plugin has never been distributed\nunder its old internal prefix. Every install anyone could download from that point\non was already past the rename. There was no table for the migration to ever find,\non any install that didn't already exist before the code was written, and there\nnever would be one again.",[16,95,96],{},"That's a different thing from \"nobody's complained.\" It's a claim about the\ndistribution history, and it's the kind of claim you can actually check rather than\nassume. Dead code that runs a permission check first and does nothing is safe. Dead\ncode you're only fairly sure is dead is a liability wearing the same clothes.",[11,98,100],{"id":99},"what-id-do-differently","What I'd do differently",[16,102,103],{},"Rename the internal prefix in the same release as the visible name, not four\nreleases later. Splitting them meant the plugin spent a stretch of its life with a\npublic name that didn't match anything in its own codebase — which is confusing to\nread, and is exactly the kind of gap a second, unplanned rename can catch you\nin the middle of. The visible name is the one wordpress.org reviews. The internal\none is the one that actually has to be right, and it deserves to move on the same\nday.",{"title":105,"searchDepth":106,"depth":106,"links":107},"",3,[108,110,111,112,113],{"id":13,"depth":109,"text":14},2,{"id":27,"depth":109,"text":28},{"id":76,"depth":109,"text":77},{"id":89,"depth":109,"text":90},{"id":99,"depth":109,"text":100},{"src":115,"alt":116,"width":117,"height":118},"\u002Fimages\u002Fblog\u002Fwhat-renaming-a-plugin-actually-touches.webp","Abstract illustration for an article about renaming a WordPress plugin and migrating its internal identity",1600,900,"2026-09-03",false,"md",{},true,"\u002Fblog\u002Fwhat-renaming-a-plugin-actually-touches",6,{"title":6,"description":105},"blog\u002Fwhat-renaming-a-plugin-actually-touches","A wordpress.org review rejected the name once. The rename itself was the easy part — the database table, the hooks API, and the REST namespace all had to move too, for a plugin that already had real installs.",[130,131,132],"wordpress","php","products","yyGeKgB7V-MTe1mpjFtKFNbd162HcN-P5NvkBD-dNpA",{"id":135,"email":136,"extension":137,"meta":138,"name":139,"role":140,"socials":141,"stem":148,"summary":149,"whatsapp":150,"__hash__":151},"site\u002Fglobal\u002Fsite.json","rupash.das.202@gmail.com","json",{},"Rupash Das","Full-stack developer",[142,145],{"label":143,"url":144},"GitHub","https:\u002F\u002Fgithub.com\u002FRupashdas",{"label":146,"url":147},"Fiverr","https:\u002F\u002Fwww.fiverr.com\u002Frupash_das","global\u002Fsite","Laravel and Vue applications, and React-based WordPress plugins. Behind them: eight years of client work, and around 45 production sites shipped as a US agency's development partner.","https:\u002F\u002Fwa.me\u002F8801873139202","EeGW4F_WwVqwPebxGBi6Bks5X-7JlYlrabI1_3un-1M",{"id":153,"colophon":154,"ctaLabel":155,"extension":137,"meta":156,"stem":157,"tagline":158,"__hash__":159},"footer\u002Fglobal\u002Ffooter.json","Rupash Das · Built with Nuxt","Start a project",{},"global\u002Ffooter","Applications, APIs and plugins. Built in Bangladesh, shipped worldwide.","ioAbs2Z6IXBmsOAL3B48yjlepJt_zX4shLK2YbnqVHc",[161,194,219,241,273,298],{"id":162,"category":163,"extension":137,"items":164,"meta":189,"order":190,"stem":191,"summary":192,"__hash__":193},"uses\u002Fpages\u002Fuses\u002F01-applications.json","The application stack",[165,169,173,177,181,185],{"name":166,"url":167,"note":168,"key":123},"Laravel","https:\u002F\u002Flaravel.com","An API that could have a second client put in front of it, rather than a framework quietly rendering its own front end. Sanctum for authentication; the authorisation layer is written by hand.",{"name":170,"url":171,"note":172,"key":123},"Vue 3, Composition API","https:\u002F\u002Fvuejs.org","Composition over Options for anything non-trivial. Logic that belongs together lives together, and it can be pulled out into a composable the day a second screen needs it.",{"name":174,"url":175,"note":176,"key":123},"Pinia","https:\u002F\u002Fpinia.vuejs.org","One store per domain, each owning its own loading and error state. A single global store is comfortable for a week and unreadable by the third feature.",{"name":178,"url":179,"note":180},"Vite","https:\u002F\u002Fvite.dev","The build for everything — applications and plugin interfaces both. Fast enough that a React panel inside wp-admin stops feeling like a compromise.",{"name":182,"url":183,"note":184},"Tailwind","https:\u002F\u002Ftailwindcss.com","Tokens in one file, utilities everywhere else. The token layer is the part that matters; the utilities are only how it gets applied.",{"name":186,"url":187,"note":188},"Nuxt","https:\u002F\u002Fnuxt.com","This site. Prerendered, with a bundle budget that fails the build rather than being admired in a readme.",{},1,"pages\u002Fuses\u002F01-applications","What I reach for when the thing being built is an application rather than a page.","hGL4lHq-kkVaIrg0fv7gwqYxrk8AVgOVVNf80UJ2_tU",{"id":195,"category":196,"extension":137,"items":197,"meta":215,"order":109,"stem":216,"summary":217,"__hash__":218},"uses\u002Fpages\u002Fuses\u002F02-local.json","Local development",[198,202,206,209,212],{"name":199,"url":200,"note":201,"key":123},"Laragon","https:\u002F\u002Flaragon.org","Where the Laravel side lives. Apache, PHP and MySQL without an afternoon of configuration.",{"name":203,"url":204,"note":205,"key":123},"LocalWP","https:\u002F\u002Flocalwp.com","One throwaway site per plugin. A plugin has to be installed on a clean WordPress before I believe it works.",{"name":207,"note":208},"MySQL","The database under both stacks. Enough of it that a data migration can be a query rather than a plugin.",{"name":210,"note":211,"key":123},"A test suite that runs locally","Just over a hundred suites on the form plugin alone. They exist because it ships to sites I will never see, and no amount of clicking replaces that.",{"name":213,"note":214},"A guarded release script","Building a release by hand is how a stray development file ends up in a zip. The script refuses rather than reminds me.",{},"pages\u002Fuses\u002F02-local","Two stacks, kept apart. Application work and WordPress work do not share an environment.","O6x-4PI-YpMIY4lMnasic5o5S74m0XvaSJobTsv9zo0",{"id":220,"category":221,"extension":137,"items":222,"meta":237,"order":106,"stem":238,"summary":239,"__hash__":240},"uses\u002Fpages\u002Fuses\u002F03-terminal.json","Version control and terminal",[223,226,229,231,234],{"name":224,"note":225,"key":123},"Git","Every project is a repository, including the client sites that were never supposed to have one. It is also the reason I can work on a live site and still have a way back.",{"name":227,"note":228,"key":123},"Git Bash on Windows Terminal","POSIX habits on a Windows machine. Most tooling assumes a Unix shell, and it is less work to bring one than to translate every command.",{"name":143,"url":144,"note":230},"Public for the plugins, private for client work. Actions for anything that has to happen on a schedule.",{"name":232,"note":233},"Composer","PHP dependencies, and increasingly the autoloader inside plugins that used to get by without one.",{"name":235,"note":236},"npm","The other half of every build. Kept boring on purpose — a dependency in a plugin is a dependency in somebody's site.",{},"pages\u002Fuses\u002F03-terminal","The layer underneath everything else. Unglamorous, and the part I would miss first.","T-s-xcuuGET_9dm2M0uVTuhlZPPSeCAvRAiH65e1P4w",{"id":242,"category":243,"extension":137,"items":244,"meta":268,"order":269,"stem":270,"summary":271,"__hash__":272},"uses\u002Fpages\u002Fuses\u002F04-wordpress.json","The WordPress side",[245,249,253,256,260,264],{"name":246,"url":247,"note":248,"key":123},"React with @wordpress\u002Fcomponents","https:\u002F\u002Fdeveloper.wordpress.org\u002Fblock-editor\u002Freference-guides\u002Fcomponents\u002F","Plugin interfaces are applications mounted inside wp-admin. Using the WordPress components is why they look like they belong there rather than like something bolted on.",{"name":250,"url":251,"note":252,"key":123},"dnd-kit","https:\u002F\u002Fdndkit.com","Dragging that works from a keyboard and announces itself to a screen reader. Retrofitting that is far more expensive than choosing it.",{"name":254,"note":255,"key":123},"The WordPress REST API","The only seam between a plugin's PHP and its interface. It is what makes the PHP testable without a browser anywhere near it.",{"name":257,"url":258,"note":259},"Advanced Custom Fields Pro","https:\u002F\u002Fwww.advancedcustomfields.com","For client sites, where the content model lives. Fields named in the client's vocabulary, not the database's.",{"name":261,"url":262,"note":263},"Elementor","https:\u002F\u002Felementor.com","Not a compromise — the right answer when an in-house team edits weekly. Locked templates, a small palette, global styles set once.",{"name":265,"url":266,"note":267},"WooCommerce","https:\u002F\u002Fwoocommerce.com","With as few extension plugins as the requirement genuinely needs. Every add-on is a licence somebody has to keep paying.",{},4,"pages\u002Fuses\u002F04-wordpress","What goes into a plugin, and what goes onto a client site. Two different lists with two different standards.","9KoxuzjlxZ4muWCbNdHrgY5KXOxLFueIx9g9orrTomo",{"id":274,"category":275,"extension":137,"items":276,"meta":293,"order":294,"stem":295,"summary":296,"__hash__":297},"uses\u002Fpages\u002Fuses\u002F05-services.json","Hosting and delivery",[277,280,283,287,290],{"name":278,"note":279,"key":123},"The client's own hosting","Shared hosting, a control panel, no staging environment and no deploy pipeline. That is the honest description of most WordPress work, and pretending otherwise is how a rebuild gets quoted when a fix was needed.",{"name":281,"note":282,"key":123},"FTP against production","Not a preference, a constraint of agency work. It teaches one discipline above all: change one thing, verify it, keep a way back.",{"name":284,"url":285,"note":286,"key":123},"Cloudflare","https:\u002F\u002Fcloudflare.com","DNS, CDN and Pages. This site is prerendered and served from it.",{"name":288,"note":289},"A transactional email provider","Site mail leaves through a real provider, never the web host's PHP mail. This is the single most common reason a contact form fails silently.",{"name":291,"note":292},"Backups the client owns","To storage in their account, not mine. Leaving me should never mean leaving their data.",{},5,"pages\u002Fuses\u002F05-services","Mostly other people's infrastructure. The skill is working inside constraints I did not choose.","FbmQ8ReFvSj2JxfK-J7EKTve-3XXK3FlX2rLGYsSX4s",{"id":299,"category":300,"extension":137,"items":301,"meta":320,"order":125,"stem":321,"summary":322,"__hash__":323},"uses\u002Fpages\u002Fuses\u002F06-working.json","How I work",[302,305,308,311,314,317],{"name":303,"note":304,"key":123},"Model the data before the interface","The fastest way to make something expensive to own is to build the screens first and discover the shape of the data afterwards. True of a Laravel schema and of an ACF field group alike.",{"name":306,"note":307,"key":123},"Read the source before the documentation","Learned in two years of having nobody to ask. It is still the fastest route into an unfamiliar framework, and it is why picking up a new one is a week rather than a season.",{"name":309,"note":310,"key":123},"Ask who maintains it, before choosing how to build it","The same question opens every project. It decides page builder or custom theme, and it decides how much of an application belongs in its API rather than its front end.",{"name":312,"note":313},"Deep work in the morning, calls at night","US clients, Dhaka time zone. The overlap is my evening, which leaves the mornings uninterrupted.",{"name":315,"note":316},"A repeated complaint is a specification","The same request from four unrelated clients is not an inconvenience. It is the only market research a one-person shop gets for free, and every plugin I have finished started there.",{"name":318,"note":319},"Write the handover in the maintainer's language","One of my plugins ships its maintainer notes in Bengali. Documentation somebody has to translate before using is documentation that will not be used.",{},"pages\u002Fuses\u002F06-working","Process rather than software. Most of it was learned the expensive way.","TXfbDM6GA9PGTV6J6tU1Hgl8j10rEHfQnjVVFF0cYe0",[325,338,348,358],{"id":326,"title":327,"category":328,"extension":137,"image":329,"meta":335,"order":190,"stem":336,"__hash__":337},"gallery\u002Fpages\u002Fgallery\u002F01-tazko-board.json","Kanban board","Applications",{"src":330,"alt":331,"width":332,"height":333,"caption":334},"\u002Fimages\u002Fgallery\u002Ftazko-board.webp","A kanban board in Tazko with Todo, In Progress, Review and Done columns and task cards carrying priority and due date",1400,1050,"Dragging updates the store first and reconciles against the API. The server still owns the order.",{},"pages\u002Fgallery\u002F01-tazko-board","TgaWh_sQJBBgJ6hawBbHU4gnRWEJFjCwfDP171Cc1ao",{"id":339,"title":340,"category":328,"extension":137,"image":341,"meta":345,"order":109,"stem":346,"__hash__":347},"gallery\u002Fpages\u002Fgallery\u002F02-permissions-matrix.json","The permission matrix",{"src":342,"alt":343,"width":332,"height":333,"caption":344},"\u002Fimages\u002Fgallery\u002Fpermissions-matrix.webp","The Tazko role editor showing capability groups for Activity, Clients and Comments, with 73 of 80 permissions enabled for the Admin role","Roles composed from capabilities, so a workspace can define its own without a release.",{},"pages\u002Fgallery\u002F02-permissions-matrix","PL75HpzIO1ckSp-VJiV57O07DaiqgK2p5ZAFrD6OjvA",{"id":349,"title":350,"category":328,"extension":137,"image":351,"meta":355,"order":106,"stem":356,"__hash__":357},"gallery\u002Fpages\u002Fgallery\u002F03-tazko-task.json","A task discussion",{"src":352,"alt":353,"width":332,"height":333,"caption":354},"\u002Fimages\u002Fgallery\u002Ftazko-task.webp","A Tazko task detail view with a subtask checklist at 67 percent and a three-message discussion thread below it","One polymorphic thread serves tasks and projects. The reply you have not sent is saved on the server, not in the tab.",{},"pages\u002Fgallery\u002F03-tazko-task","my5OA-xN5tuWban5-uTAOfVRIVLQZ8Mt-ZJ71uIfLZg",{"id":359,"title":360,"category":361,"extension":137,"image":362,"meta":366,"order":125,"stem":367,"__hash__":368},"gallery\u002Fpages\u002Fgallery\u002F06-plugin-settings.json","Settings, after the cuts","Plugins",{"src":363,"alt":364,"width":332,"height":333,"caption":365},"\u002Fimages\u002Fgallery\u002Fplugin-settings.webp","The spam settings screen in Defer Forms, with toggles for honeypot protection, a time-trap and duplicate-submission blocking","Most of what a settings page wants to be belongs in the hooks API instead.",{},"pages\u002Fgallery\u002F06-plugin-settings","11L8JKOQF2SaNPqMAzxj49pHozh58LqUmBF0Zm3N9QY",[370,373,376,379],{"id":326,"title":327,"category":328,"extension":137,"image":371,"meta":372,"order":190,"stem":336,"__hash__":337},{"src":330,"alt":331,"width":332,"height":333,"caption":334},{},{"id":339,"title":340,"category":328,"extension":137,"image":374,"meta":375,"order":109,"stem":346,"__hash__":347},{"src":342,"alt":343,"width":332,"height":333,"caption":344},{},{"id":349,"title":350,"category":328,"extension":137,"image":377,"meta":378,"order":106,"stem":356,"__hash__":357},{"src":352,"alt":353,"width":332,"height":333,"caption":354},{},{"id":359,"title":360,"category":361,"extension":137,"image":380,"meta":381,"order":125,"stem":367,"__hash__":368},{"src":363,"alt":364,"width":332,"height":333,"caption":365},{},1788434573084]