I recently looked through the folders of the projects I had been working on. There was this blog, a video production tool, a few small web services, and a desktop app. While building them one by one, I thought I was simply choosing whatever each project needed. Put together, though, the choices showed a clear pattern.

I use TypeScript as a common language and keep the front end as lightweight as I can. When a server becomes necessary, I reach for Cloudflare Workers and D1. I do not put React everywhere; I use it only when there is a clear reason, such as a complex interface or video production. Whether a technology works well with AI-assisted development has also become part of the decision.

This is not a claim that this is the one correct stack today. It is closer to a record of how much complexity I am currently willing to carry after building several projects.

TypeScript became the common language

TypeScript appears across websites, CLIs, Cloudflare Functions, and video-rendering pipelines. That does not mean everything should be built with TypeScript. For an app like ssagal, Rust and Tauri were the more natural choice.

But after building several small services, I found that adding another language cost more than I expected. Relearning syntax was not the annoying part. The real cost was having different package managers, type systems, deployment scripts, and ways of tracking down errors. TypeScript is the language of the web, but for me it has also become the default working language when moving between projects.

That is why I also brought my CLIs and video pipelines into TypeScript. My Remotion work sits on the same path. I like that front-end code and the automation around it no longer feel like separate worlds.

Can I build it together with AI?

It is difficult to choose a technology today without considering AI. That does not mean handing over the entire codebase from the beginning. AI is already deeply involved in how I start an implementation, search documentation, write tests, and work through errors when I get stuck.

When two options serve the project equally well, I first look at the language and tools that AI can read, write, and revise with reasonable consistency. Good documentation, enough examples, and a widely used package ecosystem matter more to me now. The time spent learning an unfamiliar technology is mine, but so is the time spent checking and correcting an AI that took the wrong path.

This does not make a new language, or a tool that AI still struggles with, a bad choice. If it is the only thing that makes the project possible, I should obviously use it. But when the goal is to finish the project, novelty alone no longer moves a technology up my list. For now, I favor tools I can understand and carry to completion together with AI.

I start with a lightweight front end

Many screens whose main job is to present content do not need to be large applications.

This blog is rendered as static pages with Hugo. Several record-keeping sites use Astro, while the wedding mobile invitation began with vanilla HTML, CSS, and JavaScript. The implementation differs, but the principle is the same: when writing or information is the focus, I start with a static site and keep deployment as simple as possible.

I do not choose this because static sites look more impressive. I choose it because there is less to operate. I do not have to watch a server process, and I can more easily predict the result of a content change. I moved this blog from Ghost back to a static site for a similar reason. A tool with more features does not always make me write more often.

Of course, a static site does not solve every problem. A service that keeps long-lived user state or changes continuously on screen will need a different approach. I just have not found many reasons to carry that complexity from day one.

I add only as much server as I need

Even a static site eventually reaches a point where it needs data. The guestbook in the wedding invitation needed somewhere to store messages. A report-writing web app needed data, files, and asynchronous jobs.

For these cases, Cloudflare Pages, Workers, and D1 fit well. I can add an API to the same deployment flow as the site and handle a small database or queue in the same place. The barrier to starting is lower than provisioning and managing a standalone server first.

There is still a rule behind this choice. “No server” is not the goal. I simply avoid creating a server to operate until I have a reason to operate one. I may need to reconsider when the data volume or processing time grows. At the current scale, just enough serverless functionality has been the most comfortable option.

I use React only where it earns its place

For a while, React felt like the default for any web project. But not every page needs the same amount of state management and component composition.

React and Remotion clearly earn their place in video work. Some videos need frames, subtitles, audio timing, and simulation results composed together. Another pipeline calculates scene duration from narration, then aligns subtitles and background music. At that point, breaking the UI into declarative and reusable components actually makes the work simpler.

The ssagal project, on the other hand, is a desktop app that places one small button on the screen and plays a sound. I did not add React. Plain HTML, CSS, and JavaScript handle the interface and playback logic, while Rust and Tauri focus on creating the window. Leaving out a framework was not giving up on technology. It was a decision to keep the boundary as small as the problem.

A monorepo is not always the default either

A monorepo is convenient when several apps share a design system or types. It works especially well when multiple sites share one design system, or when a CLI, shared schema, external-service integrations, and video packages need to move together.

Managing dependencies with a pnpm workspace and separating common code into packages makes it easier to find what needs to change. Still, I do not think every tiny standalone project needs to begin as a monorepo. I can move into that structure later when the need appears, while folders and configuration created up front have to be maintained from the start.

In the end, it is about a size I can operate

Looking across these projects did not leave me with a winning framework. I use TypeScript often, but I also built an app where Rust was the better fit. I prefer static sites, but add Workers and D1 when I need data. Sometimes I use React; when the screen is simple, I do not.

In the end, I seem to choose a stack by the amount of complexity I can operate, not by its feature list. Whether I can develop and verify it together with AI is now part of that calculation. I do not know whether these criteria will look the same a few years from now. For the moment, before adding another tool, I try to ask what problem truly cannot be solved without it.

In the next post, I want to look more closely at two choices that came from these criteria: why I chose Remotion, and why I left React out of a Tauri app.