Beyond the Readme: Why We Need "The Website Specification" (and How to Build One)

Hey everyone, Alex here. Welcome back to another edition of Coding with Alex on sysseder.com.

If you've been hanging out on Hacker News recently, you might have spotted a fascinating document making its rounds: The Website Specification. At first glance, you might think, "Great, another specification to read. Just what my Friday afternoon needed." But as I dove into it, it hit me right in the DevOps feels.

How many times have you inherited a legacy web project, spun up the local environment, and immediately watched your terminal explode into a wall of red error text? You open the README.md, only to find it was last updated three years ago by an engineer who now works at a sheep farm in New Zealand. You check the package.json, the Dockerfile, the CI/CD YAML files, and maybe a random bash script called deploy-old.sh, trying to piece together how this fragile house of cards actually runs, builds, and deploys.

The Web has evolved from simple static HTML pages to highly complex, multi-layered distributed applications. Yet, we still don't have a standardized, machine-readable way to say: "This is what this website is, how it behaves, and where its pieces live."

Today, we’re going to dissect "The Website Specification" concept, look at why our current documentation-first approach is failing us, and explore how we can implement a machine-readable website spec in our own development pipelines today.

The Core Problem: The Configuration Chasm

Right now, a modern website's "specification" is scattered across a dozen different files, platforms, and mental models. Take a look at this typical architecture:


[ DNS / CDN (Cloudflare) ] ──> [ Edge Routing / Redirects ] ──> [ Origin / SSR Server (Next.js) ]
                                                                      │
                                                                      ├──> [ Headless CMS API ]
                                                                      └──> [ Microservice APIs ]

To understand this setup, a developer has to look at:

  • Routing & Redirects: Handled partly in next.config.js, partly in Cloudflare Page Rules, and maybe a few lines in an Nginx config.
  • Build Outputs: Hidden inside GitHub Actions workflows or Vercel build settings.
  • Environments & Secrets: Spread across Doppler, AWS Parameter Store, and local .env.local files.
  • Metadata & SEO: Hardcoded into React components or dynamically fetched from a CMS.

There is no single source of truth. "The Website Specification" aims to solve this by creating a unified, declarative, and machine-readable file—usually website.yaml or website.json—placed at the root of a project. This file doesn't just document the website for humans; it configures it for machines.

Anatomy of a Modern Website Specification

What should a standardized website specification actually look like? Based on the emerging discussions, a robust spec needs to cover four critical pillars: Identity, Structure (Routing), Environments, and Capabilities.

Let's design a hypothetical, fully realized website.yaml for a modern e-commerce and blog site. This isn't just a static config; it's a blueprint that your deployment scripts, testing suites, and local dev servers can all read.


# website.yaml
spec_version: "1.0.0"
name: "DevStore & Blog"
description: "The ultimate developer apparel shop and tech blog."
primary_domain: "sysseder.com"

# 1. Routing & Structure
routing:
  base_path: "/"
  trailing_slash: false
  redirects:
    - source: "/old-blog/:slug"
      destination: "/blog/:slug"
      status: 301
    - source: "/twitter"
      destination: "https://twitter.com/sysseder"
      status: 302
  custom_headers:
    - path: "/*"
      headers:
        X-Frame-Options: "DENY"
        X-Content-Type-Options: "nosniff"
        Content-Security-Policy: "default-src 'self';"

# 2. Environments & Infrastructure
environments:
  production:
    url: "https://sysseder.com"
    branch: "main"
    auto_deploy: true
  staging:
    url: "https://staging.sysseder.com"
    branch: "develop"
    auto_deploy: true
  development:
    local_url: "http://localhost:3000"
    command: "npm run dev"

# 3. Build & Output Specification
build:
  engine: "nextjs"
  node_version: "20.x"
  commands:
    install: "npm ci"
    compile: "npm run build"
  output_directory: ".next"
  static_assets:
    - "/public"

# 4. Integrations & Dependencies
services:
  database:
    type: "postgresql"
    version: "15"
  cms:
    type: "contentful"
    sync_webhooks: true

By looking at this single file, any developer—or any automated tool—instantly knows exactly how this website functions, how to build it, how it routes traffic, and what external dependencies it relies on.

Why This is a Game-Changer for DevOps and Security

You might be thinking, "Alex, isn't this just another layer of abstraction? I already have a docker-compose.yml and a package.json." But the implications of having a dedicated website specification go far beyond simple local setup.

1. CI/CD Pipeline Automation

Imagine your GitHub Actions or GitLab CI file. Today, it’s probably full of hardcoded steps, environment variables, and custom bash scripts tailored to your specific framework.

If your CI/CD runner can parse a standardized website.yaml, your build pipeline becomes entirely generic and self-configuring. The runner reads the file, detects the build engine (e.g., Next.js, Astro, Hugo), runs the exact install and compile commands specified, and deploys to the correct environment based on the branch mapping. If you switch from Next.js to Astro, you change the spec file, and your CI/CD pipeline adapts automatically without you touching a single line of YAML in your .github/workflows folder.

2. Dynamic Edge Middleware and Routing

Managing redirects is a developer's nightmare. Doing it in application code (like Next.js middleware) consumes serverless execution time. Doing it at the DNS/CDN level (like Cloudflare Rules) separates the configuration from the codebase.

With a website spec, your Edge provider (Cloudflare, Fastly, AWS CloudFront) can parse the routing.redirects block during deployment and automatically generate edge routing rules. Version-controlled, peer-reviewed redirect rules that execute at the closest edge server to your user—without writing custom edge worker code!

3. Security Posture Auditing

Security teams love compliance. With a machine-readable spec, security scanners can pull down your repository and instantly verify if you have custom security headers (like CSP, HSTS, or X-Frame-Options) defined. No need to boot up a live staging server and run curl commands against it; the code defines the security posture directly, making static analysis a breeze.

Putting It to Work: Building a Simple Website Spec Parser

Let's make this practical. How can we use this spec file today in our local development workflows? Let's write a simple Node.js CLI script that parses our website.yaml to validate our local environment and boot up the development server.

First, make sure you have the js-yaml package installed to parse YAML files:

npm install js-yaml

Now, let's write our orchestrator script, dev-runner.js:


const fs = require('fs');
const path = require('path');
const yaml = require('js-yaml');
const { execSync } = require('child_process');

function bootstrapDevEnv() {
  console.log('šŸš€ Loading Website Specification...');
  
  const specPath = path.join(__dirname, 'website.yaml');
  if (!fs.existsSync(specPath)) {
    console.error('❌ Error: website.yaml not found at root!');
    process.exit(1);
  }

  try {
    const spec = yaml.load(fs.readFileSync(specPath, 'utf8'));
    
    console.log(`\nšŸ“¦ Project: ${spec.name}`);
    console.log(`šŸ“ Description: ${spec.description}`);
    console.log(`🌐 Target Domain: ${spec.primary_domain}`);
    console.log('--------------------------------------------------');

    // Validate Node version
    const expectedNodeVer = spec.build.node_version.replace('.x', '');
    const currentNodeVer = process.version.split('.')[0].replace('v', '');
    
    if (currentNodeVer !== expectedNodeVer) {
      console.warn(`⚠️  Warning: Node version mismatch! Expected ${spec.build.node_version}, running ${process.version}`);
    } else {
      console.log(`✅ Node.js Version Check Passed (${process.version})`);
    }

    // Run Install Command
    console.log(`\nšŸ“„ Running installation: "${spec.build.commands.install}"...`);
    execSync(spec.build.commands.install, { stdio: 'inherit' });

    // Boot Local Dev Server
    const devCmd = spec.environments.development.command;
    const localUrl = spec.environments.development.local_url;
    
    console.log(`\nšŸ”„ Starting local development server at ${localUrl}...`);
    console.log(`šŸ‘‰ Running: "${devCmd}"\n`);
    
    execSync(devCmd, { stdio: 'inherit' });

  } catch (error) {
    console.error('❌ Failed to parse or execute Website Spec:', error);
    process.exit(1);
  }
}

bootstrapDevEnv();

When a new developer clones your repo, they don't need to read a massive list of setup steps. They just run node dev-runner.js. The script validates their Node runtime, runs the correct installation commands, and boots up the server based on the declarative specification. Clean, repeatable, and completely self-documenting.

The Road to Standardization

Of course, for "The Website Specification" to truly succeed, it needs industry adoption. Imagine a world where hosting platforms like Vercel, Netlify, AWS Amplify, and Heroku all natively support a standardized website.json or website.yaml.

Instead of clicking through proprietary web UIs to configure redirects, environment variables, and build settings, you would commit your spec file to your repo. The platform would read the spec and configure the infrastructure to match. Vendor lock-in would decrease, and portability would skyrocket. Moving your website from Vercel to AWS would be as simple as pointing AWS to your repo and letting it read your website.yaml.

Conclusion

We've standardized our APIs (OpenAPI/Swagger), our container runtimes (Docker/OCI), and our database migrations. It's time we standardize the very thing we build most: the website itself.

Adopting a "Website Specification" mindset—even if it's just a custom YAML file in your private repositories for now—bridges the gap between development, operations, and documentation. It forces us to treat our application's operational metadata with the same level of care and version control as our source code.

What do you think? Is "The Website Specification" a step in the right direction, or do we already have too many configuration files cluttering our repositories? How do you currently manage routing, redirects, and metadata consistency across your environments?

Let me know in the comments below, and don't forget to subscribe to the newsletter for more deep dives into web architecture and DevOps!

Until next time, happy coding!

Post a Comment

Previous Post Next Post