A new bit every Tuesday of a tool || feature || blog that has helped me, or which I encountered recently and impressed me.
20. Use jq to query and manipulate JSON data
jq is a lightweight command-line tool to quickly process your JSON data.
I mainly use it to search (grep
) and select specific fields of a JSON file, but it can also be used to transform (sed
) and process (awk
) the data.
Of course, the output is also nicely formatted.
If this peaks your interest head over to the download page.
While these examples are simple and are using a local data source, you can feed data to jq
using the pipe (|
) operator.
Make sure to check out the tutorial and the playground to also learn how to fully take advantage of jq
by transforming and filtering JSON data.
19. NuGet Central Package Management
When you've worked in a mono-repository that contains multiple C# projects, you've probably felt the pain of maintaining NuGet dependencies. Updating packages to a newer version, keeping versions in sync across different projects, removing outdated dependencies, ...
With Central Package Management (CPM) this daunting task becomes easier to manage.
By creating a file Directory.Packages.props
within the root of the project, all NuGet package references can be defined with their corresponding version.
Next, the version of the package references that are defined within the project (.csproj
) files can be removed.
The result is a file that contains the single source of truth when it comes to your package versions.
You can still override versions for one-off cases, or subdivide this file into multiple files. For more info about CPM, see the documentation.
18. Pretty TypeScript types
I learned this trick from Matt Pocock, and I've seen the power by seeing Marko Stanimirović using it in the NgRx codebase. Because I think that we'll see more usage of this, I wanted to make a bit out of it.
You've probably already encountered TypeScript types where you scratch your head and think "What does this even mean?". From my own experience, this can happen when you interact with complex types from 3rd party packages.
Using the following Prettify
utility type, which you need to create yourself, some of the types will become more readable.
Prettify
takes a generic parameter T
and maps over its keys using a mapped type to essentially create a new type that has the same properties as T
.
🤖 Here's a breakdown of what's happening:
type Prettify<T>
: This is a declaration of a new type calledPrettify
that takes a generic parameterT
.T
is a placeholder for any type that you want to use withPrettify
.[K in keyof T]
: This is a mapped type. It iterates over all keys ofT
(represented byK
).keyof T
is a type that represents all keys ofT
.T[K]
: This is an indexed access type. It represents the type of propertyK
inT
.& {}
: This is an intersection type. It combines multiple types into one. In this case, it's intersecting the mapped type with an empty object type. However, since an intersection with an empty object doesn't change the type, this part doesn't have any effect on thePrettify
type.
The unwrapping of object properties turns resolved types that were previously difficult to comprehend, into types that become easier to read and understand.
Let's take a look at an example to make this clear:
With a small modification, it can also unwrap child properties.
For more info see Matt's blog The Prettify
Helper.
17. Edit and send HTTP requests directly from the Browser DevTools
Did you know you can use the browser DevTools (in Microsoft Edge and Firefox) to edit and send network requests?
This little trick can save you some time. Instead of manually reproducing a request, or copying the request via the network tab, you can right-click a network request to edit the URL, headers, body, etc, and resend the edited request! 🚀
16. How are the TypeScript Partial and Required types implemented?
I find it important to know how the most-used TypeScript utility types are implemented.
Here, we see the implementation of the Partial
(all properties of a type are optional) and Required
(all properties of a type are required) types.
Knowing this is essential because it allows you to customize or create a new behavior for your use cases.
keyof Person
generates a union of the keys of Person, sokeyof Person
results in"id" | "name"
.[key in keyof Person]
iterates over each key in that union.Person[key]
is the type of the property in Person associated with the key, so "id" is a number, and "name" is a string.- The
?
modifier makes the property optional (used for thePartial
type), while the-?
modifier removes the optional modifier from the property, making it required (used for theRequired
type).
15. Bulk updates in Entity Framework
Use bulk updates to update entities with a single command (without the need of the change tracker).
The entities will not be loaded into memory, which results in more efficient updates and deletes.
For more info, see my blog post "New in Entity Framework 7: Bulk Operations with ExecuteDelete and ExecuteUpdate", or the documentation.
14. How to quickly open a GitHub repository within an IDE
As developers, we read more code than we write.
We don't only read our own code that we wrote ourselves, or the code from our project, but we also come in contact with code from 3rd parties.
Personally, I like to browse through code to learn or to investigate a behavior or problem that I encountered. While doing so it's useful to be in a comfort zone where you already know how to navigate within the code base to find the thing that you're looking for.
The GitHub interface has improved in the last couple of years, but it isn't the same as using an IDE. An IDE has the advantage that it's smarter. It can, for example, show all the references, go to the implementation with a simple click, and more.
To have that familiar IDE experience without having to clone or download the repository locally, which can be time-consuming especially if you don't have all the dependencies installed, there are a few options.
Here are the ones that I like to use, to quickly open a codebase within a web-editor to have that familiar IDE experience. As a side note: all of them are powered by Visual Studio Code.
- use https://github.dev/USERNAME/REPOSITORY_NAME (or simply press the
.
key in a repository, or in a Pull Request) to open up a lightweight Visual Studio Code in the browser. This is useful to navigate and edit code, but you won't be able to execute scripts/commands. - use https://stackblitz.com/github/USERNAME/REPOSITORY_NAME to open the codebase within StackBlitz. Useful to play with some code or to quickly test something out. Focused on Node.js environments. StackBlitz also has a specific Pull Request explorer via pr.new.
- use https://codesandbox.io/s/github/USERNAME/REPOSITORY_NAME (or append
box
to the github url => https://githubbox.com/USERNAME/REPOSITORY_NAME) to open the codebase within CodeSandbox. Similar to StackBlitz but supports more languages. Codesandbox also has a GitHub Action to quickly review a Pull Request. - use GitHub Codespaces to create a dev environment, it's also possible to open this environment locally in your favorite IDE. Codespaces can do everything that you can do locally.
13. New in C# 12: Collection expressions
Creating collections becomes simpler in C# 12 using collection expressions.
We can also make use of the spread operator ..
to copy the values from a collection.
More info can be found in the documentation.
12. Polyglot Notebooks
As developers, we sometimes need an isolated environment or a testing sandbox. For example, to reproduce a problem, or to share a snippet with a team member.
This is already possible using online REPLs e.g. TypeScript playground and .NET fiddle, or we can resort to web containers for example GitHub Codespaces or Stackblitz.
The problem that I encounter with these tools is that I often forget or lose previously created proof of concepts, or that it's hard to share them with my team.
That's not the case with Polyglot Notebooks.
Using notebooks has the following benefits:
- testing, creating an isolated sandbox to test something out
- easy to share, can be committed to git
- enhances collaboration, can be used by all team members
- integration, can be integrated within your environment, e.g. connect to a database
Some use cases where notebooks can be useful:
- can serve as an interactive documentation tool
- can include runnable snippets to automate tasks
- teaching/onboarding interactivity makes it more fun
- an offline REPL alternative
Get the Polyglot Notebooks Visual Studio Code extension in the marketplace.
11. Angular Control Flow
The past week I cursed Angular a little bit because I just wanted to have a simple if-else
condition within my HTML template.
But, as you probably already know, we have to jump through a couple of hoops to get this working.
The result is a not-so-easy-to-read template that's prone to errors.
Angular 17 changes that by providing a new (opt-in) way on how we write our templates.
Instead of using the structural directives NgIf
, NgFor
, and NgSwitch
, you'll be able to use the new @
syntax.
This change drastically improves the Developer's Experience;
- it's easier to read
- you don't have to import the directives
- it's less verbose since you don't have to create wrapper components e.g.
ng-container
.
As an example, let's take a look at my if-else
condition and compare both solutions.
For completeness let's also take a look at the new syntaxes to iterate over a collection (*ngFor
) and how to create a switch expression (*ngSwitch
).
To already try out the new Control Flow feature before it's officialy released, update the angular compiler options within the TypeScript configuration file.
Add the _enabledBlockTypes
property, and add the blocks you want to use within your application.
For more information see the Angular blog post Meet Angular’s New Control Flow.
10. Cypress to Playwright converters
Migrating from one test library to another can be a daunting task, but with the right tools, we can make it a smoother transition.
To help with this task I've encountered two similar tools that rewrite your Cypress code to its equivalent in Playwright.
The first of these tools is https://demo.playwright.dev/cy2pw/, and the second one https://ray.run/tools/cypress-to-playwright (you should also check out the blog on this website for more goodies!).
On the websites, you can simply paste your Cypress code, and you'll receive the test cases rewritten in Playwright ✨. Just like that.
These tools can be used for a couple of reasons:
- trying out Playwright without wasting time
- quickly see what the Playwright equivalent is of a code block
- rewrite a specific piece of code (think of a test that you aren't able to write properly, or don't know where to start), or even rewrite entire files
- motivate your team to try/use Playwright
- hit the ground running from day one
In short, the mentioned tools help you with the transition from Cypress to Playwright.
Side note: if you have a GitHub Copilot license you can also prompt your Copilot to migrate a test or file for you. In a small demo application, this worked out fine for me.
Here's a small example to give you an idea of a migration:
For more Playwright content, check out my blog posts about 🎭 Playwright.
9. Switch Exhaustiveness
How many times have you added a new option (enum, union, expression, ...) without updating all code paths? Did this result in unforeseen issues?
One of the reasons why I prefer switch statements over if-else conditions is that it supports exhaustiveness checks. This is incredibly useful because it ensures that no scenario is left unhandled, reducing the risk of unexpected bugs or runtime errors. It also shortens the feedback loop because your IDE immediately flags incomplete switch statements.
Personally, I also find a switch statement more readable in contrast to an if-else condition.
The Switch Exhaustiveness check is not enabled by default, so you'll have to enable it.
In C# applications the default behavior is that a non-exhaustive switch is a warning.
To turn this into an error, configure the TreatWarningsAsErrors
compiler option in your project(s).
If you just want to treat specific warnings as an error, use the WarningAsErrors
compiler option instead, which accepts warning numbers, e.g. <WarningsAsErrors>CS8509</WarningsAsErrors>
.
Now, when a switch is not exhaustive, it results in a compilation error.
For TypeScript applications, enable the switch-exhaustiveness-check
ESLint rule.
It's best to configure this rule as an error so it catches your attention.
Enabling the ESLint rule doesn't prevent the application from building. As an alternative, manually add a default
case that turns into a compiler error when it detects a non-exhaustive switch. For this, you can use the never
type. Now this will also result in a compiler error.
8. The Evolution of C# Constructors
C# 12 introduces a new syntax to declare class constructors with Primary Constructors. That's why I thought that it's a good time to look back at the evolution of C# constructors. Starting from the most basic constructor that most of us are familiar with, to the newer variations that were added in latest the C# versions.
7. Simplified Node.js Version Management with fnm
Are you working on multiple projects that each require a different Node.js version, and are you tired of juggling between them? Then I've got the right tool for you, fnm (Fast Node Manager).
fnm is a Node.js version manager that allows you to install and switch between different Node.js versions on the fly.
What I especially like about fnm, compared to similar version managers, is that it provides a seamless experience by automatically switching to the correct version when it detects a .node-version
(or .nvm
) file in your project's directory.
For example, let's say you have two projects, awesome-project
and great-project
, and each of them requires a different Node.js version:
In the preceding example, fnm automatically switched to the correct Node.js version for each project.
Because awesome-project
requires a Node.js version that isn't installed yet, we received a prompt asking if we want to install it.
To set the Node.js version for a project, create a .node-version
file in your project's directory and add the version number to it:
Other helpful fnm commands are install
, use
, default
, and current
:
Bonus: GitHub Codespaces is great to use for switching between multiple project configurations that require bigger changes, when you just need a clean environment to work in, or to quickly test something out. You can easily create a new Codespace while working (or using an existing Codespace for reviewing) for changes that have a big impact on your environment, e.g. an upgrade of an SDK, such as .NET, Node.js, ...
6. Git Log Heatmap
Use git log
to create a heatmap of the files that have been changed the most frequently within a repository.
This is useful to identify the files that most likely need a closer look.
For example, the file contains too much logic and should be split up into multiple files, it contains hot code paths that change frequently, or maybe it's a file with a history of many fixes.
The following git log
command returns a list of files (excluding json
and lock
files) that have been changed within the last 6 months, sorted by the number of changes.
5. Raw SQL Queries for Unmapped Types in Entity Framework 8
Entity Framework 8 has a new feature that allows you to execute raw SQL queries against the database and return results as unmapped types.
To use this feature, use the new SqlQuery
method on the Database
property of your DbContext
instance.
This feature is useful when you want your query to return a specific type for a specific purpose. For example, in many cases you don't need/want the overhead of returning your full-blown entity for search queries. Instead, you want a optimized entity (e.g. a DTO) that only contains the data you need for that specific purpose. Usually this results in a faster query and less data transferred over the wire.
See my blog post You can now return unmapped types from raw SQL select statements with Entity Framework 8 for more info about this new feature.
4. Big List of Naughty Strings
Does your application correctly handle all kinds of input correctly? Do you need some inspiration to test your application with some edge/special cases?
Look no further, the Big List of Naughty Strings is here to help you! There are different sets of inputs, from the boring "Reserved Strings" and "Special Characters" cases to the more interesting variants like "(Server and Client) Injections", "Unicode fonts", "Known CVEs and Vulnerabilities", "Special Filenames", and more.
Take a look at the repository and start copy-pasting some of the strings in your application, or use one of the libraries (e.g. NaughtyStrings for .NET, or blns for Node.js ) to automate this process.
Fun fact: I couldn't generate the banner for this bit (in various tools) because it contained a few naughty strings that broke the export.
For the entire set see big-list-of-naughty-strings/blns.txt, here's a small sample:
3. Http Files
.http
files are a simple way to quickly invoke your API endpoints because you don't have to leave your IDE to use a separate tool.
I use it regularly when I'm building a new feature.
You can commit this file to your repository so that your teammates can also make use of it!
HTTP files are supported by most IDEs (for Visual Studio Code, you'll have to install the REST Client extensions), the only caveat is that the variables syntax is (currently) different between Visual Studio products and JetBrains products.
Here's an example of a .http
file for a Todo API:
For more information, check out the documentation for the various IDEs:
2. Angular Input enhancements
The Angular 16 release adds useful enhancements to the @Input
decorator.
We can make an input required, we can transform the input value, and we can bind inputs route data (parameters, query parameters, and the route data property).
We can transform the input value by providing a transform
method, which is invoked with the input value.
Angular already provides the numberAttribute
and booleanAttribute
transform methods, but you can also implement your own.
To be able to bind the route data to component inputs, you need to configure the Angular router by including the new withComponentInputBinding()
.
1. Tools to keep your NPM dependencies up-to-date
You can use the default npm commands npm outdated (check the registry to see if any packages are currently outdated) and npm update (update all the packages listed to the latest version, respecting the semver constraints of both your package and its dependencies).
But, to get a more detailed and prettier overview of the dependencies, you resort to the CLI tool 🥦 taze, which also works for monorepos.
The Visual Studio Code users among us can install the NPM extension to get a nice sidebar with the package information (and quick actions to update them). Just take a little glance at the sidebar to see if there are any updates available.
Follow me
You can find me on Twitter, LinkedIn, and GitHub.
Support me
I appreciate it if you would support me if have you enjoyed this post and found it useful, thank you in advance.