40 questions for .NET Technical Lead interviews — 26 medium, 14 hard.
About the .NET Technical Lead role
A .NET technical lead guides a team of developers through planning, design and delivery. They break features into tasks, define API contracts, decide on architecture such as modular monolith or microservices, manage technical debt and coordinate with clients and managers. They remain hands-on with critical code, set up CI/CD and cloud deployment on Azure, and are accountable for quality and timelines.
Architecture decisions Team leadership Azure deployment Estimation and planning Stakeholder communication
Solved for this roleStart practicing
3 QUESTIONS FREESign up to unlock the full question bank and track your progress.Unlock every question and answer, then continue practising company-wise and package-wise.
✓ 3 questions free • Unlock the rest in seconds
01
What are signals in Angular and why were they introduced?
Signals are reactive values: count = signal(0) holds state, computed() derives values and effect() runs side effects when dependencies change. Angular knows exactly which parts of the view read which signal, so it updates only those parts instead of checking the whole component tree. This enables zoneless change detection, which became the default for new apps in Angular 21, and gives simpler state handling than RxJS for local UI state.
Authorization filters run first and can block a request. Resource filters run before model binding, useful for caching. Action filters run before and after an action, for logging or validation. Exception filters handle errors thrown by actions. Result filters run around the result execution, for example to add headers. Filters apply globally, per controller or per action. Unlike middleware, they can access MVC details like action arguments.
03
What are services and dependency injection in Angular?
A service is a class, marked @Injectable, that holds logic or data shared across components, such as API calls, authentication or a cart. Angular's DI creates the service and provides it to components that request it in the constructor or with the inject() function. With providedIn: 'root', one shared instance exists for the whole app. This keeps components focused on UI and makes logic easy to test.
04
A PaymentService has a large if-else on payment type (card, UPI, netbanking, wallet) and every new method means editing it. How do you refactor?
Apply the Strategy pattern with the Open/Closed principle. Define IPaymentProcessor with a Type property and ProcessAsync method, and create one class per method. Register all of them in DI, inject IEnumerable<IPaymentProcessor>, and pick the right one by type, possibly through a dictionary. Adding a new payment type is now a new class plus a registration, with no change to existing, tested code.
05
Which design patterns do you commonly see in ASP.NET Core projects?
Dependency Injection is built in. The Repository and Unit of Work patterns wrap data access, although DbContext already implements them. Factory creates objects by type, for example IHttpClientFactory. Strategy swaps algorithms, such as different tax or discount rules, behind one interface. Options pattern binds configuration to classes. Middleware is a Chain of Responsibility, and Mediator libraries implement CQRS by sending commands to handlers.
06
How do you manage application state in Angular and React apps?
Start simple: local component state, then share through a parent or a service. In Angular, a service holding signals covers most needs, and NgRx or NgRx SignalStore suits large apps with complex flows. In React, Context works for low-frequency data like theme or user, Redux Toolkit or Zustand for complex client state, and TanStack Query for server data with caching. Keep server data and UI state separate.
07
Give an example of a Liskov Substitution Principle violation and how to fix it.
A classic case is Square inheriting Rectangle. Setting Width on a square also changes Height, so code that sets width 5 and height 4 and expects area 20 breaks. Another real case is a ReadOnlyRepository that inherits Repository and throws NotSupportedException on Save. The fix is to model shared behaviour with interfaces such as IShape or IReadRepository and IWriteRepository, so subclasses never weaken the contract.
08
How do you explain good object-oriented design to a junior developer on your team?
I avoid theory first. I take a real piece of our code, like a controller that talks to the database directly, and pair with them to move logic into a service behind an interface, then write a unit test with a mock. Seeing how much easier testing becomes explains SOLID better than slides. I then review their next pull request with specific comments and point them to one or two good examples in our codebase.
09
You must change an API response format, but an older mobile app version still calls it. How do you avoid breaking those users?
Versioning lets you change contracts without breaking existing clients such as mobile apps that users have not updated. Common approaches are URL segments (/api/v1/orders), query strings (?api-version=2.0) and headers. The Asp.Versioning library supports all of them and integrates with OpenAPI docs. URL versioning is the most visible and cache-friendly. Keep old versions running for a published period, then deprecate them with headers.
10
An API creates a new HttpClient inside a using block for every outgoing call, and under load you see socket exhaustion errors. How do you fix it?
Disposing HttpClient does not immediately free the underlying socket; connections stay in TIME_WAIT, so thousands of short-lived clients exhaust ports. A single static client fixes that but misses DNS changes. The recommended fix is IHttpClientFactory: register builder.Services.AddHttpClient<PaymentClient>() and inject the typed client. The factory pools and rotates handlers, and you can add resilience with Microsoft.Extensions.Http.Resilience for retries and timeouts.
11
You need to add a new method to a widely implemented interface without breaking existing classes. How can you do it?
Since C# 8, an interface can provide a method body. Classes implementing the interface get that behaviour without writing it, though they can override it. The main use is evolving a published interface: you can add a new member without breaking every existing implementation. The default method is only callable through the interface type, not the class, and overusing it blurs the line with abstract classes.
An event is a delegate wrapped with restricted access. Outside the class, subscribers can only add or remove handlers with += and -=; they cannot invoke the event or overwrite the whole list with =. This protects the publisher. The standard pattern uses EventHandler<TEventArgs> with sender and args. Remember to unsubscribe long-lived subscriptions, otherwise the publisher keeps subscribers alive and causes memory leaks.
13
Write an Angular HTTP interceptor that adds a JWT to every API request.
public class TimingMiddleware(RequestDelegate next, ILogger<TimingMiddleware> logger)
{
public async Task InvokeAsync(HttpContext context)
{
var sw = Stopwatch.StartNew();
await next(context);
sw.Stop();
logger.LogInformation("{Method} {Path} -> {Status} in {Ms} ms",
context.Request.Method, context.Request.Path,
context.Response.StatusCode, sw.ElapsedMilliseconds);
}
}
// Program.cs
app.UseMiddleware<TimingMiddleware>();
Code before next runs on the request, code after it runs on the response.
15
A large Angular dashboard becomes sluggish because change detection runs constantly. How do OnPush and zoneless mode help?
Traditionally Zone.js patched browser events and timers, and after each one Angular checked every component for changes, top to bottom. OnPush makes a component re-check only when its inputs change by reference, an event occurs inside it, an async pipe emits or a signal it reads changes, which saves work in big screens. Zoneless mode removes Zone.js entirely and relies on signals and explicit triggers, reducing bundle size and improving performance.
16
Fetch products from an ASP.NET Core API in React with loading and error states.
Make the API stateless so you can scale it horizontally behind a load balancer or Kubernetes autoscaler. Cache read-heavy data such as product details in Redis or HybridCache. Ensure all I/O is async and database queries are indexed, projected and paged. Move slow work like emails to queues. Add rate limiting, timeouts and circuit breakers to dependencies, then load test with k6 or JMeter and watch metrics.
18
How do you handle requirements that keep changing during a sprint?
In one sprint (Situation) the product owner changed a checkout screen twice. My task was to deliver without losing quality. I asked for a quick meeting to understand the reason, split the work into a stable API part and a flexible UI part, and agreed with the team which change would move to the next sprint. Result: we shipped the core flow on time, and the team started confirming acceptance criteria before sprint planning.
19
Under load, an API's response time jumps to many seconds while CPU stays low. Thread count keeps rising. What is likely happening?
This pattern points to thread pool starvation. Some code is blocking threads with .Result, .Wait(), Thread.Sleep or synchronous I/O, so pool threads are stuck waiting and the pool injects new threads slowly. Confirm with dotnet-counters, watching thread pool queue length and thread count, and a dump showing many threads blocked in Wait. Fix by making the call chain fully async; raising minimum threads is only a temporary patch.
Func<decimal, decimal> addGst = price => price * 1.18m;
Action<string> log = msg => Console.WriteLine($"[LOG] {msg}");
Predicate<int> isAdult = age => age >= 18;
decimal total = addGst(1000m); // 1180
log($"Total {total}");
bool ok = isAdult(21); // true
Func returns a value (last type argument is the return type), Action returns void, and Predicate<T> returns bool. They save you from declaring custom delegate types.
21
Access tokens in your app expire in 15 minutes and users complain about being logged out. How do you implement refresh tokens securely?
Issue a short-lived JWT access token plus a long-lived, random refresh token stored hashed in the database with expiry and device info. When the access token expires, the client calls /refresh with the refresh token; the server validates it, revokes it and issues a new pair, which is rotation. Reuse of a revoked token signals theft, so revoke the whole chain. In browsers, store the refresh token in an HttpOnly, Secure, SameSite cookie.
22
An admin page renders 20,000 rows in a table and the browser freezes. How do you fix it?
Do not send or render everything at once. Add server-side paging, sorting and filtering in the ASP.NET Core API so the page loads perhaps 50 rows at a time. If users truly need to scroll a long list, use virtual scrolling, such as Angular CDK virtual scroll or TanStack Virtual in React, so only visible rows exist in the DOM. Also use trackBy or keys, and debounce search inputs.
23
Users report intermittent errors saying their transaction was chosen as a deadlock victim. How do you investigate and fix it?
A deadlock happens when two sessions each hold a lock the other needs. Get the deadlock graph from the system_health Extended Events session to see the statements and objects involved. Common fixes: access tables in the same order in all code paths, keep transactions short, add indexes so queries lock fewer rows, and consider RCSI. Also add retry logic in the app, since EF Core's SQL Server execution strategy can retry deadlocks.
24
A query that used to run in 1 second now takes 30 seconds. How do you troubleshoot it?
Capture the actual execution plan and look for table or index scans on large tables, key lookups, big differences between estimated and actual rows, and warnings like implicit conversions or spills to tempdb. Check missing or fragmented indexes and outdated statistics, and update them. Use Query Store to compare the old and new plans; if a plan regressed, you can force the good plan while fixing the root cause.
25
Your security team objects to storing the JWT in localStorage in your Angular app. What are the options?
localStorage is readable by any JavaScript on the page, so an XSS bug can steal the token. A safer pattern is to keep the short-lived access token in memory and store the refresh token in an HttpOnly, Secure, SameSite cookie that scripts cannot read. Even better is the backend-for-frontend pattern, where ASP.NET Core holds tokens server-side and the browser uses a session cookie. Add CSRF protection when using cookies.
26
Show how to bind configuration from appsettings.json using the Options pattern.
// appsettings.json
// "Smtp": { "Host": "smtp.example.com", "Port": 587 }
public class SmtpOptions
{
public string Host { get; set; } = "";
public int Port { get; set; }
}
builder.Services.Configure<SmtpOptions>(builder.Configuration.GetSection("Smtp"));
public class EmailService(IOptions<SmtpOptions> options)
{
private readonly SmtpOptions _smtp = options.Value;
}
appsettings.{Environment}.json and environment variables override base values. Use IOptionsMonitor to pick up changes without restart.
27
What is the difference between controllers and Minimal APIs?
Controllers group actions into classes with attributes for routing, filters and model binding, which suits large APIs with many conventions. Minimal APIs map endpoints directly with app.MapGet and lambdas, with less ceremony and slightly better performance, ideal for microservices and small APIs. Since .NET 7 they support filters, route groups and OpenAPI, and .NET 10 adds built-in validation, so both are production-ready choices.
28
A singleton CacheService injects AppDbContext, and users start seeing stale data and occasional 'a second operation was started on this context' errors. What is wrong?
This is a captive dependency: a scoped DbContext is captured by a singleton, so one context lives forever and is shared by all requests across threads. DbContext is not thread-safe and its change tracker grows stale. Fix it by injecting IServiceScopeFactory or IDbContextFactory<AppDbContext> into the singleton and creating a short-lived context per operation. Enabling ValidateScopes in development catches this at startup.
29
Show an example of the Dependency Inversion Principle in C#.
public interface INotifier { Task SendAsync(string to, string msg); }
public class SmsNotifier : INotifier
{
public Task SendAsync(string to, string msg) => Task.CompletedTask; // call SMS API
}
public class OrderService(INotifier notifier)
{
public Task ConfirmAsync(Order o) => notifier.SendAsync(o.Phone, "Order placed");
}
// Program.cs
builder.Services.AddScoped<INotifier, SmsNotifier>();
OrderService depends on the abstraction, so you can swap to email or a mock without changing it.
30
What is the difference between a static class and a singleton?
A static class cannot be instantiated, cannot implement interfaces and is initialised by the runtime; it suits stateless helpers like Math. A singleton is a normal class with exactly one instance, so it can implement interfaces, be passed as a parameter and be mocked in tests. In ASP.NET Core, you rarely write a singleton by hand; you register the class with AddSingleton and let the DI container manage its lifetime.
31
Explain covariance and contravariance in C# generics.
Covariance, marked out T, lets you use a more derived type where a base type is expected: IEnumerable<string> can be assigned to IEnumerable<object> because the interface only returns T. Contravariance, marked in T, allows the reverse for inputs: an Action<object> can be used as Action<string>. They apply only to interfaces and delegates, and only with reference types. List<T> is invariant because it both reads and writes T.
32
Describe an API you designed end to end and the decisions you made.
In my last project (Situation) we needed a leave-management API for 3,000 employees. My task was the full design. I modelled resources like /employees/{id}/leaves, used DTOs separate from EF entities, JWT with role policies for managers, FluentValidation, ProblemDetails errors and paging on every list. I documented it in OpenAPI and agreed the contract with the Angular team first. Result: front and back ends were built in parallel and launched on time.
33
What are useMemo and useCallback used for in React?
useMemo caches the result of an expensive calculation and recalculates only when its dependencies change, such as filtering 10,000 rows. useCallback caches a function reference so that a child wrapped in React.memo does not re-render just because a new function was created. Both are optimisations and add complexity, so use them where profiling shows a problem. The React Compiler, stable since late 2025, can apply much of this memoisation automatically.
34
Your product API runs on four instances behind a load balancer and needs caching. Which ASP.NET Core caching options would you consider?
IMemoryCache stores data in the server's memory, fast but not shared across instances. IDistributedCache uses Redis or SQL Server so all instances share the cache. HybridCache, added in .NET 9, combines an in-memory layer with a distributed one and prevents cache stampedes. Output caching stores whole HTTP responses on the server, while response caching sets HTTP headers for browsers and proxies. Always set expiry and invalidate when data changes.
35
An API endpoint calls _db.Orders.ToList().Where(o => o.CustomerId == id) and is slow as the table grows. Explain and fix it.
ToList executes immediately, so the entire Orders table is loaded into memory and then filtered in C#. As data grows, memory and response time grow with it. Move the filter before materialisation: await _db.Orders.Where(o => o.CustomerId == id).AsNoTracking().ToListAsync(). Now EF Core sends a WHERE clause, the database uses an index on CustomerId, and only matching rows travel over the network.
36
Tell me about a time you led a team to deliver a complex feature.
As tech lead (Situation) I owned a vendor-payments module touching Angular, three APIs and SQL Server. My task was to deliver it in six weeks with four developers. I broke it into vertical slices, defined API contracts first, set up feature flags and reviewed every pull request within a day. When one developer struggled with EF Core, I paired with them. Result: we released on time with no critical bugs and two juniors grew into module owners.
37
Add rate limiting to a login endpoint in ASP.NET Core.
Deep hierarchies are fragile: a change in a base class ripples into every child, and behaviours that do not fit the tree get duplicated. I would suggest composition over inheritance. Keep a single Employee entity and give it composed parts such as a Role, an ApprovalPolicy and a list of Permissions, injected as interfaces. New behaviour then means a new policy class, not a new subclass, which is easier to test.
39
Your Angular app's first load is slow on mobile because the main bundle is very large. How does lazy loading of routes help?
Lazy loading splits the app into chunks that download only when the user navigates to that route, so the initial bundle is smaller and the first page loads faster, especially on mobile data. In Angular use loadComponent or loadChildren with a dynamic import in the route config; in React use React.lazy with Suspense or the router's lazy option. Angular's @defer blocks can also delay heavy parts of a page.
40
Explain the access modifiers in C#, including protected internal and private protected.
public is accessible everywhere, private only inside the type, protected inside the type and derived types, and internal anywhere in the same assembly. protected internal means accessible from the same assembly or from derived types in any assembly. private protected is stricter: only derived types inside the same assembly. Class members default to private and top-level types default to internal. Use the narrowest modifier that works.
Frequently asked questions
What does a .NET Technical Lead do?
A .NET technical lead guides a team of developers through planning, design and delivery. They break features into tasks, define API contracts, decide on architecture such as modular monolith or microservices, manage technical debt and coordinate with clients and managers. They remain hands-on with critical code, set up CI/CD and cloud deployment on Azure, and are accountable for quality and timelines.
Which skills are tested in a .NET Technical Lead interview?
Expect questions on Architecture decisions, Team leadership, Azure deployment, Estimation and planning, Stakeholder communication. Most rounds also ask you to explain a project and solve one practical problem live.
How do I prepare for a .NET Technical Lead interview?
Revise ASP.NET Core Web API, OOP in C#, Angular and React Front End first using the topic sheets, practise the questions on this page aloud, then do one full mock interview to practise timing.