Frontend CI/CD in the age of AI -- Part 1 — Neciu Dan
Skip to content<br>🛡️ FREE · Master Frontend Security · All 7 modules live · 100% free · Start free →
Subscribe
Search blog posts
Discover more from The Neciu Dan Newsletter
A weekly column on Tech & Education, startup building and occasional hot takes.
Over 1,000 subscribers
Website
Company
Subscribe<br>Subscribing...<br>Skip for now
The Neciu Dan Newsletter
Once a week I share what I’m learning from building products, teaching<br>engineers, and running a startup — tech & education, career<br>lessons, and the occasional hot take, plus community reads from the<br>frontend ecosystem. Free, and you can unsubscribe anytime.
By Neciu Dan · Over 1,000 subscribers
Website
Company
Subscribe<br>Subscribing...
No thanks
Table of Contents<br>CI and CD are different problems<br>CI, part 1: stop running everythingFiltering by path<br>Step one: ask git what you changed<br>Step two: walk your imports backward<br>Step three: hand that list to your three tools<br>When you would rather not maintain a regex<br>Step four: problems
CI, part 2: let’s make it fastWhat cache: 'npm' actually caches<br>Extracting the setup<br>Caching what the tools already know<br>The snails<br>Four shards, four reports<br>Narrowing E2E tests
CI, part 3: fail before the PR existsWiring it into git<br>The base ref problem, in reverse<br>Agents will bypass this, so plan for it
The shape of the whole thing<br>References
Continuous Integration (CI) and Continuous Deployment(CD) are responsible for two things: making sure that when we merge our PR, it does not introduce any faults (bugs) into our main branch (Integration), and after every merge, our code gets released in some way to production in a timely manner (Deployment).
Then, everything changed when the Fire Nation attacked (and by that I mean the AI Revolution)
AI writes almost all the code now, and its particulary good at writing tests. It doesn’t always write good or useful tests, but it likes to write a lot of tests.
As a result, the time it takes our CI to run is growing exponentially, leaving developers waiting for PRs to build, run, and get feedback that everything is working or not.
It doesn’t have to work like this. Google claims the majority of its new code is now AI-generated and engineer-approved, and they got there by making verification cheap and rollout reversible.
Let’s dig in.
PS: You can find the whole demo and script files in this repo
CI and CD are different problems
The first thing we need to establish is a clear separation between CI and CD. People treat it as a single concept and a single flow, which causes a lot of problems and headaches.
Continuous Integration answers: is this change correct? It runs Lint, types, unit tests, e2e tests, builds. It runs before merging and slow CI compounds into batched PRs, stale branches, and agents idling.
Continuous Delivery answers: is this change safe in production? Deployment, rollout, rollback. It runs after the merge.
In this article (part 1) I want to focus on building a robust and FAST CI pipeline for Frontend projects. In part we will tackle the deployment part.
CI, part 1: stop running everything
Imagine your app has a Dashboard, a Settings page, a Billing page, and folders for shared components and shared utilities. Everything lives in a single app and repo, with no workspaces or internal packages.
You change one line in Button.tsx, push the PR, and your pipeline lints every file you own, typechecks the whole project, runs all 340 unit tests, and builds the app.
Which is fine, but with exponential growth and some apps having millions lines of code, this can get slow fast (pun intented)
# .github/workflows/ci.yml<br>name: CI<br>on: pull_request
jobs:<br>ci:<br>runs-on: ubuntu-latest<br>steps:<br>- uses: actions/checkout@v4<br>- uses: actions/setup-node@v4<br>with:<br>node-version: 22<br>cache: 'npm'<br>- run: npm ci<br>- run: npm run lint<br>- run: npm run typecheck<br>- run: npm run test<br>- run: npm run build<br>You wait twenty-five minutes, and so does everyone queued behind you.
What you actually want is narrow and specific.
Change Button, and you want Button’s tests to run, plus the tests of everything that imports Button, however deep the chain goes, to also run.
You want the same for lint and typecheck. The Billing feature never imports Button, so Billing should not be touched at all.
You want that list computed from your code every time, so it is never wrong, and you never have to maintain it.
That setup takes away the thing most tooling relies on. You have no package.json boundaries, so nothing in your repo declares that Billing and Settings are separate.
Every tool that solves this for monorepos is reading workspace manifests that we do not have.
We will derive the same information from our imports instead.
Filtering by path
The first thing you might try is filtering by path within GitHub Actions itself. You attach a paths filter to the trigger, and the workflow only runs when your PR touches files matching those...