After 5.5 years building production platforms across e-commerce, transportation, trading, CRM and personalization, I've learned something counterintuitive: the tech stack changes every time, but the problems stay remarkably the same. Whether you're tracking a ride in real-time or updating a trading position, whether you're managing inventory or personalizing content, you're solving variations of three core challenges. That's the lesson that matters.
Every domain needed reliable data, clear APIs and good UX
My first instinct when joining a new team was to study the tech stack. What database? What framework? What message broker? It felt important. It wasn't.
What actually mattered was recognizing that every product—regardless of industry—had the same three dependencies. The business couldn't move without reliable data flowing through the system. The frontend and backend couldn't coexist without clear contracts between them. And none of it mattered if users couldn't understand what was happening.
In e-commerce, reliable data meant inventory counts that didn't oversell. A clear API meant the cart service and order service could work independently without creating race conditions. Good UX meant showing real-time stock status so customers didn't add items to carts that would fail at checkout.
In ride-hailing, reliable data meant knowing exactly where each driver was and which orders were assigned. A clear API meant the mobile app, dispatcher, and payment system could all fetch truth from a single source. Good UX meant the customer seeing their driver's location actually matched where the driver was, and the 30-second latency from polling didn't cause a 5-minute time jump.
The names changed. The problem was identical.
This taught me to stop asking "what's the best way to implement this in NestJS?" and start asking "what's the contract this service needs to expose?" The answer to the second question doesn't change when you switch from .NET to Go. The answer to the first question is always context-dependent.
// Bad: thinking about implementation first
async function getOrderStatus(orderId: string): Promise<OrderStatus> {
const order = await db.orders.findById(orderId);
const shipment = await db.shipments.findByOrderId(orderId);
const payment = await db.payments.findByOrderId(orderId);
return {
status: order.status,
shipment: shipment?.status,
payment: payment?.status
};
}
// Better: thinking about the contract first
// Clients don't care if you fetch from cache, DB, or microservices
async function getOrderStatus(orderId: string): Promise<{
current: OrderState;
lastUpdated: Date;
canCancel: boolean;
estimatedDelivery?: Date;
}> {
// Implementation details are now invisible
// The contract is what matters
}
Patterns from ride tracking reappeared in trading screens
One afternoon, I was debugging a WebSocket layer for a trading platform. Traders needed to see price updates with sub-100ms latency. The feed was dropping messages during high-volume market moves. The solution was classical: acknowledgment-based windowing, backpressure handling, and reconnection logic with exponential backoff.
I realized I'd solved this exact problem three years earlier in ride-hailing. Drivers needed real-time location updates, passengers needed to see driver positions, and during surge pricing events, the WebSocket layer would collapse under message volume. The business impact was different—traders losing a price tick versus passengers seeing stale driver location—but the system was the same.
That pattern showed up again in e-commerce during flash sales, in CRM when syncing large contact lists, and in personalization engines during recommendation recalculation. High-volume, real-time data flow with strict latency requirements and the need for graceful degradation.
Once you've seen the pattern, you recognize it instantly. You stop reinventing solutions. More importantly, you know which trade-offs to make without running expensive experiments.
// The pattern: acknowledgment-based flow control
class ReliableDataStream<T> {
private queue: T[] = [];
private inFlight = 0;
private maxInFlight = 100;
send(item: T, onAck: () => void): void {
if (this.inFlight >= this.maxInFlight) {
this.queue.push(item);
return;
}
this.inFlight++;
this.ws.send(JSON.stringify(item));
// Once client acknowledges, process next batch
this.ws.once('ack', () => {
this.inFlight--;
onAck();
if (this.queue.length > 0) {
this.send(this.queue.shift()!, onAck);
}
});
}
}
// Used in ride-hailing for driver locations
// Used in trading for price updates
// Used in e-commerce for inventory changes
// Same pattern. Different domain.
Breadth across industries gave me a pattern library that I could apply immediately. It wasn't about memorizing solutions. It was about training my brain to spot the structural similarity under the surface-level differences.
Breadth taught me to spot the real problem faster
When a frontend engineer complained that an API was "too slow," my first question used to be "have you profiled it?" That was lazy. Now I ask "which use case is slow, and what does the user see?"
In an e-commerce platform, slow product listing calls could mean abandoned shopping sessions. In a trading platform, slow price updates could mean executed trades at worse prices. In ride-hailing, slow driver assignment could mean driver churn. Same latency problem. Wildly different business impact.
Working across industries taught me that the real problem is rarely what people describe. Someone says "our API is slow." What they mean is one of these:
- Users are leaving before completing transactions.
- Downstream services are timing out and retrying, amplifying load.
- Database connections are leaking and we're exhausting the pool.
- We're doing N+1 queries and nobody measured it.
- Latency has high variance and tail percentiles matter more than averages.
The diagnostic questions stayed consistent. But the urgency and impact changed by industry, which forced me to learn how to dig deeper faster. Was this a "fix it when you have time" problem or a "this is draining revenue right now" problem?
Exposure to multiple businesses meant I couldn't hide behind technology. I had to understand the business metrics tied to the problem. In tourism, a slow hotel search might cost bookings. In personalization, a slow recommendation engine might tank engagement metrics. Same system, different consequence.
Depth in fundamentals made every new domain easier
None of this breadth would have been useful without depth in fundamentals. When I switched stacks—from .NET to NestJS to React Native—the new tools felt foreign for about two weeks. Then the fundamentals took over.
Event sourcing works the same way whether you're using C# or TypeScript. Database normalization is the same principle whether you're using PostgreSQL or any RDBMS. Asynchronous task processing via message queues operates on the same concepts whether you're using Kafka, RabbitMQ, or AWS SQS. The pattern is the pattern.
This is why I could move between industries relatively quickly. I wasn't learning new concepts in each domain. I was learning new domain language and new business constraints. The technical foundations were stable.
A junior engineer might see that progression as "jumping around too much." A senior engineer sees it as "built core competencies that transfer across every context." The difference is whether you're learning tools or learning principles.
// Fundamental principle: separation of concerns
// Works whether you're building e-commerce, ride-hailing, or trading
interface DataAccessLayer {
getEntity(id: string): Promise<Entity>;
saveEntity(entity: Entity): Promise<void>;
}
interface BusinessLogicLayer {
processOrder(order: Order): Promise<Result>; // e-commerce
assignRide(rideRequest: Request): Promise<Result>; // ride-hailing
executeTrade(trade: Trade): Promise<Result>; // trading
}
interface PresentationLayer {
renderOrderStatus(): JSX.Element; // e-commerce UI
renderDriverLocation(): JSX.Element; // ride-hailing UI
renderTradeConfirmation(): JSX.Element; // trading UI
}
// The principle is the same. The domain logic is different.
// Once you've applied this principle a few times, new domains become learnable.
Common mistakes
- Treating breadth as a resume builder instead of a learning opportunity. Jumping between companies just to add industries to your LinkedIn looks good, but if you don't actively identify the patterns, you're just collecting job titles. Take time between roles to reflect on what problems appeared repeatedly.
- Assuming a new domain requires completely new knowledge. This kills momentum. Most backend engineers could competently join a ride-hailing, trading, or e-commerce team within two weeks. Not because they know those domains, but because they know fundamentals. If you catch yourself thinking "I need to learn everything from scratch," you're not thinking structurally.
- Optimizing the stack before clarifying the contract. Choosing between PostgreSQL and MongoDB matters way less than deciding what your API promises to clients. Get the contract right first. The database is implementation.
- Confusing pattern recognition with domain expertise. Recognizing that a ride-hailing tracking problem is similar to a trading price-feed problem doesn't make you a trading domain expert. It makes you faster at implementing the similar parts. Respect what you don't know about the business logic.
Key takeaways
- Every product needs reliable data, clear APIs, and good UX. The industry doesn't matter. These three pillars do.
- Patterns repeat across industries. Once you've solved distributed message delivery, you've solved it regardless of whether you're tracking drivers or prices.
- Business impact determines urgency, not technology. Learn to ask "what does the user actually see?" instead of just "how do I optimize this?"
- Fundamentals are your portable skill. Event sourcing, normalization, asynchronous processing, caching strategies—these are investments that compound across every domain you enter.
- Breadth without depth is resume padding. Depth without breadth is narrow expertise. Combine them intentionally—learn new domains while reinforcing core principles.






Comments
Loading comments…