In case you’re an engineer, you’ve in all probability had the expertise of making an attempt to arrange or keep an area improvement surroundings. And whereas I could possibly be unsuitable, I’d be prepared to wager that, most of the time, it was…not a terrific expertise.
At a earlier firm, once I was simply beginning out writing code, a superb senior engineer spent a number of hours on a name with me summoning arcane CLI instructions to attempt to get my surroundings up and operating so I may contribute to a venture. We lastly succeeded, however then it broke the next week in a brand new and complicated means when one thing modified. It wanted extra painful TLC earlier than I may get to the precise factor I wished to do, which was not, in reality, sustaining an area improvement surroundings, however transport code to ship worth to clients.
At Assist Scout, our developer expertise workforce has made large progress lately to enhance what was already a reasonably good native setup (you’ll hear extra about their essential work in future posts!). As of late, more often than not, my dev surroundings simply works, and when it doesn’t (normally as a result of I haven’t up to date it just lately sufficient), I can typically work out why, unblock myself, and get again to work.
However then I’m a software program engineer, a kind of one who, relative to the inhabitants at massive, has a a lot larger tolerance for throwing spaghetti on the wall of an issue I don’t actually perceive. I scan by outdated Stack Overflow threads for related morsels of data, work together with Docker andbrew
andbash
profiles, and, you already know, typically flip it off, strive one thing else, and switch it on once more (and time and again and once more) till one thing adjustments.
Not everybody needs to be a hacker
At most corporations, although, there are a broader group of stakeholders — designers, product managers, QA specialists, assist workforce members — who would profit from the flexibility to check improvement code however who realistically (and fairly!) are in all probability by no means going to need to take care of the trouble of configuring and sustaining a dev surroundings.
There are other ways to deal with the issue of getting code to those of us earlier than it goes to clients. One basic method is to ship the code to manufacturing behind a characteristic flag, which you’ll then allow selectively for the oldsters in your workforce who need to try it out. That is nice by way of simplicity of entry — these stakeholders simply have to log into particular accounts within the app they already use — but it surely actually limits agility and iteration as a result of every change requires a brand new Jira card, PR, evaluation, CI cross, and deployment earlier than the brand new code reveals up behind the characteristic flag.
The new new method to this drawback is cloud-based environments, the shiny successors to staging servers. With this, you get the patron simplicity of testing in manufacturing (simply go to this URL and have at it!) with out really having to ship to manufacturing, making iteration cycles means sooner. We’re experimenting with this paradigm and have had some good experiences, however then we get to a different drawback, which is how exactly an area surroundings can mirror manufacturing. I feel everybody who’s had bother organising a dev surroundings has in all probability additionally had the expertise of getting one thing working domestically after which, after merging and deploying it, discovering out that it doesn’t fairly work the identical means in manufacturing.
Standing on the shoulders of giants
I felt these two issues in my work and will perceive how they have been holding issues again, however, for some time, I didn’t actually know what to do about them. As I onboarded and received extra context about Assist Scout’s structure and engineering tradition (normally by looking Slack to strive to determine one thing I didn’t perceive), I might often see references to utilization of an older Assist Scout venture referred to as ProxyPack, so at some point, led by curiosity, I made a decision to look into it.
From an archived GitHub repo, I discovered that ProxyPack was an internally-developed Webpack plugin that was used to make it doable to run native improvement JS code towards our manufacturing app’s backend (a PHP monolith supported by a community of Java providers). It wasn’t accessible to everybody and nonetheless required some native setup and terminal manipulation (together with organising a spoofed SSL cert), however QA engineers I talked to who had been round when it was in use mentioned it was actually helpful for them by way of saving time and having extra confidence of their testing. Nevertheless, it was apparently tough to take care of (once more, you needed to take care of SSL certs!) and after the developer who constructed it left, it will definitely fell into obsolescence.
Study One thing Day
I’ll be sincere that my private pleasure in life shouldn’t be Webpack configuration (is it anybody’s?), so I didn’t contemplate making an attempt to revive ProxyPack itself. However the core thought was actually intriguing! Assist Scout encourages workers throughout the group to take common Study One thing Days the place they step away from their regular duties and concentrate on studying issues that may assist them (and by extension, Assist Scout) develop. This will take a wide range of varieties: taking programs, attending conferences, and spiking out prototypes and proofs of idea (POCs).
My favourite method to study is by doing, so I at all times use today to hack on concepts. Whereas I’m not nice with Webpack, what I do really know loads about is Chrome extensions. In truth, a part of the rationale I work at Assist Scout is that in my earlier job, I constructed Chrome extensions on prime of it to customise the app to my workforce’s particular wants. So the following time I had a Study One thing Day I began to noodle round with an thought: May I get the identical end-user end result of ProxyPack — the flexibility to simply check dev code towards manufacturing — by an extension?
Bundles of pleasure
My inspiration got here once I was wanting on the Community tab to look at an API response in manufacturing towards the one I used to be getting in my native surroundings. Whereas I used to be scanning by the record, I by chance clicked on one of many JavaScript bundle requests somewhat than the API response I used to be searching for. Like many apps, we retailer our JS code in a distributed CDN for the quickest doable entry by customers regardless of the place they’re.
Let’s say the bundle URL for one of many SPAs that make up our app seems like this:
https://helpscoutblog.cloudfront.web/frontend/inbox-settings.js
An thought began to develop in my thoughts. I switched to the opposite browser the place I used to be operating the native surroundings. The native dev surroundings builds the JS code and runs it on a easynginx
server that’s utilized in lieu of the CDN.
https://localenv.helpscout.com/frontend/inbox-settings.js
I had a eureka second — what if, whereas utilizing the manufacturing app, I may programmatically intercept requests to the manufacturing variations of the bundles and redirect them to the native variations of the identical information?
Declaration of independence
Chrome extensions have a wide range of superpowers not granted to straightforward net apps. One of the crucial highly effective of these is the flexibility to work together with HTTP requests made in a person’s browser. The normal mechanism for doing so was thewebRequest
API, which allow you to register a callback for HTTP requests and allowed you to pay attention to them or, with theblocking
choice, intercept and modify them (URL/headers/request physique) whereas they’re in flight.
ThewebRequest
API would have made implementing my thought very easy, and I began with that as a proof of idea, however then I discovered that theblocking
choice forwebRequest
is within the technique of being deprecated. Nevertheless, Chrome has offered a successor towebRequest
, thedeclarativeNetRequest
API.
This API is far tighter by way of what it means that you can do to requests (and presumably its declarative nature additionally makes it simpler for Chrome’s app reviewers to automate scans for dangerous extension habits). Quite than having full management to imperatively modify the requests your self in your extension code withwebRequest
, you register guidelines (which explicit requests to do issues to) and actions (what issues to do) in accordance with a restricted JSON schema after which the browser itself handles all of that externally (and extra securely) as a substitute of inside your extension code.
Request…intercepted
On the root of a Chrome extension’s code is a manifest requesting the permissions your extension wants and itemizing the placement of varied property in your app so it is aware of the place to run your code. Right here’s a minimal manifest for our use case, claiming thedeclarativeNetRequest
permission and directing the extension to the place our rule is registered:
{
"title": "native proxy experiment",
"model": "1.0",
"manifest_version": 3,
"description": "Proxies hs-app-ui JS requests to hs-stack bundles",
"host_permissions": ["" ],
"declarative_net_request": {
"rule_resources": [
{
"id": "proxy",
"enabled": true,
"path": "proxyRule.json"
}
]
},
"permissions": [
"declarativeNetRequest",
]
}
I began out by hardcoding a rule for one file:
{
"id": 1,
"precedence": 1,
"motion": {
"sort": "redirect",
"redirect": {
"url": "https://localenv.helpscout.com/frontend/inbox-settings.js"
}
},
"situation": {
"urlFilter": "https://helpscoutblog.cloudfront.web/frontend/inbox-settings.js",
}
}
As you may see, this rule has aurlFilter
situation in order that when a request is made for the manufacturing bundle, it redirects it to the native model. I activated the extension, made a change, and refreshed the web page, and it labored — I may see my native dev code within the manufacturing app!
We break up our bundles by route to cut back pointless code loading, although, and I didn’t need to need to hardcode all of them after which have to replace the extension any time we added a brand new one.declarativeNetRequest
has an choice to assist deal with that, fortunately — you’re ready to make use of regexes each for setting circumstances and for modifying the ensuing URL. That sounds scary (OK, regexes at all times sound scary to me!), but it surely’s actually not that dangerous. Right here’s the up to date model:
{
"id":1,
"precedence": 1,
"motion": {
"sort": 'redirect',
"redirect": {
"regexSubstitution": 'https://localenv.helpscout.com/frontend/1',
},
},
"situation": {
"regexFilter": 'https://helpscoutblog.cloudfront.web/frontend/(.*)'',
},
}
What’s occurring right here is that within thesituation
, we’re capturing the filename with(.*)
then utilizing the numbered capturing group1
to append that to the tip of the URL we’re redirecting to. With that setup, I may transfer by numerous apps in Assist Scout, make adjustments domestically, refresh the tab, and see my adjustments “in manufacturing.” I shared this POC (which, in tribute to ProxyPack, I named ProxyPal) with the workforce and began utilizing it in my very own work; a number of engineers and QA of us additionally reported discovering it useful.
Increasing the person base
I used to be pleased with what I had constructed, but it surely didn’t absolutely deal with my supposed use case. It helped for extra technical QA of us (and was additionally nice for engineers testing bug fixes in improvement towards the manufacturing app and information the place the bug was occurring), however you continue to wanted to take care of an area git repo and run the dev server, so it wasn’t as universally accessible as I wished it to be.
Fortunately, my teammate Maxi Ferreira is a Cloudflare professional (in addition to a terrific e-newsletter author), and he helped me use that service to make ProxyPal out there to everybody on the workforce. Right here’s what we did:
On push to a PR department, we constructed a replica of the JS property and revealed it to Cloudflare Pages.
Utilizing Cloudflare’s superior built-in GitHub integration, each PR consists of an auto-generated remark with a hyperlink to that construct.
The extension does a little bit further magic utilizing a content material script that injects a button subsequent to the Cloudflare remark:
import './github.css';
const getPrTitle = () => {
return doc.querySelector('.gh-header-title').innerText;
};
perform handleLinkInjection() {
perform injectLink(aTag) {
if (aTag.href.consists of('pages.dev') && aTag.href !== 'https://pages.dev/') {
var newLink = doc.createElement('button');
const prTitle = getPrTitle();
newLink.textContent = 'ProxyPal';
newLink.className = 'proxyPalButton';
newLink.onclick = () => {
chrome.runtime.sendMessage({
sort: 'SET_ACTIVATION_STATUS',
worth: {
standing: true,
envUrl: aTag.href,
envDescription: prTitle,
},
});
alert(`Now proxying to ${prTitle} at ${aTag.href}`);
};
aTag.parentNode.insertBefore(newLink, aTag.nextSibling);
}
}
doc.querySelectorAll('a').forEach(injectLink);
var observer = new MutationObserver(perform (mutations) {
mutations.forEach(perform (mutation) {
mutation.addedNodes.forEach(perform (node) {
if (node.nodeType === 1 && node.tagName === 'A') {
injectLink(node);
}
});
});
});
observer.observe(doc.physique, {
childList: true,
subtree: true,
});
}
perform handleNavigation() {
if (window.myMutationObserver) {
window.myMutationObserver.disconnect();
}
handleLinkInjection();
}
handleLinkInjection();
let lastUrl = location.href;
setInterval(() => {
const currentUrl = location.href;
if (currentUrl !== lastUrl) {
lastUrl = currentUrl;
handleNavigation();
}
}, 1000);
When the person clicks on the button, this script passes a message to the extension’s background service employee, the place we’re now utilizing a template string to dynamically set the bottom URL to the hyperlink we received from Github like this:
chrome.runtime.onMessage.addListener((request) => {
swap (request.sort) {
case 'SET_ACTIVATION_STATUS': {
const { standing, remoteUrl } = request.worth;
if (standing) {
chrome.declarativeNetRequest.updateDynamicRules({
removeRuleIds: [PROXY_ID],
addRules: [
{
id: PROXY_ID,
priority: 1,
action: {
type: 'redirect',
redirect: {
regexSubstitution: `${remoteUrl}/1`,
},
},
condition: {
regexFilter: "https://helpscoutblog.cloudfront.net/frontend/(.*)",
},
},
],
});
}
}
}
});
We uploaded the extension to a non-public Chrome retailer for our group and now, with no native set up required and one click on of a button in a PR, anybody on the workforce can check in manufacturing. When fine-tuning a PR with a designer or PM or QA tester, they’ll present notes, and I can implement them, push up a commit, after which inform them to refresh their browser and see the adjustments instantly.
You do want an area surroundings
The title of this submit is a bit facetious — your app nonetheless wants an area surroundings, for a wide range of causes! One is, you already know, your backend builders (we love you, backend builders!)! One other is that this explicit method limits your means to check cross-browser earlier than manufacturing — in principle, the extension must also work within the Chromium-based Firefox and Edge, however on the time that we began improvement, thedeclarativeNetRequest
API wasn’t absolutely supported in manufacturing for Firefox (although it’s now!) and we haven’t but explored it (Safari is sadly a non-starter), which signifies that we’ve to take extra care to check these flows in a conventional native surroundings or in precise manufacturing.
However even when you want an area surroundings, there are a variety of different “yous” in your group which have a lot to supply to in-progress work. An method like this may also help you carry them into the event course of earlier and extra simply, which may solely profit your product and, by extension, your clients.