Reducing CI Runtime and Cost in a Frontend Monorepo
Context
I joined a small media-streaming SaaS company as a Software Engineer during a period of significant technical change.
The company was modernising its frontend platform, moving content and marketing-related functionality from an existing React SPA into a Next.js application backed by Strapi. Both applications were managed within a Turborepo.
The repository had accumulated significant dependency and configuration differences between the applications, including older Webpack configuration in the React application and mismatches across dependencies, Storybook, and other tooling.
I was the only engineer working on this area of the platform. My primary responsibility was supporting the product team with frontend development and maintenance, with technical guidance and support from the Technical Manager.
I also had dedicated time each week for maintenance work, which gave me an opportunity to investigate recurring engineering problems outside the immediate product roadmap.
One of the most visible problems was the CI workflow.
Intervention
I raised the CI work as a maintenance task so that it could be planned alongside the product roadmap. After getting it prioritised, I investigated the existing workflows and implemented the changes independently, with the Technical Manager available for guidance and review.
Separating the CI workflow
I first restructured the workflow into independent jobs.
Previously, unrelated tasks were chained together in a single flow. I separated these responsibilities so that independent jobs could execute asynchronously and provide feedback independently.
While doing this, I also refactored the workflow configuration itself. Repeated blocks were extracted into reusable modules, and common build operations were moved into Bash scripts.
This reduced duplication in the workflow files and established a single implementation for operations that previously appeared as repeated command sequences.
Parallelising Cypress
After the general CI structure was improved, the Cypress E2E job remained the main bottleneck.
Cypress executes tests sequentially by default, so simply cleaning up the existing commands produced only a small improvement.
I therefore built a shell script that inspected the test suite, counted the available tests, and divided them into groups of up to five tests.
The CI workflow could then launch these groups as separate jobs, allowing multiple groups of E2E tests to run concurrently.
This reduced the E2E execution time substantially.
One of the parallel jobs remained significantly slower than the others. I investigated the tests in that group and identified unnecessary repeated requests and other operations that could be reduced or shared.
I extracted common functionality and optimised those tests, ultimately achieving an approximately 85% reduction in E2E execution time.
Reducing CI overhead
I also changed the Cypress configuration so that test videos were not uploaded for every successful test.
Videos were retained for failing tests, where they were useful for diagnosing failures, while successful runs no longer incurred the same upload overhead.
This reduced unnecessary CI work and associated storage and upload costs without removing the diagnostic information needed when a test failed.
Validating redirects
Last improvement was a bash script to check for redirects defined in the Netlify configuration. The script parsed the configured redirects and verified that the application returned the expected destinations, moving redirect validation from manual checks into the CI workflow.
Outcome
The most significant improvement was the reduction in E2E execution time, with the final implementation achieving an approximately 85% reduction.
The broader CI workflow also became more modular. Independent jobs could run concurrently, failures could be isolated to specific stages, and common build operations were no longer duplicated throughout the workflow configuration.
The changes also reduced GitHub Actions expenditure by more than one-third.
The resulting workflow provided substantially faster feedback to development while reducing the amount of CI capacity consumed by each development cycle.
Additional Context
The work was performed alongside an ongoing frontend modernisation, so I deliberately focused on improving the existing CI system rather than attempting to replace the wider build infrastructure.
The repository contained two applications with different technical histories and dependency requirements. The objective was therefore to make the existing workflow more reliable and efficient without requiring the applications to be standardised first.
The Cypress parallelisation was also implemented without changing the tests into a fundamentally different testing strategy. The existing tests remained the source of verification; the main change was how they were distributed across CI workers and how the slowest tests were subsequently optimised.
The final workflow retained video artefacts for failed tests while removing them from successful runs, balancing debugging capability against unnecessary CI overhead.