Automation for Web Development: Streamline Your Workflow
Practical automation that saves hours, reduces bugs, and keeps quality high—without bloating your stack. This is the playbook I use to ship fast, consistent, and secure projects.
Why Automate Your Web Dev Workflow?
Automation turns habits into systems. It removes human variance from repetitive tasks, enforces quality at the edges, and frees your brain for creative, high-leverage work. If you’ve ever shipped a broken link, forgot alt text, or pushed a 3MB hero image—automation is your fix.
- Speed: One command triggers formatting, linting, tests, build, and deploy.
- Consistency: Every commit passes the same checks—no “it works on my machine.”
- Quality: Automated accessibility, performance budgets, and link integrity.
- Security: Headers, CSP, and dependency checks happen on every build.
The Automation Stack (Lean, Static, Durable)
Layer | Tooling | Purpose |
---|---|---|
Task Runner | npm scripts | Single source of truth for dev, build, test, deploy. |
Pre-commit | Husky + lint-staged | Format/lint only changed files; block bad commits early. |
CI/CD | GitHub Actions → Netlify | Repeatable builds, preview URLs, automated deploys. |
Quality Gates | ESLint, Prettier, Stylelint, Markdownlint | Code style + reliability before merge. |
Perf/UX | Lighthouse CI, Playwright, axe-core | Core Web Vitals, E2E flows, accessibility budgets. |
Assets | ImageMagick + sharp | Automated image resizing, .webp conversion, and compression. |
Related reads: Lazy vs Eager Loading · Internal Linking Best Practices · Topical Authority
One Command to Rule Them All (npm Scripts)
Use npm scripts as your orchestrator. No extra CLIs required:
{
"scripts": {
"dev": "eleventy --serve",
"clean": "rimraf dist .cache",
"images": "node scripts/images.js",
"format": "prettier -w . && markdownlint-cli2-fix",
"lint": "eslint . && stylelint \"src/**/*.css\"",
"test": "node scripts/links-check.js && playwright test",
"a11y": "node scripts/a11y-ci.cjs",
"perf": "lighthouse-ci autorun",
"build": "npm run clean && npm run images && eleventy",
"deploy": "netlify deploy --dir=dist --prod",
"ship": "npm run format && npm run lint && npm run test && npm run a11y && npm run perf && npm run build && npm run deploy"
}
}
Tip: keep ship
as your golden path—everything required to publish safely.
Stop Bad Commits at the Door (Husky + lint-staged)
Run targeted checks only on staged files—fast feedback, zero nagging:
# .husky/pre-commit
npx lint-staged
{
"lint-staged": {
"*.{js,ts}": ["eslint --fix", "prettier -w"],
"*.css": ["stylelint --fix", "prettier -w"],
"*.{md,mdx}": ["markdownlint-cli2-fix", "prettier -w"],
"*.{njk,html}": ["prettier -w"]
}
}
Automate Image Optimization (Speed Wins Rankings)
Images are the #1 bloat source. Automate resizing, format conversion, and compression so you never ship a 4K hero by accident.
// scripts/images.js (sharp)
import sharp from "sharp";
import fg from "fast-glob";
import { mkdirSync } from "fs";
import { dirname } from "path";
const sources = await fg("src/assets/images/**/*.{jpg,jpeg,png}");
for (const src of sources) {
const out = src.replace("src/assets/images", "dist/assets/images/webp")
.replace(/\.(jpe?g|png)$/i, ".webp");
mkdirSync(dirname(out), { recursive: true });
await sharp(src).resize({ width: 1200, withoutEnlargement: true })
.webp({ quality: 78 })
.toFile(out);
}
Use explicit width
/height
, lazy-load non-critical images, and target 1200×630 ≤ 50KB when possible.
Never Ship Broken Links (Internal + External)
Dead links bleed trust and rankings. Add a fast crawler to your tests:
// scripts/links-check.js
import { spawnSync } from "node:child_process";
const res = spawnSync("npx", ["linkinator", "dist", "--silent", "--recurse"], { stdio: "inherit" });
process.exit(res.status);
Accessibility Checks on Every Build
Accessibility isn’t optional. Bake axe-core checks into CI so regressions never reach production.
// scripts/a11y-ci.cjs (Playwright + axe-core)
const { chromium } = require("playwright");
const { injectAxe, checkA11y } = require("axe-playwright");
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("http://localhost:8888");
await injectAxe(page);
await checkA11y(page, null, { detailedReport: true, detailedReportOptions: { html: true } });
await browser.close();
})();
Enforce tap targets ≥44×44px, visible focus states, and descriptive link text.
Performance Budgets (Automated)
Set budgets that fail the build when pages bloat:
// lighthouserc.json
{
"ci": {
"collect": { "staticDistDir": "dist" },
"assert": {
"assertions": {
"largest-contentful-paint": ["error", { "maxNumericValue": 2500 }],
"cumulative-layout-shift": ["error", { "maxNumericValue": 0.05 }],
"interactive": ["error", { "maxNumericValue": 3500 }],
"total-byte-weight": ["error", { "maxNumericValue": 1200000 }]
}
}
}
}
Targets match Maelstrom’s budgets: LCP ≤ 2.5s, CLS ≤ 0.05, TTI ≤ 3.5s, weight ≤ 1.2MB.
CI/CD: Every Commit Builds, Tests, and Deploys
Repeatable builds create trust. Use GitHub Actions to run your golden path on push:
# .github/workflows/deploy.yml
name: build-and-deploy
on:
push:
branches: [ main ]
jobs:
ship:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: 20, cache: 'npm' }
- run: npm ci
- run: npm run ship
env:
NETLIFY_AUTH_TOKEN: $
NETLIFY_SITE_ID: $
Preview deploys on PRs help you review UX before merging.
Content Automation: Front-Matter, Components, & TOCs
Generate consistent pages from YAML front-matter and Nunjucks includes. Turn mistakes into impossibilities:
- Front-matter validators: CI script that checks required keys (title, description, image, altText, canonicalUrl).
- Auto-TOC: Build a sidebar from H2/H3 anchors.
- Schema injection: JSON-LD (
BlogPosting
,BreadcrumbList
,Organization
) from front-matter.
// scripts/validate-frontmatter.js
import fg from "fast-glob"; import fs from "fs";
const required = ["title","description","canonicalUrl","image","altText"];
const files = await fg("src/**/*.md");
let ok = true;
for (const file of files) {
const raw = fs.readFileSync(file, "utf8");
const fm = /---([\s\S]*?)---/.exec(raw)?.[1] ?? "";
for (const key of required) if (!new RegExp(`^${key}:`, "m").test(fm)) {
console.error(`Missing '${key}' in ${file}`); ok = false;
}
}
process.exit(ok ? 0 : 1);
Security & Delivery: Automate Safety Nets
Bake security into deploys: strict CSP, HSTS, Referrer-Policy, and long-cache rules for assets.
# netlify.toml (snippet)
[[headers]]
for = "/*"
[headers.values]
Content-Security-Policy = "default-src 'self'; img-src 'self' data: https:; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self';"
Strict-Transport-Security = "max-age=31536000; includeSubDomains; preload"
Referrer-Policy = "strict-origin-when-cross-origin"
X-Content-Type-Options = "nosniff"
Tighten CSP as you externalize any inline scripts and consolidate third-party calls.
Quick Ship Checklist (Pre-Publish)
- ✅ Format & lint: Prettier, ESLint, Stylelint clean.
- ✅ Images:
.webp
, responsive sizes, explicit dims, lazy-loaded. - ✅ Links: No 4xx/5xx; internal routing correct.
- ✅ A11y: No critical axe violations; headings logical; focus states visible.
- ✅ Performance: Budgets pass (LCP, CLS, TTI, total bytes).
- ✅ Schema: BlogPosting + BreadcrumbList + Organization valid.
- ✅ Security: CSP/HSTS headers emitted in preview.
Common Automation Pitfalls (and Fixes)
- Over-engineering: If you need a handbook to run your scripts, you went too far. Favor npm scripts over heavy task runners.
- Alert fatigue: Trim CI logs to actionable errors; use budgets not lectures.
- Blind trust in plugins: Inspect outputs—especially schema, sitemaps, and image pipelines.
- Ignoring content: Automation supports craft; it doesn’t replace it. High-signal writing wins.
Key Takeaways
- Automate the boring; obsess over the meaningful.
- One
ship
command should format, lint, test, budget-check, build, and deploy. - Image automation and performance budgets pay the biggest dividends.
- Security headers and accessibility checks build long-term trust.