i built a browser extension in a weekend and learned more than in a year of tutorials

tech · 2024-03-15 · 6 min

i built a browser extension in a weekend and learned more than in a year of tutorials

the tutorial taught me how to put a button in a toolbar. the weekend taught me what an extension is actually for.

what i built and why

the thing i built was small: a button that watches a page for a specific kind of information and surfaces it without me having to look for it. i had been doing the same manual step three or four times a day for months, and i had finally reached the point where the repetition was louder than the effort of automating it.

the first version was the smallest thing that could work. content script, a message back to the extension, a tiny popup. no options page, no settings, no persistence beyond what the browser gives you for free. the goal was not to build a product. the goal was to stop doing the thing by hand.

that frame matters. a lot of tutorials implicitly teach you to build as if you will publish, as if the extension has to be general, as if the first version is also the public version. that is a good way to spend a weekend and end up with nothing. build the specific thing first.

what the extension api actually is

an extension is not a webpage with extra steps. it is a small program that the browser runs in a context the page cannot see. that is the whole selling point. a script on the page can read the page, mostly. an extension can read the page and also do things the page cannot: add a toolbar button, read tabs, store data the site cannot touch, run in the background.

the parts i touched:

  • a manifest that says what the extension is and what it asks for
  • a content script that runs in the page and can see the dom
  • a background script, or service worker, that listens for browser events
  • a popup or action button that gives the user a place to click
  • optional storage for small bits of state

that is not a lot of surface area. what surprised me was how little glue there is between the parts once you accept the model. the content script and the background script are not in the same scope. they message each other. once you stop expecting them to share variables, the whole thing gets simpler.

what the tutorials left out

the tutorials left out the parts that only matter once you have something running.

the first is permissions. it is tempting to ask for a broad permission because it is easier than asking for a narrow one and revising later. resist that. a permission is a promise you make to the person installing the extension, and broad permissions are harder to justify when the thing is small. ask only for what the extension genuinely needs.

the second is update friction. an extension is not a site you deploy and forget. the user has a copy installed. when you push a new version, the browser has to fetch it, and the user may have to approve it depending on the store and the platform. that means version one should be embarrassingly small, because version one will be the version you explain to yourself when you are tired and trying to figure out why the new button does not appear.

the third is the gap between localhost and installed. a lot of extension development is not writing code. it is reloading, reclicking, re-reading the console in a context that is not the page console and not quite the devtools console either. i underestimated how much of the weekend would be diagnosing where a message went missing.

the fourth is that content scripts are isolated by design. they see the page, but they do not share javascript context with the page. that is good for the user. it is mildly annoying for the developer who expected to reach into the page's scripts and pull out a value by hand. there are reasons to want that and reasons not to, and the reasons not to are usually the better ones.

what i learned that the tutorials never said

the first lesson is that shipping the smallest useful version is more educational than building the interesting version halfway. i could have spent the weekend planning a richer thing. instead i spent a few hours on the thing that removed the repetition, and then a few more hours understanding what i had built well enough to explain it to myself.

the second lesson is that browser extensions are one of the few places where a weekend project can be genuinely useful to one person without pretending to be useful to everyone. a tool that helps one workflow is still a tool. not everything has to generalize to be worth building.

the third lesson is that the browser is a more honest platform than it looks. the extension model is constrained on purpose. that constraint is the point. if you are building a thing that runs in someone else's browser, on someone else's machine, with a user who installed it expecting a specific job, the constraints are not an obstacle to work around. they are the shape of the job.

the fourth lesson is that debugging an extension is mostly tracing boundaries. which part of the extension is running, which part is not, where the message crossed from one context to another, what permission was actually granted, what the store rejected, what the browser cached. once you stop looking for a logic error in the part of the code that was never reached, most of these problems become ordinary.

what i would do differently next time

i would start with the manifest and the smallest permission set i could justify, and i would not add a permission because it is easier than thinking about the message path. i would treat the first version as disposable by default. i would test on a real page sooner, not a representative page i built to look like the real one.

and i would remember that the goal is not to publish. the goal is to stop doing the thing by hand. if the extension does that, it has already won, even if nobody else ever installs it.

for the bigger browser-side automation it inspired: apple intelligence first impressions. for the week-of-testing discipline behind choosing any tool: product testing one device.