In the past month, for one of my clients, I became a default reviewer. I had the chance to check everybody else’s code and advocate for change. After dozens of Pull Requests (PRs) reviewed, these are the lessons I learned.
I’ve noticed that most of the comments fall into two categories. I will call them “babysitting” and “surprising solution.”
1. Babysitting
In these projects, before opening a PR, we have to cover all major code changes with tests, have zero analyzer warnings, and format all C# code. But try to guess what the most common comments are. Things like “please write tests to cover this method,” “address analyzers warnings,” and “run CodeMaid to format this code.”
As a reviewee, before opening a PR, wear the reviewer hat and review your own code. It’s frustrating when the code review process becomes a slow and expensive linting process.
To have a smooth code review, let’s automate some of the things checked during the review process. For example, let’s clean and format our files with a Git hook or Visual Studio extension. And let’s turn all warnings into compilation errors.
Apart from making developers follow conventions, the next most common comments are clarification comments. Things like “why did you do that? Possibly, this is a simpler way.” Often, it’s easy when there’s a clear and better solution. Like when a developer used semaphores to prevent concurrent access to dictionaries. We have concurrent collections for that.
As a reviewee, use the PR description and comments to give enough context to avoid unnecessary discussion. The most frustrating PRs are the ones with only a ticket number in their title. Show the entry point of your changes, tell why you chose a particular design, and signal places where you aren’t sure if there’s a better way.
Voilà! These are some of the lessons I learned after being a reviewer. The next time you open a PR, review your own code first and give enough context to your reviewers.
But, the one thing to improve code reviews is to use short PRs. PRs everyone could review in 10 or 15 minutes without too much discussion. As a reviewer, I wouldn’t mind reviewing multiple short PR’s in a working session than reviewing one single long PR that exhausts all my mental energy.
For better or worse, we all have something to learn from our bosses and co-workers.
These are three lessons I learned from three of my ex-coworkers and ex-bosses about software engineering, designing, and programming.
I didn’t take the time to thank them when I worked with them. This is my thank you note.
1. Inspire Change
From Edgardo, the most senior of all developers, I learned to inspire change. He didn’t talk too much. But when he did, everybody listened.
He always brought new ideas to improve our development process. Instead of doing things himself, he dropped a seed on us. “Hey, what if we do something? Think of a way of achieving something else.”
He was the kind of guy who inspired trust to ask him anything, not only about coding. I tapped his shoulder: “hey, Edgardo. I have a question about life” and he dropped whatever he was doing to listen, answer, and inspire us all.
In emergencies, while everybody panicked, Edgardo was calm, going through log files and running diagnostics.
2. Stand on the shoulders of giants
From Javier, the architect, I learned to stand on the shoulder of giants.
When we ran into issues, he always said “you’re not the first one solving that problem” and “smarter people have already solved that.” He made us look out there first.
Every time I’m tempted to start something from scratch, I start looking at GitHub. Maybe I can stand on somebody else’s shoulders.
Recently, a coworker told me that reading an authorization token from a custom header with ASP.NET Core was impossible. And my first thought was: “we’re not the first ones doing that.” After some Googling, we definitively can do that. Javier was right!
Also, from Javier, I learned to read other people’s source code. He believed that’s the way of learning from others. By looking at his code.
3. Identify your users and their goals
From Pedro, the boss, I learned to keep in mind who our end users are.
More than once, I remember Pedro asking designers to change fonts and increase their size. He said: “you aren’t the one who’s going to use this app. This is for your dad and granddad. This is for oldies.”
Also, from Pedro, I learned to optimize for the most frequent scenario. Once we had to read and validate XML files, Pedro suggested storing the XML documents first and then validating them and continuing with the rest of the processing. Because “90% of the time, those documents are valid.”
Voilà! These are some of the lessons I learned from some of my post coworkers. What have you learned from your coworkers and bosses? I bet they have something to teach you.
Software projects don’t fail because of the tech stack, programming languages, or frameworks.
Sure, choosing the right tech stack is critical for the success of a software project. But software projects fail due to unclear expectations and communication issues. Even unclear expectations are a communication issue too.
This is a failed software project that was killed by unclear expectations and poor communication.
This was a one-month project to integrate a Property Management System (PMS) with a third-party Guest Messaging System. The idea was to sync reservations and guest data to this third-party system, so hotels could send their guests reminders and Welcome messages.
Even though our team delivered the project, we made some mistakes and I learned a lesson or two.
1. Minimize Moving Parts
Before starting the project, we worked with an N-tier architecture. Controllers call Services that use Repositories to talk to a database. There’s nothing wrong with that.
But, the new guideline was to “start doing DDD.” That’s not a bad thing per se. The thing was: almost nobody in the team was familiar with DDD.
I had worked with some of the DDD artifacts before. But, we didn’t know what the upper management wanted with “start doing DDD.”
With this decision, a one-month project ended up being behind schedule.
After reading posts and sneaking into GitHub template projects, two or three weeks later, we agreed on the project structure, aggregate and entity names, and an overall approach. We were already late.
For such a small project with a tight schedule, there was no room for experimentation.
“The best tool for a job is the tool you already know.” At that time, the best tool for my team was N-tier architecture.
For my future projects, I will minimize the moving parts.
2. Define a Clear Path
We agreed on reading the “guests” and “reservations” tables inside a background processor to call the third-party APIs. And we stared working on it.
But another team member was analyzing how to implement an Event-Driven solution with a message queue.
Our team member didn’t realize that his solution required “touching” some parts of the Reservation lifecycle, with all the Development and Testing effort implied.
Although his idea could be the right solution, in theory, we already chose the low-risk solution. He wasted some time we could have used on something else.
For my future projects, I will define a clear path and have everybody on-boarded.
3. Don’t Get Distracted. Cross the Finish Line
With a defined solution and everybody working on it, the team lead decided to put the project back on track with more meetings and ceremonies.
We started to estimate with poker planning.
During some of the planning sessions, we joked about putting “in one month” as the completion date for all tickets and stopped doing those meetings.
Why should everyone in the team vote for an estimation on a task somebody else was already working on? We all knew what we needed and what everybody else was doing.
It was time to focus on the goal and not get distracted by unproductive ceremonies or meetings. I don’t mean stop writing unit tests or doing code reviews. Those were the minimum safety procedures for the team.
For my future projects, I will focus on crossing the finish line.
Voilà! These are the postmortem lessons I learned from this project. Although tech choice plays a role in the success of a project, I found that “people and interactions” are way more important than choosing the right libraries and frameworks.
I bet we can find fail projects using the most state-of-the-art technology or programming languages.
Like marriages, software projects fail because of unclear expectations and poor communication. This was one of them.
LINQ is the perfect way to work with collections. It’s declarative and immutable. But, from time to time, I take some extension methods with me to the projects I work with. These are some extension methods to work with collections.
1. Check if a collection is null or empty
These are three methods to check if a collection is null or empty. They’re wrappers around the LINQ Any method.
Notice we used the [NotNullWhen] attribute to let the compiler know if the source collection is null. This way, when we turn on the nullable references feature, the compiler can generate more accurate warnings. If we don’t add this attribute, we get some false positives. Like this one,
IEnumerable<Movie>?movies=null;if(movies.IsNotNullOrEmpty()){movies.First();// ^^^^^// CS8604: Possible null reference argument for parameter 'source'//// But we don't want this warning here...}
2. EmptyIfNull
In the same spirit of DefaultIfEmpty, let’s create a method to return an empty collection if the source collection is null. This way, we can “go with the flow” by nesting this new method with other LINQ methods.
.NET 9 introduced the Index method that works like our Enumerated(). We don’t need to roll our own method anymore in recent versions of .NET.
Voilà! These are some of my favorite extension methods to work with collections. Some of them are workarounds to avoid the NullReferenceException when working with collections. What extension methods do you use often?
Want to write more expressive code for collections? Join my course, Getting Started with LINQ on Udemy! You'll learn from what LINQ is, to refactoring away from conditionals, and to new methods and overloads from recent .NET versions. Everything you need to know to start working productively with LINQ — in less than two hours.
That post reminded me I have my own script to create the folder structure for ASP.NET Core API projects. Currently, I work with a client where I have to engage in short (3-5 month) projects. Every now and then, I create new projects. And these are the types of tasks we don’t do often and always forget how to do it. Why not scripting it!
How to create project structure with dotnet cli
This is the script I use to create the source and test projects with the references between them for an ASP.NET Core API project:
# Change to suit your own needsPrefix=Acme.CoolProject
# ^^^^^# Change it to use your project name prefix# 1. Create solution
dotnet new sln --name$Prefix.Api
# 2. Create src projects# Create class librariesfor name in'Data''Domain''Infrastructure''Messages'do# Optionally:# dotnet new classlib -o $Prefix.$name/src -n $name
dotnet new classlib -o src/$Prefix.$name
dotnet sln add src/$Prefix.$name/$Prefix.$name.csproj --in-rootdone# Create Console projects
dotnet new console -o src/$Prefix.Data.Migrator
dotnet sln add src/$Prefix.Data.Migrator/$Prefix.Data.Migrator.csproj --in-root# Create Api projects
dotnet new webapi -o src/$Prefix.Api
dotnet sln add src/$Prefix.Api/$Prefix.Api.csproj --in-root# Api depends on Data, Infrastructure, and Messagesfor dependsOn in'Data''Infrastructure''Messages'do
dotnet add src/$Prefix.Api/$Prefix.Api.csproj reference src/$Prefix.$dependsOn/$Prefix.$dependsOn.csproj
done# Data depends on Domain and Infrastructurefor dependsOn in'Domain''Infrastructure'do
dotnet add src/$Prefix.Data/$Prefix.Data.csproj reference src/$Prefix.$dependsOn/$Prefix.$dependsOn.csproj
done# Data.Migrator depends on Data
dotnet add src/$Prefix.Data.Migrator/$Prefix.Data.Migrator.csproj reference src/$Prefix.Data/$Prefix.Data.csproj
# Infrastructure depends on Domain and Messagesfor dependsOn in'Domain''Messages'do
dotnet add src/$Prefix.Infrastructure/$Prefix.Infrastructure.csproj reference src/$Prefix.$dependsOn/$Prefix.$dependsOn.csproj
done# 3. Create test projectsfor name in'Api''Data''Domain''Infrastructure'do
dotnet new mstest -o tests/$Prefix.$name.Tests
dotnet sln add tests/$Prefix.$name.Tests/$Prefix.$name.Tests.csproj -s Tests
dotnet add tests/$Prefix.$name.Tests/$Prefix.$name.Tests.csproj reference src/$Prefix.$name/$Prefix.$name.csproj
done# 4. Copy template files# .gitignore, .editorconfig, .dockerignore# Copy dotfilesfor file in$(ls-I"*.cs" ~/Documents/_Projects/_FolderStructure/Templates/)do
cp ~/Documents/_Projects/_FolderStructure/Templates/$file.done# 5. Cleanup
find .-name"WeatherForecastController.cs"-type f -delete
find .-name"WeatherForecast.cs"-type f -delete
find .-name"Class1.cs"-type f -delete
find .-name"UnitTest1.cs"-type f -delete
When I need to create a new project, I only change the Prefix at the top of the file.
Notice this script copies some template files (.gitignore, .editorconfig, .dockerignore) from a shared location.
This script creates a project structure like this:
Even we can create folders and csproj files with shorter names by passing the -n flag and a name in the dotnet new command.
How to update the csproj files with Powershell
Then to update csproj files, like making nullable warning errors or adding a root namespace, instead of doing it by hand, I tweak this PowerShell file:
$projects=Get-ChildItem-Filter*.csproj-Recurse-Exclude*Tests*.csproj$projects|foreach{try{$path=$_.FullName;$proj=[xml](Get-Content$path);$propertyGroup=$proj.Project.PropertyGroup|where{-not[String]::IsNullOrWhiteSpace($_.TargetFramework)};$shouldSave=$falseif($propertyGroup.RootNamespace-eq$null){$RootNamespace=$propertyGroup.ParentNode.ParentNode.CreateElement('RootNamespace');$propertyGroup.AppendChild($RootNamespace)|out-null;$propertyGroup.RootNamespace="Acme.CoolProject";$shouldSave=$true}if($shouldSave){$proj.Save($path);Write-Host"RootNamespace added to $path"}}catch{Write-Host$path([System.Environment]::NewLine)$_}}
Voilà! That’s how I create the folder and project structure for one of my clients. This is another script that saved my day! Kudos to Humble Toolsmith for inspiring me to write this one.