mediocrity.media

skill builder updates

Could I Just Build It?
Building a Serverless Publishing Pipeline on AWS

published 8 min read

Over the past year I've been maintaining an archive of the monthly AWS Skill Builder updates. It started simply enough. I'd receive the PDF each month, share it with customers and connections, and before long someone would ask, "Do you still have the March issue?" or "Which update announced that new certification?"

The PDFs were packed with useful information, but they weren't particularly searchable, weren't pleasant to browse on the web, and if you missed a month they could easily disappear into an inbox forever. I wanted something that was easier to explore. A searchable archive where readers could browse by topic, certification, learning domain or skill level, and revisit editions they may have missed.

Building the archive solved one problem. It created another.

People still had to remember to come back.

From PDFs to an Archive

One important detail that isn't obvious from the architecture is that the PDFs aren't converted inside AWS.

That work happens locally.

Using Python and modern LLMs, I built a workflow that extracts links, restructures the content, and transforms each monthly PDF into a standalone HTML page. This gives me complete control over the final presentation before anything is published. By the time the page reaches AWS, it is already exactly how I want readers to experience it.

This was an intentional design choice. AI is fantastic at accelerating repetitive work, but I still wanted to be the editor. Every issue is reviewed locally before it's uploaded.

Once I'm happy with the page, I upload a single HTML file.

From that point onwards, the publishing pipeline takes over.

Removing Friction

I've often heard developers described as "professionally lazy." I don't think that's quite right.

I don't mind hard work. I actually enjoy it. What frustrates me is repetitive work. If I find myself performing the same task over and over again, I start wondering if the computer could do it instead.

That thinking shaped this project from the very beginning.

Upload once. Publish automatically. Update the homepage. Archive the edition. No forgotten links. No manual edits. No second guessing.

The original publishing pipeline was intentionally simple. Uploading a single HTML document was all I needed to do. Everything else happened automatically.

Subscribers became the next obvious step. People were already finding the archive through LinkedIn and recommendations from others. Rather than managing a mailing list myself, why not let readers decide whether they wanted updates?

Could I Just Build It?

Over the past few years I've spent so much time learning AWS that my instinct has gradually changed.

Instead of immediately looking for another SaaS platform, I usually find myself asking a different question.

Could I just build it?

Sometimes the answer is no.

This time it was a very satisfying yes.

It wasn't about replacing Mailchimp or Substack. They're fantastic products. This was about learning, understanding the services more deeply, owning the workflow, and seeing just how far a small serverless application could go.

AWS gives you building blocks rather than finished products. That freedom is one of the things I enjoy most. You decide how the pieces fit together.

The Architecture

The finished solution naturally separated itself into three responsibilities: publishing, subscriptions and distribution.

aws architecture for sillbuilder updates
skill builder updates — aws architecture

Publishing

Once an HTML page is uploaded, Lambda moves it into the correct location, updates the homepage, and publishes the latest edition. One upload becomes one complete deployment.

Subscriptions

Readers subscribe themselves. Their details are stored in DynamoDB, with SES handling confirmation emails. Double opt-in wasn't simply about meeting regional requirements. It was about respect. If somebody enters your email address, you deserve the opportunity to confirm whether it was actually you. Good email reputation matters, particularly when you own the sending domain.

Distribution

When a new edition is ready, another Lambda converts the published HTML into a clean EML email and dispatches it through Amazon SES. Each successful delivery is recorded so I know exactly which editions have been sent.

One Decision I Deliberately Didn't Automate

It would have been very easy to automatically send emails the moment a new HTML page appeared in S3. SNS could have triggered the workflow immediately.

I deliberately chose not to.

Anyone who's published a website knows the feeling. You notice a typo five minutes later. A heading wraps unexpectedly. An image isn't quite aligned. You read the page one more time and decide a sentence could be clearer.

Publishing and notification are therefore two completely separate actions.

Publish. Review. Then notify subscribers.

Pushing Myself

This project also became an excuse to challenge myself.

I'd recently moved beyond the constraints of Amplify on another project and had started relying more heavily on CloudFormation from Node.js. YAML felt like the natural next step.

I'd seen templates referencing resources that had only just been created. Outputs feeding directly into other resources. Entire applications describing themselves in a single document.

There was something incredibly elegant about that idea.

Writing an entire serverless application in one YAML template is both powerful and intimidating. Buckets, Lambda functions, DynamoDB tables, IAM permissions and SES configuration all stitched together declaratively.

I'm certainly not a CloudFormation expert, and modern LLMs proved invaluable here. Not by writing the application for me, but by helping validate resources, checking properties and allowing me to spend more time thinking about the architecture rather than remembering every line of template syntax.

Why These Services?

Some of the service choices were simply a reflection of where I've become most comfortable building.

Amazon SES already powered another project of mine, so extending it into a mailing list felt like a natural progression. It's inexpensive, scales effortlessly, and gives me ownership of the complete email pipeline.

Likewise, DynamoDB has quietly become my default choice whenever I need structured data. Years ago I would have instinctively reached for SQL. Today I barely hesitate. It's simple to model, incredibly fast, and removes an enormous amount of operational overhead.

What's Next?

Like every side project, the backlog continues to grow.

  • RSS feeds
  • Bounce handling and automatic subscriber cleanup
  • Delivery reports archived to S3
  • An administration dashboard
  • Scheduling future editions
  • Eventually publishing the project on GitHub without exposing sensitive infrastructure details

I'm intentionally avoiding detailed reader analytics. Whilst open rates are interesting, I care just as much about respecting reader privacy. Sometimes collecting less data is the better engineering decision.

Final Thoughts

If there's one thing I hope people take away from this project, it's that building on AWS doesn't have to mean enterprise budgets or overwhelming complexity.

This entire publishing pipeline costs me less than fifty cents a month to operate.

Yet it's built using the same managed services I'd happily trust for applications many orders of magnitude larger.

The best way I've found to learn AWS isn't reading another whitepaper.

It's building something you genuinely wish existed.

Pick a problem that annoys you.

Build the smallest thing that solves it.

Then improve it, one lesson at a time.

See it in action

If you'd like to explore the archive or receive future AWS Skill Builder updates, both are available below.

Two things I'm still working out

The last item on my backlog is the one I have no plan for: publishing this on GitHub without handing out infrastructure details I'd rather keep. Account identifiers, domain configuration, SES identities and the shape of my IAM policies all sit in a single template, and I haven't found a way to split the public parts from the private ones that I'm actually happy with. If you've open-sourced a CloudFormation or SAM stack and solved this properly, I'd like to hear how.

The other one I'd take an argument on. I deliberately don't send the moment a page lands in S3, and I deliberately don't collect open rates. Both decisions cost me something real. If you think either is the wrong call, make the case - I'd rather be talked out of it than quietly wrong about it.

Side projects have taught me more about engineering than any course or certification I've taken.