perf: strip unread fields from the shared plugin payload (metric: total byte weight / FCP) - #6
Open
phyceClaw wants to merge 1 commit into
Open
Conversation
Metric: total byte weight / FCP (~272 KB uncompressed off every page) `plugins` is a shared Inertia prop, so the full 2048-plugin list is embedded in the HTML of every page, not just the homepage. Measured on production, it dominates the payload everywhere: /naturalspeech 925 KB JSON, 907 KB plugins (98%) /developers/growing 938 KB JSON, 907 KB plugins (97%) /top 969 KB JSON, 907 KB plugins (94%) /developers 1,095 KB JSON, 907 KB plugins (83%) Only two consumers read that list: the homepage table and the header search box. Between them they never touch `git_repo`, `support` or `created_on`, which cost 133 KB, 62 KB and 80 KB respectively - 29.6% of the list. Those three fields are read elsewhere, which is why they cannot simply be dropped from the API client: PluginDetail reads all three off its own single-plugin prop, and TopAbsolute/TopRelative read `created_on` off `entry.plugin`. Both come from different endpoints and are untouched here. The trim is applied only where the list is handed to the client. GenerateSitemap consumes the same array but only reads `name` and `updated_on`, both retained. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Metric targeted: Total byte weight → FCP
pluginsis a shared Inertia prop (HandleInertiaRequests::share()), so the full 2048-plugin list is embedded in the HTML of every page — not just the homepage. Measured against production:plugins/naturalspeech/developers/growing/top/developersA plugin detail page currently downloads and parses ~907 KB of plugin records it never renders.
What this changes
Only two things read that shared list: the homepage table (
Index.vue) and the header search (scoreSearchResultinformatting.ts). Between them they never touch three fields:git_repocreated_onsupportgetClientPlugins()strips them at the point the list is handed to the client.Why they can't just be removed from the API client
All three are used — just not from this list:
PluginDetail.vuereadsgit_repo,supportandcreated_onfrom its own single-plugin prop (getPlugin()).TopAbsolute.vue/TopRelative.vuereadcreated_onfromentry.plugin(getTopAbsolute()/getTopRelative()).Those come from different endpoints and are deliberately untouched.
GenerateSitemapconsumes the same array but only readsnameandupdated_on, both retained.Tests
Added
tests/Feature/ClientPluginPayloadTest.php, which fakes the API and asserts the shared prop drops exactly those three keys and keeps the ten the frontend reads. Verified it is not vacuous — reverting the controller togetPlugins()makes it fail.Note: the tests are class-based PHPUnit to match the existing siblings.
tests/Pest.phpis currently empty, so Pest is not bound toTests\TestCaseand Pest-styleit()tests fail with Call to undefined method ::get(). Worth fixing separately.vendor/bin/pint --dirtypasses; full suite green (4 passed).Deliberately not included
The remaining ~635 KB is
description+tags+ the table columns, which the search genuinely needs. Getting rid of that means either an Inertia deferred prop or moving search server-side — a behavioural change, so it belongs in its own PR.The TypeScript
Plugininterface is intentionally left as the full shape: narrowing it would breakPluginDetail/TopAbsolute, which legitimately read the wider record. Splitting the type is a follow-up.🤖 Generated with Claude Code