Ali Abdiyev is a software engineer who specializes in backend architecture using Node.js and NestJS, building high-load systems for fintech and data-intensive products. We talked about modular design, event-driven patterns, and keeping codebases healthy as teams grow.
You talk about NestJS a lot. When did it become your go-to framework?
When I first switched from classic Node.js applications to NestJS, it felt like… I don’t know, switching from just writing code to actually engineering something? NestJS gives you structure from day one – modules, dependency injection, clear separation of controllers, services, data access.
For simple projects you can stay close to the framework defaults and move really quickly. For complex systems you lean on the same concepts but introduce domain boundaries, shared libraries, background workers. The important thing is the framework gives you a common language. When someone joins a NestJS project they can usually navigate the codebase within a few hours.
How do you approach architecture when you know the system needs to handle high load?
I start with the domain, not the framework. Before we create the first controller we try to understand which parts of the system change often, which parts are stable, which flows are most sensitive to latency or failure.
Then we translate that into modules. Critical domains become separate modules with their own services, data mappers, integration points. In many cases they become separate deployable services later. Less critical parts can stay inside the main application for a while.
For high load I put a lot of emphasis on read and write paths. You want controllers very thin, move logic into services where you can cache results, use queues, apply patterns like bulk updates. NestJS works really well with tools like Redis, PostgreSQL, message brokers, so you can keep the framework code clean while pushing heavy lifting into the right layer.
You mentioned Redis, PostgreSQL, brokers – how do you decide when to bring these in?
PostgreSQL is almost always the default for structured data in my projects. It’s reliable, well understood, works nicely with tools like Prisma.
Redis enters when we see repeated reads of similar data or expensive calculations. For example, in one trading system I worked on, we cached precomputed strategy data and user permissions. This cut API response time by around a third and removed pressure from the database during peak hours.
Message brokers like Kafka or RabbitMQ become important when you see lots of cross-service communication or background work. A typical pattern is accept a request in the NestJS API, validate it, write a compact record, then publish an event. Other services react to send emails, update analytics, talk to external providers. Keeps the main request fast and isolates failures.
A lot of teams are moving from monolithic Node.js apps to microservices. How does NestJS help with that?
The biggest issue I see is teams trying to jump straight from a messy monolith to a messy collection of services. NestJS encourages you to think in modules first. If you design modules with clear boundaries, moving some into separate services later becomes a technical detail, not a full rewrite.
In practice, we start with a modular monolith. We keep strict rules about dependencies between modules, avoid shared state. Once we understand the load and communication patterns, we can extract a module into its own NestJS service with its own database or schema, its own deployment. Because both sides use the same framework, the learning curve for the team is small.
Performance tuning is always tricky. What techniques do you find most effective?
I try to address it at three levels.
At the code level we use interceptors and guards to centralize logging, rate limiting, access control. Keeps controllers simple, helps avoid duplication. Also gives us a single place to measure timings and track slow paths.
At the data level we pay a lot of attention to indexes, query plans, connection management. It’s common to gain more from one good index than from hours of micro-optimizations in TypeScript. Tools like Prisma make it easier to reason about queries, but you still need to look at what’s actually happening in PostgreSQL.
At the system level we rely on caching, queues, horizontal scaling. NestJS is friendly to stateless services, so you can scale them behind a load balancer without major changes. The important part is knowing which endpoints must stay fast under any conditions and designing them with a hard budget in mind.
You’ve led migrations from plain Node.js to NestJS. What are the most common mistakes?
First mistake is trying to wrap the old project in NestJS without changing the structure. Teams move files into controllers and services but keep all the old coupling and hidden dependencies. The result is a project that uses NestJS syntax but doesn’t benefit from its ideas.
Second mistake is creating too many modules too early. Every module has a cost. If you split the system into tiny pieces from the beginning, you’ll spend more time on wiring than on logic. I prefer starting with a few coarse modules and splitting them only when there’s a clear reason.
Third mistake is underestimating tests. When you change architecture you need a safety net. NestJS testing utilities help a lot, but you still need to invest time to cover critical flows before you move code around.
You also do a lot of mentoring. How does that influence your architecture work?
Mentoring forces you to explain decisions in simple terms. In the Beyond Boundaries Mentorship Programme I’ve done… dozens of sessions, I think, with engineers taking their first steps in backend development. When you see how they struggle with concepts like dependency injection or domain boundaries, you learn to design APIs and modules that are easier to reason about.
Same with being a lead judge at hackathons. When you review many different projects in a short time, clear structure becomes really important. A project with modest features but clean architecture is usually more promising than a complex one nobody can understand after a week.
This constant need to explain architecture shapes how I design it. I try to build systems another engineer can understand quickly, because that’s the foundation of scaling a team.
You built Git Axiom and contribute to hackathons and mentorship. How does that connect to your NestJS work?
Git Axiom is a small CLI tool that automates commit messages and branch names using LLMs. I created it because I saw how much time teams lose on naming things and how inconsistent history makes debugging harder. The same attention to structure appears in my NestJS work. Good commit history and clear architecture both serve the same purpose – they make it easier for people to understand what’s going on.
Hackathons and mentorship give me a way to test ideas in different contexts. When I help teams design services during a hackathon, I see which patterns work even under pressure and which cause confusion. Later I bring those lessons back to my own projects.
What would you tell engineers who want to specialize in backend architecture with NestJS?
First, invest in fundamentals. Learn HTTP, SQL, concurrency, basic distributed systems patterns. NestJS will change over time, but these foundations stay.
Second, treat NestJS as a toolbox, not magic. Understand how modules, dependency injection, providers actually work. Read the official documentation and, when possible, the source code.
Third, practice on real problems. Build a small system with authentication, payments, background jobs, monitoring. You’ll learn more from one well-finished project than from many tutorials.
And finally, don’t work in isolation. Share your code, ask for reviews, mentor others when you can. Architecture isn’t only about code – it’s about the way people understand and extend the system together.