Most websites we are asked to rescue look the same on the way in. A cheap theme bought off ThemeForest, layered with a page builder and a dozen plugins. No observability, so an error has been firing on the contact form for three months and nobody noticed the leads stopped coming. No tested backup, so the last restore attempt is theoretical. No CI/CD, so every deploy is a direct FTP push and every change is a held breath. Security headers absent. Dependencies a year and a half out of date. Lighthouse in the 40s on mobile. The pattern is familiar because it is what happens when a site is built by the cheapest available bid and inherited by a marketing team with no engineering function behind them.
A properly engineered build is built around the failure modes, not just the happy path. Errors are caught by observability and surfaced before the customer notices. Backups are tested quarterly so recovery is real. Deployments go through CI/CD with preview environments and rollback paths. Security headers are set, dependencies are scanned weekly, secrets stay out of the codebase. Performance is measured against real-user metrics, not lab scores. The site behaves on a budget Android in Johannesburg as well as it does on a MacBook in Cape Town. Six years on, the codebase still reads cleanly and the next engineer can pick it up without an archaeological dig.
The third option, increasingly the right one, is a decoupled stack: a static or SSR front end (Astro, Next.js, Nuxt) talking to a headless CMS (WordPress headless, Sanity, Contentful, Storyblok) and any number of integrated services, hosted on Cloudflare Pages or Vercel with a global CDN by default. Editorial flexibility, sub-second page loads, near-zero hosting cost, and an architecture that survives the agency churn most websites cannot. We build all three patterns (decoupled, headless monolith, classic monolith); the recommendation follows the project and the team, not the architecture we find most interesting to ship.