Ten years of building: the patterns that keep repeating
SMB commerce, an e-commerce marketplace, a travel platform, an AI startup, cloud infrastructure — different domains, the same lessons. A map of everything I've written, and what I'd tell my 2015 self.
I started my career in 2015 building REST APIs for an SMB commerce platform: orders, inventory, billing, purchase orders. Since then I’ve worked on checkout, wallet and refund services at an e-commerce marketplace, an email platform sending around 20 million personalized emails a day at a travel company, a feature-store platform at an AI startup, and large-scale cloud infrastructure.
Different domains, different stacks. The same lessons kept coming back. This post collects them, and links to everything I’ve written about each.
Change running systems one reversible step at a time
Splitting monoliths, moving data between systems, upgrading platforms underneath live traffic: the approach that works is a series of small, reversible steps. Doing it in one go is where these projects get into trouble.
- Zero-downtime schema and data migrations with expand–contract
- The strangler fig in practice: carving a service out of a monolith
- Durable workflows vs. a queue and a cron job
- Model business workflows as state machines
- Choose a database by access pattern, not by hype
Assume the network will lie to you
Distributed systems fail partially: a request times out after it succeeded, a message arrives twice, a dependency slows down instead of failing. Designing for that up front is far cheaper than discovering it in production.
- Idempotency keys: making retries safe for money
- Retries without a retry storm
- The transactional outbox
- Kafka consumers in production
The non-code half of senior engineering
The further I’ve gone, the more of my impact has come from things other than code: how incidents are handled, how decisions get made and recorded, which debt gets paid, and whether my team is a queue or a platform.
- Debugging production: a method, not a hunch
- Design docs that get approved
- Tech debt as a portfolio
- Self-serve platforms: turn your team from a queue into a product
- AI-assisted engineering: what actually stuck
Built in the open
Most production work lives behind NDAs, so I try ideas out in public projects too:
- Building an annotation-driven MCP server in Java
- GraphQL Federation with Java subgraphs, without the N+1
- One dependency for logs, traces and metrics: writing a Spring Boot starter
- YT Studio AI: wiring Vision, NLP and LLMs into a creator tool
- Three small tools, three lessons
What I’d tell my 2015 self
- Read the old code before you judge it. Most “weird” code encodes a lesson someone learned the hard way. Find out which one before deleting it.
- Learn the database properly. Indexes, transactions, isolation levels, locking. A surprising number of scaling problems turn out to be data problems.
- Make failure boring. Timeouts, retries, idempotency, rollbacks. The systems that are calm during incidents are the ones designed around failure from the start.
- Measure before and after. “It’s faster now” convinces nobody; “p99 went from 1.2 s to 300 ms” convinces everyone.
- Automate what you repeat — and then hand the automation to others. The biggest jumps in team capacity I’ve seen came from turning expert-only tasks into safe self-serve tools.
- Write it down. Design docs, decision records, runbooks, and now this blog. Writing is how one engineer’s judgment scales to a team.
If you’re working on similar problems — distributed systems, platform engineering, AI infrastructure — I’d like to hear from you: email me or find me on LinkedIn.