Category: Brownfield Apps

  • WebForms to Blazor: A Realistic Migration Path, Not a Rewrite

    WebForms to Blazor: A Realistic Migration Path, Not a Rewrite

    WebForms applications have a reputation problem. They are old, the tooling around them has quietly stopped improving, and every conference talk assumes you have already left. None of that changes the fact that a lot of WebForms applications are still running real logistics and operations businesses today, reliably, and a full rewrite is rarely the responsible next step.

    Here is a more realistic path from WebForms to Blazor, one that respects the fact that the current system works and the team maintaining it has other priorities too.

    Start With What Actually Needs to Change

    Not every screen in a WebForms application needs modernising at the same time. Before writing any Blazor code, map the application by how often each screen is touched and how much business risk it carries. A rarely used admin screen with stable requirements can wait. A dispatch screen that every warehouse operator opens fifty times a day, built on postback patterns that make small changes painful, is where the return on modernisation is highest.

    This mapping exercise alone often reshapes the whole plan. Teams that assume they need to migrate everything usually find that twenty percent of the screens carry eighty percent of the daily pain.

    Run Both Frameworks Side by Side

    You do not need a big bang cutover. IIS can host a WebForms application and a Blazor Server or Blazor WebAssembly application side by side, with routing or a reverse proxy directing traffic to the right one per URL path. This means you can migrate one screen, ship it, get real feedback from the people using it daily, and migrate the next one, rather than committing to a multi month freeze on new features while the whole thing gets rebuilt.

    // Reverse proxy style routing:

    // /dispatch/* -> new Blazor app

    // everything else -> existing WebForms app

    The Business Logic Usually Survives Intact

    The part people worry about most, the actual business rules buried in code behind files, is usually the part that survives a migration best. Extracting that logic into plain C# classes with no dependency on the WebForms page lifecycle means it can be called from both the old pages and the new Blazor components during the transition, and it becomes properly testable for the first time, often years after it was written.

    This is also where the real value of the migration shows up. Not “it looks modern now,” but “we can finally write a unit test for the freight calculation logic that nobody has dared touch since 2014.”

    What Blazor Actually Buys You

    Beyond the obvious modern component model, the concrete wins for a logistics operations team tend to be:

    1. Real time updates without full page postbacks, which matters when dispatchers need to see order status change live.
    2. A component model that lets you reuse UI logic across screens instead of copying markup between pages.
    3. A path to a genuinely responsive interface for warehouse staff using tablets, which WebForms never handled gracefully.

    Set Realistic Expectations

    A WebForms to Blazor migration done screen by screen, prioritised by actual business risk, typically takes months, not weeks, for a system of meaningful size. That is not a failure of planning. It reflects the fact that the system has been absorbing business logic for years and deserves to be moved carefully rather than quickly.

    The goal is not to declare the old system dead on a fixed date. It is to reach a point where the screens that matter most are on a framework your team can actually build on, while the rest keeps running exactly as it has, until its turn comes.

  • Before Any AI Project, We Ask One Question First

    Before Any AI Project, We Ask One Question First

    Most companies approaching an AI integration in 2026 have already decided they need one. The board asked for it, a competitor announced something, or a vendor made a compelling pitch. Very few have stopped to ask whether the specific use case actually benefits from AI, or whether a well designed rule based system would be cheaper, faster to build, and easier to maintain.

    That question comes first, before any architecture discussion. Here is how we work through it, and what genuinely justified AI integration looks like once the answer is yes.

    The Question That Gets Skipped

    Ask this before anything else: does the task require judgment on ambiguous input, or does it require consistent execution of known rules? Sorting invoices by vendor name is a rules problem. Extracting a total from an invoice that arrives in twelve different formats, some scanned, some handwritten notes in the margin, is closer to a judgment problem, and that is where AI genuinely earns its complexity.

    A surprising number of “AI projects” we get asked to scope turn out to be the first kind. When that happens, the honest recommendation is to build the simpler system. It costs less, it is easier for your own team to maintain afterward, and it will not silently degrade in ways that are hard to detect.

    Where AI Actually Belongs in a .NET System

    When the answer genuinely points to AI, the integration work tends to fall into a few recurring patterns:

    Document intelligence. Companies receiving invoices, forms, or contracts that are currently keyed in by hand. Azure Document Intelligence or a similar service extracts structured data, which then flows into the existing .NET backend and SQL Server without disrupting the workflow already in place.

    Semantic search over existing data. Legacy applications where keyword search returns results that technically match but are not actually useful. Adding embeddings on top of existing SQL or Cosmos DB data lets users search in natural language and find what they meant, not just what they typed.

    var embedding = await openAiClient.GetEmbeddingsAsync( deploymentName: "text-embedding-3-large", input: userQuery);

    Narrative summaries on existing dashboards. A Power BI dashboard that shows the numbers but requires someone to interpret them can gain a natural language summary layer that explains what changed and why it matters, generated on top of data you already have.

    What Genuine Integration Requires, Beyond the Model

    The hard part of an AI project is rarely the model call itself. Most teams can wire up an API in an afternoon. The real work is everything around it: handling the cases where the model is uncertain, deciding what happens when confidence is low, logging enough to debug a wrong answer six weeks later, and making sure latency does not break a workflow that used to be instant.

    This is also the most common reason AI pilots work well in a demo and then quietly fail once they reach production. The demo tested the happy path. Production tests every edge case the demo never saw, at volume, with real user patience wearing thin.

    The Honest Version of AI Positioning

    We are not an AI company building models from scratch. We are a .NET team that reads your existing system first, asks whether AI genuinely belongs in a specific part of it, and if it does, builds the integration properly, including the parts that never make it into a product demo. If the honest answer is that you do not need AI for a given problem, we will say so, and suggest what we would build instead.

    That is a less exciting pitch than “add AI to everything.” It also tends to be the one that still works eighteen months later.

  • Signs Your .NET Framework App Is a Business Risk, Not Just Old

    Signs Your .NET Framework App Is a Business Risk, Not Just Old

    “It still works” is not the same thing as “it’s fine.” Most companies don’t decide to modernise a legacy .NET Framework application because someone made a strategic call. They decide because something broke on a Friday afternoon, the person who understood that part of the system left two years ago, and nobody can say with confidence what else is quietly at risk.

    Here is how to tell the difference between an old system that’s simply old, and one that has become an active liability, before it forces the decision for you.

    1. Nobody Can Explain Why a Piece of Code Exists

    Every long lived codebase accumulates logic nobody fully understands anymore: a strange conditional, a hardcoded value, a workaround for a bug in a library version from a decade ago. That’s normal. The risk isn’t the mystery code itself. It’s when nobody on the current team can even investigate it safely.

    If a change request gets quietly deprioritised because “we’re not sure what that part does,” that’s not a scheduling problem. It’s a sign the system has outgrown the team’s shared understanding of it, and every future change carries hidden risk.

    2. Your Hosting or Runtime Is Approaching End of Support

    .NET Framework itself isn’t going away overnight, but the surrounding stack often is: older Windows Server versions, IIS configurations, and dependent libraries all have support timelines. When any of these reach end of life, you lose access to security patches at exactly the moment the application is hardest to touch.

    A useful exercise: list every piece of infrastructure your application depends on (OS version, .NET Framework version, SQL Server version, any third party components) and check each one’s support end date. If more than one is within 18 months, that’s a business risk, not an IT footnote.

    3. Onboarding a New Developer Takes Months, Not Weeks

    This is one of the clearest tells. In a healthy codebase, a competent developer becomes productive within a few weeks. In a system that has become a liability, onboarding stretches to months, not because the developer is slow, but because the knowledge needed to work safely in that codebase was never written down.

    // A small but real example of the kind of thing that // only makes sense with tribal knowledge: if (order.Status == 4 && !order.Flags.HasFlag(OrderFlags.Legacy)) { // DO NOT REMOVE. Breaks EOD batch job for the Manchester warehouse order.Status = 7; }

    Comments like that are a symptom, not a fix. They are a sign the business logic and the documentation parted ways a long time ago.

    4. Every Deployment Needs a Tribal Knowledge Person on Standby

    If releases only go smoothly when one specific person is available, not because of process, but because they’re the only one who remembers the manual steps, the gotchas, and what to check afterward, that’s a single point of failure with a name and a phone number. Ask what would happen to the next three releases if that person took a month off. If the honest answer is “we would slow down significantly,” the risk is already priced into your roadmap whether it’s visible on it or not.

    5. The System Carries More Weight Than It Was Designed For

    Systems built for a specific scope tend to keep absorbing responsibility over the years: more integrations bolted on, more edge cases handled, more departments depending on it, without ever being redesigned for that expanded role. A tool that started as an internal reporting utility can end up, ten years later, quietly running order fulfilment for hundreds of locations. The risk shows up when the system’s actual importance to the business has outpaced the investment made in keeping it maintainable.

    What to Do About It

    None of this means rewrite everything. In most cases that’s the wrong answer. A full rewrite of a system that has been quietly running the business for over a decade is its own significant risk, and it throws away years of accumulated correctness that a fresh build won’t have. The more realistic path is usually:

    1. Document the parts nobody understands, before the person who does understand them leaves. This alone reduces more risk than any code change.
    2. Target the highest risk dependencies first, the OS, runtime, or library versions closest to end of support, rather than modernising evenly across the whole system.
    3. Get the system onto a supported .NET version incrementally, module by module, rather than as a single high stakes cutover.
    4. Bring in someone who can read the codebase quickly and work inside it safely without needing six months of ramp up first. This is usually the actual bottleneck, not the code itself.

    Legacy isn’t the problem. An undocumented, unmonitored, single point of failure legacy system is. The difference between the two is usually a few weeks of focused work. The trouble is finding time for those few weeks before something forces the issue.