avatar Post

Blog v3: simplifying and improving my blog

I deliberately built a complex AWS ecosystem for this blog to play and learn. Now I'm tearing it down. Here's why, and where things stand.

Blog v3: simplifying and improving my blog

For the full context of v1 and v2, see the original article: How I Decided on the Technology Behind the Blog.

1. The original goal

When I created this blog, the goal was very specific:

Use as many AWS services as possible, practice with them on a real project, at near-zero cost.

And I delivered. For years, the blog was my personal AWS lab: I deployed Amplify, migrated to Terraform, used CodePipeline and CodeBuild for CI/CD, set up a full serverless backend with API Gateway, Lambda, Step Functions, DynamoDB, SES, SNS, SSM Parameter Store and EventBridge for contact forms, feedback, and email subscriptions. I ended up managing exactly 7 repositories related to the blog, essentially zero cost thanks to the free tier and AWS credits.

Every service I added had a clear purpose: not just reading about it in theory, but building it myself in production, integrating it, breaking it, understanding its real limits, and learning from the experience. That’s what I was after.

At some point, that question came up:

Do I keep maintaining all this out of inertia, or do I simplify and keep only what still makes sense?

2. What was there: the inventory

To understand the actual scale, here’s the before state.

Archived repositories

RepositoryDescriptionVisibility
blog-legacy-web-codeOriginal Jekyll source codePublic
blog-legacy-web-code-pipelineCI/CD pipeline for web code deploymentPrivate
blog-legacy-frontend-infrastructureBlog Terraform infrastructure, mainly S3 and CloudFrontPrivate
blog-legacy-frontend-infrastructure-pipelineCodePipeline pipeline for infrastructure deploymentPrivate
blog-legacy-contact-serviceAWS SAM: Step Functions, Lambda, DynamoDB, SES and supporting services for the contact formPrivate
blog-legacy-feedback-serviceAWS SAM: Step Functions, Lambda, DynamoDB, SES and supporting services for the feedback formPrivate
blog-legacy-backend-infrastructureCDK TypeScript: API Gateway, Lambda, DynamoDB and backend resources for comments and subscriptionsPublic

Total: exactly 7 repositories.

Removed AWS services

The hosting layer stayed, but everything managed inside AWS for backend and CI/CD is gone.

These are the main services I stopped using for the blog:

  • Amazon CodePipeline: pipelines for frontend, infrastructure, contact service, and feedback service
  • AWS CodeBuild: Terraform and SAM builds tied to those pipelines
  • Amazon API Gateway: REST endpoints for the forms
  • AWS Lambda: functions to process contact, feedback, and subscriptions
  • AWS Step Functions: workflow orchestration for the form flows
  • Amazon DynamoDB: tables for contacts, feedback, and subscribers
  • Amazon SES: email notifications
  • Amazon SNS: messaging and auxiliary notifications
  • AWS Systems Manager Parameter Store: configuration and parameters used by the services
  • Amazon EventBridge: events and automations related to the backend
  • Amazon EventBridge Pipes: connections between event sources and processing services
  • AWS CDK / CloudFormation: backend stacks and associated resources
  • IAM roles and policies: permissions required by all those services
  • Amazon CloudWatch Logs: logs from Lambdas, builds, Step Functions and other components

Not all of these carried the same weight, but all of them were part of the service ecosystem. The blog reached over 15 AWS resources when counting the full backend, CI/CD, observability, configuration, and permissions stack.

All of it was deployed, all of it worked, all of it was serverless, and all of it cost zero euros. I removed it anyway.

3. Why remove something that works

Each of those resources existed for a valid reason at the time. The problem is that reason no longer applies.

I knew those services. I’d built them, integrated them, broken them, and written about them here on my blog. Once you’ve done that, keeping the infrastructure running adds nothing new. It’s just something else to maintain, and in architecture, what delivers no value is debt. Maybe not right now, but it will be eventually. That’s certain.

Knowing what to remove, and when, is just as important as knowing what to build.

The mindset shift is this: before, I chose services to learn; now, I choose them to solve. The simplest solution that works wins, regardless of whether it comes from AWS or anywhere else.

4. The current architecture: v3

4.1. Hosting: keeping what made sense

The hosting layer barely changed from v2. S3 + CloudFront is still the right choice for a static site: no servers to manage, global cache distribution, HTTPS, and near-zero cost.

flowchart LR
  A(User) --> B(Route53)
  B --> C(CloudFront)
  C --> F("CloudFront Functions")
  C --> D(S3)

The AWS services currently running for the blog:

  • S3: static site generated by Jekyll
  • CloudFront: global CDN, per-resource-type caching, HTTPS
  • CloudFront Functions: URL rewriting so Jekyll works correctly behind CloudFront
  • ACM: SSL certificate for the domain
  • Route53: DNS

Total: 5 AWS services maintained.

The relevant change in this layer was replacing Lambda@Edge with CloudFront Functions. For this use case, simple URL rewriting, I didn’t need the extra flexibility of Lambda@Edge. CloudFront Functions fits better: less complexity, fewer moving parts, and more than enough to solve the problem.

What also changed is how I manage it. In v2, the blog had its own dedicated infrastructure repository. In v3, the infrastructure lives inside a shared Terraform project I use to deploy all my static sites. Each site has its own configuration folder but shares the same reusable base.

1
2
3
4
5
6
7
deploy-websites/
├── s3.tf
├── cloudfront.tf
├── cert_generation.tf
├── route53.tf
└── projects/
    └── blog/      ← blog-specific variables

One shared repository instead of a separate infrastructure repository per project.

4.2. CI/CD: from CodePipeline to GitHub Actions

Before, in v2:

flowchart LR
  A(GitHub push) --> B(Amazon CodePipeline)
  B --> C(AWS CodeBuild)
  C --> D(Terraform apply)
  D --> E(S3 + CloudFront)

Now, in v3:

flowchart LR
  A(GitHub push) --> B(GitHub Actions)
  B --> C(Terraform apply / Jekyll build)
  C --> D(S3 + CloudFront)

The result is identical. GitHub Actions lives in the same repository as the code, is more straightforward to maintain, eliminates all pipeline management in AWS, and is free.

Note: The change wasn’t driven by cost. My v2 solution wasn’t free, but it ended up costing me zero euros. The main reason was operational simplicity: fewer pieces, fewer repositories, fewer pipelines, and less surface to maintain.

4.3. Backend: removed

The contact and feedback forms existed on the blog, but in practice nobody used them, and I wasn’t trying to change that either. Email subscriptions were different: there were subscribers stored in DynamoDB, but I never got around to implementing automated sending, and honestly had no intention of doing so. I decided to delete that data and avoid maintaining a half-built feature that wasn’t delivering real value.

Maintaining API Gateway, Lambda, Step Functions, DynamoDB, SES, SNS, SSM Parameter Store, EventBridge, EventBridge Pipes, permissions, logs, and associated deployments for features that weren’t delivering real value made no sense. Yes, it was serverless and cost me zero euros with no usage — but still.

The fact that something has no cost doesn’t mean it has no impact. Every resource adds configuration, permissions, logs, dependencies, and potential failure points. Once it stops delivering value, all it does is increase the complexity of the solution and the effort required to understand and maintain it.

Comments still work via giscus and GitHub Discussions, an external solution with no infrastructure of its own to manage.

5. Full redesign and AI assistants

The simplification wasn’t only technical. The way I build and maintain the blog also changed.

v3 includes a complete UX redesign: new navigation structure, reorganized content, and a cleaner overall experience. I actually started these changes almost 9 months ago, motivated by the release of Kiro, but left it unfinished because it didn’t fully convince me and I ended up focusing on other things. It was time to pick it back up and finally close out this new v3 of the blog.

And of course, AI assistants have also changed how I work quite a bit. They help me organize ideas, review structure, improve writing, develop blog features, generate drafts to iterate on, and spot improvements that used to take much longer to surface.

AI doesn’t do everything, but it does speed things up. It’s an incredible amplifier — just make sure you stay in control and understand what it’s doing.

I don’t want AI to do the work for me — what would be the point? I want it to help me do it better and faster, but the blog should remain mine. It’s my space to play, learn, and share what I discover. The day that stops being true, the blog will have lost most of its reason to exist.

I have a post about what I learned using a coding assistant after six months of heavy use if you want to see how I apply it in practice.

6. Before vs now

Aspectv2v3
RepositoriesExactly 7 blog-related repositories2 repositories: blog-web-code and deploy-websites
CI/CDCodePipeline + CodeBuildGitHub Actions
HostingS3 + CloudFront + Lambda@Edge, with dedicated Terraform repositoryS3 + CloudFront + CloudFront Functions, inside a shared Terraform module
BackendAPI Gateway + Lambda + Step Functions + DynamoDB + SES + SNS + SSM Parameter Store + EventBridge + EventBridge PipesRemoved
AWS services in useOver 15 services involved in total5 services: S3, CloudFront, CloudFront Functions, Route53, and ACM
Infrastructure as codeCDK + SAM + TerraformTerraform
UX and contentOriginal design, organic growthFull redesign
Creative processMostly manualSupported by AI assistants
Selection criteriaMore AWS services = more learningThe simplest solution that solves the problem

7. Closing thoughts

Building that full ecosystem was a deliberate decision, and it was worth it. Tearing it down has also been deliberate, and so far I have no regrets.

The difference between the two phases isn’t technical. It’s about criteria: early on, I chose services to learn. Now I want simplicity, efficiency, and focus.

That’s probably one of the clearest signs of growth in architecture: not adding more pieces, but knowing how to simplify and which ones you no longer need.

Before, I learned by adding services. Now, I learn by simplifying solutions.

This post is licensed under CC BY 4.0 by the author.