Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
158018 stories
·
33 followers

Inside Platform Engineering with Joep Piscaer

1 Share

The CNCF landscape has hundreds of logos, dozens of categories, and no shortage of people telling you what belongs in your platform. Joep Piscaer, Field CTO at Portainer, joined me on Inside Platform Engineering with a take I don't hear often enough: that the best platform decision is frequently to add nothing at all.

Joep calls the CNCF landscape a candy shop, and it's a comparison that stuck with me. Just because something's on the shelf doesn't mean it belongs in your cart, and the cost of a bad choice doesn't show up at checkout, it shows up months or years later when someone has to support it.

Watch the episode

You can watch the episode with Joep below.

Inside Platform Engineering with Joep Piscaer

The candy shop problem

Joep's argument is simple but easy to forget in practice, which is that every tool you add to your platform is a tool you now have to operate, secure, and explain to whoever inherits it. He's not against new tooling on principle, he's against choosing it reflexively because it's popular or well-marketed. His rule of thumb is that the best choice is often no choice at all, and that resisting the landscape is itself a skill worth developing, not a sign you're falling behind.

Small teams will build lean platforms

One of the more provocative points Joep made was that a platform team with a budget and an SLA will naturally start building for its own survival, not just for its users. His suggested fix leans further than most people may be comfortable with, keep the team small, ideally under eight people (this is very contextual to the organization), so there's only time for the basics. I liked how he framed a bloated platform as a freight ship rather than a speedboat. Once it's big, you can only change course by a single degree at a time, no matter how good your intentions are.

Talk to your users before you build

Joep was adamant that understanding why you're building something matters more than the build itself, going as far as to say a good developer might spend as little as 20% of their time actually writing code. The rest goes into figuring out what's actually needed. This came up multiple times throughout our conversation. His advice for platform teams is to get out of meetings and sit next to the people doing the work, which he only half-jokingly compared to Fisher-Price's old "soul-crushing meeting" toy.

:::figure

:img{ src="/blog/img/inside-platform-engineering-joep-piscaer/soul-crushing-meeting.png" alt="An image of a satirical Fisher-Price toy box parody designed by Daniel Picard." loading="lazy" }

:::

Vibe coding is changing who the platform needs to support

We spent time on how AI-assisted coding is reshaping who your platform needs to serve. Joep's read is that business users are increasingly vibe-coding their own tools because commodity software rarely fits the way their teams actually work. Once they've got something that works, they just want a URL, not a ticket in your backlog or a crash course in Kubernetes. What struck me was Joep's parallel back to Platform Engineering itself. Just as we're told to go and understand what our users actually need rather than guessing, these business users are doing exactly the same thing for themselves, they just build it rather than ask for it. The job for a platform then becomes hiding all of that complexity so those tools can be deployed simply, while staying lean, secure, and compliant underneath. Whether that's a threat to platform teams or an opportunity probably depends on how ready your platform already is to support something it didn't design.

Happy deployments!

:::div{.hint}

Inside Platform Engineering is a series of conversations with Matt Allford and a guest, bringing their own experience and perspective from the world of Platform Engineering.

You can find more episodes on YouTube.

:::

Read the whole story
alvinashcraft
just a second ago
reply
Pennsylvania, USA
Share this story
Delete

5 Software Architecture Mistakes That Make Systems Hard to Change

1 Share

You can follow every enterprise best practice. You can use Clean Architecture, some type of layered architecture, event-driven architecture, microservices, or whatever else is popular.

It can still end up in the same place.

You have a system that is really hard to change.

YouTube

Check out my YouTube channel, where I post all kinds of content on Software Architecture & Design, including this video showing everything in this post.

When you do make a change, you are afraid you are going to break something. Eventually, the same comment keeps coming up:

We would be better off rewriting this whole thing.

The codebase might not even look that bad. It might be organized and relatively easy to follow. Except it is not easy to follow when you actually need to change it. That is when everything becomes convoluted and complicated.

One of the reasons is probably one of these five architectural decisions. In each case, you are making an expensive decision without having enough information.

That is the common thread between all five. You are making expensive decisions before you truly understand the business or the problems you are trying to solve. Rather than solving an actual problem, you are adding technical nonsense as a solution to a problem you might not even have.

The shift is in the question.

It is not:

What architecture should we use? Should we use Clean Architecture or event driven architecture?

The question should be:

What do we understand about the problem that justifies the architectural decision we are about to make?

1. Choosing an Architecture Before Understanding the Domain

The first bad decision is choosing your architecture, tooling, frameworks, and infrastructure without really understanding the business domain.

If the start of a project conversation is about how you need microservices, event sourcing, CQRS, Kafka, and Kubernetes, but nobody can explain the business processes or workflows, you are doing it backwards.

Can anybody explain the constraints? What consistency issues might exist? What are the possible failure modes? Which parts of the system change frequently? Where are delays acceptable?

If nobody can answer those questions, why are you already deciding on the solution?

Architectural patterns and tooling are not a bingo card. They are not a checklist. Every one of them comes with tradeoffs and added complexity.

You want independent deployability? Now you have distributed operations.

You want to scale parts of the system independently? Now you have network failures to deal with.

You want team autonomy, with different teams managing their own services and deployments? You are going to have cross service and cross team communication. Even if that communication is asynchronous and uses events, there is still a contract that needs to be managed.

You want isolation between boundaries? You are going to have consistency challenges across those boundaries.

There is always a tradeoff. There is always added complexity. You might be getting a solution, but that solution has a cost.

The conversation should not start with whether microservices are good or bad. That is irrelevant. The focus should be on the forces acting against your system and the problems you are trying to solve.

Do you have consistency issues that need to be addressed? Is there a part of the system where the rules change quickly and should be isolated? Does a workflow contain delays that downstream processes need to account for?

Those are the questions you should be asking.

Do not start with, “Should we use this pattern or that pattern?” Use the pattern that fits as a solution to the problem you actually have.

If you are making technical decisions first, you are guessing that you are eventually going to have the problem those decisions are supposed to solve.

2. Building Entity Services Driven by CRUD

The second bad decision is what I call entity services. This is where the system is completely driven by its data model rather than its behavior.

At first glance, the code can look great. It might be well organized. Maybe you have Clean Architecture or some type of layered architecture. You have controllers, services, and repositories that interact with the database.

But the entities are really just tables or object graphs representing customers, orders, products, and other records.

Ultimately, you just have CRUD.

Consider a simple order with an ID, a customer ID, a status, and a total. What does that model tell us?

It tells us that some data exists. It does not tell us anything about how an order behaves.

Can you cancel an order? You would assume so, but how does that happen? Do you just change the status? At what point can the order be shipped? Does it need to be in a particular status? Can it be changed after inventory has been reserved?

The model tells us none of that. It only gives us data.

Where does all the business logic go when the system is driven by CRUD and entity services?

Often, it stays in the end users’ heads. They are the ones who actually understand the processes and workflows.

Some of that logic will make its way into the system, but it is usually sprinkled everywhere. It might be in controllers, message handlers, stored procedures, or increasingly, frontends that contain most of the actual business logic.

CRUD itself is not bad. There are always parts of a system that are inherently CRUD. Referential data and simple lists might have no meaningful behavior behind them. They are CRUD, and they should be treated that way.

The issue is assuming everything is CRUD. It is developing everything as if it were just an updatable record when it is not.

There is a significant difference between an operation called UpdateOrder, where you change properties on an order, and an operation called CancelOrder, where you explicitly communicate that the user intends to cancel an order and provide the reason.

That is not simply CRUD. It expresses behavior and business intent.

The question you need to ask is:

Does the operation I am performing communicate business intent?

In this example, the intent is to cancel the order. Once that intent is explicit, you can start asking useful questions.

Can the order still be cancelled based on how long ago it was placed? Has inventory already been reserved? Has the order been shipped? Does the customer need a refund?

Those questions are derived from the intent of the operation.

When everything is treated as a generic update, you lose that intent. You also lose the natural place where business rules, validation, and workflow decisions should exist.

3. Using the Database Schema as an Integration Point

The third bad decision follows directly from the second. It comes from thinking only about the data model and using the database schema as an integration point.

Imagine two parts of a system: sales and warehouse.

They use a single database instance, but the database is somewhat separated into data related to sales and data related to the warehouse.

Sales interacts with the data it owns. But then it also reaches over and queries data that appears to belong to the warehouse. Potentially worse, it writes directly to warehouse data.

Who actually owns that data?

Is it sales, or is it the warehouse?

At that point, there is no real separation and no clear ownership. The database schema has become the integration point.

Some people will say they do not care. They have one large database, and the different parts of the system need consistency constraints. They want to use the database directly.

That can be fine, as long as you understand the implications. You have no explicit contract. You are treating the database as shared storage.

The problems usually start when you need ownership.

When data is written, who controls how it is written? If a table, collection, or stream needs to change, who owns that change? Who needs to be involved? Which other parts of the system could break?

Without clear ownership, you do not know.

One solution is to have the warehouse expose an API. That API becomes a contract that sales can use to send requests or retrieve information.

A database view can also be a contract. A view can explicitly define how other boundaries are allowed to access data. If you create a view for that purpose and treat it as a contract, it can be a completely valid alternative.

The important distinction is that sharing a database instance is not automatically a problem. Different boundaries can own separate schemas within the same database instance.

The problem is the lack of ownership.

When the database becomes a free for all and anything can read or change any data, you eventually start breaking things.

Suppose an order gets into an invalid state. How did it get there?

You have no idea.

Anything could have changed it. The update might not have gone through the actual process, business rules, and validation required to enter that state correctly.

The order ended up in an invalid state because nobody clearly owned the process of changing it.

4. Creating Abstractions Before You Know What Varies

The fourth bad decision, and arguably my biggest pet peeve, is building abstractions when you do not know what varies.

It usually starts with a reasonable sounding idea.

Maybe we will need to swap something out later.

You begin with a shared service. Then you notice some patterns, so you create a generic workflow engine. After that, you start thinking about what happens if you change the database or messaging library, so you create abstractions around those as well.

Only after all of that do you start building the actual application.

It sounds like it makes sense. We are all taught to value reuse. There is also the constant “what if” question.

What if the underlying provider changes? We will have an abstraction. We can create a new implementation behind it without rewriting large parts of the application.

For example, maybe we want to replace our messaging library. If everything is behind an abstraction, that should be easy.

I understand the reasoning. The problem is that if you only have one implementation of the abstraction you are creating, you probably do not have an abstraction.

You do not have enough information.

You do not understand the other concrete implementations, where they overlap, or where they differ. You are trying to generalize something when you only understand one example.

Messaging libraries are a good example. RabbitMQ and Azure Service Bus have significant differences. Kafka is different again. They might all appear to involve sending and receiving messages, but they have different semantics.

If you start with one of them and immediately build an abstraction around it, that abstraction will be shaped entirely by the only implementation you know.

You did not remove the coupling. You hid it behind an interface.

Are abstractions bad? Of course not.

But a bad abstraction pretends to remove coupling when it does not. It often makes the system harder to understand because developers now need to understand both the abstraction and the concrete technology hidden behind it.

You should create abstractions based on actual variation that you understand, not variation you are imagining might exist someday.

5. Building for Scale You Do Not Have Yet

The fifth bad decision is building for scale you do not have yet.

“Yet” is the important word.

This means paying all the upfront complexity to build for a level or type of scale that you do not know you will ever have.

That does not mean you should design a system that cannot scale if the need arrives. It means you should not pay the entire cost upfront based on hypotheticals.

We might have millions of users.

We might need to replace the database.

This might eventually become a global system.

What do any of those statements actually mean?

When you say the system needs to scale, are you talking about more users, more data, more transactions, or more geographical regions?

“It needs to scale” is not a requirement.

This is not about ignoring scale. It is not about assuming growth will never happen. It is about defining clear boundaries and managing the coupling between those boundaries.

You also need to understand that logical boundaries and physical boundaries are not the same thing.

When you define logical boundaries and avoid coupling them unnecessarily, you give yourself a much better chance of scaling different parts of the system if that need actually appears.

This is also why the conversation about modular monoliths versus microservices is often misleading.

People assume microservices automatically scale better because everything is independent. But a modular monolith and microservices can often be scaled in the same ways.

Once you understand that logical and physical boundaries are different things, the idea that these architectural styles are complete opposites starts to fall apart.

You can define meaningful boundaries without immediately turning every boundary into a separately deployed service.

Start with the boundaries. Decide on the physical deployment model when you have enough information to justify it.

Expensive Decisions Require Real Information

These five decisions cause a lot of pain in software systems.

You make technical decisions first without understanding the domain.

You treat your data model as if it were a domain model, while the actual workflows remain in users’ heads or are scattered throughout the system.

You have no clear ownership, turning the database into a free for all where anything can read or change data anywhere.

You create abstractions without understanding what they are supposed to abstract.

You build for hypothetical scale and pay the cost of complexity before you know whether you will ever need it.

In every case, the problem is the same. You are making an expensive decision without enough information.

Architecture should not start with patterns, frameworks, or infrastructure. It should start with understanding the business, the workflows, the constraints, and the actual problems the system needs to solve.

Then you can make the architectural decision that fits.

Join CodeOpinon!
Developer-level members of my Patreon or YouTube channel get access to a private Discord server to chat with other developers about Software Architecture and Design and access to source code for any working demo application I post on my blog or YouTube. Check out my Patreon or YouTube Membership for more info.

The post 5 Software Architecture Mistakes That Make Systems Hard to Change appeared first on CodeOpinion.

Read the whole story
alvinashcraft
19 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

How to Evaluate AI Code Quality: A Practical Guide for Engineers

1 Share

You asked the AI to write a function. It gave you something that looks right. It even runs. But is it actually good?

Most engineers stop there. They see green and move on. That habit will quietly cause you problems.

AI coding tools like GitHub Copilot, Cursor, and Claude are genuinely useful. But they're non-deterministic, meaning the same prompt can produce different outputs on different days.

They can produce code that's plausible-looking but subtly wrong, or code that works for the happy path but falls apart on edge cases. Without a system for evaluating what the AI gives you, you're essentially shipping untested third-party code and hoping for the best.

This guide walks you through a practical, beginner-friendly approach to evaluating AI-generated code, so you can use these tools with confidence instead of crossed fingers.

What We'll Cover:

Why AI Code Needs Its Own Evaluation Discipline

When a human colleague writes code, you can ask them questions. You can read their commit history. You have context.

When an AI writes code, you have none of that. The output arrives fully formed, often with confident-sounding comments, and it's easy to assume competence where there may be none.

The other problem is that AI models are trained on vast amounts of public code, including bad public code. They can reproduce anti-patterns fluently. They can write code that passes a quick read but fails under real-world load, unusual inputs, or security scrutiny.

Evaluating AI code isn't about distrusting AI. It's about applying the same engineering discipline you would to any code that enters your codebase.

Step One: Define Correctness Before You Generate

The single most effective thing you can do is write your tests before you ask the AI to write the implementation. This is the spirit of test-driven development (TDD), and it maps perfectly onto AI-assisted workflows.

When you define correctness upfront, you give yourself an objective measure the moment the code arrives. You're not eyeballing it. You're running it against a contract you wrote yourself.

Here's a simple example. Say you want an AI to write a function that parses a price string like "$12.99" and returns a float. Before prompting the AI, write this:

def test_parse_price():
    assert parse_price("$12.99") == 12.99
    assert parse_price("$0.00") == 0.0
    assert parse_price("$1,299.99") == 1299.99
    assert parse_price("") is None
    assert parse_price("free") is None

Now prompt the AI: "Write a Python function called parse_price that takes a price string like $12.99 or $1,299.99 and returns a float. Return None for invalid input."

Run your tests immediately. The AI might pass four out of five. Now you know exactly what to fix and you didn't have to read a single line of implementation to find the gap.

Step Two: Build a Golden Dataset

A golden dataset is a small collection of inputs with known correct outputs. Think of it as a permanent test suite for any AI feature you build. You start with five or ten examples. You add to it whenever something breaks in production.

This becomes your regression set. Every time you tweak a prompt, upgrade a model, or refactor a pipeline, you run the golden dataset first. If anything breaks, you know immediately.

Here's what a golden dataset might look like for the price parser. A simple JSON file works fine:

[
  { "input": "$12.99",    "expected": 12.99,  "note": "basic case" },
  { "input": "$1,299.99", "expected": 1299.99, "note": "thousands separator" },
  { "input": "12.99",     "expected": 12.99,  "note": "missing dollar sign" },
  { "input": "$ 12.99",   "expected": 12.99,  "note": "space after symbol, from prod bug #142" },
  { "input": "€12.99",    "expected": null,   "note": "unsupported currency" },
  { "input": "free",      "expected": null,   "note": "non-numeric text" },
  { "input": "",          "expected": null,   "note": "empty string" }
]

Each entry is just an input, the correct output, and a short note on why it's there. A script loads the file, runs each input through your function or prompt, and compares results.

For a code-generation use case, the same idea scales up: a folder of input prompts paired with expected output files, diffed by a script. For data extraction, a CSV of sample inputs alongside expected parsed values.

So how do you decide what goes in? Three sources cover most of it.

First, the representative cases: the ordinary inputs your feature handles ninety percent of the time. Second, the boundary cases you can predict upfront, like empty strings, unusual formats, and inputs that should be rejected. Third, and most valuable, real failures.

Notice the $ 12.99 entry above tagged with a production bug number. A user hit that input, the parser choked, and now it's in the dataset forever. That's the test: if an input broke something once, or plausibly could, it earns a permanent spot. If it's just a minor variation of a case you already cover, skip it and keep the dataset small enough to run on every change.

The key discipline is this: don't just fix the failing case. Add it to the golden dataset, fix it, and verify everything else still passes. This is how you stop the whack-a-mole problem where fixing one AI failure silently breaks three others.

Step Three: Measure Reliability, Not Just Correctness

AI outputs aren't deterministic. Correct once doesn't mean correct always. This is especially important if you're embedding AI into a product: a prompt that works 80% of the time will fail your users 20% of the time, and that's not acceptable in production.

The fix is to run your evaluation across multiple samples. Run the same prompt ten times and check how many outputs pass your tests. Tools like promptfoo make this easy to automate. You define your test cases in a config file, point it at your prompt, and it runs the evals and reports pass rates.

Here's what a simple promptfoo config looks like:

prompts:
  - "Parse the following price string and return only a float: {{input}}"

providers:
  - openai:gpt-4o

tests:
  - vars:
      input: "$12.99"
    assert:
      - type: equals
        value: "12.99"
  - vars:
      input: "$1,299.99"
    assert:
      - type: equals
        value: "1299.99"
  - vars:
      input: "free"
    assert:
      - type: equals
        value: "null"

Run this across ten iterations and you'll quickly see if your prompt is brittle. A 100% pass rate across ten runs gives you real confidence. A 70% rate tells you the prompt needs tightening before it goes anywhere near production.

Step Four: Review for What Tests Can't Catch

Tests tell you if code is correct. They don't tell you if it's readable, maintainable, or secure. After your automated checks pass, do a focused human review on three things.

The first is security. AI models can produce code with real vulnerabilities like SQL injection via string concatenation, missing input sanitization, and hardcoded credentials in examples it then forgets to flag. Run AI-generated code through a static analysis tool like Bandit for Python or ESLint with a security plugin for JavaScript as a baseline check.

The second is edge cases the AI didn't consider. Look at the test cases you wrote and ask: what did I not cover? Empty lists, null values, very large inputs, concurrent calls, and so on might not be handled by AI. You need to push it on the edges.

The third is over-engineering. AI sometimes produces elaborate solutions to simple problems. If you asked for a function that checks whether a number is even and got back a class with three methods and a configuration object, that's a red flag.

Complexity is a cost. Prefer simple code you understand over clever code you do not.

Step Five: Treat Prompt Changes Like Code Changes

If you're using AI in a repeatable way like an internal tool, a product feature, or a script you run regularly, your prompts are part of your codebase. Version control them. Review changes to them. Don't just edit a prompt and hope for the best.

The practical habit is to store prompts in files rather than hardcoding them inline, commit them to Git alongside your code, and re-run your golden dataset any time a prompt changes. This takes maybe ten minutes to set up and saves hours of debugging later.

LangSmith and Weights & Biases both offer prompt versioning and eval tracking if you want a more structured solution. For most small projects, a prompts folder in your repo and a simple test runner is enough.

The Mindset That Makes This Work

Every technique in this guide comes down to one shift: treat AI outputs like external inputs, not trusted code.

You wouldn't deploy an API response to production without validating its shape. You wouldn't accept a file upload without checking its contents. AI-generated code deserves the same skepticism, not because the AI is unreliable, but because all external inputs are unreliable, and good engineering accounts for that.

The engineers who get the most out of AI tools aren't the ones who trust them most. They are the ones who verify fastest. Write the tests first. Build the golden dataset. Measure reliability. Review what automation misses. Version your prompts.

Do those five things consistently and you'll ship AI-assisted code with the same confidence you bring to anything else in your stack.

Hope you enjoyed this article. You can connect with me on LinkedIn.



Read the whole story
alvinashcraft
32 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Rider 2026.2: IDE Intelligence for AI Agents, Faster Performance, and Spectacular Game Dev Updates

1 Share

Rider 2026.2 opens up the IDE’s own intelligence to your AI coding agents, so they work from real project knowledge instead of reconstructing it from files and terminal output.

A new set of agent skills covers testing, profiling, refactoring, and official Microsoft .NET workflows, and GitHub Copilot now joins the lineup as a natively integrated agent. This release also delivers a wave of performance gains that make the IDE feel faster across both .NET and game development.

Bring Rider’s intelligence to your AI agents

Rider 2026.2 connects coding agents directly to the IDE’s coverage data, profiler insights, refactoring engine, and framework-specific guidance, so they find context faster and make safer changes with less guesswork and token waste. 

Bundled skills ship with the IDE as built-in workflows, helping agents handle specific tasks without working out every step themselves. Other skills you add through the agent skills manager.

A few of the skills that stand out this release:

  • dottrace-analyze is a bundled skill that can read a dotTrace .dtp snapshot you hand to it, find where the CPU actually went, and trace the hot path back into your code. Read more on the blog.
  • Code quality check hooks for Claude Code validate every change an agent makes before it can continue, blocking on errors and returning warnings as feedback. Read more on the blog.
  • finding-tests uses dotCover data to tell your agent where to put new tests, and how to format them to follow your conventions.
  • Game-aware Unreal Engine skills for UE C++ authoring, live debugging, and test authoring.

Aside from JetBrains Rider’s proprietary skills, the latest release also comes with support for the official Microsoft .NET, Aspire, and Azure skills, which are easily discoverable and installable straight from the IDE.

You can explore the full list of available agent skills in Rider’s Settings/Preferences | Tools | AI Assistant | Skills. Code quality check hooks can be found and configured under Tools | AI Agent Hooks.

More about the AI agent skills included in this release here.

More AI choice: Copilot built in, plus your own models for code completion

GitHub Copilot is now a natively integrated agent, as the result of a direct partnership between JetBrains and Microsoft. Copilot is available out of the box from the agent picker in AI chat, with OAuth sign-in and no ACP Registry setup required (an active Copilot subscription is needed).

AI completion now supports third-party providers. Alongside the JetBrains-trained models available to all JetBrains AI users out of the box, you can now connect your own completion model, configured independently from your agent provider. Supported options include OpenAI-compatible endpoints like LM Studio or llama.cpp, and Mercury by Inception Labs.

Performance gains for .NET and Unreal Engine

Rider 2026.2 trims the waits that interrupt your flow. On Windows, debugger launch is about 2.8 seconds quicker for .NET apps, branch switching in Roslyn-backed solutions is generally 2–3× faster, and backend processes use around 7–8% less memory. For large Unreal Engine projects opened via the generated .sln, C++ indexing runs roughly twice as fast as in 2026.1.

More detail is in the What’s New in Rider 2026.2 performance section.

Hot Reload for WPF

One of the biggest .NET productivity wins this release: WPF Hot Reload lets you edit your XAML while the app runs under the Rider debugger and see saved changes applied in place. Tweak layouts, styles, templates, and resources without rebuilding, restarting, or navigating back to the screen you were on. Paired with Rider’s existing C# Hot Reload, it turns UI iteration into a tight, uninterrupted loop.

In this example we’re using changes to a weather app UI to illustrate the seamless Hot Reload experience for a WPF project in Rider 2026.2

Read the dedicated blog post.

Game development

Game development is a priority every release, and 2026.2 delivers on three fronts: the Unreal indexing speedups, game-aware AI agent skills, and first-class debugging with basic Natvis support arriving on Linux and macOS (including the recommended godot-cpp.natvis path for Godot types).

Godot development also feels more complete, with a new configurable GDScript formatter, the ability to drag scene nodes into code as paths or @onready/@export variables, the official JetBrains Rider Integration addon on the Godot Asset Store, and more accurate resolution of Autoloads and uid:// references.

Drag nodes from the Scene Preview tree directly into the editor, and Rider will insert the right node path or variable declaration for you.

On the Unreal side, UInterface navigation and Gameplay Tag usages now surface across both C++ and Blueprints, and ISPC gets first-class editor support.

More updates for game development.

The latest language support

  • C#: Rider ships the latest ReSharper updates for analysis, refactoring, and language support. See What’s New in ReSharper 2026.2.
  • C++: initial support for C++26 reflection (the ^^ operator, splicing, and consteval blocks), plus a constexpr evaluator that now handles dynamic allocations and exceptions. See What’s New in ReSharper C++ 2026.2.
  • F#: a new action to disable and restore compiler warnings, more reliable symbol imports from errors, smoother C# 14 interop, and additional debugging fixes.

Other release highlights

  • File-based C# app templates: create, edit, and run single-file C# scripts, repo utilities, and CI helpers without a full project.
  • TypeScript 7 support: the Go-based compiler cuts project load time dramatically; in our testing on the Kibana codebase, from ~12 seconds to ~3.
  • Built-in Azure Functions: create, run, debug, and containerize Functions projects locally without the separate Azure Toolkit plugin.
  • Azure DevOps pull requests: list, filter, review, vote on, and create PRs from a new tool window, without leaving Rider.
  • Intention previews: see the diff a quick-fix or context action will produce before you apply it.
  • Redesigned NuGet tool window: browsing, installed packages, and updates now have separate, more focused paths.
  • Smarter debugging: more predictable Step Into and cleaner return values for C#/F#, plus more reliable source generator debugging on Linux and macOS.

Try Rider 2026.2

You’ll find the full overview on the What’s New in Rider 2026.2 page. Download the latest version and let us know how it fits into your workflow, in the comments below or on X or Bluesky.

Read the whole story
alvinashcraft
49 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

ReSharper 2026.2: AI Agent Freedom in Visual Studio, .NET Debugging for VS Code, and More

1 Share

ReSharper 2026.2 takes the first step toward ACP-based agent support in Visual Studio, starting with Junie in Preview.

This is the beginning of an open AI ecosystem for .NET developers: your choice of agents and models, connected through a single protocol, paired with the code intelligence ReSharper is known for. The release also extends ReSharper’s debugging engine to VS Code-compatible editors, sharpens core C# analysis and refactoring, and broadens tool support in Out-of-Process mode.

Our vision: any agent, your choice, no lock-in

The AI landscape is moving fast, and we don’t think .NET developers should be locked into a single ecosystem to get their work done. Our goal for AI in Visual Studio is simple: no vendor lock-in, no forced choices, just the freedom to use the agents and models that work best for you.

That freedom is built on the Agent Client Protocol (ACP), an open standard for connecting coding agents to the IDE. We’re building the ACP into ReSharper so that soon you’d be able to:

  • Discover local, remote, and in-house agents.
  • Connect them all through the same interface.
  • Switch between agents to pick the best one for each task.
  • Stay current as new models are released. 

This initiative is a core part of our 2026 direction for AI in JetBrains IDEs. We firmly believe that AI-assisted workflows and your classic coding routines should coexist beautifully, never hindering one another. By embracing open protocols like ACP and prioritizing zero vendor lock-in, we ensure that while agents help you build faster, your IDE remains the ultimate place to review, understand, and own the code you ship.

Meet Junie in Visual Studio (Preview)

This Preview release introduces Junie, our first step toward full ACP support in ReSharper inside Visual Studio. Junie is an LLM-agnostic agent, you don’t have to wait for the full Registry to break free – pick your model and start now.

In the AI Assistant tool window, select Junie from the drop-down list of available agents to move from AI Chat into Agent mode.

From there, Junie can:

  • Write and edit code: turn a plain-text prompt into complex logic, or let Junie find and fix suboptimal code on its own.
  • Refactor autonomously: hand off the heavy lifting, like splitting a massive, complex class into separate logical modules.
  • Run terminal commands: create or delete files and run commands without ever opening a command line.
  • Manage Git workflows: initialize repositories, work with branches, stage changes, and write commit messages straight from the chat.
  • Explore and advise: ask project-specific questions, decode complex legacy algorithms, and get architectural suggestions.

Because Junie is LLM-agnostic, you’re not tied to one model. Choose or switch the model it runs on under Extensions | ReSharper | Options | AI Assistant | Junie.

Be sure to select the AI Assistant plugin when you install or update ReSharper. Junie ships on board that plugin, so without it you won’t see Agent mode in the AI Assistant tool window.

You can learn more about Junie in ReSharper from our documentation.

Licensing

Junie’s AI interactions draw on your JetBrains AI quota, which is already included with dotUltimate, an All Products Pack, or a separate JetBrains AI subscription. Don’t have any of those yet? A free trial is available, so you can try Agent mode before committing. 

Debugging for VS Code-compatible editors

The most requested ReSharper feature for VS Code is here. This release introduces the first version of debugging for VS Code-compatible editors, including VS Code, Cursor, Google Antigravity IDE, Devin Desktop, and Kiro. Built on the same core engine that powers JetBrains Rider, it brings:

  • Breakpoint management, including conditional, hit-count, dependent, and tracepoints.
  • Real-time variable and expression inspection.
  • Full step navigation.
  • Launch-and-attach support, directly in your editor.

Together, these make ReSharper the most complete .NET extension available for VS Code-compatible editors. For the full story on this release of, see the dedicated blog post.

Sharper C# analysis and refactoring

  • More collection-expression cases. New inspections suggest collection expressions in additional scenarios, helping you keep code concise and, in some cases, avoid creating intermediate collections. Support for upcoming C# 15 language features is also actively in development, with complete language understanding planned for the next major release.
  • Enhanced Extract Method. Extract Method now detects exact and parameterized duplicates of the selected code within a file and lets you choose which occurrences to replace, so you can consolidate repeated logic into one well-named method without cleaning up each duplicate by hand.
  • .editorconfig and Roslyn analyzer integration. Adjust .editorconfig settings for compiler warnings straight from the Alt+Enter menu as warnings appear, and rely on more accurate handling of Roslyn analyzer configuration, including .editorconfig settings and #pragma suppression rules.
  • Smoother Import missing references popup. The popup has been revamped with better placement, no flickering, and a design that stays out of the way while you type.

C++ updates

ReSharper 2026.2 adds initial support for C++26 reflection, laying the groundwork for next-generation compile-time metaprogramming, along with constexpr evaluation for memory allocations and exceptions, first-class ISPC editor support, significantly faster Unreal Engine indexing for C++ code, and new code inspections. 

See the What’s New in ReSharper C++ 2026.2 page for the full breakdown.

Other release highlights

  • Integrated profiling and coverage tools. Performance profiling, memory profiling, and code coverage tools are now installed automatically with ReSharper and ready to use inside Visual Studio, with nothing extra to download. Prefer to keep things lean? Disable them anytime under Options | Products and Features, or keep using the standalone dotTrace, dotMemory, and dotCover apps.
  • Broader Out-of-Process tool support. dotTrace, dotMemory, and the Monitoring tool now work when ReSharper runs in Out-of-Process mode.
  • Dynamic Program Analysis has been retired. With Monitoring now available in Out-of-Process mode, the transition announced in the previous release is complete: DPA is sunset in 2026.2, and its core capabilities live on in the new Monitoring experience.
  • Performance. Processing of .cshtml and .razor files is faster and more memory-efficient thanks to cached parsed syntax trees, improving features like Find Usages and refactorings, and reduced memory allocations speed up indexing at startup.
  • Updated dotUltimate offline installer for Windows. The offline installer now ships as a .ZIP archive instead of a standalone .EXE. Extract the archive and run the included installer, keeping the installer executable and its companion .dat file (which holds the Rider binaries) together. Deployment scripts may need to account for the extra extraction step.

Try ReSharper 2026.2

ReSharper 2026.2 is the start of a more open AI story for .NET in Visual Studio, alongside real gains in debugging, analysis, and tooling. Download the latest version and give Junie’s Agent mode a try.

You can also get your voice heard by filling out this survey to tell us exactly which AI agents you want to see in the Registry. Your input directly shapes how we expand agent support in ReSharper. 

You can also reach us in the comments below or on X or Bluesky.

Read the whole story
alvinashcraft
53 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Stop Accepting Breached Passwords: Integrating HaveIBeenPwned with Duende UserManagement

1 Share
Defending against credential stuffing attacks is a critical component of modern IdentityServer best practices, yet many developers rely on outdated password policies. A password like Tr0ub4dor&3 passes every complexity rule you can think of: uppercase, lowercase, digits, and a symbol. But if it appeared in a single data breach three years ago, attackers have it in their lists right now. Your complexity checks never had a chance at stopping attackers from using compromised passwords.
Read the whole story
alvinashcraft
1 minute ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories