The Stack I Use and Why

Excerpt: Every tool earns its place. Here’s what I build with and the reasoning behind each choice.


Developers love arguing about tools. I’d rather just explain what I use and why it works for me. No hot takes, no “X is dead” — just honest reasoning.

Frontend: TypeScript + Next.js + Tailwind

TypeScript is non-negotiable. Once you’ve caught a bug at compile time that would’ve been a production incident, you don’t go back to plain JavaScript. The upfront cost of typing pays for itself within the first week of any project.

Next.js because it handles routing, SSR, API routes, and image optimization out of the box. I’ve tried Remix, I’ve tried Vite + React Router — they’re fine. Next.js just has the most batteries included while still getting out of your way.

Tailwind CSS because I got tired of naming things. .card-wrapper-inner-container-header is not a productive use of my time. Tailwind lets me style directly in the markup, scan a component and immediately understand what it looks like, and never fight specificity wars.

Radix UI + shadcn/ui for components. Accessible by default, unstyled so they don’t fight your design system, and composable. I stopped building custom dropdowns and modals from scratch once I found these.

State Management: Zustand + TanStack Query

Zustand for client state. It’s Redux without the ceremony. Create a store in five lines, use it anywhere, no providers wrapping your entire app. It just works.

TanStack Query for server state. Caching, background refetching, optimistic updates, pagination — all handled. The mental model of “server state is not client state” changed how I think about data flow entirely.

Backend: Python + FastAPI

Python because the AI/ML ecosystem lives here. When you’re working with LLMs, embeddings, document processing, and vector databases, fighting the ecosystem by using a different language is a waste of time.

FastAPI because it’s async-first, automatically generates OpenAPI docs, and Pydantic validation means your request/response contracts are enforced at runtime. Express is fine. Django is fine. FastAPI is fast — in development speed, runtime performance, and how quickly new devs can read the code.

Databases: PostgreSQL + pgvector + Neo4j + Redis

PostgreSQL (via Supabase) is the workhorse. Relational data, full-text search, Row Level Security, real-time subscriptions — Postgres does it all and has for decades.

pgvector for embedding similarity search. When you need to find “documents similar to this one,” you need vector search. pgvector keeps it in the same database instead of adding another service.

Neo4j for knowledge graphs. When relationships between entities are the primary thing you’re querying, a graph database isn’t a luxury — it’s the right tool. “Show me everything connected to this concept within two hops” is a one-line Cypher query and a nightmare in SQL.

Redis for caching. LLM API calls are expensive and slow. Cache the results. Redis makes this trivial.

Mobile: React Native + Expo

React Native because I already know React. The alternative is learning Swift AND Kotlin and maintaining two separate codebases. Life is short.

Expo because it handles 90% of the native configuration I’d otherwise spend days on. Push notifications, camera, file system, app store builds — Expo abstracts the pain.

NativeWind so I can use Tailwind syntax in React Native. Same mental model across web and mobile.

Desktop: Electron

Electron gets a lot of hate for being “just a web browser.” It is. And it works. VS Code is Electron. Slack is Electron. Discord is Electron. When you need a cross-platform desktop app and your team already knows React, Electron is pragmatic.

AI/ML: OpenAI + Anthropic + LangChain

OpenAI and Anthropic because using multiple LLM providers keeps you flexible. Different models are better at different tasks. Claude is great for long-context analysis. GPT is great for structured output. Use both.

LangChain + LangGraph for orchestrating multi-agent workflows. When one LLM call isn’t enough and you need agents that plan, execute, and iterate — you need an orchestration framework. LangGraph’s graph-based approach maps well to complex workflows.

DevOps: Docker + AWS + Turborepo

Docker because “works on my machine” isn’t a deployment strategy. Containerize everything, run the same image locally and in production.

AWS (S3, SQS, ECS) for production infrastructure. It’s not the simplest, but it scales and the tooling is mature.

Turborepo + pnpm for monorepo management. When you have a web app, mobile app, desktop app, and shared packages, you need a build system that understands dependencies between them. Turborepo caches builds intelligently so you’re not rebuilding the world on every change.

Testing: Vitest + Playwright + pytest

Vitest for unit tests on the frontend. It’s Jest but faster and works natively with Vite/TypeScript.

Playwright for E2E tests. Cross-browser, fast, and the auto-waiting means fewer flaky tests.

pytest on the backend. Simple, powerful, and the fixture system is genuinely great.

The Philosophy

I pick tools that:

  1. Have strong ecosystems — good docs, active community, not going to disappear next year
  2. Compose well — I can swap one piece without rewriting everything
  3. Reduce decisions — conventions over configuration where possible
  4. Are boring where it counts — PostgreSQL over the new hotness, every time

The flashy stuff is fun for portfolios. Production code should be boring, predictable, and maintainable.

Speaking of flashy portfolios — you’re reading this on one built with Comic Sans and neon pink. But the deployment pipeline uses GitHub webhooks and auto-deploys to a DigitalOcean droplet. Even the joke has real infrastructure behind it.


Got opinions about my stack? Disagree violently? That’s what the guestbook is for. (It’s under construction. Since 2026.)