Szalapski.com
Software development, .NET, and more
Tuesday, October 21, 2025
Don't complain about your stakeholders; instead, collaborate, then build trust
Monday, November 25, 2024
The way to do a no-op (do nothing) action in GitHub Actions is `run: exit 0`
Saturday, August 17, 2024
Readability Above All Else in Code Reviews
In my last post, I made a case that code reviews cannot be relied upon as the main activity to achieve defect prevention, correctness, robustness, usability, or learning. Even so, these goals are worth pursuing in code reviews, though other practices such as unit testing and QA should be the primary driver to such goals. Aside from those goals, code reviews can indeed be the primary driver of other areas of improvement for a software team. It is arguable (comments welcome), but I say above all else code reviews routinely and consistently improve code readability. Highly readable code might not be the first measure of software quality or of team competency that you'd list, but it should be. Code is typically written once and revised a few times, but read dozens of times. Programs are for humans to read and only incidentally for computers to execute. I or any competent developer can deal with any code that we can understand, and the quicker we can understand it, the faster it will be to troubleshoot, maintain, and enhance. Readable code gives us more than a fighting chance. Too often, when teams are under the gun and avoiding spending any time or effort into code review, we get indecipherable, weird, arcane, and confusing code, and this incurs troubleshooting, maintenance, and enhancement costs to go way up in the future, and in unpredictable ways.
Thursday, August 15, 2024
Code Reviews Are Overrated (but not how you think!)
When I first began leading a "two-pizza" software team in 2007, our
code reviews focused on code robustness and correctness. Our architect
would choose code to print out--usually around 4-6 pages' worth each
week. We would gather in a conference room and spend an hour
scrutinizing, and ultimately arguing, about what in that code should
change. A few months into the job, he turned over that duty to me and I
knew both the printed approach and the goals we were pursuing had to shift! Since that time, tools have greatly improved, code review
practices have become easier and more widely known, and the way we
structure code is less novel and more relatable. On top of these
improvements, developers expect to review code more commonly and with a
much healthier attitude; the benefits are more intuitive to all. However, after doing code reviews on most days for the last 17 years, I
have come to see that some of the supposed benefits of code review are overrated. Let's acknowledge the limitations of code review
toward those goals and consider other goals for which code reviews might be the primary means to gain.
Thursday, April 18, 2024
Full-stack? How about "Full Scope"
Hi, I’m Patrick Szalapski.
I make software and help others to make software.
I lead teams to make the most optimally-scoped business web apps and tools, often with .NET, growing such teams to deliver faster, better, and less expensively. I have a proven track record at multiple companies saving businesses millions of dollars and thousands of hours. I can lead or support the effort to figure out what you need, partnering with stakeholders to attack problems and quickly move to building solutions. My teams quickly grow to be highly productive, technically excellent, self-organizing, and happy.
Best of all, I pivot among these roles to whatever a team most needs for sustainable success.
Wednesday, June 22, 2022
Catch me presenting on Blazor at Minnesota Developers Conference 2022 on June 22 in Minneapolis
I'll be presenting at the Minnesota Developers Conference, June 22, 2022 in Minneapolis on Practical Blazor. I hope you can come and introduce yourself!
Also presenting will be some of my favorites, including Kamran Ayub with a fantastic approach to personal finance, Erik Onarheim on building a game engine in JavaScript, and Robert Boedigheimer on Regular Expressions in .NET. So good to finally get back to an in-person conference!
EDIT: That's today! Here's my slide deck for reference.
Friday, March 25, 2022
Avoiding unnecessary rendering in Blazor
In my previous post, I laid out the fundamentals of what can cause a Blazor site to be slow due to re-rendering. By far the most common cause of such slowness is when hundreds or thousands of components all need to re-render multiple times based on changes to parameters or explicit calls to StateHasChanged(). Often it is the case that only a few of the component instances in question truly need to re-render, but Blazor has no way to avoid such re-rendering, so it is up to the developer to worry about this concern whenever dealing with a proliferation of component instances.
The parent component
This potential slowness is often manifest in lists, grids, or tables where the content of each item is non-trivial. Imagine a UI with a grid of product images: a ProductGrid.razor something like…
<button @onclick="Add">Add new to top</button>
<ul>
<Virtualize Context="productViewModel" Items="Products">
<ProductTile Product="productViewModel" @key="productViewModel.Code" />
</Virtualize>
</ul>
@code {
[Parameter, EditorRequired]
public List<ProductViewModel> Products { get; set; } = null!;
private void Add() => Products.Insert(0, new());
}
Note here we have used the Virtualize component to avoid rendering components for items in the List that aren’t currently shown (due to scrolling). We are sure also to use a @keyattribute on the root child element or component inside Virtualize.
So this parent component seems pretty simple and already optimized using Virtualize; how could it be a problem?
Narrow edits can cause wide re-renders
Note there is a button to add a new empty product. When this button is clicked, Blazor re-renders the home component, which means every child component will re-render as well. In our case, this probably means our ProductGrid and the 50+ shown ProductTiles, and suppose each of which has a dozen small components it shows and thus has to rerender. So that’s easily 770+ rerenders needed just to show the new item.
Ideally, when clicking the add button, there would only be a re-render of the ProductGrid, exactly one ProductTile (the new one), and all the small components (but only those in that one instance): thus 14 re-renders.
The reason each of the ProductTiles has to rerender is that its parent is rerendering, and it takes a ProductTileViewModel (one of our domain classes) as a parameter. Blazor cannot determine that nothing has changed in the fifty shown products, as it cannot interrogate all the contents of ProductTileViewModel and compare them to the previous value–remember, Blazor automatically avoids rerendering based on values only when all parameters are of certain well-known immutable or primitive types.
The main way to address this in the ProductTile component is to override OnParametersSet to detect to store the previous values for every part of the parameter that affects the displayed state, and override ShouldRender on the ProductTile component so that it will return false when the previous values are all the same as the current values.
But such logic comparing “previous to current” would have to be repeated in every component, yet customized for each one to ensure you are tracking all the properties you care about. I’ll avoid showing you the example code for this since it is not the way I recommend doing it.
Avoiding the re-renders
So here’s where my Nuget package Sz.BlazorRenderReducers comes into play. Using its DisplayHashRerenderComponentBase and implementing the protected override string DisplayHash get-only property, I can simply return a string that is unique for each display state that is possible; usually, this is just going to be a string that contains the value of every property that affects the display state in any way.
This has the benefit of reducing the code required to finely control re-renderings to two lines of code in most cases: one line to inherit from DisplayHashRerenderComponentBase and one line to override the DisplayHash getter. So, such a ProductTile.razor will look something like:
@using Sz.BlazorRerenderReducers
@inherits DisplayHashRerenderComponentBase
<li class="product-tile">
<ProductImage Product="@Product" />
<div>
@if (!Product.IsInStock)
{
<OutOfStockBadge />
}
<ProductCode Product="@Product" />
<ProductPriceTag Product="@Product" />
</div>
</li>
@code {
[Parameter, EditorRequired]
public ProductViewModel Product { get; set; } = null!;
// when this value is unchanged, supresses unnecessary re-rendering
protected override string? GetDisplayHash() =>
$"{Product.Code};{Product.Price};{Product.IsInStock}";
// write a console line on every render
protected override void OnAfterRender(bool _) =>
Console.WriteLine($"Rendered {GetType()}");
}
Here, the DisplayHash get-only property can return anything that is uniquely determined by the data that affects what could be rendered by the component. True to its name, it is a hash for the display of the component. In this example, I’ve just shown a simple concatenation of the values that influence the display, but you could have any function here you’d like – perhaps using hash functions implemented on the parameters themselves, or serializing them using System.Text.Json, but I have found often a interpolated string is simplest and easiest to understand. The base component DisplayHashRerenderComponentBase will then properly store the previous and current values of this hash, and ShouldRender will return true or false appropriately, removing the need to reimplement such custom ShouldRender code in each component.
Drawbacks
Of course, one drawback to this approach is that it requires that all such components must inherit from DisplayHashRerenderComponentBase, thus removing the possibility of inheriting from any other base component, abstract or otherwise (unless of course you can in turn make that base component inherit from DisplayHashRerenderComponentBase). Fortunately, I have not seen much need to inherit a component from anything other than ComponentBase (which DisplayHashRerenderComponentBase itself inherits from), so this has not yet been an issue for me.
If you’ve followed along to this point, perhaps you can imagine the bigger drawback to this approach: it requires that the developer specify every potential source of changes to the display state. In effect, you have to “register” (by including in the DisplayHash getter logic) every property that might affect the GUI in each component, including its children components (after all. if a parent component avoids re-rendering, there’s nothing to cause its children to re-render either). As you can imagine, this can lead to confusion as to why a component isn’t re-rendering when it needs to–sometimes parameter properties are used but forgotten to be added to the DisplayHash. No error results and no cue is possible to tell the developer that they forgot to tweak the DisplayHash because of a new binding or some other new code indirectly affecting a bound reference.
However, even this latter drawback seems lower-risk than the frequent errors and confusion that results from manually making every component have a ShouldRender method that would itself need to take into account every relevant property and also would need to track the current and old values of each such property in order to determine if a render is necessary. So this technique seems to be a distinct improvement over having such nuanced and perhaps repetitive (though custom) ShouldRender overrides in many components.
Demo
So try it now on this demo page. To the code from above, I’ve added a toggle checkbox at the top to enable and disable the technique. We can open the console an d add an item, you’ll see a console line was written on each ProductTile render. With GetDisplayHash implemented, we’ve reduced the number of ProductTile renders from dozens to one–but don’t forget the child components that are unchanged: we’ve reduced the total number of components that render from several hundred to five. In my experience, this often cuts the “lag” after the user clicks the button from a few seconds to imperceptibly fast. On apps where users do lots of navigation or small manipulations, lag of a few seconds can kill your perceived performance, so this is a big win.
So if you have (or suspect you have) hundreds or thousands of unnecessary renders happening on many changes, give Sz.BlazorRerenderReducers a try to help drastically reduce the re-rendering that is needed. You can read more on the BlazorRerenderReducers project GitHub page and leave an issue with any concerns or questions, or just reply to this post.