Why I abandoned “easy” stacks for a custom PDF engine

alterimg.com

My “simple” PDF project turned into a 6-month engineering rabbit hole.

I’ve been building web apps for over a decade, and if there’s one thing I’ve learned, it’s that “simple” is a lie. When I first sat down to sketch out AlterIMG, I thought I could just slap together a few open-source libraries and call it a day. I was wrong. The goal was clear: a high-performance, secure PDF manipulation tool that didn’t feel like a relic from 2005.

Most PDF tools out there are bloated. They either rely on heavy server-side processing that scales poorly or client-side libraries that crash the browser when you hand them a 100-page document. I wanted something different. I chose Node.js with TypeScript 5.x for the backend and React 18 for the frontend. But the real challenge wasn’t the UI; it was the plumbing.

I spent three weeks just testing different PDF engines. I tried PDF-lib, MuPDF, and even wrapped some legacy C++ tools. Eventually, I realized that to get the performance I wanted—especially for features like page reordering and instant previews—I had to build a hybrid streaming architecture.

The core of AlterIMG isn’t just a file uploader. It’s a specialized pipeline. I decided to use GCP (Google Cloud Platform) because their networking is superior for large file transfers. But I didn’t want to click around a console like an amateur. I built the whole thing using Terraform.

Every piece of infrastructure, from the VPC to the Cloud Run instances, is version-controlled. This was a “measure twice, cut once” moment. If my server went down, I didn’t want to be guessing what firewall rule I changed. I wanted to run `terraform apply` and walk away.

I remember staring at my screen at 2 AM, trying to figure out why my TypeScript types were clashing with a third-party PDF library. It was frustrating. But that’s the reality of senior-level dev work. It’s not about writing the most code; it’s about making the right architectural bets early on so you don’t have to rewrite the whole thing in three months.

I looked at the existing landscape of PDF “SaaS” tools. Most of them are just wrappers around the same three libraries. They offer the same features, the same bugs, and the same slow upload speeds. I wanted to break that mold.

The decision to go with TypeScript was non-negotiable. When you’re dealing with complex binary data and nested objects like a PDF’s internal structure, you need the safety that types provide. I can’t count the number of times a compiler error saved me from a runtime catastrophe that would have been impossible to debug.

I also spent a significant amount of time on the Terraform configuration. Most developers treat infrastructure as an afterthought, but for AlterIMG, it was the foundation. I needed a way to guarantee that my staging and production environments were identical. By codifying the VPC, the subnets, and the firewall rules, I eliminated a whole class of “it works on my machine” bugs.

alterimg.com

The breakthrough came when I stopped trying to find a library that did everything. I broke the problem down: one service for rendering, one for manipulation, and one for storage. This separation of concerns is what keeps AlterIMG snappy while other tools lag. It allowed me to scale the rendering service independently during high-traffic periods without affecting the core API.

I also had to consider the long-term maintenance. A complex stack is only good if you can manage it. I opted for a monorepo structure to keep the API and the web frontend in sync. This made refactoring much easier, as I could update a shared type and immediately see the impact on both ends of the stack.

Looking back, the architecture phase was the most mentally taxing. It required a level of foresight that only comes from years of seeing projects fail. But seeing the first PDF render in under 200ms made all those long nights worth it.