In How This Website Was Built with Prompts, I described how AI helped turn an old WordPress installation into a static website hosted on AWS.
What I underestimated was how quickly a simple website could become a surprisingly complex software project.
How complexity crept in
It began with S3, CloudFront, and a few pages. Then came weather forecasts, training data, games, security checks, automated releases, and nine languages.
Each translated article also needs consistent navigation, metadata, feeds, and generated HTML. Eventually, even small content changes could trigger a substantial CI/CD pipeline.
When safety mechanisms become obstacles
Our recent Git history is a useful reality check. We encountered secret-scanner friction, reproducible-build mismatches, stalled font installations, workflow configuration errors, and missing AWS IAM permissions.
One partial deployment recovery involved checking 534 public objects: 501 aligned with expectations, 24 had unexpected differences, and nine new pages could not be read. Getting back to a safe release took multiple tightly controlled PRs.
Each safeguard had a purpose. But together, they made the system difficult to change.
The lesson: more automation does not automatically mean better automation.
From an idea to a live page
A PR is not a deployment. Changes first pass site and security checks, then require review and merge. The release independently validates the generated site, AWS permissions, budget, and exact files before uploading anything to S3.
If a check fails, the right response is to investigate the cause—not weaken a safety gate just to make a red badge turn green.
My favorite workflow: the evening debugging loop
One especially useful habit is asking ChatGPT to keep working through a PR's failing checks: inspect the logs, propose a narrow fix, commit it, and check CI again.
That's particularly handy in the evening, just before bed. I can leave the repetitive investigation to the connected session instead of personally following every failure.
This isn't a promise that ChatGPT will run indefinitely or autonomously overnight: it only works while an active session and the necessary tools are available. I still review the result and decide when to merge.
Making CI/CD simpler again
We're now deliberately removing work that adds little value to routine changes:
- Gitleaks: check new secrets on every PR; run full-history scans separately.
- Semgrep and pip-audit: check relevant changes, with scheduled full scans.
- Documentation fast path: skip heavy site builds for docs-only PRs.
- Dependency caching: reuse Python and Sharp downloads, not deployment artifacts.
- Timing data: measure font setup and quality gates before optimizing further.
We keep the important protections: reproducible builds, SHA-256 manifests, OIDC, least-privilege AWS permissions, budget checks, and no CloudFront invalidations.
Measuring the outcome
We'll compare similar changes before and after these improvements: time to green PR, failed or retried checks, and merge-to-deploy time. Documentation-only changes must be compared with documentation-only changes—not with full multilingual site builds.
The first optimizations are in place. The next five to ten PRs will tell us whether they really make the workflow more predictable.
The next improvement to this website may not be another feature. It may simply be making the existing system easier to maintain.
First measured results: two comparable changelog updates (7 November 2026)
We compared two updates to the changelog in all nine languages, using their last successful CI runs. The earlier update established a baseline by publishing retrospective entries across the translated pages. The later update published two more entries after several careful improvements to the build and validation pipeline.
What actually changed?
- Fewer duplicate asset checks: Each identical local asset path is checked once per quality-gate run, but every missing reference is still reported on its original page.
- Measurable bottlenecks: Timing data now identifies slow validation scripts and individual generators within the navigation build.
- Reused navigation hashes: Shared CSS and JavaScript SHA-256 hashes and navigation lookups are calculated once instead of separately for hundreds of pages.
- More efficient multilingual generation: French, Portuguese, Japanese and Hindi generators, the language-switcher builder, and navigation checks reuse identical hashes within each run. Build ordering and byte-for-byte reproducibility checks remain intact.
| Metric | Earlier update | Later update |
|---|---|---|
| Offline checks | 3:38 | 3:18 (9.2% faster) |
| Full site release gate | 2:55 | 2:27 (16.0% faster) |
| Gitleaks job | 0:56 | 1:07 (slower) |
| Semgrep job | 0:10 | 0:11 |
| pip-audit job | 0:13 | 0:12 |
| Change opened to merged | 41:50 | 34:29 |
The release gate is a step within offline checks, not an additional sequential job. Security jobs run in parallel; Semgrep and pip-audit both skipped their expensive scan steps because relevant files were unchanged. These job times are not full-scan benchmarks.
The release gate fell from 175 to 147 seconds. Gitleaks, however, grew from 56 to 67 seconds, including 52 seconds just to install the pinned CLI on the later run. This suggested investigating download and caching reliability, not relaxing security.
Two samples do not prove a durable speedup. Runner variation, different content and repair cycles affect the comparison. Time from opening a change to merging it measures the human-and-CI process, not just computation. The later production release completed in 5 minutes 50 seconds, including around 2 minutes 32 seconds of conditional S3 uploads, but there is no equivalent before/after deployment measurement here.
Next we'll observe additional comparable changes and separately measure generators, retries, time to green and merge-to-deploy. Byte-for-byte reproducibility, security checks, OIDC, budget limits and the no-CloudFront-invalidation policy remain non-negotiable.


cloud / systems