How we made reviewing frontend features pain-free

Konstantin Ostrovsky
torq
Published in
6 min readAug 11, 2024

At Torq, we are building a no-code security automation product. UI is a major part of the user experience, and users may spend hours working with it, building automation workflows.

There are many engineers working on features in the frontend application. Each feature usually has several people that need to review it before it’s deployed to production.

It is very hard to review frontend features without actually seeing them and interacting with them. Until we had a proper way to do it, engineers had to demonstrate features right on their laptops. This created a lot of friction points and negatively affected development velocity.

We decided that we no longer want to have such friction and want to be able to easily demonstrate frontend changes to all stakeholders.

In this blog post, I will go over the solution we came up with to address this challenge.

Let me explain the problem we faced

Frontend applications can’t be reviewed without actually seeing the changes made to them or playing around with them. Our frontend application is a single repository written in TypeScript with VueJS. Early in the project’s lifetime, we already had a way of running the frontend application locally on our own machines and having it interact with the backend APIs of our staging environment.

Each time a new feature was added, the code reviewer or product/designer who wanted to see it in action had 2 choices:

  1. Check out the branch and run the application locally on their laptop — This is quite a long process as we needed to check out the code, install the node modules and then run the code. This could easily amount to 10–15 minutes of time spent mostly waiting.
  2. Ask the engineer to show it to them on their laptop — This required physical coordination with the engineer, meaning syncing the schedules of 2 or more people. Not ideal.

Both of the options introduced unnecessary friction into the engineering process, wasting a lot of time for multiple people.

We set out to solve this problem by building a tool that would spawn a preview environment for each side branch of our frontend application.

Let’s talk a bit about how services are deployed and built at Torq

At Torq, we use an automated CI/CD process and deploy our services as docker images on a Kubernetes cluster.

Each feature is built on a side branch. Once the side branch is pushed into GitHub, the CI system picks it up, compiles the code, and builds a docker image containing the result of the build process.

The docker image is tagged with the last commit id of that branch, and is then pushed into a docker image repository.

Eventually, that docker image will be deployed to production in the same exact form as it was built for the side branch. Our goal was to have the side branch deployed as a K8s service with an external ingress that could be easily accessible and used by engineers at Torq.

Here’s how we made it happen

In order to manage preview environments, we decided to build a new service responsible for managing the deployments of side branch builds on our staging environment Kubernetes cluster.

It was implemented as a Go app, called “app-preview-daemon,” and deployed to a namespace called “application-previews” in our staging environment. It included Kubernetes API permissions to manage resources in the application-reviews namespace.

Using Kubernetes’ admin API, it created and updated these objects for each side branch:

  1. Service
  2. Deployment
  3. Ingress

The service was controlled by gRPC API, which supports create, delete, and list operations.

Given a docker image and branch name, it created an external ingress in the form of app-<branch>.stg.torq.io and deployed the side branch container there.

Each deployment had a predefined TTL (time to live), after which a reaper built into the service cleaned up the created resources.

With this service in place, it’s now very simple to integrate it into our CI pipeline and create preview environments on the fly.

How the deployment works

App-preview-daemon’s API endpoint is called at the end of our CI process. Once a build is complete, and the side branch of the frontend application is tagged, the Create method is triggered with the name of the docker image and the branch name.

It creates new deployment/service/ingress K8s objects to roll out the newly tagged docker image. Then it configures some 3rd party integrations that require strict origin URLs or specific CORS configurations. This deployment is then accessible via app-<branch>.stg.torq.io

When the deployment is ready, a Slack notification is sent to users tagged in the pull request, including the owner and reviewers notifying them that a new updated version is available.

At that point, they can access and start using it, playing with the side branch as if it were a real frontend application.

Since we have many engineers working on features in our frontend application, there may be quite a lot of active branches running in parallel. We try to be efficient with our Kubernetes resources, which is why each resource has a predefined TTL, after which it is deleted by a scheduled task running as a part of the app-preview-daemon service.

Usually, the environment doesn’t need to run forever. In our case, it’s relevant for several hours after the commit. Each new commit will reset the timer. If our engineers want a longer-running environment, they are able to deploy one using a Slack bot.

In addition to that, we have a setting that allows us to deploy environments with more resources. This is needed for running tests that may generate quite a bit of traffic on the K8s pod, and require more memory and CPU allocation than needed for preview use cases.

Additional cool features we’ve implemented into the service

We wanted to give our team an amazing developer experience using preview environments, which is why we implemented some cool additional features.

Slack Notification when the preview is created
  1. Automatic Slack notifications when the preview environment is ready for both the pull request owner and the reviewers they can tag on the GitHub pull request.
  2. GitHub comments on the pull request notifying about deployments of the preview environments.
  3. Running end-to-end automation tests on preview environments, shifting the discovery of bugs left into the development process. The engineer can know, during the pull request phase, that their code changes are going to break some end-to-end tests, so they can fix this before merging into the main branch and deploying it to the staging environment, potentially affecting other engineers.
GitHub comment in the Pull Request

Lessons learned from our experience

Having preview environments available for every side branch has been a great velocity booster for our team. Designers and product managers can easily view and play with new UI features, without having to wait for an engineer to show it to them on their laptop or doing any manual tasks.

This allows closing development loops much faster, like just sending a preview link via Slack asking them to review the behavior or design of a new feature.

In addition, being able to run end-to-end tests before deploying to the staging environment has been a major upgrade as well. This means that bugs are spotted much earlier in the deployment process, and there’s more confidence in merging pull requests.

--

--

Konstantin Ostrovsky
torq
Writer for

I used to write kernel drivers in C. Now I write Backend in Go :) #gRPC #GoLang #OpenTelemetry