Performance work is most effective when it begins with measurement rather than assumptions.
I’ve worked on production systems where meaningful performance improvements came not from replacing the technology stack, but from understanding where the application was actually spending its time.
In one large business application, performance optimisation and database indexing contributed to a 60% reduction in system response time.
The experience reinforced an important principle:
Don’t optimise what you haven’t measured.
Start With the Request
When an ASP.NET application feels slow, I start by looking at the complete request path.
A request may involve:
- middleware
- authentication
- business logic
- database queries
- external APIs
- file operations
- serialization
- network latency
- frontend requests
Any of those can become the bottleneck.
Changing application code before identifying where the time is being spent can create complexity without improving performance.
The Database Is Often the First Place to Look
Business applications frequently spend a significant amount of their request time waiting for the database.
SQL Server performance can be affected by:
- missing indexes
- poor query patterns
- large result sets
- unnecessary joins
- repeated queries
- blocking
- incorrect data types
- inefficient pagination
- and queries that retrieve far more information than the application actually needs.
A small improvement in a frequently executed query can sometimes have a larger impact than a significant application-level refactor.
Indexes Need to Support Real Queries
Indexes are powerful, but simply adding more indexes isn’t a performance strategy.
An index should support the way the application actually queries the data.
That means examining:
WHEREconditionsJOINcolumns- sorting
- grouping
- query frequency
- selectivity
- execution plans
Indexes also have a cost.
They consume storage and can increase the cost of writes.
The objective is not maximum indexing.
It’s appropriate indexing.
Avoid Loading Data You Don’t Need
ORMs make data access productive, but convenience can make inefficient queries easy to write.
If a page needs five fields, loading a complete object graph containing dozens of columns and relationships is unnecessary.
Projection can make a significant difference.
Retrieve the data required for the operation rather than automatically loading everything associated with an entity.
Watch for N+1 Queries
N+1 query problems can be easy to miss during development because they may perform adequately with small datasets.
As data grows, they become expensive.
A page that appears to execute one operation may actually generate dozens or hundreds of database queries.
SQL profiling and application telemetry can make these patterns visible.
Once identified, the data-access strategy can usually be improved significantly.
Pagination Matters
Large datasets should rarely be loaded in full simply to display a small portion of them.
Pagination reduces:
- database work
- network transfer
- application memory usage
- serialization cost
- browser rendering
But pagination also needs to be designed carefully.
As datasets grow, different pagination strategies can behave very differently.
Async Doesn’t Automatically Mean Fast
ASP.NET Core makes asynchronous programming straightforward, particularly for I/O-bound work.
Async operations can improve scalability by avoiding blocked threads while waiting for databases, APIs or other external resources.
But adding async everywhere doesn’t fix an inefficient query or a slow external dependency.
Concurrency and performance are related, but they aren’t the same thing.
External APIs Need Boundaries
A page that depends synchronously on several external services inherits the latency and reliability of every one of them.
Depending on the application, useful strategies can include:
- timeouts
- caching
- background processing
- retry policies
- circuit-breaking
- queue-based workflows
The right approach depends on how current the information needs to be and what should happen when the external system is unavailable.
Cache the Right Things
Caching can dramatically improve performance when applied to data that is:
- expensive to calculate
- frequently requested
- relatively stable
But caching everything introduces its own problems.
The difficult part of caching isn’t storing data.
It’s deciding when that data is no longer valid.
A caching strategy therefore needs an invalidation strategy.
Measure Again
After making an optimisation, measure the system again.
- Did response time actually improve?
- Did database load decrease?
- Did another bottleneck become visible?
- Did memory consumption increase?
- Did the optimisation affect correctness?
Performance work should be iterative:
- measure
- identify
- change
- verify
- repeat
Performance Is a System Property
ASP.NET Core is only one part of application performance.
SQL Server is only one part.
Real performance emerges from the interaction between:
- application code
- database design
- network behaviour
- external services
- infrastructure
- caching
- frontend behaviour
- and actual usage patterns.
The most effective optimisation work comes from understanding that complete system rather than trying to make one layer individually fast.