PRODUCT BUILDERJakub KulhanJK
← Skills library

System architecture

Designing how the technical system is structured – what runs where, how components communicate, and how to choose the right infrastructure for the job.

Solution DiscoveryEngineerProduct Builder
System architecture
ACTIVITIES

What you need to do

The practical activities and techniques you need to master – what to practice, what to get good at, and what to keep doing.

Mapping workload types

Every system runs a mix of workloads – request-response (user clicks, API calls), scheduled jobs (daily reports, nightly syncs), background processing (image resizing, email sending), and event streams (order placed → inventory updated → notification sent). Before choosing any technology, list every workload your product needs. Each type has different requirements for latency, reliability, and cost. A product builder who can identify workload types avoids the classic mistake of forcing everything through a single architecture pattern – running a 30-minute report inside a request handler, or polling for events that should trigger automatically.

Designing service-oriented architecture

Structuring your system as a set of services that communicate through well-defined interfaces. The monolith vs. microservices debate is a false dichotomy – real systems always have different workload types (request-response APIs, event consumers, batch jobs) that naturally suggest service boundaries. Start with a modular monolith where internal boundaries are clear, then extract services only when you have a concrete reason: independent scaling, different deployment cadence, or team boundary. The key is clear interfaces between components, whether they run in one process or many.

Deciding what's stateful and what's stateless

Stateless services are easy to scale – spin up more copies, put a load balancer in front, done. Stateful services (databases, caches, WebSocket servers) can't be copied freely – they need coordination. The architectural discipline is pushing state out of your application code and into purpose-built stores: PostgreSQL for durable data, Redis for ephemeral data, S3 for files. When every app server is stateless, deployment is a swap – no draining connections, no session migration, no 'which server has the user's data' questions.

Selecting deployment infrastructure

The deployment platform is an architectural decision, not an ops afterthought. Start simple: a VPS (Virtual Private Server) with Docker Compose handles most early-stage products and gives you full control at low cost. Vercel for Next.js apps with edge functions and preview deployments. Railway or Render for services that need persistent processes, background workers, or cron jobs. Fly.io for latency-sensitive workloads deployed close to users. Cloudflare Workers for edge compute. AWS or GCP when you need full control or specific managed services. The product builder's advantage: you pick the platform that fits the workload instead of building a Kubernetes cluster because 'that's what real companies do.'

Designing for events and queues

Not everything needs to happen in the request. When a user places an order, the response should confirm the order – not wait for inventory to update, email to send, and analytics to record. Event-driven architecture decouples these concerns: publish an event ('order.placed'), let independent workers handle the consequences. Tools like BullMQ, Upstash QStash, or cloud pub/sub make this straightforward. The pattern is simple – emit facts about what happened, let subscribers decide what to do. Start with a simple job queue before reaching for a full event bus.

Setting up DNS and domains

DNS is the front door of your architecture. Understand A records (point to IP), CNAME records (point to another domain), MX records (email routing), and TXT records (verification, SPF, DKIM). Use Cloudflare for DNS management – it's fast, free, and gives you CDN and DDoS protection. Set up your domain structure early: apex domain for the marketing site, subdomains for the app, API, docs. Get SSL certificates automated (Let's Encrypt or Cloudflare) so you never think about HTTPS again.

Making scalability decisions at the right time

Premature optimization is the root of all evil – but so is ignoring obvious bottlenecks. The product builder's rule: optimize when you have evidence, not when you have anxiety. Instrument before you optimize – you need to know where the bottleneck actually is. Add a cache when you measure slow repeated queries, not when you imagine future load. Add a CDN when your static assets are measurably slow, not because 'best practices' say so. Horizontal scaling (more instances) solves most problems cheaper than vertical optimization (faster code). The architecture that handles 100 users and the one that handles 100,000 users are often the same – with a cache layer and a CDN.

KEY FRAMEWORKS

Frameworks to know

The mental models and structured approaches that guide how you think about this skill – not rules to follow, but lenses to see through.

The twelve-factor app (Heroku)

Twelve principles for building apps that deploy cleanly on modern platforms: one codebase tracked in version control, explicit dependency declaration, config stored in environment variables, backing services as attached resources, strict separation of build and run stages, stateless processes, port binding, concurrency through the process model, disposability (fast startup, graceful shutdown), dev/prod parity, logs as event streams, admin processes as one-off tasks. You don't need to follow all twelve religiously – but understanding them explains why modern platforms (Vercel, Railway, Fly.io) work the way they do.

THE PRODUCT BUILDER WAY

Good vs. bad

Common mistakes paired with what experienced practitioners do instead – and why the product builder approach changes the game.

Microservices from day one
Splitting into services before you know your domain boundaries. You end up with a distributed monolith – all the complexity of microservices with none of the benefits. Every change touches three repos and requires coordinated deployments.
Monolith with clear module boundaries
One deployable unit with well-defined internal modules. Refactor freely, deploy in seconds, debug with a single stack trace. Extract a service only when you have a concrete reason – independent scaling, different deployment cadence, or a team boundary.
Kubernetes for a team of three
Spending weeks configuring clusters, ingress controllers, and Helm charts for an app that serves 500 requests per minute. The infrastructure becomes the product instead of the product being the product.
Managed platforms that match your scale
Vercel, Railway, Render, or Fly.io handle deployment, scaling, and SSL. You deploy with git push. When you genuinely outgrow them – and you'll know because you'll have the metrics – then consider more infrastructure.
Designing for millions of users on day one
Sharding the database, adding a distributed cache, setting up multi-region replication – for a product with 47 active users. Architecture astronautics that delays shipping by months.
Architecture that handles current load plus one order of magnitude
If you have 100 users, architect for 1,000. A single Postgres instance, a CDN for static assets, and stateless app servers will carry you further than most teams expect. Scale when evidence demands it.
No architecture documentation
The system exists only in the heads of people who built it. New team members spend weeks piecing together how things connect. When the original builder leaves, the architecture becomes an archaeological site.
Two diagrams and a folder of ADRs
A C4 Context diagram, a Container diagram, and Architecture Decision Records in the repo. Anyone can understand the system in 15 minutes. Decisions are traceable – you know what was chosen, what was rejected, and why.
Synchronous everything
Every action blocks the user while the system processes all side effects. Order placement waits for inventory update, email send, analytics recording, and webhook delivery. Slow, fragile, and impossible to retry individual steps.
Request-response for the user, events for the consequences
Confirm the order in 200ms. Publish an event. Let independent workers handle inventory, notifications, and analytics asynchronously. Each worker can retry independently, scale independently, and fail without affecting the user.
Choosing technology by hype
Adopting the framework that just hit Hacker News front page. Your architecture becomes a technology showcase instead of a solution to user problems. Every component uses a different stack because it was 'the best choice at the time.'
Boring technology that your team knows
PostgreSQL, Redis, a language your team writes fluently, a framework with good documentation. Innovation tokens are limited – spend them on your product's unique problems, not on infrastructure that thousands of companies have already solved.

System architecture is the skill where product builders have the biggest advantage over traditional role splits. When you understand users, business context, and technology, you make architecture decisions that serve the product – not decisions that serve the technology. I've seen countless over-engineered systems built by architects who never talked to a user and had no idea that the 'critical feature' they were scaling for was used by 12 people.

Most products need embarrassingly simple architecture. A Next.js app on Vercel with a PostgreSQL database covers 80% of SaaS products. Add Redis for caching and BullMQ for background jobs when you need them. That's it. The architecture that lets you ship fast and iterate daily is worth more than the architecture that handles theoretical scale you'll never reach.

The monolith vs. microservices debate is over for small teams. If you have fewer than 50 engineers, start with a monolith. Shopify runs one of the largest e-commerce platforms in the world on a monolith. Basecamp has done it for 20 years. The people advocating microservices for your 5-person startup are solving Google's problems, not yours.

I think of architecture as a series of bets, not a blueprint. Every decision is a trade-off with consequences you'll discover later. ADRs make those bets explicit – when the consequences arrive, you can trace back to why you made the choice and decide whether to double down or change course. Without ADRs, architecture decisions become folklore: 'we use Redis because... I think someone said it was faster?'

The product builder's architecture instinct is different from the specialist architect's. A specialist optimizes for technical elegance. A product builder optimizes for shipping speed, operational simplicity, and the ability to change direction when users tell you something unexpected. The best architecture is the one that lets you deploy a new feature tomorrow morning – not the one that earns you praise in a system design interview.