Note: Slides do not tell the whole story of the talk, so take the stand alone slides with a grain of salt. Things may be taken out of context.
Author: Scott Sauber
Iowa .NET User Group – 10 Opinions For More Maintainable .NET Apps
RocketMortgage TechCon 2021 – HTTP Security Headers You Need On Your Web App
KCDC 2021 – 10 Opinions For More Maintainable .NET Apps

Using Husky Git Hooks and Lint-Staged With Nested Folders
What is husky?
Husky is a JavaScript package that allows you to run some code during various parts of your git workflow. Husky leverages git hooks to allow you to hook into various git events such as pre-commit and pre-push.
What is lint-staged?
Lint-staged allows you to run code against your staged git files to automate the tedious part of your workflows, such as formatting with Prettier and/or linting with ESLint.
Using husky with lint-staged
You can use husky to trigger lint-staged during the pre-commit hook, so that your coding standards are enforced right as you commit. The major benefit of this is it enforces your coding standards without someone needing to configure and install certain extensions in their IDE to enforce them on save or remembering to do anything. Your code gets fixed before it ever leaves your machine, so you don’t have to wait for your CI to inform you that you forgot to run the formatter or linter.
The Problem: husky expects your package.json at the root
The problem is, husky expects your package.json
to be at the root of your project. That’s a fine assumption to make a lot of times, but sometimes we might be in more of a monorepo situation, or where a single repo contains both the Server and Client in separate folders.
The Solution
I’m going to use the example from the default React and ASP.NET Core template when you run dotnet new react
. The default folder structure has the React code nested under a /ClientApp folder with the corresponding package.json
. The folder structure looks like this:

Let’s dive into the steps. You can view the completed repo here.
cd
into the directory with yourpackage.json
- In my case:
./ClientApp
- In my case:
- Install husky and lint-staged:
npm i husky lint-staged -D
- Add a “prepare” npm script to your
package.json
with the following contents (note: the initialcd
goes to the repo root and thehusky install
goes from the repo root down to your directory with thepackage.json
):
{
"scripts": {
// other scripts omitted
"prepare": "cd ../ && husky install ./ClientApp/.husky"
}
}
- Run
npm install
- FYI – a
.husky
folder will appear in the same path as yourpackage.json
, in my case under./ClientApp
- Create a
pre-commit
file with no file extension under the.husky
folder with the following contents (note: the./ClientApp
is the path to yourpackage.json
relative to the root of your repository):
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
cd ./ClientApp && npx lint-staged
- Make sure the
pre-commit
file is executable viachmod
:
chmod +x ./.husky/pre-commit
- Add a
.lintstagedrc
file under your ./ClientApp folder (or wherever yourpackage.json
lives) with the following contents:
{
"*.{js,ts,tsx,scss,css,md}": ["eslint", "prettier --write"]
}
As you can probably guess, the above says look for any changes to any of the file extensions defined on the left, and run the commands in the array on the right if you find any changes to those files.
And that’s it! Now if you try to make a commit, you will see that eslint and prettier will run and fix themselves as you would expect. See an example of it in action below:

Minnesota Developer Conference – HTTP Security Headers You Need To Have On Your Web Apps
Note: Slides do not tell the whole story of the talk, so take the stand alone slides with a grain of salt. Things may be taken out of context.
Twin Cities .NET User Group – The Background on Background Tasks in .NET Core
Note: Slides do not tell the whole story of the talk, so take the stand alone slides with a grain of salt. Things may be taken out of context.
Writing Better Assertions: Stop Using Assert.True for Expressions
The Problem: Using Assert.True For Expressions
One thing I keep running into in codebases lately (especially those where the team was new to writing automated tests), is assertions using expressions with Assert.True
(or equivalent depending on your framework or language). The problem here is consider the following code:
At quick glance, you can probably tell that this assertion will fail, because 1+1 is not equal to 3. Let’s take a look at the test output:

Cool, we found out it failed… now what? How do we debug what the problem is? There’s very little useful information here. All we know is that result
is not equal to 3, but we have no clue what the value of result
even is. We would have to either set a break point to inspect the value or add some sort of logging.
The Solution: Use Assert.Equal
The solution to this is using Assert.Equal
(or equivalent) that will automatically log out the actual
value for you in the event the test fails. So the test output for this code:
Will yield this result:

Awesome, now we know that result
is 2 without having to do any extra work!
Next Blog Post: Use Something Other Than Assert.Equal
There are still improvements we can make to Assert.Equal
which is to say, using something else entirely, but that’ll be for the next blog post. 😄
NDC London 2021 – HTTP Security Headers You Need To Have On Your Web Apps
Note: Slides do not tell the whole story of the talk, so take the stand alone slides with a grain of salt. Things may be taken out of context.
Using dotnet watch run with JetBrains Rider
What is dotnet watch?
dotnet watch
is a way to immediately trigger a dotnet
command when a file changes. The most common uses for this are using it to automatically re-run your application (using dotnet watch run
) or automatically re-run your tests (using dotnet watch test
) after a file change. This obviously speeds up your workflow so you don’t have to restart your server or your tests manually.
A new feature of
dotnet watch run
in .NET 5+ is that it will automatically launch a browser and auto-refresh the browser after it detects a change and finishes compiling (if your application has a UI).
What is JetBrains Rider?
JetBrains Rider is a cross-platform .NET IDE from the people at JetBrains (who make many developer productivity tools such as Resharper for Visual Studio, TeamCity, IntelliJ, and more). It is my go-to IDE now, due to all of its productivity enhancements over the base install of Visual Studio. I’ve been a user of Resharper for years, but Resharper and Visual Studio never seemed to play very nice together and ended up slowing down Visual Studio significantly. I put up with it due to all the extra functionality Resharper provided.
With Rider, I get all the benefits of Resharper and it’s fast. I can use Rider on Windows or macOS (which I bounce between for personal and professional work), and a lot of features are included for $150 that I would have to spend thousands to get in Visual Studio Enterprise (such as Continuous Testing).
How do I integrate dotnet watch and Rider?
Alright, now for the part you came here for. Obviously, you could run dotnet watch run
directly using the terminal, but it’d be nice to have this as a launch configuration option right in Rider that is only a CTRL + F5 away. Here’s how to do that:
- Open your solution in Rider
- Select your Configuration and hit Edit Configurations

- Click the Plus in the top left to Add New Configuration

- Choose Native Executable (Note: you CAN search)

- Give it a Name, I called mine dotnet watch
- For “Exe path” choose C:\Program Files\dotnet\dotnet.exe if you’re on Windows or /usr/local/share/dotnet/dotnet if you’re on macOS
- For “Program arguments” type watch run
- For “Working directory” choose the directory that your application’s csproj resides in.
- For “Environment variables” You could add ASPNETCORE_ENVIRONMENT=Development if it’s an ASP.NET Core app, but the environment variables defined in your
launchSettings.json
will take precedence (under the node that contains"command name": "project"
). - The final output should look like this:

- Hit OK
- Now start the app with your new configuration selected

- That’s it! You’ll notice that your Run tab of Rider will now show your
watch
command running

That’s it!
I hope this helps someone else. An example of this in action is below:

Note: auto-attaching the debugger does not work with this option in Rider. The issue to track that is here if you want to give that a thumbs up to vote for JetBrains to work on that feature in an upcoming release.