Building a Production-Ready DevOps Pipeline for a Next.js Dealership Platform
🏁 TL;DR
Section titled “🏁 TL;DR”Dealership websites have unique challenges: customer data security, inventory performance, compliance requirements, and zero-tolerance for downtime. We built a modern DevOps pipeline for a Next.js dealership platform featuring 9 GitHub Actions workflows with smart filtering, comprehensive testing (Jest + Playwright), Lighthouse CI performance gates, Sentry monitoring, and Vercel deployment — all while maintaining sub-2-second load times and 90%+ accessibility scores.
🎯 Context / Why It Matters
Section titled “🎯 Context / Why It Matters”Automotive dealerships operate in a highly regulated environment with unique technical requirements:
- Customer Data: Credit applications, soft credit pulls, personal information requiring FCRA compliance
- Performance: Users abandon slow sites — critical when competing for car buyers
- Accessibility: WCAG compliance isn’t optional when serving diverse demographics
- Security: VIN validation, payment calculators, and financial tools must be bulletproof
- SEO: Dealerships live or die by local search rankings
Traditional “ship fast and fix later” doesn’t work when handling credit applications and customer financial data. We needed DevOps practices that balance velocity with safety.
Who benefits: Dealerships, automotive SaaS platforms, and anyone building customer-facing applications with strict compliance and performance requirements.
⚙️ The Challenge
Section titled “⚙️ The Challenge”The Problem
Section titled “The Problem”When we started, our deployment process looked like this:
# Manual deployment checklist (error-prone)1. Run tests locally (maybe)2. Check accessibility manually (occasionally)3. Deploy to Vercel4. Hope nothing breaks in production5. Discover issues when customers report themPain points we faced:
- Inconsistent quality: Different developers had different testing habits
- Performance regression: No automated performance tracking — discovered slow pages after deployment
- Security gaps: Dependencies updated manually, vulnerabilities slipped through
- Accessibility debt: WCAG compliance checked sporadically, issues accumulated
- Design drift: Tailwind and shadcn/ui patterns inconsistent across PRs
- Customer impact: Credit application forms breaking = lost revenue
The metrics that scared us:
- 18-minute manual QA checklist per PR (when done thoroughly)
- Performance regressions caught in production 4x in one month
- High-severity vulnerability sat unnoticed for 2 weeks
- Accessibility issues found during compliance audit
We needed automation that could scale with our team while maintaining dealership-grade reliability.
🧩 The Solution / Approach
Section titled “🧩 The Solution / Approach”We designed a multi-layered CI/CD pipeline with 9 specialized GitHub Actions workflows, each handling a specific concern:
Core Architecture
Section titled “Core Architecture”┌─────────────────────────────────────────────────────────┐│ Pull Request Opened │└────────────────────┬────────────────────────────────────┘ │ ┌────────────┴────────────┐ │ │ ┌────▼─────┐ ┌─────▼──────┐ │ Required │ │ Advisory │ │ Checks │ │ Checks │ └────┬─────┘ └─────┬──────┘ │ │┌───────▼────────┐ ┌────────▼─────────┐│ • PR Checks │ │ • Lighthouse CI ││ • Security │ │ • Claude Review ││ • Design │ │ ││ • Test Suite │ │ │└───────┬────────┘ └──────────────────┘ │ ▼ Merge Ready │ ▼ Vercel Deploy │ ▼ Cache Warming1. Smart PR Checks (Required)
Section titled “1. Smart PR Checks (Required)”The foundation of quality control:
name: PR Quality Checkson: pull_request: types: [opened, synchronize, reopened]
jobs: test-build-lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4
- name: Setup pnpm uses: pnpm/action-setup@v4 with: version: 9.15.9
- name: Run Jest Tests run: pnpm test
- name: TypeScript Check run: npx tsc --noEmit
- name: Build Verification run: pnpm buildWhy it works: Fast feedback loop (2-3 minutes), catches 80% of issues before human review.
2. Security Review with Smart Filtering (Required*)
Section titled “2. Security Review with Smart Filtering (Required*)”Here’s where we got creative. Running security scans on every PR created noise:
# Smart filtering logic- name: Determine if security scan needed id: check run: | # Skip if PR only touches docs/styles CHANGED_FILES=$(git diff --name-only origin/${{ github.base_ref }}...HEAD)
if echo "$CHANGED_FILES" | grep -qE '\.(ts|tsx|js|jsx|json); then echo "should_run=true" >> $GITHUB_OUTPUT else echo "should_run=false" >> $GITHUB_OUTPUT fi
- name: Security Audit if: steps.check.outputs.should_run == 'true' run: | pnpm audit --audit-level=high --production # Fail on critical or high vulnerabilities if [ $? -ne 0 ]; then exit 1; fiKey innovation: We analyze the PR’s changed files and skip security scans for documentation-only or style-only changes. Reduces CI runtime by 40% without sacrificing safety.
3. Design System Compliance (Required*)
Section titled “3. Design System Compliance (Required*)”Dealership branding consistency matters:
- name: Check Tailwind/shadcn Usage run: | # Ensure shadcn components used correctly node scripts/validate-design-patterns.js
# Verify no hardcoded colors (use Tailwind theme) ! grep -r "bg-#[0-9a-fA-F]" src/components/
# WCAG color contrast validation node scripts/validate-color-contrast.js4. Lighthouse CI (Advisory)
Section titled “4. Lighthouse CI (Advisory)”Performance gates that prevent regression:
module.exports = { ci: { collect: { numberOfRuns: 3, url: ['http://localhost:3000', 'http://localhost:3000/inventory'] }, assert: { assertions: { 'categories:performance': ['error', { minScore: 0.7 }], 'categories:accessibility': ['error', { minScore: 0.9 }], 'categories:seo': ['error', { minScore: 0.9 }], 'first-contentful-paint': ['error', { maxNumericValue: 2000 }], 'largest-contentful-paint': ['error', { maxNumericValue: 2500 }] } } }};Critical insight: We set accessibility and SEO as required (0.9 minimum) but performance as advisory (0.7). Why? Accessibility regressions are unacceptable; performance can sometimes be traded for features with conscious decision-making.
5. Comprehensive Test Suite
Section titled “5. Comprehensive Test Suite”Two-tier testing strategy:
Unit Tests (Jest): 100% coverage on critical utilities
describe('VIN Validator', () => { it('rejects invalid VIN checksums', () => { expect(validateVIN('1HGBH41JXMN109186')).toBe(false); // Bad checksum });
it('accepts valid VIN', () => { expect(validateVIN('1HGBH41JXMN109187')).toBe(true); });});E2E Tests (Playwright): 33+ tests covering critical user journeys
test('credit application submission with soft pull consent', async ({ page }) => { await page.goto('/financing/credit-application');
// Fill form await page.fill('[name="firstName"]', 'John'); await page.fill('[name="ssn"]', '123-45-6789');
// Verify soft pull disclosure await expect(page.locator('text=soft credit inquiry')).toBeVisible();
// Submit await page.click('button:has-text("Submit Application")');
// Verify Turnstile CAPTCHA triggered await expect(page.locator('.cf-turnstile')).toBeVisible();});6. Automated Theme Cleanup (Manual Dispatch)
Section titled “6. Automated Theme Cleanup (Manual Dispatch)”Dealerships often have 20+ color themes. Unused CSS bloats bundles:
const themes = ['red', 'blue', 'green', /* ... 17 more */];const activeTheme = process.env.DEALERSHIP_THEME;
// Remove unused theme CSSthemes.filter(t => t !== activeTheme).forEach(theme => { const themeFile = `src/styles/themes/${theme}.css`; if (fs.existsSync(themeFile)) { fs.unlinkSync(themeFile); console.log(`Removed unused theme: ${theme}`); }});Triggered manually via GitHub Actions dispatch before production builds.
7. Sentry Integration for Production Monitoring
Section titled “7. Sentry Integration for Production Monitoring”Sentry.init({ dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, environment: process.env.VERCEL_ENV || 'development',
// Session Replay for debugging replaysSessionSampleRate: process.env.NODE_ENV === 'production' ? 0.01 : 0.1,
// Performance monitoring tracesSampleRate: 0.1,
beforeSend(event) { // Scrub sensitive data if (event.request?.data?.ssn) { event.request.data.ssn = '[Redacted]'; } return event; }});Critical: We automatically redact PII (SSN, credit card numbers) before sending to Sentry.
🔍 Deep Dive / Technical Details
Section titled “🔍 Deep Dive / Technical Details”Multi-Layer Security Architecture
Section titled “Multi-Layer Security Architecture”Our security approach has 4 layers:
Layer 1: Dependency Scanning
- name: Audit Dependencies run: pnpm audit --audit-level=high --productionLayer 2: Static Code Analysis
- name: Static Analysis run: | # Check for common vulnerabilities grep -r "dangerouslySetInnerHTML" src/ && exit 1 grep -r "eval(" src/ && exit 1 grep -r "process.env" src/components/ && exit 1 # No env vars in client componentsLayer 3: Input Validation
// All forms use Zod schemasconst creditAppSchema = z.object({ ssn: z.string().regex(/^\d{3}-\d{2}-\d{4}$/), income: z.number().min(0).max(1000000), vin: z.string().refine(validateVINChecksum)});Layer 4: CAPTCHA Protection
// Cloudflare Turnstile on all forms<Turnstile siteKey={process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY} onSuccess={handleCaptchaSuccess}/>Performance Optimization Pipeline
Section titled “Performance Optimization Pipeline”Our build process optimizes aggressively:
module.exports = { images: { formats: ['image/avif', 'image/webp'], deviceSizes: [640, 750, 828, 1080, 1200, 1920], imageSizes: [16, 32, 48, 64, 96, 128, 256, 384] },
webpack: (config) => { // Chunk splitting config.optimization.splitChunks = { chunks: 'all', cacheGroups: { vendor: { test: /[\\/]node_modules[\\/]/, maxSize: 200000 // 200KB vendor chunks }, common: { minChunks: 2, maxSize: 150000 // 150KB common chunks } } };
return config; },
// Use SWC minification (faster than Terser) swcMinify: true};Post-deploy cache warming:
// Pre-warm inventory images after deploymentconst inventoryVehicles = await fetch('/api/inventory').then(r => r.json());
for (const vehicle of inventoryVehicles) { await fetch(vehicle.imageUrl); // Trigger CDN cache await sleep(100); // Throttle}Environment Management Strategy
Section titled “Environment Management Strategy”Three-tier environment setup:
| Environment | Branch | Deploy Trigger | Features |
|---|---|---|---|
| Development | feature/* | Auto (preview) | Mock APIs, verbose logging, hot reload |
| Staging | develop | Auto | Real APIs, Sentry sampling, real CAPTCHA |
| Production | main | Auto (after merge) | Optimized builds, cache warming, monitoring |
# Environment variable structure.env.local # Local development (gitignored).env.development # Shared dev config.env.production # Production config.env.example # Template for new developersVercel integration:
{ "env": { "NEXT_PUBLIC_SENTRY_DSN": "@sentry-dsn", "DEALERSHIP_ID": "@dealership-id", "ENCRYPTION_KEY": "@encryption-key" }, "build": { "env": { "NEXT_TELEMETRY_DISABLED": "1" } }}🧠 Results / Outcomes
Section titled “🧠 Results / Outcomes”Before vs. After
Section titled “Before vs. After”| Metric | Before Automation | After Implementation | Improvement |
|---|---|---|---|
| Manual QA Time | 18 min/PR | 3 min/PR | 83% reduction |
| Production Incidents | 4/month | 0.5/month | 87% reduction |
| Time to Detect Issues | Hours-Days | Minutes | 95%+ faster |
| Security Vulnerabilities | Discovered manually | Blocked in CI | 100% prevention |
| Performance Regressions | 4 in 1 month | 0 in 6 months | Eliminated |
| Accessibility Compliance | 78% average | 93% average | +15 points |
| Deploy Confidence | Low (manual checklist) | High (automated gates) | Immeasurable |
Real-World Impact
Section titled “Real-World Impact”Incident Example: A developer introduced a dependency with a critical vulnerability. Before our pipeline:
- Vulnerability sat unnoticed for 2 weeks
- Discovered during manual security review
- Emergency patch required
After our pipeline:
- PR blocked automatically within 2 minutes
- Developer notified immediately
- Fixed before merge
Performance Example: Lighthouse CI caught a 3rd-party script causing 800ms JavaScript execution delay:
Lighthouse CI Failed❌ Total Blocking Time: 1,247ms (threshold: 300ms)
Blocking Script: widget.somevendor.com/analytics.js (847ms)Recommendation: Load async or deferThis would have impacted thousands of users. Caught in PR review instead.
Cost Savings
Section titled “Cost Savings”- Security audit service: $5k/year → $0 (automated)
- Manual QA hours: 72 hrs/month → 12 hrs/month (83% reduction)
- Incident response: 16 hrs/month → 2 hrs/month (87% reduction)
ROI: ~$85k annually in labor + avoided incidents.
🧰 Lessons Learned
Section titled “🧰 Lessons Learned”What Went Well
Section titled “What Went Well”✅ Smart filtering reduced noise: Not every PR needs every check. Documentation-only changes don’t need security scans.
✅ Advisory vs. Required distinction: Making some checks advisory (Lighthouse) vs. required (accessibility) gave us flexibility without sacrificing standards.
✅ Invest in E2E tests for critical flows: Our 33 Playwright tests cover credit applications, contact forms, and soft pull flows — the revenue-generating paths. Worth every hour of development.
✅ Automate the boring stuff: Theme cleanup, cache warming, dependency audits — automate everything that doesn’t require human judgment.
What Didn’t Work (At First)
Section titled “What Didn’t Work (At First)”❌ Too many required checks initially: We started with 12 required checks. PRs took 15+ minutes. Developers got coffee while waiting. We consolidated to 4 core workflows.
❌ Lighthouse CI flakiness: Performance scores varied ±5 points between runs. Solution: Run 3 times, take median score.
❌ Over-aggressive security scanning: Flagged false positives constantly. Solution: Whitelist known-safe patterns, focus on high/critical only.
❌ Sentry noise in development: Local errors polluted production dashboard. Solution: Separate DSNs per environment, sample aggressively (1% prod, 10% staging).
What We’d Do Differently
Section titled “What We’d Do Differently”🔄 Start with E2E tests earlier: We added Playwright 6 months in. Wish we’d started Day 1.
🔄 Document the “why” for each workflow: New developers asked “why do we have 9 workflows?” A lot. Now we have this article.
🔄 Invest in faster CI runners: GitHub’s default runners are slow. Self-hosted or GitHub’s 4-core runners would cut CI time in half.
🔄 Build a “CI dashboard”: Tracking workflow trends over time would help optimize further.
🚀 Key Takeaways
Section titled “🚀 Key Takeaways”✅ Smart automation beats blind automation — Use changed file detection to skip unnecessary checks
✅ Required vs. Advisory matters — Not all quality gates should block merges; some should inform decisions
✅ Security must be layered — Dependency scanning + static analysis + input validation + CAPTCHA = defense in depth
✅ Performance is a feature — Lighthouse CI gates prevent regressions; treat performance like any other requirement
✅ E2E tests for revenue paths — Focus automation on flows that impact business (credit apps, contact forms, inventory search)
✅ Monitor production relentlessly — Sentry with session replay catches what testing misses
✅ Optimize for developer experience — Fast CI, clear failures, automatic fixes where possible
🔗 References & Resources
Section titled “🔗 References & Resources”Official Documentation
Section titled “Official Documentation”- Next.js Deployment Best Practices
- Playwright Testing Guide
- Lighthouse CI
- GitHub Actions Workflow Syntax
- Sentry Next.js Integration
Tools We Use
Section titled “Tools We Use”- CI/CD: GitHub Actions
- Testing: Jest 30.1.3, Playwright 1.55.1
- Monitoring: Sentry 10.15.0
- Performance: Lighthouse CI, Vercel Analytics
- Security: npm audit, Cloudflare Turnstile
- Package Management: pnpm 9.15.9
Related Reading
Section titled “Related Reading”💬 Discussion / Call to Action
Section titled “💬 Discussion / Call to Action”Questions for the community:
- How do you balance CI speed vs. thoroughness? We settled on 9 workflows with smart filtering — what’s your approach?
- What’s your strategy for E2E test maintenance? Our 33 Playwright tests are growing — concerned about flakiness at scale.
- How do you handle performance testing for dynamic content (inventory changes daily)?
Facing similar challenges?
This architecture works for dealership websites, but the patterns apply to any compliance-heavy, customer-facing application:
- Healthcare portals (HIPAA compliance)
- Financial services (PCI-DSS)
- E-commerce platforms (PCI + performance-critical)
- SaaS products with enterprise security requirements
📊 Appendix: Workflow Summary Table
Section titled “📊 Appendix: Workflow Summary Table”| Workflow | Trigger | Purpose | Status | Avg Runtime |
|---|---|---|---|---|
| PR Checks | PR open/sync | Jest tests, TypeScript, build | Required | 2-3 min |
| Security Review | PR open/sync (smart filter) | Dependency audit, static analysis | Required* | 1-2 min |
| Design Review | PR open/sync (smart filter) | Tailwind/shadcn compliance, WCAG | Required* | 1 min |
| Lighthouse CI | PR open/sync | Performance, SEO, accessibility metrics | Advisory | 3-4 min |
| Claude AI Review | PR comment trigger | Intelligent code analysis | Optional | 2-3 min |
| Test Suite | Post-merge to main | Final validation (Jest + Playwright) | Required | 5-7 min |
| Cleanup Themes | Manual dispatch | Remove unused theme CSS | Manual | 1 min |
| Sentry Release | Production deploy | Create Sentry release, upload source maps | Auto | 1 min |
| Cache Warming | Post-deploy hook | Pre-warm inventory images in CDN | Auto | 2-3 min |
Total PR Review Time: ~7-10 minutes (down from 18 minutes manual)
Last updated: 2025-10-21 Reading time: ~12 minutes Technical level: Intermediate to Advanced