Why CancellationToken Should Flow Through the System
Asynchroneous Programming · 2026-05-26
A practical look at why cancellation should be treated as part of an operation itself, how CancellationToken propagation affects responsiveness and resource usage, and why incomplete cancellation flow often creates hidden reliability and maintainability problems.
Why CancellationToken Should Flow Through the System
1. Introduction: Cancellation Is Often Added Too Late
Asynchronous programming has become a normal part of modern software development. Web requests, database access, file operations, HTTP calls, queues, cloud services, and background processing all involve work that may take time to complete. As systems become more distributed and responsive, asynchronous operations naturally become more common.
At the same time, many systems treat cancellation as an afterthought.
A method is made asynchronous first. The operation works correctly. The feature is tested under normal conditions. Only later does someone notice that requests continue running after the user closes the page, background operations continue after a shutdown begins, or expensive database queries continue executing even though the result is no longer needed.
At that point, a CancellationToken parameter is often added to a few methods near the surface of the system. But the token may stop there. Some lower-level operations ignore it entirely. Some services create new tokens instead of propagating the original one. Some infrastructure calls never receive the token at all.
The result is a system that technically supports cancellation in some places while still behaving as if cancellation barely exists.
This problem usually develops gradually because cancellation is easy to postpone. During early development, operations are often fast, local, and lightly loaded. A request that continues for a few extra seconds may not appear important. But as systems grow, incomplete cancellation handling starts affecting scalability, responsiveness, resource usage, and operational behavior.
Requests may continue consuming database connections after the client has disconnected. Background work may continue during application shutdown. Queues may process work that no longer matters. Long-running operations may compete for resources even though the original caller has already abandoned the result.
In many cases, the issue is not that developers forgot cancellation entirely. The issue is that cancellation was treated as an optional technical detail instead of part of the operation itself.
A CancellationToken is not merely a parameter added for completeness. It represents a signal that the caller no longer requires the operation to continue. When that signal is ignored or inconsistently propagated, the system loses an important part of its ability to cooperate under real operating conditions.
Good cancellation handling therefore begins with a broader architectural perspective: cancellation should not be viewed as a low-level implementation detail. It should be treated as part of the contract and lifetime of the operation itself.
2. What Cancellation Actually Represents
One reason cancellation is often implemented inconsistently is that it is easy to think of CancellationToken as merely a technical feature of asynchronous APIs rather than as part of the meaning of the operation itself.
But cancellation represents something more important than stopping a thread or interrupting execution.
A cancellation request is a statement from the caller that the result of the operation is no longer needed.
That distinction matters because asynchronous operations are usually cooperative rather than forcibly interrupted. In .NET, a CancellationToken does not terminate execution automatically. Instead, it allows different parts of the system to observe that cancellation has been requested and decide how to respond safely.
This makes cancellation fundamentally different from failure.
A failed operation means the system attempted to complete the work but could not do so successfully. A cancelled operation means the caller intentionally withdrew interest in the result before completion.
Those are not the same thing, and systems should usually treat them differently.
For example:
- A failed database operation may need retries, logging, alerts, or compensation behavior.
- A cancelled database operation may simply mean the user navigated away from the page.
- A failed HTTP request may indicate network instability.
- A cancelled HTTP request may indicate that the application is shutting down normally.
When cancellation is treated like an ordinary error, systems often become noisy and misleading operationally. Logs fill with expected cancellation exceptions. Monitoring systems report failures that are not actually failures. Developers become less able to distinguish real operational problems from normal cooperative shutdown behavior.
Cancellation also represents coordination between layers of the system.
Consider a web request that triggers several downstream operations:
- A controller receives the request.
- An application service coordinates the use case.
- A repository performs database access.
- An external HTTP API is called.
- Additional processing occurs in background components.
If the client disconnects halfway through the operation, the entire chain should ideally become aware that the result is no longer needed. That awareness is what the CancellationToken communicates.
Without propagation, the upper layer may stop waiting while the lower layers continue consuming resources unnecessarily.
This is why cancellation should not be viewed merely as “supporting async correctly.” It is part of how the system communicates operational intent across boundaries.
A useful way to think about cancellation is this:
A
CancellationTokenexpresses the lifetime of interest in an operation.
As long as the caller still cares about the result, the operation continues. Once that interest ends, cooperative cancellation allows the rest of the system to respond accordingly.
Seen from that perspective, cancellation becomes less about syntax and more about responsibility and coordination throughout the architecture.
3. Why Partial Cancellation Flow Creates Problems
Cancellation is most useful when it flows through the whole operation. If the token is accepted near the entry point but not passed further down, the system may appear to support cancellation while still doing most of the work after cancellation has been requested.
This creates a misleading design.
Correct cancellation flow compared to cancellation stopping prematurely in a layered system. Click the diagram to open it in full size.
For example, a page handler or controller may accept a CancellationToken:
public async Task<IActionResult> OnPostAsync(CancellationToken ct)
{
await _articleService.PublishAsync(articleId, ct);
return RedirectToPage("/Admin/Articles");
}
But if the service ignores the token when calling the repository, cancellation stops at the application layer:
public async Task PublishAsync(string articleId, CancellationToken ct)
{
var article = await _articleRepository.GetByIdAsync(articleId,
CancellationToken.None);
article.Publish(DateTime.UtcNow);
await _articleRepository.SaveAsync(article, CancellationToken.None);
}
The method signature suggests that cancellation is supported, but the expensive or slow operations do not actually receive the cancellation signal. If the request is aborted, the database work may still continue.
That kind of partial cancellation flow creates several problems.
First, it wastes resources. Database connections, HTTP requests, file operations, and background work may continue even though the caller no longer needs the result. Under light load, this may not be noticeable. Under heavier load, it can reduce throughput and make the system less responsive.
Second, it makes shutdown behavior less predictable. When an application is stopping, cancellation tokens are often used to signal that ongoing work should finish quickly or stop safely. If parts of the operation ignore the token, shutdown may take longer than expected or leave work in an unclear state.
Third, it creates false confidence. Developers may see CancellationToken parameters in method signatures and assume cancellation is handled properly. But unless the token is passed to the meaningful waiting operations, the support is mostly cosmetic.
Fourth, it complicates diagnostics. Some parts of the system may stop quickly, while others continue running. This can make it harder to understand why a request still consumed resources after the caller disconnected, or why a background process continued during shutdown.
Partial cancellation flow can also hide architectural inconsistency. If some layers propagate cancellation and others replace it with CancellationToken.None, it becomes unclear which operations are considered part of the same lifetime. The system no longer communicates operation intent consistently.
A useful guideline is:
A
CancellationTokenshould usually travel as far as the meaningful waiting work it represents.
That does not mean every method in the system needs a token. Small synchronous helpers, pure calculations, formatting methods, and domain operations that only work with already-loaded state usually do not need one.
But once a method accepts a cancellation token because it coordinates work that may wait, it should normally pass that token to the dependencies that perform the waiting.
Cancellation loses much of its value when it only exists at the edges of the system. It becomes useful when it is propagated through the actual work.
4. Cancellation as Part of the Operation Contract
A CancellationToken should not be treated as a decorative parameter. When a method accepts one, it communicates something about the operation: this work may take time, and the caller may later decide that the result is no longer needed.
That makes cancellation part of the method’s contract.
For example, this signature says more than “the method is asynchronous”:
public Task<IReadOnlyList<ArticleSummary>> SearchArticlesAsync(
string searchText, CancellationToken ct)
It says that searching articles may involve waiting, and that the caller can signal that the search should stop if it is no longer relevant. In a UI application, that may happen when the user changes the search text. In a web application, it may happen when the request is aborted. In a background service, it may happen when the application is shutting down.
Because the token is part of the contract, the implementation should normally respect it. That does not mean every line of code must constantly check ct.IsCancellationRequested. In many cases, the most important thing is simply to pass the token to the operations that already know how to observe it:
var articles = await _articleRepository.SearchAsync(searchText, ct);
or:
var response = await _httpClient.GetAsync(url, ct);
The caller should be able to trust that cancellation is not silently discarded without a reason.
This also affects interface design. If an abstraction represents work that may wait, the cancellation token belongs naturally in the abstraction:
public interface IArticleRepository
{
Task<Article?> GetByIdAsync(string id, CancellationToken ct);
Task SaveAsync(Article article, CancellationToken ct);
}
The repository abstraction does not need to expose whether the current implementation uses SQL, JSON files, cloud storage, or an API. But it does communicate that persistence may involve waiting, and that waiting can be cancelled.
By contrast, an immediate in-memory domain operation usually should not need a token:
article.Publish(publishedUtc);
If publishing only checks already-loaded state and updates the article, cancellation does not add value there. Adding a token would make the method look like it participates in asynchronous or long-running work when it does not.
This distinction keeps the architecture clearer. Cancellation belongs where the operation may wait, not everywhere by default.
A good method signature should therefore be honest about the operation it represents. If the method performs or coordinates waiting work, accepting a CancellationToken is often appropriate. If the method only performs immediate business logic, formatting, mapping, or calculation, a token is usually unnecessary.
The practical question is:
Would the caller reasonably need a way to stop waiting for this operation?
If the answer is yes, cancellation belongs in the contract. If the answer is no, adding a token may only create noise.
Treating cancellation as part of the operation contract makes the code more predictable. It helps callers understand which operations may wait, gives long-running workflows a shared lifetime signal, and prevents cancellation from becoming a superficial feature that appears in signatures but disappears before the real work begins.
5. Where CancellationToken Should Normally Flow
A CancellationToken should usually flow through the parts of the system that participate in the same waiting operation.
In a web application, that often starts at the request boundary. A Razor Page handler or controller action may receive a token that represents the lifetime of the request. If the client disconnects, the request is aborted, or the server begins shutting down, that token can be signaled.
The handler should normally pass the token into the application service:
public async Task<IActionResult> OnPostAsync(CancellationToken ct)
{
var result = await _articleService.UpdateArticleAsync(Input, ct);
if (!result.Succeeded)
{
return Page();
}
return RedirectToPage("/Admin/Articles");
}
The application service should then pass the same token to the operations that may wait:
public async Task<UpdateArticleResult> UpdateArticleAsync(
UpdateArticleRequest request, CancellationToken ct)
{
var article = await _articleRepository.GetByIdAsync(request.Id, ct);
if (article is null)
{
return UpdateArticleResult.NotFound();
}
article.UpdateContent(
request.Title,
request.ContentMarkdown,
_clock.UtcNow);
await _articleRepository.SaveAsync(article, ct);
return UpdateArticleResult.Success(article.Id);
}
The repository then passes the token to the actual persistence operation:
public async Task<Article?> GetByIdAsync(string id, CancellationToken ct)
{
return await _dbContext.Articles
.FirstOrDefaultAsync(article => article.Id == id, ct);
}
This creates a clear flow:
Request → Page handler → Application service → Repository → Database
The same principle applies to external services:
await _emailSender.SendAsync(message, ct);
await _httpClient.SendAsync(request, ct);
await _fileStore.WriteAsync(path, content, ct);
The token should normally reach the point where the system is actually waiting.
This does not mean every method in the call chain must perform cancellation logic itself. Many methods simply accept the token and pass it along. That is still valuable because it preserves the operation’s lifetime signal until it reaches the dependency that can act on it.
In UI applications, the token often begins with a user action. A search box, import operation, export process, or save command may create a token source so the operation can be cancelled when the user starts a new action, closes the window, or presses Cancel.
For example:
_searchCancellation?.Cancel();
_searchCancellation = new CancellationTokenSource();
var results = await _articleSearchService.SearchAsync(
searchText, _searchCancellation.Token);
The service and repository should then propagate that token to the actual search operation.
In background services, the token usually represents application shutdown or worker lifetime:
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await ProcessNextItemAsync(stoppingToken);
}
}
The worker should pass stoppingToken to queue reads, delays, database operations, HTTP calls, and other waiting work that should stop when the application is shutting down.
A useful rule is:
Pass the token to anything that may wait for I/O, external services, timers, locks, queues, or long-running work.
Typical places include:
- database queries and saves,
- file reads and writes,
- HTTP requests,
- email sending,
- queue operations,
- delays and timers,
- long-running imports or exports,
- background worker loops,
- streaming operations.
By contrast, the token usually does not need to flow into simple synchronous methods:
article.Publish(_clock.UtcNow);
var displayTitle = FormatTitle(article.Title);
var isValid = ArticleTitle.IsValid(title);
These operations do not wait. They complete immediately using data already in memory. Adding cancellation there usually makes the code noisier without making the system more responsive.
The goal is not to spread CancellationToken everywhere. The goal is to preserve the cancellation signal until it reaches the parts of the operation that can meaningfully stop waiting.
6. Common Mistakes and Misunderstandings
Most cancellation problems are not caused by the presence of CancellationToken itself. They are caused by unclear assumptions about what the token means and how far it should travel.
A common mistake is accepting a token but not passing it on:
public async Task SaveAsync(Article article, CancellationToken ct)
{
await _repository.SaveAsync(article, CancellationToken.None);
}
This makes the method signature misleading. The caller appears to have cancellation support, but the actual waiting operation ignores the caller’s signal.
Another common mistake is creating a new token source unnecessarily inside the operation:
using var timeout = new CancellationTokenSource(
TimeSpan.FromSeconds(10));
await _repository.SaveAsync(article, timeout.Token);
This may be valid if the operation needs its own timeout. But if it replaces the caller’s token entirely, the operation no longer responds to the caller’s cancellation request. A better approach is often to link the timeout with the caller’s token:
using var timeout = new CancellationTokenSource(
TimeSpan.FromSeconds(10));
using var linked = CancellationTokenSource.CreateLinkedTokenSource(
ct, timeout.Token);
await _repository.SaveAsync(article, linked.Token);
This allows the operation to stop either because the caller cancelled it or because the timeout expired.
A third mistake is treating cancellation as an ordinary error. In many cases, OperationCanceledException does not mean that something failed. It means the caller no longer wanted the result, or the system is shutting down. Logging every cancelled operation as an error can make logs noisy and misleading.
That does not mean cancellation should always be ignored. It may still be useful to log cancellation at a low level, especially for diagnostics. But it should usually not be treated the same way as an unexpected failure.
Another misunderstanding is expecting cancellation to be immediate. Cancellation in .NET is cooperative. The token signals that cancellation has been requested, but the operation must observe that signal. Some APIs check the token quickly. Others may only observe it at certain points. CPU-bound work may need explicit checks if it runs for a long time.
For example:
foreach (var item in largeCollection)
{
ct.ThrowIfCancellationRequested();
ProcessItem(item);
}
This can be useful when processing a large in-memory collection. Without the explicit check, the operation may continue until the loop finishes, even if cancellation was requested earlier.
A related mistake is adding cancellation checks to every small method. That usually creates noise. A method that trims a string, formats a title, maps a small object, or checks a simple property does not normally need to inspect a token. Cancellation should be placed where the work may wait or run long enough that stopping matters.
Another common mistake is swallowing cancellation accidentally:
try
{
await _repository.SaveAsync(article, ct);
}
catch (Exception)
{
return SaveArticleResult.Failed("The article could not be saved.");
}
This catches OperationCanceledException too, because it derives from Exception. The method may then report cancellation as an ordinary failure. In many cases, that is not what the caller expects.
A better approach is to let cancellation flow, or handle it explicitly:
try
{
await _repository.SaveAsync(article, ct);
}
catch (OperationCanceledException) when (ct.IsCancellationRequested)
{
throw;
}
catch (StorageUnavailableException ex)
{
_logger.LogWarning(ex, "Article storage was unavailable.");
return SaveArticleResult.Failed(
"The article could not be saved right now.");
}
Another mistake is using cancellation as a substitute for rollback or consistency. A cancellation request means the caller no longer wants the operation to continue if it can be stopped safely. It does not automatically undo work that has already happened.
If an operation has already written data, sent an email, charged a payment, or published a message, cancellation may arrive too late to prevent that side effect. In those cases, the system needs clear transactional boundaries, idempotency, compensation, or other consistency mechanisms. Cancellation alone is not enough.
A useful distinction is:
Cancellation can stop work that has not yet completed. It does not automatically reverse work that has already happened.
Finally, a common misunderstanding is thinking that every method needs a token once one method in the call chain has one. That leads to noisy signatures and unnecessary plumbing.
The better rule is more focused:
Pass cancellation through meaningful waiting or long-running work. Do not spread it into immediate logic where it cannot add value.
Used this way, cancellation remains clear. It becomes part of the operation’s lifetime, not a parameter added everywhere without purpose.
7. Deliberate Boundaries: When Not to Propagate Cancellation
Although cancellation should usually flow through meaningful waiting work, it should not be propagated blindly everywhere. There are situations where allowing cancellation to continue would make the system less clear or less reliable.
The most important distinction is whether the operation is still safely cancellable.
Some work should be stopped when the caller no longer needs the result. A search query, a file read, an HTTP request, or a long-running import step may be reasonable to cancel. If the result will not be used, continuing may only waste resources.
Other work may need to finish once it has passed a certain point.
For example, if an operation has started committing a transaction, writing an audit record, charging a payment, sending a confirmation email, or publishing an event, cancellation may arrive too late to simply stop without consequences. At that point, the system may need to complete the critical section, roll back safely, or record enough information to recover later.
This means cancellation boundaries should be deliberate.
A useful example is a purchase flow:
public async Task<PurchaseResult> CompletePurchaseAsync(
PurchaseRequest request, CancellationToken ct)
{
var order = await _orderRepository.GetByIdAsync(request.OrderId, ct);
if (order is null)
{
return PurchaseResult.NotFound();
}
await _paymentProvider.CapturePaymentAsync(order.PaymentId, ct);
await _licenseService.IssueLicenseAsync(order.Id,
CancellationToken.None);
return PurchaseResult.Success();
}
This example should not be copied blindly, but it illustrates the concern. If the payment has already been captured, issuing the license may no longer be optional just because the original web request was cancelled. The caller may have disappeared, but the business operation now needs to reach a consistent state.
In such cases, replacing the caller’s token with CancellationToken.None may be intentional, but it should be rare and clearly justified. It means:
From this point onward, this work must complete or be handled by a recovery mechanism.
A better design may be to move the non-cancellable part into a durable background process. For example, after payment capture, the system could record a fulfillment task and let a worker issue the license reliably. The web request can be cancelled, but the business process still has a durable record of what must happen next.
The important point is that cancellation should not accidentally interrupt work that protects consistency.
Another place to be careful is cleanup. If an operation is cancelled, cleanup may still need to run:
try
{
await ProcessImportAsync(filePath, ct);
}
finally
{
await DeleteTemporaryFileAsync(filePath, CancellationToken.None);
}
Again, CancellationToken.None should not be used casually. But cleanup is often different from the original operation. Even if the caller cancelled the import, the system may still need to delete temporary files, release locks, or restore state.
There are also simple cases where cancellation should not be propagated because the method does not represent waiting work. Domain operations, small value-object checks, formatting, mapping, and immediate calculations usually do not need a token:
article.Publish(_clock.UtcNow);
var title = NormalizeTitle(input.Title);
var canPublish = article.CanPublish();
Adding CancellationToken to these methods does not make the system more responsive. It only makes the signatures heavier and suggests that the operations may wait when they do not.
A useful distinction is:
Cancellation belongs to operation lifetime, not to every function call.
Deliberate cancellation boundaries should therefore be visible in the code. If a token is not propagated, there should be a reason:
- the remaining work is immediate,
- the operation has entered a consistency-critical section,
- cleanup must run regardless of caller cancellation,
- the work has been handed off to a durable background process,
- or the method does not participate in waiting work.
What should be avoided is silent disappearance of the token without explanation. If a method accepts ct and then calls lower-level waiting operations with CancellationToken.None, the reader should be able to understand why.
Cancellation is most useful when it is consistent, but consistency does not mean mechanical propagation everywhere. It means the lifetime of the operation is handled deliberately.
This is closely related to the orchestration responsibilities discussed in Application Services Should Orchestrate - not own Everything.1
8. Testing and Observing Cancellation Behavior
Cancellation handling is easy to assume is correct because the code compiles and the application appears to work normally during successful operations. But many cancellation problems only appear under real operating conditions: aborted requests, application shutdown, timeouts, overloaded systems, or interrupted background processing.
For that reason, cancellation behavior should be tested and observed deliberately rather than treated as an implicit property of asynchronous code.
One useful form of testing is verifying that operations actually stop when cancellation is requested.
For example, an integration test might start a long-running operation, cancel the token, and verify that the operation terminates appropriately:
using var cts = new CancellationTokenSource();
var task = _importService.ImportAsync(filePath, cts.Token);
cts.Cancel();
await Assert.ThrowsAsync<OperationCanceledException>(
async () => await task);
This kind of test is not mainly about the exception itself. It is about verifying that the cancellation signal flows through the meaningful waiting work.
Tests can also verify that cancellation does not leave the system in an inconsistent state. For example:
- partially written files should be cleaned up,
- temporary records should not remain indefinitely,
- incomplete transactions should not appear successful,
- queued work should not become duplicated,
- shutdown should not abandon important consistency operations halfway through.
These scenarios become especially important in background processing systems and long-running workflows.
Behavior-oriented testing is valuable here as well. The important question is usually not whether a specific internal method observed ct.IsCancellationRequested. The important question is whether the operation behaved correctly when cancellation occurred.
For example:
- Did the HTTP request stop?
- Did the worker terminate cleanly?
- Did the database operation stop consuming resources?
- Did the system avoid unnecessary follow-up work?
- Did cleanup still happen correctly?
- Did the application remain in a valid state?
This focus on observable behavior tends to produce more resilient tests than verifying internal cancellation implementation details.
Cancellation behavior should also be observable operationally.
In production systems, it is often useful to distinguish:
- failures,
- timeouts,
- expected cancellations,
- shutdown-related interruptions,
- and abandoned client requests.
If everything becomes a generic exception in logs, diagnosing real operational problems becomes harder.
For example, logging every OperationCanceledException as an error may create noise during normal shutdown or under ordinary client disconnect behavior. At the same time, completely suppressing cancellation visibility can make it difficult to understand why operations stopped unexpectedly.
Good observability therefore requires proportion and context.
Some systems benefit from lightweight informational logging:
_logger.LogInformation(
"Import operation was cancelled for file {FilePath}.", filePath);
Others may only need cancellation metrics or tracing rather than detailed logs.
Cancellation behavior is also closely related to graceful shutdown. In hosted services, background workers, queues, and long-running tasks, application shutdown tokens should normally be tested under realistic conditions:
- Does the worker stop accepting new work?
- Does in-progress work complete safely?
- Are resources released correctly?
- Does shutdown complete within an acceptable time?
These behaviors often matter more operationally than whether the code contains cancellation checks in the “correct” places.
A useful principle is:
Cancellation handling should be validated through system behavior, not assumed from method signatures alone.
A method accepting CancellationToken does not guarantee that cancellation actually works meaningfully. Real confidence comes from observing how the system behaves when operations are interrupted under realistic conditions.
When cancellation is tested and observed deliberately, it becomes part of the system’s operational reliability rather than merely a syntactic feature of asynchronous programming.
9. Conclusion
Cancellation is often introduced gradually into a system. A few asynchronous methods gain a CancellationToken parameter, some infrastructure APIs receive the token correctly, and the application appears responsive under normal conditions. But partial cancellation support is not the same as a system that truly cooperates around operation lifetime.
A CancellationToken represents more than a technical API detail. It represents the caller’s continued interest in the result of an operation. When that signal is propagated consistently through meaningful waiting work, the system becomes more responsive, more resource-efficient, and easier to operate under real conditions such as shutdown, timeouts, disconnected clients, and changing user actions.
The goal is not to pass cancellation tokens everywhere mechanically. Immediate in-memory operations, simple calculations, formatting, and domain rules usually do not benefit from cancellation support. Propagating tokens into every small method can make the code noisier without improving behavior.
Instead, cancellation should flow deliberately through operations that may wait, block, or consume meaningful resources.
At the same time, not every operation should remain cancellable forever. Some workflows eventually cross boundaries where consistency matters more than immediate interruption. Payment capture, transactional persistence, audit recording, cleanup, and reliable background processing may require carefully chosen cancellation boundaries or durable recovery mechanisms. Good cancellation handling therefore requires architectural judgment, not only correct syntax.
The same principle applies to testing. Cancellation support should not merely exist in method signatures. It should be observable in system behavior. Operations should stop when cancellation is meaningful, cleanup should remain reliable, and the system should remain consistent when interruptions occur.
A useful way to summarize the principle is:
Cancellation only works well when the system treats it as part of the operation itself.
When cancellation is treated as a real part of operation lifetime rather than an optional afterthought, asynchronous code becomes easier to reason about, infrastructure behaves more predictably, and the system becomes more resilient under real operating conditions.
References
Want occasional updates?
Subscribe to receive occasional practical notes on .NET architecture, maintainable software, project progress, and new articles.
