Skip to content
Back to blog
Part 2 of 10 in series
Building a Production-Grade Blog

A curated collection of articles exploring this topic in depth.

4 min read

Architecture Before Tools

Why choosing a framework should be the final step in designing a system, even for a personal blog.


Most technical projects begin with a tool choice. We ask whether to use Next.js, Hugo, or Astro before we have defined what the system needs to do. This is a reversal of sound engineering practice.

When I started building this blog, the framework was the last decision I made. Before that, I defined the architectural constraints that would govern the system for years to come.

The primary constraint: Static export#

The most significant decision was to use a static export. In this model, the result of the build process is a collection of immutable files, including HTML, CSS, JavaScript, and images. There is no server-side runtime, no database, and no application logic executing at the edge.

This constraint was chosen for three reasons:

  1. Security: The attack surface is reduced to the delivery layer and the storage layer. There are no server-side vulnerabilities to exploit because there is no server.
  2. Reliability: A static site is fundamentally more resilient. If the build pipeline breaks, the existing site remains live. There are no runtime dependencies to fail, no database connections to drop, and no memory leaks to debug.
  3. Longevity: Static files are the most portable and long-lived format on the web. Even if the framework used to generate them becomes obsolete, the output remains readable by any web browser.

Separation of concerns#

With the static constraint in place, the architecture naturally divides into four distinct layers. Keeping these layers decoupled is what provides the system with its resilience and portability.

1. The content layer#

Content is treated as structured data rather than just text. It exists as MDX files with validated frontmatter schemas. This layer is entirely independent of how it is eventually rendered.

2. The build layer#

The build layer consumes the content and produces the static assets. This is where the framework lives. By treating the framework as a replaceable transformation engine, I avoid being locked into its specific ecosystem. If I move from Next.js in the future, the content and infrastructure layers remain untouched.

3. The infrastructure layer#

Infrastructure is managed through declarative code using Terraform. It defines the storage buckets, the delivery network, and the security boundaries. It is the executable documentation of the environment.

4. The delivery layer#

The delivery layer handles TLS termination, global caching, and URL rewrites. It is responsible for getting the files from the storage bucket to the user with minimal latency.

Failure modes avoided#

By designing for these separations, I explicitly avoided several common failure modes:

  • Platform Lock-in: Many developers rely on platform-specific features that make it difficult to move away from a specific hosting provider. By sticking with a static export using standard AWS primitives, I maintain full ownership of the hosting environment.
  • Silent Build Failures: Because the build process is a hard gate, I can run validation on the output before it ever touches the delivery layer.
  • Credential Leakage: By separating the build environment from the delivery environment using OIDC, I eliminate the need to store long-lived cloud credentials in my CI/CD system.

Conceptual scaling#

This architecture scales conceptually because it separates the complexity of authoring from the simplicity of delivery. Adding a thousand posts increases the build time, but it has zero impact on the complexity or performance of the delivery layer.

A small site does not excuse sloppy architecture. Small systems are the best place to practise disciplined design, and in this case because the stakes are low enough to allow for a deep exploration of these topics.

In the next post, we will look at how treating content as data rather than markup allows for a more resilient and searchable digital garden.